X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=cli%2Fcli_trace.cpp;h=f87a383b009cd0c13e29754144b63b13ad16cbfb;hb=e8fbc5deea39f8eac43cf5f1a6033f97e0484297;hp=92251b4bef0ad6cd10b4d9b924a0414f27b9e67b;hpb=8f28cd84c31bfe031a0f9ae119f0bcc55d59286e;p=apitrace diff --git a/cli/cli_trace.cpp b/cli/cli_trace.cpp index 92251b4..f87a383 100644 --- a/cli/cli_trace.cpp +++ b/cli/cli_trace.cpp @@ -28,12 +28,180 @@ #include #include +#include +#include #include +#include "os_string.hpp" +#include "os_process.hpp" + #include "cli.hpp" +#include "cli_resources.hpp" + + +#if defined(__APPLE__) +#define TRACE_VARIABLE "DYLD_LIBRARY_PATH" +#define GL_TRACE_WRAPPER "OpenGL" +#elif defined(_WIN32) +#define GL_TRACE_WRAPPER "opengl32.dll" +#else +#define TRACE_VARIABLE "LD_PRELOAD" +#define GL_TRACE_WRAPPER "glxtrace.so" +#define EGL_TRACE_WRAPPER "egltrace.so" +#endif + + +static inline bool +copyWrapper(const os::String & wrapperPath, + const char *programPath, + bool verbose) +{ + os::String wrapperFilename(wrapperPath); + wrapperFilename.trimDirectory(); + + os::String tmpWrapper(programPath); + tmpWrapper.trimFilename(); + tmpWrapper.join(wrapperFilename); + + if (verbose) { + std::cerr << wrapperPath << " -> " << tmpWrapper << "\n"; + } + + if (tmpWrapper.exists()) { + std::cerr << "error: not overwriting " << tmpWrapper << "\n"; + return false; + } + + if (!os::copyFile(wrapperPath, tmpWrapper, false)) { + std::cerr << "error: failed to copy " << wrapperPath << " into " << tmpWrapper << "\n"; + return false; + } + + return true; +} + + +static int +traceProgram(trace::API api, + char * const *argv, + const char *output, + bool verbose) +{ + const char *wrapperFilename; + std::vector args; + int status = 1; + + /* + * TODO: simplify code + */ + + bool useInject = false; + switch (api) { + case trace::API_GL: + wrapperFilename = GL_TRACE_WRAPPER; + break; +#ifdef EGL_TRACE_WRAPPER + case trace::API_EGL: + wrapperFilename = EGL_TRACE_WRAPPER; + break; +#endif +#ifdef _WIN32 + case trace::API_D3D7: + wrapperFilename = "ddraw.dll"; + break; + case trace::API_D3D8: + wrapperFilename = "d3d8.dll"; + break; + case trace::API_D3D9: + wrapperFilename = "d3d9.dll"; + break; + case trace::API_DXGI: + wrapperFilename = "dxgitrace.dll"; + useInject = true; + break; +#endif + default: + std::cerr << "error: unsupported API\n"; + return 1; + } + + os::String wrapperPath = findWrapper(wrapperFilename); + if (!wrapperPath.length()) { + std::cerr << "error: failed to find " << wrapperFilename << "\n"; + goto exit; + } + +#if defined(_WIN32) + useInject = true; + if (useInject) { + args.push_back("inject"); + args.push_back(wrapperPath); + } else { + /* On Windows copy the wrapper to the program directory. + */ + if (!copyWrapper(wrapperPath, argv[0], verbose)) { + goto exit; + } + } +#else /* !_WIN32 */ + (void)useInject; +#endif /* !_WIN32 */ -#include "trace_tools.hpp" +#if defined(__APPLE__) + /* On Mac OS X, using DYLD_LIBRARY_PATH, we actually set the + * directory, not the file. */ + wrapperPath.trimFilename(); +#endif + +#if defined(TRACE_VARIABLE) + if (verbose) { + std::cerr << TRACE_VARIABLE << "=" << wrapperPath.str() << "\n"; + } + /* FIXME: Don't modify the current environment */ + os::setEnvironment(TRACE_VARIABLE, wrapperPath.str()); +#endif /* TRACE_VARIABLE */ + + if (output) { + os::setEnvironment("TRACE_FILE", output); + } + + for (char * const * arg = argv; *arg; ++arg) { + args.push_back(*arg); + } + args.push_back(NULL); + + if (verbose) { + const char *sep = ""; + for (unsigned i = 0; i < args.size(); ++i) { + std::cerr << sep << args[i]; + sep = " "; + } + std::cerr << "\n"; + } + + status = os::execute((char * const *)&args[0]); + +exit: +#if defined(TRACE_VARIABLE) + os::unsetEnvironment(TRACE_VARIABLE); +#endif +#if defined(_WIN32) + if (!useInject) { + os::String tmpWrapper(argv[0]); + tmpWrapper.trimFilename(); + tmpWrapper.join(wrapperFilename); + os::removeFile(tmpWrapper); + } +#endif + + if (output) { + os::unsetEnvironment("TRACE_FILE"); + } + + return status; + +} static const char *synopsis = "Generate a new trace by executing the given program."; @@ -41,7 +209,7 @@ static const char *synopsis = "Generate a new trace by executing the given progr static void usage(void) { - std::cout << "usage: apitrace trace PROGRAM [ARGS ...]\n" + std::cout << "usage: apitrace trace [OPTIONS] PROGRAM [ARGS ...]\n" << synopsis << "\n" "\n" " The given program will be executed with the given arguments.\n" @@ -50,67 +218,87 @@ usage(void) " with other apitrace utilities for replay or analysis.\n" "\n" " -v, --verbose verbose output\n" - " -a, --api API specify API to trace (gl or egl);\n" + " -a, --api=API specify API to trace (" +#ifdef _WIN32 + "gl, d3d7, d3d8, d3d9, or dxgi (for d3d10 and higher) " +#else + "gl or egl" +#endif + ");\n" " default is `gl`\n" - " -o, --output TRACE specify output trace file;\n" + " -o, --output=TRACE specify output trace file;\n" " default is `PROGRAM.trace`\n"; } +const static char * +shortOptions = "+hva:o:"; + +const static struct option +longOptions[] = { + {"help", no_argument, 0, 'h'}, + {"verbose", no_argument, 0, 'v'}, + {"api", required_argument, 0, 'a'}, + {"output", required_argument, 0, 'o'}, + {0, 0, 0, 0} +}; + static int command(int argc, char *argv[]) { bool verbose = false; trace::API api = trace::API_GL; const char *output = NULL; - int i; - - for (i = 0; i < argc; ) { - const char *arg = argv[i]; - - if (arg[0] != '-') { - break; - } - ++i; - - if (strcmp(arg, "--") == 0) { - break; - } else if (strcmp(arg, "--help") == 0) { + int opt; + while ((opt = getopt_long(argc, argv, shortOptions, longOptions, NULL)) != -1) { + switch (opt) { + case 'h': usage(); return 0; - } else if (strcmp(arg, "-v") == 0 || - strcmp(arg, "--verbose") == 0) { + case 'v': verbose = true; - } else if (strcmp(arg, "-a") == 0 || - strcmp(arg, "--api") == 0) { - arg = argv[i++]; - if (strcmp(arg, "gl") == 0) { + break; + case 'a': + if (strcmp(optarg, "gl") == 0) { api = trace::API_GL; - } else if (strcmp(arg, "egl") == 0) { + } else if (strcmp(optarg, "egl") == 0) { api = trace::API_EGL; + } else if (strcmp(optarg, "d3d7") == 0) { + api = trace::API_D3D7; + } else if (strcmp(optarg, "d3d8") == 0) { + api = trace::API_D3D8; + } else if (strcmp(optarg, "d3d9") == 0) { + api = trace::API_D3D9; + } else if (strcmp(optarg, "dxgi") == 0 || + strcmp(optarg, "d3d10") == 0 || + strcmp(optarg, "d3d10_1") == 0 || + strcmp(optarg, "d3d11") == 0 || + strcmp(optarg, "d3d11_1") == 0) { + api = trace::API_DXGI; } else { - std::cerr << "error: unknown API `" << arg << "`\n"; + std::cerr << "error: unknown API `" << optarg << "`\n"; usage(); return 1; } - } else if (strcmp(arg, "-o") == 0 || - strcmp(arg, "--output") == 0) { - output = argv[i++]; - } else { - std::cerr << "error: unknown option " << arg << "\n"; + break; + case 'o': + output = optarg; + break; + default: + std::cerr << "error: unexpected option `" << opt << "`\n"; usage(); return 1; } } - if (i == argc) { + if (optind == argc) { std::cerr << "error: no command specified\n"; usage(); return 1; } assert(argv[argc] == 0); - return trace::traceProgram(api, argv + i, output, verbose); + return traceProgram(api, argv + optind, output, verbose); } const Command trace_command = {