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