]> git.cworth.org Git - apitrace/blob - retrace/retrace_main.cpp
ca03745c64d241ba836f718ca7aef041c2749cbc
[apitrace] / retrace / retrace_main.cpp
1 /**************************************************************************
2  *
3  * Copyright 2011 Jose Fonseca
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26
27 #include <string.h>
28 #include <limits.h> // for CHAR_MAX
29 #include <iostream>
30 #include <getopt.h>
31
32 #include "os_binary.hpp"
33 #include "os_time.hpp"
34 #include "os_thread.hpp"
35 #include "image.hpp"
36 #include "trace_callset.hpp"
37 #include "trace_dump.hpp"
38 #include "trace_option.hpp"
39 #include "retrace.hpp"
40
41
42 static bool waitOnFinish = false;
43
44 static const char *comparePrefix = NULL;
45 static const char *snapshotPrefix = NULL;
46 static trace::CallSet snapshotFrequency;
47 static trace::CallSet compareFrequency;
48
49 static unsigned dumpStateCallNo = ~0;
50
51 retrace::Retracer retracer;
52
53
54 namespace retrace {
55
56
57 trace::Parser parser;
58 trace::Profiler profiler;
59
60
61 int verbosity = 0;
62 bool debug = true;
63 bool dumpingState = false;
64
65 Driver driver = DRIVER_DEFAULT;
66 const char *driverModule = NULL;
67
68 bool doubleBuffer = true;
69 bool coreProfile = false;
70
71 bool profiling = false;
72 bool profilingGpuTimes = false;
73 bool profilingCpuTimes = false;
74 bool profilingPixelsDrawn = false;
75 bool useCallNos = true;
76
77 unsigned frameNo = 0;
78 unsigned callNo = 0;
79
80
81 void
82 frameComplete(trace::Call &call) {
83     ++frameNo;
84 }
85
86
87 static Dumper defaultDumper;
88
89 Dumper *dumper = &defaultDumper;
90
91
92 /**
93  * Take/compare snapshots.
94  */
95 static void
96 takeSnapshot(unsigned call_no) {
97     static unsigned snapshot_no = 0;
98
99     assert(snapshotPrefix || comparePrefix);
100
101     image::Image *ref = NULL;
102
103     if (comparePrefix) {
104         os::String filename = os::String::format("%s%010u.png", comparePrefix, call_no);
105         ref = image::readPNG(filename);
106         if (!ref) {
107             return;
108         }
109         if (retrace::verbosity >= 0) {
110             std::cout << "Read " << filename << "\n";
111         }
112     }
113
114     image::Image *src = dumper->getSnapshot();
115     if (!src) {
116         std::cout << "Failed to get snapshot\n";
117         return;
118     }
119
120     if (snapshotPrefix) {
121         if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
122             char comment[21];
123             snprintf(comment, sizeof comment, "%u",
124                      useCallNos ? call_no : snapshot_no);
125             src->writePNM(std::cout, comment);
126         } else {
127             os::String filename = os::String::format("%s%010u.png",
128                                                      snapshotPrefix,
129                                                      useCallNos ? call_no : snapshot_no);
130             if (src->writePNG(filename) && retrace::verbosity >= 0) {
131                 std::cout << "Wrote " << filename << "\n";
132             }
133         }
134     }
135
136     if (ref) {
137         std::cout << "Snapshot " << call_no << " average precision of " << src->compare(*ref) << " bits\n";
138         delete ref;
139     }
140
141     delete src;
142
143     snapshot_no++;
144
145     return;
146 }
147
148
149 /**
150  * Retrace one call.
151  *
152  * Take snapshots before/after retracing (as appropriate) and dispatch it to
153  * the respective handler.
154  */
155 static void
156 retraceCall(trace::Call *call) {
157     bool swapRenderTarget = call->flags &
158         trace::CALL_FLAG_SWAP_RENDERTARGET;
159     bool doSnapshot = snapshotFrequency.contains(*call) ||
160         compareFrequency.contains(*call);
161
162     // For calls which cause rendertargets to be swaped, we take the
163     // snapshot _before_ swapping the rendertargets.
164     if (doSnapshot && swapRenderTarget) {
165         if (call->flags & trace::CALL_FLAG_END_FRAME) {
166             // For swapbuffers/presents we still use this
167             // call number, spite not have been executed yet.
168             takeSnapshot(call->no);
169         } else {
170             // Whereas for ordinate fbo/rendertarget changes we
171             // use the previous call's number.
172             takeSnapshot(call->no - 1);
173         }
174     }
175
176     callNo = call->no;
177     retracer.retrace(*call);
178
179     if (doSnapshot && !swapRenderTarget)
180         takeSnapshot(call->no);
181
182     if (call->no >= dumpStateCallNo &&
183         dumper->dumpState(std::cout)) {
184         exit(0);
185     }
186 }
187
188
189 class RelayRunner;
190
191
192 /**
193  * Implement multi-threading by mimicking a relay race.
194  */
195 class RelayRace
196 {
197 private:
198     /**
199      * Runners indexed by the leg they run (i.e, the thread_ids from the
200      * trace).
201      */
202     std::vector<RelayRunner*> runners;
203
204 public:
205     RelayRace();
206
207     ~RelayRace();
208
209     RelayRunner *
210     getRunner(unsigned leg);
211
212     inline RelayRunner *
213     getForeRunner() {
214         return getRunner(0);
215     }
216
217     void
218     run(void);
219
220     void
221     passBaton(trace::Call *call);
222
223     void
224     finishLine();
225
226     void
227     stopRunners();
228 };
229
230
231 /**
232  * Each runner is a thread.
233  *
234  * The fore runner doesn't have its own thread, but instead uses the thread
235  * where the race started.
236  */
237 class RelayRunner
238 {
239 private:
240     friend class RelayRace;
241
242     RelayRace *race;
243
244     unsigned leg;
245     
246     os::mutex mutex;
247     os::condition_variable wake_cond;
248
249     /**
250      * There are protected by the mutex.
251      */
252     bool finished;
253     trace::Call *baton;
254
255     os::thread thread;
256
257     static void *
258     runnerThread(RelayRunner *_this);
259
260 public:
261     RelayRunner(RelayRace *race, unsigned _leg) :
262         race(race),
263         leg(_leg),
264         finished(false),
265         baton(0)
266     {
267         /* The fore runner does not need a new thread */
268         if (leg) {
269             thread = os::thread(runnerThread, this);
270         }
271     }
272
273     /**
274      * Thread main loop.
275      */
276     void
277     runRace(void) {
278         os::unique_lock<os::mutex> lock(mutex);
279
280         while (1) {
281             while (!finished && !baton) {
282                 wake_cond.wait(lock);
283             }
284
285             if (finished) {
286                 break;
287             }
288
289             assert(baton);
290             trace::Call *call = baton;
291             baton = 0;
292
293             runLeg(call);
294         }
295
296         if (0) std::cerr << "leg " << leg << " actually finishing\n";
297
298         if (leg == 0) {
299             race->stopRunners();
300         }
301     }
302
303     /**
304      * Interpret successive calls.
305      */
306     void
307     runLeg(trace::Call *call) {
308         /* Consume successive calls for this thread. */
309         do {
310             assert(call);
311             assert(call->thread_id == leg);
312             retraceCall(call);
313             delete call;
314             call = parser.parse_call();
315         } while (call && call->thread_id == leg);
316
317         if (call) {
318             /* Pass the baton */
319             assert(call->thread_id != leg);
320             flushRendering();
321             race->passBaton(call);
322         } else {
323             /* Reached the finish line */
324             if (0) std::cerr << "finished on leg " << leg << "\n";
325             if (leg) {
326                 /* Notify the fore runner */
327                 race->finishLine();
328             } else {
329                 /* We are the fore runner */
330                 finished = true;
331             }
332         }
333     }
334
335     /**
336      * Called by other threads when relinquishing the baton.
337      */
338     void
339     receiveBaton(trace::Call *call) {
340         assert (call->thread_id == leg);
341
342         mutex.lock();
343         baton = call;
344         mutex.unlock();
345
346         wake_cond.signal();
347     }
348
349     /**
350      * Called by the fore runner when the race is over.
351      */
352     void
353     finishRace() {
354         if (0) std::cerr << "notify finish to leg " << leg << "\n";
355
356         mutex.lock();
357         finished = true;
358         mutex.unlock();
359
360         wake_cond.signal();
361     }
362 };
363
364
365 void *
366 RelayRunner::runnerThread(RelayRunner *_this) {
367     _this->runRace();
368     return 0;
369 }
370
371
372 RelayRace::RelayRace() {
373     runners.push_back(new RelayRunner(this, 0));
374 }
375
376
377 RelayRace::~RelayRace() {
378     assert(runners.size() >= 1);
379     std::vector<RelayRunner*>::const_iterator it;
380     for (it = runners.begin(); it != runners.end(); ++it) {
381         RelayRunner* runner = *it;
382         if (runner) {
383             delete runner;
384         }
385     }
386 }
387
388
389 /**
390  * Get (or instantiate) a runner for the specified leg.
391  */
392 RelayRunner *
393 RelayRace::getRunner(unsigned leg) {
394     RelayRunner *runner;
395
396     if (leg >= runners.size()) {
397         runners.resize(leg + 1);
398         runner = 0;
399     } else {
400         runner = runners[leg];
401     }
402     if (!runner) {
403         runner = new RelayRunner(this, leg);
404         runners[leg] = runner;
405     }
406     return runner;
407 }
408
409
410 /**
411  * Start the race.
412  */
413 void
414 RelayRace::run(void) {
415     trace::Call *call;
416     call = parser.parse_call();
417     if (!call) {
418         /* Nothing to do */
419         return;
420     }
421
422     RelayRunner *foreRunner = getForeRunner();
423     if (call->thread_id == 0) {
424         /* We are the forerunner thread, so no need to pass baton */
425         foreRunner->baton = call;
426     } else {
427         passBaton(call);
428     }
429
430     /* Start the forerunner thread */
431     foreRunner->runRace();
432 }
433
434
435 /**
436  * Pass the baton (i.e., the call) to the appropriate thread.
437  */
438 void
439 RelayRace::passBaton(trace::Call *call) {
440     if (0) std::cerr << "switching to thread " << call->thread_id << "\n";
441     RelayRunner *runner = getRunner(call->thread_id);
442     runner->receiveBaton(call);
443 }
444
445
446 /**
447  * Called when a runner other than the forerunner reaches the finish line.
448  *
449  * Only the fore runner can finish the race, so inform him that the race is
450  * finished.
451  */
452 void
453 RelayRace::finishLine(void) {
454     RelayRunner *foreRunner = getForeRunner();
455     foreRunner->finishRace();
456 }
457
458
459 /**
460  * Called by the fore runner after finish line to stop all other runners.
461  */
462 void
463 RelayRace::stopRunners(void) {
464     std::vector<RelayRunner*>::const_iterator it;
465     for (it = runners.begin() + 1; it != runners.end(); ++it) {
466         RelayRunner* runner = *it;
467         if (runner) {
468             runner->finishRace();
469         }
470     }
471 }
472
473
474 static void
475 mainLoop() {
476     addCallbacks(retracer);
477
478     long long startTime = 0; 
479     frameNo = 0;
480
481     startTime = os::getTime();
482
483     RelayRace race;
484     race.run();
485
486     long long endTime = os::getTime();
487     float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency);
488
489     if ((retrace::verbosity >= -1) || (retrace::profiling)) {
490         std::cout << 
491             "Rendered " << frameNo << " frames"
492             " in " <<  timeInterval << " secs,"
493             " average of " << (frameNo/timeInterval) << " fps\n";
494     }
495
496     if (waitOnFinish) {
497         waitForInput();
498     } else {
499         return;
500     }
501 }
502
503
504 } /* namespace retrace */
505
506
507 static void
508 usage(const char *argv0) {
509     std::cout << 
510         "Usage: " << argv0 << " [OPTION] TRACE [...]\n"
511         "Replay TRACE.\n"
512         "\n"
513         "  -b, --benchmark         benchmark mode (no error checking or warning messages)\n"
514         "      --pcpu              cpu profiling (cpu times per call)\n"
515         "      --pgpu              gpu profiling (gpu times per draw call)\n"
516         "      --ppd               pixels drawn profiling (pixels drawn per draw call)\n"
517         "  -c, --compare=PREFIX    compare against snapshots with given PREFIX\n"
518         "  -C, --calls=CALLSET     calls to compare (default is every frame)\n"
519         "      --call-nos[=BOOL]   use call numbers in snapshot filenames\n"
520         "      --core              use core profile\n"
521         "      --db                use a double buffer visual (default)\n"
522         "      --driver=DRIVER     force driver type (`hw`, `sw`, `ref`, `null`, or driver module name)\n"
523         "      --sb                use a single buffer visual\n"
524         "  -s, --snapshot-prefix=PREFIX    take snapshots; `-` for PNM stdout output\n"
525         "  -S, --snapshot=CALLSET  calls to snapshot (default is every frame)\n"
526         "  -v, --verbose           increase output verbosity\n"
527         "  -D, --dump-state=CALL   dump state at specific call no\n"
528         "  -w, --wait              waitOnFinish on final frame\n";
529 }
530
531 enum {
532     CALL_NOS_OPT = CHAR_MAX + 1,
533     CORE_OPT,
534     DB_OPT,
535     DRIVER_OPT,
536     PCPU_OPT,
537     PGPU_OPT,
538     PPD_OPT,
539     SB_OPT,
540 };
541
542 const static char *
543 shortOptions = "bc:C:D:hs:S:vw";
544
545 const static struct option
546 longOptions[] = {
547     {"benchmark", no_argument, 0, 'b'},
548     {"call-nos", optional_argument, 0, CALL_NOS_OPT },
549     {"calls", required_argument, 0, 'C'},
550     {"compare", required_argument, 0, 'c'},
551     {"core", no_argument, 0, CORE_OPT},
552     {"db", no_argument, 0, DB_OPT},
553     {"driver", required_argument, 0, DRIVER_OPT},
554     {"dump-state", required_argument, 0, 'D'},
555     {"help", no_argument, 0, 'h'},
556     {"pcpu", no_argument, 0, PCPU_OPT},
557     {"pgpu", no_argument, 0, PGPU_OPT},
558     {"ppd", no_argument, 0, PPD_OPT},
559     {"sb", no_argument, 0, SB_OPT},
560     {"snapshot-prefix", required_argument, 0, 's'},
561     {"snapshot", required_argument, 0, 'S'},
562     {"verbose", no_argument, 0, 'v'},
563     {"wait", no_argument, 0, 'w'},
564     {0, 0, 0, 0}
565 };
566
567
568 static void exceptionCallback(void)
569 {
570     std::cerr << retrace::callNo << ": error: caught an unhandled exception\n";
571 }
572
573
574 extern "C"
575 int main(int argc, char **argv)
576 {
577     using namespace retrace;
578     int i;
579
580     assert(compareFrequency.empty());
581     assert(snapshotFrequency.empty());
582
583     int opt;
584     while  ((opt = getopt_long_only(argc, argv, shortOptions, longOptions, NULL)) != -1) {
585         switch (opt) {
586         case 'h':
587             usage(argv[0]);
588             return 0;
589         case 'b':
590             retrace::debug = false;
591             retrace::verbosity = -1;
592             break;
593         case CALL_NOS_OPT:
594             useCallNos = trace::boolOption(optarg);
595             break;
596         case 'c':
597             comparePrefix = optarg;
598             if (compareFrequency.empty()) {
599                 compareFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
600             }
601             break;
602         case 'C':
603             compareFrequency = trace::CallSet(optarg);
604             if (comparePrefix == NULL) {
605                 comparePrefix = "";
606             }
607             break;
608         case 'D':
609             dumpStateCallNo = atoi(optarg);
610             dumpingState = true;
611             retrace::verbosity = -2;
612             break;
613         case CORE_OPT:
614             retrace::coreProfile = true;
615             break;
616         case DB_OPT:
617             retrace::doubleBuffer = true;
618             break;
619         case DRIVER_OPT:
620             if (strcasecmp(optarg, "hw") == 0) {
621                 driver = DRIVER_HARDWARE;
622             } else if (strcasecmp(optarg, "sw") == 0) {
623                 driver = DRIVER_SOFTWARE;
624             } else if (strcasecmp(optarg, "ref") == 0) {
625                 driver = DRIVER_REFERENCE;
626             } else if (strcasecmp(optarg, "null") == 0) {
627                 driver = DRIVER_NULL;
628             } else {
629                 driver = DRIVER_MODULE;
630                 driverModule = optarg;
631             }
632             break;
633         case SB_OPT:
634             retrace::doubleBuffer = false;
635             break;
636         case 's':
637             snapshotPrefix = optarg;
638             if (snapshotFrequency.empty()) {
639                 snapshotFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
640             }
641             if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
642                 os::setBinaryMode(stdout);
643                 retrace::verbosity = -2;
644             }
645             break;
646         case 'S':
647             snapshotFrequency = trace::CallSet(optarg);
648             if (snapshotPrefix == NULL) {
649                 snapshotPrefix = "";
650             }
651             break;
652         case 'v':
653             ++retrace::verbosity;
654             break;
655         case 'w':
656             waitOnFinish = true;
657             break;
658         case PGPU_OPT:
659             retrace::debug = false;
660             retrace::profiling = true;
661             retrace::verbosity = -1;
662
663             retrace::profilingGpuTimes = true;
664             break;
665         case PCPU_OPT:
666             retrace::debug = false;
667             retrace::profiling = true;
668             retrace::verbosity = -1;
669
670             retrace::profilingCpuTimes = true;
671             break;
672         case PPD_OPT:
673             retrace::debug = false;
674             retrace::profiling = true;
675             retrace::verbosity = -1;
676
677             retrace::profilingPixelsDrawn = true;
678             break;
679         default:
680             std::cerr << "error: unknown option " << opt << "\n";
681             usage(argv[0]);
682             return 1;
683         }
684     }
685
686     retrace::setUp();
687     if (retrace::profiling) {
688         retrace::profiler.setup(retrace::profilingCpuTimes, retrace::profilingGpuTimes, retrace::profilingPixelsDrawn);
689     }
690
691     os::setExceptionCallback(exceptionCallback);
692
693     for (i = optind; i < argc; ++i) {
694         if (!retrace::parser.open(argv[i])) {
695             return 1;
696         }
697
698         retrace::mainLoop();
699
700         retrace::parser.close();
701     }
702     
703     os::resetExceptionCallback();
704
705     // XXX: X often hangs on XCloseDisplay
706     //retrace::cleanUp();
707
708     return 0;
709 }
710