]> git.cworth.org Git - apitrace/blobdiff - retrace/retrace_main.cpp
retrace: Implement glxCopySubBufferMESA
[apitrace] / retrace / retrace_main.cpp
index a59ee9ae81f3f4aecbdd8b6501a77eaf3f05bffb..bff8983b15a99becb7ac6dbe06ab58fcffcafdf5 100644 (file)
@@ -1,6 +1,8 @@
 /**************************************************************************
  *
  * Copyright 2011 Jose Fonseca
+ * Copyright (C) 2013 Intel Corporation. All rights reversed.
+ * Author: Shuang He <shuang.he@intel.com>
  * All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -28,6 +30,9 @@
 #include <limits.h> // for CHAR_MAX
 #include <iostream>
 #include <getopt.h>
+#ifndef _WIN32
+#include <unistd.h> // for isatty()
+#endif
 
 #include "os_binary.hpp"
 #include "os_time.hpp"
 #include "image.hpp"
 #include "trace_callset.hpp"
 #include "trace_dump.hpp"
+#include "trace_option.hpp"
 #include "retrace.hpp"
 
 
 static bool waitOnFinish = false;
+static bool loopOnFinish = false;
 
-static const char *comparePrefix = NULL;
 static const char *snapshotPrefix = NULL;
+static enum {
+    PNM_FMT,
+    RAW_RGB
+} snapshotFormat = PNM_FMT;
+
 static trace::CallSet snapshotFrequency;
-static trace::CallSet compareFrequency;
+static trace::ParseBookmark lastFrameStart;
 
 static unsigned dumpStateCallNo = ~0;
 
@@ -61,6 +72,9 @@ int verbosity = 0;
 bool debug = true;
 bool dumpingState = false;
 
+Driver driver = DRIVER_DEFAULT;
+const char *driverModule = NULL;
+
 bool doubleBuffer = true;
 bool coreProfile = false;
 
@@ -68,6 +82,9 @@ bool profiling = false;
 bool profilingGpuTimes = false;
 bool profilingCpuTimes = false;
 bool profilingPixelsDrawn = false;
+bool profilingMemoryUsage = false;
+bool useCallNos = true;
+bool singleThread = false;
 
 unsigned frameNo = 0;
 unsigned callNo = 0;
@@ -85,50 +102,43 @@ Dumper *dumper = &defaultDumper;
 
 
 /**
- * Take/compare snapshots.
+ * Take snapshots.
  */
 static void
 takeSnapshot(unsigned call_no) {
-    assert(snapshotPrefix || comparePrefix);
-
-    image::Image *ref = NULL;
+    static unsigned snapshot_no = 0;
 
-    if (comparePrefix) {
-        os::String filename = os::String::format("%s%010u.png", comparePrefix, call_no);
-        ref = image::readPNG(filename);
-        if (!ref) {
-            return;
-        }
-        if (retrace::verbosity >= 0) {
-            std::cout << "Read " << filename << "\n";
-        }
-    }
+    assert(snapshotPrefix);
 
     image::Image *src = dumper->getSnapshot();
     if (!src) {
+        std::cerr << call_no << ": warning: failed to get snapshot\n";
         return;
     }
 
     if (snapshotPrefix) {
         if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
             char comment[21];
-            snprintf(comment, sizeof comment, "%u", call_no);
-            src->writePNM(std::cout, comment);
+            snprintf(comment, sizeof comment, "%u",
+                     useCallNos ? call_no : snapshot_no);
+            if (snapshotFormat == RAW_RGB)
+                src->writeRAW(std::cout);
+            else
+                src->writePNM(std::cout, comment);
         } else {
-            os::String filename = os::String::format("%s%010u.png", snapshotPrefix, call_no);
+            os::String filename = os::String::format("%s%010u.png",
+                                                     snapshotPrefix,
+                                                     useCallNos ? call_no : snapshot_no);
             if (src->writePNG(filename) && retrace::verbosity >= 0) {
                 std::cout << "Wrote " << filename << "\n";
             }
         }
     }
 
-    if (ref) {
-        std::cout << "Snapshot " << call_no << " average precision of " << src->compare(*ref) << " bits\n";
-        delete ref;
-    }
-
     delete src;
 
+    snapshot_no++;
+
     return;
 }
 
@@ -143,8 +153,7 @@ static void
 retraceCall(trace::Call *call) {
     bool swapRenderTarget = call->flags &
         trace::CALL_FLAG_SWAP_RENDERTARGET;
-    bool doSnapshot = snapshotFrequency.contains(*call) ||
-        compareFrequency.contains(*call);
+    bool doSnapshot = snapshotFrequency.contains(*call);
 
     // For calls which cause rendertargets to be swaped, we take the
     // snapshot _before_ swapping the rendertargets.
@@ -257,6 +266,12 @@ public:
         }
     }
 
+    ~RelayRunner() {
+        if (thread.joinable()) {
+            thread.join();
+        }
+    }
+
     /**
      * Thread main loop.
      */
