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, bool debug) { const char *wrapperFilename; std::vector<const char *> 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; case trace::API_D2D1: wrapperFilename = "d2d1trace.dll"; useInject = true; break; #endif default: std::cerr << "error: unsupported API\n"; return 1; } os::String wrapperPath = findWrapper(wrapperFilename, verbose); if (!wrapperPath.length()) { std::cerr << "error: failed to find " << wrapperFilename << " wrapper (rerun with -v option for more details)\n"; goto exit; } #if defined(_WIN32) /* * Use DLL injection method on Windows, even for APIs that don't stricly * need it. */ useInject = true; if (useInject) { args.push_back("inject"); if (debug) { args.push_back("-d"); } args.push_back("-D"); args.push_back(wrapperPath); args.push_back("--"); } else { /* On Windows copy the wrapper to the program directory. */ if (!copyWrapper(wrapperPath, argv[0], verbose)) { goto exit; } } #else /* !_WIN32 */ (void)useInject; #endif /* !_WIN32 */ #if defined(__APPLE__) /* On Mac OS X, using DYLD_LIBRARY_PATH, we actually set the * parent directory, not the file. */ wrapperPath.trimFilename(); wrapperPath.trimFilename(); #endif /* * Spawn child process. */ { #if defined(TRACE_VARIABLE) const char *oldEnvVarValue = getenv(TRACE_VARIABLE); if (oldEnvVarValue) { wrapperPath.append(OS_PATH_SEP); wrapperPath.append(oldEnvVarValue); } std::string ex; if (debug) { #if defined(__APPLE__) bool lldb = true; #else bool lldb = false; #endif if (lldb) { /* * Debug with LLDB. * * See also http://lldb.llvm.org/lldb-gdb.html */ char scriptFileName[] = "/tmp/apitrace.XXXXXX"; int scriptFD = mkstemp(scriptFileName); if (scriptFD < 0) { std::cerr << "error: failed to create temporary lldb script file\n"; exit(1); } FILE *scriptStream = fdopen(scriptFD, "w"); fprintf(scriptStream, "env " TRACE_VARIABLE "='%s'\n", wrapperPath.str()); fclose(scriptStream); args.push_back("lldb"); args.push_back("-s"); args.push_back(scriptFileName); args.push_back("--"); } else { /* * Debug with GDB. */ ex = "set exec-wrapper env " TRACE_VARIABLE "='"; ex.append(wrapperPath.str()); ex.append("'"); args.push_back("gdb"); args.push_back("--ex"); args.push_back(ex.c_str()); args.push_back("--args"); } os::unsetEnvironment(TRACE_VARIABLE); } else { /* FIXME: Don't modify our (ie parent) environment */ os::setEnvironment(TRACE_VARIABLE, wrapperPath.str()); } if (verbose) { std::cerr << TRACE_VARIABLE << "=" << wrapperPath.str() << "\n"; } #endif /* TRACE_VARIABLE */ if (output) { os::setEnvironment("TRACE_FILE", output); } for (char * const * arg = argv; *arg; ++arg) { args.push_back(*arg); } if (verbose) { const char *sep = ""; for (unsigned i = 0; i < args.size(); ++i) { const char *quote; if (strchr(args[i], ' ') != NULL) { quote = "\""; } else { quote = ""; } std::cerr << sep << quote << args[i] << quote; sep = " "; } std::cerr << "\n"; } args.push_back(NULL); status = os::execute((char * const *)&args[0]); #if defined(TRACE_VARIABLE) if (oldEnvVarValue) { os::setEnvironment(TRACE_VARIABLE, oldEnvVarValue); } else { os::unsetEnvironment(TRACE_VARIABLE); } #endif } exit: #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; }
int traceProgram(API api, char * const *argv, const char *output, bool verbose) { const char *wrapperFilename; /* * TODO: simplify code */ switch (api) { case API_GL: wrapperFilename = GL_TRACE_WRAPPER; break; #ifdef EGL_TRACE_WRAPPER case API_EGL: wrapperFilename = EGL_TRACE_WRAPPER; break; #endif #ifdef _WIN32 case API_D3D7: wrapperFilename = "ddraw.dll"; break; case API_D3D8: wrapperFilename = "d3d8.dll"; break; case API_D3D9: wrapperFilename = "d3d9.dll"; break; case API_D3D10: wrapperFilename = "d3d10.dll"; break; case API_D3D10_1: wrapperFilename = "d3d10_1.dll"; break; case API_D3D11: wrapperFilename = "d3d11.dll"; 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"; return 1; } #if defined(_WIN32) /* On Windows copy the wrapper to the program directory. */ os::String tmpWrapper(argv[0]); tmpWrapper.trimFilename(); tmpWrapper.join(wrapperFilename); if (verbose) { std::cerr << wrapperPath << " -> " << tmpWrapper << "\n"; } if (tmpWrapper.exists()) { std::cerr << "error: not overwriting " << tmpWrapper << "\n"; return 1; } if (!os::copyFile(wrapperPath, tmpWrapper, false)) { std::cerr << "error: failed to copy " << wrapperPath << " into " << tmpWrapper << "\n"; return 1; } #endif /* _WIN32 */ #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); } if (verbose) { const char *sep = ""; for (char * const * arg = argv; *arg; ++arg) { std::cerr << *arg << sep; sep = " "; } std::cerr << "\n"; } int status = os::execute(argv); #if defined(TRACE_VARIABLE) os::unsetEnvironment(TRACE_VARIABLE); #endif #if defined(_WIN32) os::removeFile(tmpWrapper); #endif if (output) { os::unsetEnvironment("TRACE_FILE"); } return status; }