#include <string.h>
+#include <limits.h> // for CHAR_MAX
#include <iostream>
+#include <getopt.h>
#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"
bool debug = true;
bool dumpingState = false;
+Driver driver = DRIVER_DEFAULT;
+const char *driverModule = NULL;
+
bool doubleBuffer = true;
bool coreProfile = false;
bool profilingGpuTimes = false;
bool profilingCpuTimes = false;
bool profilingPixelsDrawn = false;
+bool useCallNos = true;
unsigned frameNo = 0;
unsigned callNo = 0;
}
+static Dumper defaultDumper;
+
+Dumper *dumper = &defaultDumper;
+
+
/**
* Take/compare snapshots.
*/
static void
takeSnapshot(unsigned call_no) {
+ static unsigned snapshot_no = 0;
+
assert(snapshotPrefix || comparePrefix);
image::Image *ref = NULL;
}
}
- image::Image *src = getSnapshot();
+ image::Image *src = dumper->getSnapshot();
if (!src) {
+ std::cout << "Failed to get snapshot\n";
return;
}
if (snapshotPrefix) {
if (snapshotPrefix[0] == '-' && snapshotPrefix[1] == 0) {
char comment[21];
- snprintf(comment, sizeof comment, "%u", call_no);
+ snprintf(comment, sizeof comment, "%u",
+ useCallNos ? call_no : snapshot_no);
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";
}
delete src;
+ snapshot_no++;
+
return;
}
takeSnapshot(call->no);
if (call->no >= dumpStateCallNo &&
- dumpState(std::cout)) {
+ dumper->dumpState(std::cout)) {
exit(0);
}
}
"Usage: " << argv0 << " [OPTION] TRACE [...]\n"
"Replay TRACE.\n"
"\n"
- " -b benchmark mode (no error checking or warning messages)\n"
- " -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 PREFIX compare against snapshots\n"
- " -C CALLSET calls to compare (default is every frame)\n"
- " -core use core profile\n"
- " -db use a double buffer visual (default)\n"
- " -sb use a single buffer visual\n"
- " -s PREFIX take snapshots; `-` for PNM stdout output\n"
- " -S CALLSET calls to snapshot (default is every frame)\n"
- " -v increase output verbosity\n"
- " -D CALLNO dump state at specific call no\n"
- " -w waitOnFinish on final frame\n";
+ " -b, --benchmark benchmark mode (no error checking or warning messages)\n"
+ " --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"
+ " --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"
+ " -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";
+}
+
+enum {
+ CALL_NOS_OPT = CHAR_MAX + 1,
+ CORE_OPT,
+ DB_OPT,
+ DRIVER_OPT,
+ PCPU_OPT,
+ PGPU_OPT,
+ PPD_OPT,
+ SB_OPT,
+};
+
+const static char *
+shortOptions = "bc:C:D:hs:S:vw";
+
+const static struct option
+longOptions[] = {
+ {"benchmark", no_argument, 0, 'b'},
+ {"call-nos", optional_argument, 0, CALL_NOS_OPT },
+ {"calls", required_argument, 0, 'C'},
+ {"compare", required_argument, 0, 'c'},
+ {"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},
+ {"sb", no_argument, 0, SB_OPT},
+ {"snapshot-prefix", required_argument, 0, 's'},
+ {"snapshot", required_argument, 0, 'S'},
+ {"verbose", no_argument, 0, 'v'},
+ {"wait", no_argument, 0, 'w'},
+ {0, 0, 0, 0}
+};
+
+
+static void exceptionCallback(void)
+{
+ std::cerr << retrace::callNo << ": error: caught an unhandled exception\n";
}
int main(int argc, char **argv)
{
using namespace retrace;
+ int i;
assert(compareFrequency.empty());
assert(snapshotFrequency.empty());
- int i;
- for (i = 1; i < argc; ++i) {
- const char *arg = argv[i];
-
- if (arg[0] != '-') {
- break;
- }
-
- if (!strcmp(arg, "--")) {
- break;
- } else if (!strcmp(arg, "-b")) {
+ int opt;
+ while ((opt = getopt_long_only(argc, argv, shortOptions, longOptions, NULL)) != -1) {
+ switch (opt) {
+ case 'h':
+ usage(argv[0]);
+ return 0;
+ case 'b':
retrace::debug = false;
retrace::verbosity = -1;
- } else if (!strcmp(arg, "-c")) {
- comparePrefix = argv[++i];
+ break;
+ case CALL_NOS_OPT:
+ useCallNos = trace::boolOption(optarg);
+ break;
+ case 'c':
+ comparePrefix = optarg;
if (compareFrequency.empty()) {
compareFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
}
- } else if (!strcmp(arg, "-C")) {
- compareFrequency = trace::CallSet(argv[++i]);
+ break;
+ case 'C':
+ compareFrequency = trace::CallSet(optarg);
if (comparePrefix == NULL) {
comparePrefix = "";
}
- } else if (!strcmp(arg, "-D")) {
- dumpStateCallNo = atoi(argv[++i]);
+ break;
+ case 'D':
+ dumpStateCallNo = atoi(optarg);
dumpingState = true;
retrace::verbosity = -2;
- } else if (!strcmp(arg, "-core")) {
+ break;
+ case CORE_OPT:
retrace::coreProfile = true;
- } else if (!strcmp(arg, "-db")) {
+ break;
+ case DB_OPT:
retrace::doubleBuffer = true;
- } else if (!strcmp(arg, "-sb")) {
+ 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;
- } else if (!strcmp(arg, "--help")) {
- usage(argv[0]);
- return 0;
- } else if (!strcmp(arg, "-s")) {
- snapshotPrefix = argv[++i];
+ break;
+ case 's':
+ snapshotPrefix = optarg;
if (snapshotFrequency.empty()) {
snapshotFrequency = trace::CallSet(trace::FREQUENCY_FRAME);
}
os::setBinaryMode(stdout);
retrace::verbosity = -2;
}
- } else if (!strcmp(arg, "-S")) {
- snapshotFrequency = trace::CallSet(argv[++i]);
+ break;
+ case 'S':
+ snapshotFrequency = trace::CallSet(optarg);
if (snapshotPrefix == NULL) {
snapshotPrefix = "";
}
- } else if (!strcmp(arg, "-v")) {
+ break;
+ case 'v':
++retrace::verbosity;
- } else if (!strcmp(arg, "-w")) {
+ break;
+ case 'w':
waitOnFinish = true;
- } else if (arg[1] == 'p') {
+ break;
+ case PGPU_OPT:
retrace::debug = false;
retrace::profiling = true;
retrace::verbosity = -1;
- if (!strcmp(arg, "-pcpu")) {
- retrace::profilingCpuTimes = true;
- } else if (!strcmp(arg, "-pgpu")) {
- retrace::profilingGpuTimes = true;
- } else if (!strcmp(arg, "-ppd")) {
- retrace::profilingPixelsDrawn = true;
- }
- } else {
- std::cerr << "error: unknown option " << arg << "\n";
+ retrace::profilingGpuTimes = true;
+ break;
+ case PCPU_OPT:
+ retrace::debug = false;
+ retrace::profiling = true;
+ retrace::verbosity = -1;
+
+ retrace::profilingCpuTimes = true;
+ break;
+ case PPD_OPT:
+ retrace::debug = false;
+ retrace::profiling = true;
+ retrace::verbosity = -1;
+
+ retrace::profilingPixelsDrawn = true;
+ break;
+ default:
+ std::cerr << "error: unknown option " << opt << "\n";
usage(argv[0]);
return 1;
}
retrace::profiler.setup(retrace::profilingCpuTimes, retrace::profilingGpuTimes, retrace::profilingPixelsDrawn);
}
- for ( ; i < argc; ++i) {
+ os::setExceptionCallback(exceptionCallback);
+
+ for (i = optind; i < argc; ++i) {
if (!retrace::parser.open(argv[i])) {
- std::cerr << "error: failed to open " << argv[i] << "\n";
return 1;
}
retrace::parser.close();
}
+
+ os::resetExceptionCallback();
// XXX: X often hangs on XCloseDisplay
//retrace::cleanUp();