@@ -292,13 +307,34 @@ public:
      */
     void
     runLeg(trace::Call *call) {
+
         /* Consume successive calls for this thread. */
         do {
+            bool callEndsFrame = false;
+            static trace::ParseBookmark frameStart;
+
             assert(call);
             assert(call->thread_id == leg);
+
+            if (loopOnFinish && call->flags & trace::CALL_FLAG_END_FRAME) {
+                callEndsFrame = true;
+                parser.getBookmark(frameStart);
+            }
+
             retraceCall(call);
             delete call;
             call = parser.parse_call();
+
+            /* Restart last frame if looping is requested. */
+            if (loopOnFinish) {
+                if (!call) {
+                    parser.setBookmark(lastFrameStart);
+                    call = parser.parse_call();
+                } else if (callEndsFrame) {
+                    lastFrameStart = frameStart;
+                }
+            }
+
         } while (call && call->thread_id == leg);
 
         if (call) {
@@ -406,6 +442,14 @@ RelayRace::run(void) {
         return;
     }
 
+    /* If the user wants to loop we need to get a bookmark target. We
+     * usually get this after replaying a call that ends a frame, but
+     * for a trace that has only one frame we need to get it at the
+     * beginning. */
+    if (loopOnFinish) {
+        parser.getBookmark(lastFrameStart);
+    }
+
     RelayRunner *foreRunner = getForeRunner();
     if (call->thread_id == 0) {
         /* We are the forerunner thread, so no need to pass baton */
@@ -467,8 +511,17 @@ mainLoop() {
 
     startTime = os::getTime();
 
-    RelayRace race;
-    race.run();
+    if (singleThread) {
+        trace::Call *call;
+        while ((call = parser.parse_call())) {
+            retraceCall(call);
+            delete call;
+        };
+        flushRendering();
+    } else {
+        RelayRace race;
+        race.run();
+    }
 
     long long endTime = os::getTime();
     float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency);
@@ -501,47 +554,61 @@ usage(const char *argv0) {
         "      --pcpu              cpu profiling (cpu times per call)\n"
         "      --pgpu              gpu profiling (gpu times per draw call)\n"
         "      --ppd               pixels drawn profiling (pixels drawn per draw call)\n"
-        "  -c, --compare=PREFIX    compare against snapshots with given PREFIX\n"
-        "  -C, --calls=CALLSET     calls to compare (default is every frame)\n"
+        "      --pmem              memory usage profiling (vsize rss per call)\n"
+        "      --call-nos[=BOOL]   use call numbers in snapshot filenames\n"
         "      --core              use core profile\n"
         "      --db                use a double buffer visual (default)\n"
+        "      --driver=DRIVER     force driver type (`hw`, `sw`, `ref`, `null`, or driver module name)\n"
         "      --sb                use a single buffer visual\n"
         "  -s, --snapshot-prefix=PREFIX    take snapshots; `-` for PNM stdout output\n"
+        "      --snapshot-format=FMT       use (PNM or RGB; default is PNM) when writing to stdout output\n"
         "  -S, --snapshot=CALLSET  calls to snapshot (default is every frame)\n"
         "  -v, --verbose           increase output verbosity\n"
         "  -D, --dump-state=CALL   dump state at specific call no\n"
-        "  -w, --wait              waitOnFinish on final frame\n";
+        "  -w, --wait              waitOnFinish on final frame\n"
+        "      --loop              continuously loop, replaying final frame.\n"
+        "      --singlethread      use a single thread to replay command stream\n";
 }
 
 enum {
-    CORE_OPT = CHAR_MAX + 1,
+    CALL_NOS_OPT = CHAR_MAX + 1,
+    CORE_OPT,
     DB_OPT,
+    DRIVER_OPT,
     PCPU_OPT,
     PGPU_OPT,
     PPD_OPT,
+    PMEM_OPT,
     SB_OPT,
+    SNAPSHOT_FORMAT_OPT,
+    LOOP_OPT,
+    SINGLETHREAD_OPT
 };
 
 const static char *
-shortOptions = "bc:C:D:hs:S:vw";
+shortOptions = "bD:hs:S:vw";
 
 const static struct option
 longOptions[] = {
     {"benchmark", no_argument, 0, 'b'},
-    {"calls", required_argument, 0, 'C'},
-    {"compare", required_argument, 0, 'c'},
+    {"call-nos", optional_argument, 0, CALL_NOS_OPT },
     {"core", no_argument, 0, CORE_OPT},
     {"db", no_argument, 0, DB_OPT},
+    {"driver", required_argument, 0, DRIVER_OPT},
     {"dump-state", required_argument, 0, 'D'},
     {"help", no_argument, 0, 'h'},
     {"pcpu", no_argument, 0, PCPU_OPT},
     {"pgpu", no_argument, 0, PGPU_OPT},
     {"ppd", no_argument, 0, PPD_OPT},
+    {"pmem", no_argument, 0, PMEM_OPT},
     {"sb", no_argument, 0, SB_OPT},
     {"snapshot-prefix", required_argument, 0, 's'},
+    {"snapshot-format", required_argument, 0, SNAPSHOT_FORMAT_OPT},
     {"snapshot", required_argument, 0, 'S'},
     {"verbose", no_argument, 0, 'v'},
     {"wait", no_argument, 0, 'w'},
+    {"loop", no_argument, 0, LOOP_OPT},
+    {"singlethread", no_argument, 0, SINGLETHREAD_OPT},
     {0, 0, 0, 0}
 };
 
@@ -558,7 +625,6 @@ int main(int argc, char **argv)
     using namespace retrace;
     int i;
 
-    assert(compareFrequency.empty());
     assert(snapshotFrequency.empty());
 
     int opt;
@@ -571,17 +637,8 @@ int main(int argc, char **argv)
             retrace::debug = false;
             retrace::verbosity = -1;
             break;
-        case 'c':
-            comparePrefix = optarg;
-            if (compareFrequency.empty()) {
-                compareFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
-            }
-            break;
-        case 'C':
-            compareFrequency = trace::CallSet(optarg);
-            if (comparePrefix == NULL) {
-                comparePrefix = "";
-            }
+        case CALL_NOS_OPT:
+            useCallNos = trace::boolOption(optarg);
             break;
         case 'D':
             dumpStateCallNo = atoi(optarg);
@@ -594,9 +651,26 @@ int main(int argc, char **argv)
         case DB_OPT:
             retrace::doubleBuffer = true;
             break;
+        case DRIVER_OPT:
+            if (strcasecmp(optarg, "hw") == 0) {
+                driver = DRIVER_HARDWARE;
+            } else if (strcasecmp(optarg, "sw") == 0) {
+                driver = DRIVER_SOFTWARE;
+            } else if (strcasecmp(optarg, "ref") == 0) {
+                driver = DRIVER_REFERENCE;
+            } else if (strcasecmp(optarg, "null") == 0) {
+                driver = DRIVER_NULL;
+            } else {
+                driver = DRIVER_MODULE;
+                driverModule = optarg;
+            }
+            break;
         case SB_OPT:
             retrace::doubleBuffer = false;
             break;
+        case SINGLETHREAD_OPT:
+            retrace::singleThread = true;
+            break;
         case 's':
             snapshotPrefix = optarg;
             if (snapshotFrequency.empty()) {
@@ -605,8 +679,31 @@ int main(int argc, char **argv)
             if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
                 os::setBinaryMode(stdout);
                 retrace::verbosity = -2;
+            } else {
+                /*
+                 * Create the snapshot directory if it does not exist.
+                 *
+                 * We can't just use trimFilename() because when applied to
+                 * "/foo/boo/" it would merely return "/foo".
+                 *
+                 * XXX: create nested directories.
+                 */
+                os::String prefix(snapshotPrefix);
+                os::String::iterator sep = prefix.rfindSep(false);
+                if (sep != prefix.end()) {
+                    prefix.erase(sep, prefix.end());
+                    if (!prefix.exists() && !os::createDirectory(prefix)) {
+                        std::cerr << "error: failed to create `" << prefix.str() << "` directory\n";
+                    }
+                }
             }
             break;
+       case SNAPSHOT_FORMAT_OPT:
+            if (strcmp(optarg, "RGB") == 0)
+                snapshotFormat = RAW_RGB;
+            else
+                snapshotFormat = PNM_FMT;
+            break;
         case 'S':
             snapshotFrequency = trace::CallSet(optarg);
             if (snapshotPrefix == NULL) {
@@ -619,6 +716,9 @@ int main(int argc, char **argv)
         case 'w':
             waitOnFinish = true;
             break;
+        case LOOP_OPT:
+            loopOnFinish = true;
+            break;
         case PGPU_OPT:
             retrace::debug = false;
             retrace::profiling = true;
@@ -640,6 +740,13 @@ int main(int argc, char **argv)
 
             retrace::profilingPixelsDrawn = true;
             break;
+        case PMEM_OPT:
+            retrace::debug = false;
+            retrace::profiling = true;
+            retrace::verbosity = -1;
+
+            retrace::profilingMemoryUsage = true;
+            break;
         default:
             std::cerr << "error: unknown option " << opt << "\n";
             usage(argv[0]);
@@ -647,9 +754,15 @@ int main(int argc, char **argv)
         }
     }
 
+#ifndef _WIN32
+    if (!isatty(STDOUT_FILENO)) {
+        dumpFlags |= trace::DUMP_FLAG_NO_COLOR;
+    }
+#endif
+
     retrace::setUp();
     if (retrace::profiling) {
-        retrace::profiler.setup(retrace::profilingCpuTimes, retrace::profilingGpuTimes, retrace::profilingPixelsDrawn);
+        retrace::profiler.setup(retrace::profilingCpuTimes, retrace::profilingGpuTimes, retrace::profilingPixelsDrawn, retrace::profilingMemoryUsage);
     }
 
     os::setExceptionCallback(exceptionCallback);