From: José Fonseca Date: Tue, 11 Dec 2012 07:20:26 +0000 (+0000) Subject: Merge branch 'trim-auto' X-Git-Url: https://git.notmuchmail.org/git?a=commitdiff_plain;h=9db16b3989481f8d6dfc8932d760fcc16217ecbd;hp=55bbe59856e69770490186ff08b6668e6d1c674c;p=apitrace Merge branch 'trim-auto' Conflicts: cli/CMakeLists.txt --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ace8ac..549d066 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -206,8 +206,6 @@ set (PNG_DEFINITIONS "") set (PNG_LIBRARIES png_bundled) add_subdirectory (thirdparty/libpng EXCLUDE_FROM_ALL) -include_directories (${PNG_INCLUDE_DIR}) -add_definitions (${PNG_DEFINITIONS}) if (MSVC) add_subdirectory (thirdparty/getopt EXCLUDE_FROM_ALL) @@ -270,19 +268,6 @@ endif () set (SCRIPTS_INSTALL_DIR ${LIB_INSTALL_DIR}/scripts) set (WRAPPER_INSTALL_DIR ${LIB_ARCH_INSTALL_DIR}/wrappers) -# Expose the binary/install directories to source -# -# TODO: Use the same directory layout, for both build and install directories, -# so that binaries can find each other using just relative paths. -# -add_definitions( - -DAPITRACE_BINARY_DIR="${CMAKE_BINARY_DIR}" - -DAPITRACE_INSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}" - -DAPITRACE_PROGRAMS_INSTALL_DIR="${CMAKE_INSTALL_PREFIX}/bin" - -DAPITRACE_SCRIPTS_INSTALL_DIR="${CMAKE_INSTALL_PREFIX}/${SCRIPTS_INSTALL_DIR}" - -DAPITRACE_WRAPPERS_INSTALL_DIR="${CMAKE_INSTALL_PREFIX}/${WRAPPER_INSTALL_DIR}" -) - ############################################################################## # Common libraries / utilities @@ -320,13 +305,7 @@ add_library (common STATIC common/trace_writer_local.cpp common/trace_writer_model.cpp common/trace_loader.cpp - common/trace_resource.cpp - common/trace_tools_trace.cpp common/trace_profiler.cpp - common/image.cpp - common/image_bmp.cpp - common/image_pnm.cpp - common/image_png.cpp common/trace_option.cpp common/${os} ) @@ -350,6 +329,7 @@ endif () add_subdirectory (dispatch) add_subdirectory (helpers) add_subdirectory (wrappers) +add_subdirectory (image) add_subdirectory (retrace) @@ -357,6 +337,9 @@ add_subdirectory (retrace) # CLI if (ENABLE_CLI) + if (WIN32) + add_subdirectory (inject) + endif () add_subdirectory (cli) endif () diff --git a/README.markdown b/README.markdown index c821495..b1eb083 100644 --- a/README.markdown +++ b/README.markdown @@ -241,6 +241,13 @@ Then run the application as usual. You can specify the written trace filename by setting the `TRACE_FILE` environment variable before running. +For D3D10 and higher you really must use `apitrace trace -a DXGI ...`. This is +because D3D10-11 API span many DLLs which depend on each other, and once a DLL +with a given name is loaded Windows will reuse it for LoadLibrary calls of the +same name, causing internal calls to be traced erroneously. `apitrace trace` +solves this issue by injecting a DLL `dxgitrace.dll` and patching all modules +to hook only the APIs of interest. + Emitting annotations to the trace --------------------------------- diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt index 5df363a..5f8e116 100644 --- a/cli/CMakeLists.txt +++ b/cli/CMakeLists.txt @@ -1,3 +1,14 @@ +# Expose the binary/install directories to source +# +# TODO: Use the same directory layout, for both build and install directories, +# so that binaries can find each other using just relative paths. +# +add_definitions( + -DAPITRACE_PROGRAMS_INSTALL_DIR="${CMAKE_INSTALL_PREFIX}/bin" + -DAPITRACE_SCRIPTS_INSTALL_DIR="${CMAKE_INSTALL_PREFIX}/${SCRIPTS_INSTALL_DIR}" + -DAPITRACE_WRAPPERS_INSTALL_DIR="${CMAKE_INSTALL_PREFIX}/${WRAPPER_INSTALL_DIR}" +) + add_executable (apitrace cli_main.cpp cli_diff.cpp @@ -11,12 +22,12 @@ add_executable (apitrace cli_retrace.cpp cli_trace.cpp cli_trim.cpp + cli_resources.cpp trace_analyzer.cpp ) target_link_libraries (apitrace common - ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} ${SNAPPY_LIBRARIES} ${GETOPT_LIBRARIES} diff --git a/cli/cli_diff.cpp b/cli/cli_diff.cpp index ffc0e30..76cdce1 100644 --- a/cli/cli_diff.cpp +++ b/cli/cli_diff.cpp @@ -31,14 +31,14 @@ #include "cli.hpp" #include "os_string.hpp" #include "os_process.hpp" -#include "trace_resource.hpp" +#include "cli_resources.hpp" static const char *synopsis = "Identify differences between two traces."; static os::String find_command(void) { - return trace::findScript("tracediff.py"); + return findScript("tracediff.py"); } static void diff --git a/cli/cli_diff_images.cpp b/cli/cli_diff_images.cpp index 3932fa2..ba8df3e 100644 --- a/cli/cli_diff_images.cpp +++ b/cli/cli_diff_images.cpp @@ -31,14 +31,14 @@ #include "cli.hpp" #include "os_string.hpp" #include "os_process.hpp" -#include "trace_resource.hpp" +#include "cli_resources.hpp" static const char *synopsis = "Identify differences between two image dumps."; static os::String find_command(void) { - return trace::findScript("snapdiff.py"); + return findScript("snapdiff.py"); } static void diff --git a/cli/cli_diff_state.cpp b/cli/cli_diff_state.cpp index 7f75484..341d244 100644 --- a/cli/cli_diff_state.cpp +++ b/cli/cli_diff_state.cpp @@ -33,7 +33,7 @@ #include "cli.hpp" #include "os_string.hpp" #include "os_process.hpp" -#include "trace_resource.hpp" +#include "cli_resources.hpp" static const char *synopsis = "Identify differences between two state dumps."; @@ -83,7 +83,7 @@ command(int argc, char *argv[]) file1 = argv[optind]; file2 = argv[optind + 1]; - os::String command = trace::findScript("jsondiff.py"); + os::String command = findScript("jsondiff.py"); char *args[5]; diff --git a/cli/cli_resources.cpp b/cli/cli_resources.cpp new file mode 100644 index 0000000..255a98b --- /dev/null +++ b/cli/cli_resources.cpp @@ -0,0 +1,148 @@ +/********************************************************************* + * + * Copyright 2011 Intel Corporation + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + *********************************************************************/ + + +#include + +#include "os_string.hpp" + +#include "cli_resources.hpp" + + +os::String +findProgram(const char*programFilename) +{ + os::String programPath; + + os::String processDir = os::getProcessName(); + processDir.trimFilename(); + + programPath = processDir; + programPath.join(programFilename); + if (programPath.exists()) { + return programPath; + } + +#ifndef _WIN32 + // Try absolute install directory + programPath = APITRACE_PROGRAMS_INSTALL_DIR; + programPath.join(programFilename); + if (programPath.exists()) { + return programPath; + } +#endif + + return ""; +} + +os::String +findWrapper(const char *wrapperFilename) +{ + os::String wrapperPath; + + os::String processDir = os::getProcessName(); + processDir.trimFilename(); + + // Try relative build directory + // XXX: Just make build and install directory layout match + wrapperPath = processDir; + wrapperPath.join("wrappers"); + wrapperPath.join(wrapperFilename); + if (wrapperPath.exists()) { + return wrapperPath; + } + + // Try relative install directory + wrapperPath = processDir; +#if defined(_WIN32) + wrapperPath.join("..\\lib\\wrappers"); +#elif defined(__APPLE__) + wrapperPath.join("../lib/wrappers"); +#else + wrapperPath.join("../lib/apitrace/wrappers"); +#endif + wrapperPath.join(wrapperFilename); + if (wrapperPath.exists()) { + return wrapperPath; + } + +#ifndef _WIN32 + // Try absolute install directory + wrapperPath = APITRACE_WRAPPERS_INSTALL_DIR; + wrapperPath.join(wrapperFilename); + if (wrapperPath.exists()) { + return wrapperPath; + } +#endif + + return ""; +} + +os::String +findScript(const char *scriptFilename) +{ + os::String scriptPath; + + os::String processDir = os::getProcessName(); + processDir.trimFilename(); + + // Try relative build directory + // XXX: Just make build and install directory layout match + scriptPath = processDir; + scriptPath.join("scripts"); + scriptPath.join(scriptFilename); + if (scriptPath.exists()) { + return scriptPath; + } + + // Try relative install directory + scriptPath = processDir; +#if defined(_WIN32) + scriptPath.join("..\\lib\\scripts"); +#elif defined(__APPLE__) + scriptPath.join("../lib/scripts"); +#else + scriptPath.join("../lib/apitrace/scripts"); +#endif + scriptPath.join(scriptFilename); + if (scriptPath.exists()) { + return scriptPath; + } + +#ifndef _WIN32 + // Try absolute install directory + scriptPath = APITRACE_SCRIPTS_INSTALL_DIR; + scriptPath.join(scriptFilename); + if (scriptPath.exists()) { + return scriptPath; + } +#endif + + std::cerr << "error: cannot find " << scriptFilename << " script\n"; + + return ""; +} diff --git a/cli/cli_resources.hpp b/cli/cli_resources.hpp new file mode 100644 index 0000000..dde86dd --- /dev/null +++ b/cli/cli_resources.hpp @@ -0,0 +1,46 @@ +/************************************************************************** + * + * Copyright 2011 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + +#ifndef _CLI_RESOURCES_HPP_ +#define _CLI_RESOURCES_HPP_ + + +#include + +#include "os_string.hpp" +#include "trace_api.hpp" + + +os::String +findProgram(const char*programFilename); + +os::String +findScript(const char *name); + +os::String +findWrapper(const char *wrapperFilename); + + +#endif /* _CLI_RESOURCES_HPP_ */ diff --git a/cli/cli_retrace.cpp b/cli/cli_retrace.cpp index a2dbd09..d22f719 100644 --- a/cli/cli_retrace.cpp +++ b/cli/cli_retrace.cpp @@ -35,7 +35,7 @@ #include "os_process.hpp" #include "trace_parser.hpp" -#include "trace_resource.hpp" +#include "cli_resources.hpp" #include "cli.hpp" #include "cli_retrace.hpp" @@ -46,6 +46,7 @@ guessApi(const char *filename) { trace::Parser p; if (!p.open(filename)) { + exit(1); return trace::API_UNKNOWN; } trace::Call *call; @@ -76,10 +77,8 @@ executeRetrace(const std::vector & opts, case trace::API_D3D7: case trace::API_D3D8: case trace::API_D3D9: - case trace::API_D3D10: - case trace::API_D3D10_1: - case trace::API_D3D11: - // Can be used with WINE + case trace::API_DXGI: + // Use prefix so that it can be used with WINE retraceName = "d3dretrace.exe"; break; default: @@ -89,7 +88,7 @@ executeRetrace(const std::vector & opts, } std::vector command; - os::String retracePath = trace::findProgram(retraceName); + os::String retracePath = findProgram(retraceName); if (retracePath.length()) { command.push_back(retracePath); } else { diff --git a/cli/cli_trace.cpp b/cli/cli_trace.cpp index 8077509..2646ffd 100644 --- a/cli/cli_trace.cpp +++ b/cli/cli_trace.cpp @@ -28,13 +28,179 @@ #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) + 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 */ + +#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); + } -#include "trace_tools.hpp" + 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."; @@ -102,12 +268,12 @@ command(int argc, char *argv[]) api = trace::API_D3D8; } else if (strcmp(optarg, "d3d9") == 0) { api = trace::API_D3D9; - } else if (strcmp(optarg, "d3d10") == 0) { - api = trace::API_D3D10; - } else if (strcmp(optarg, "d3d10_1") == 0) { - api = trace::API_D3D10_1; - } else if (strcmp(optarg, "d3d11") == 0) { - api = trace::API_D3D11; + } 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 `" << optarg << "`\n"; usage(); @@ -131,7 +297,7 @@ command(int argc, char *argv[]) } assert(argv[argc] == 0); - return trace::traceProgram(api, argv + optind, output, verbose); + return traceProgram(api, argv + optind, output, verbose); } const Command trace_command = { diff --git a/cli/pickle.hpp b/cli/pickle.hpp new file mode 100644 index 0000000..84b643a --- /dev/null +++ b/cli/pickle.hpp @@ -0,0 +1,331 @@ +/************************************************************************** + * + * Copyright 2012 Jose Fonseca + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + +/* + * Python pickle writer + */ + +#ifndef _PICKLE_HPP_ +#define _PICKLE_HPP_ + +#include +#include +#include + +#include +#include +#include + + +class PickleWriter +{ +private: + std::ostream &os; + + /* + * Python pickle opcodes. See pickle.py and pickletools.py from Python + * standard library for details. + */ + enum Opcode { + MARK = '(', + STOP = '.', + POP = '0', + POP_MARK = '1', + DUP = '2', + FLOAT = 'F', + INT = 'I', + BININT = 'J', + BININT1 = 'K', + LONG = 'L', + BININT2 = 'M', + NONE = 'N', + PERSID = 'P', + BINPERSID = 'Q', + REDUCE = 'R', + STRING = 'S', + BINSTRING = 'T', + SHORT_BINSTRING = 'U', + UNICODE = 'V', + BINUNICODE = 'X', + APPEND = 'a', + BUILD = 'b', + GLOBAL = 'c', + DICT = 'd', + EMPTY_DICT = '}', + APPENDS = 'e', + GET = 'g', + BINGET = 'h', + INST = 'i', + LONG_BINGET = 'j', + LIST = 'l', + EMPTY_LIST = ']', + OBJ = 'o', + PUT = 'p', + BINPUT = 'q', + LONG_BINPUT = 'r', + SETITEM = 's', + TUPLE = 't', + EMPTY_TUPLE = ')', + SETITEMS = 'u', + BINFLOAT = 'G', + + PROTO = '\x80', + NEWOBJ = '\x81', + EXT1 = '\x82', + EXT2 = '\x83', + EXT4 = '\x84', + TUPLE1 = '\x85', + TUPLE2 = '\x86', + TUPLE3 = '\x87', + NEWTRUE = '\x88', + NEWFALSE = '\x89', + LONG1 = '\x8a', + LONG4 = '\x8b', + }; + +public: + PickleWriter(std::ostream &_os) : + os(_os) { + } + + inline void begin() { + os.put(PROTO); + os.put(2); + } + + inline void end() { + os.put(STOP); + } + + inline void beginDict() { + os.put(EMPTY_DICT); + os.put(BINPUT); + os.put(1); + } + + inline void endDict() { + } + + inline void beginItem() { + } + + inline void beginItem(const char * name) { + writeString(name); + } + + inline void beginItem(const std::string &name) { + beginItem(name.c_str()); + } + + inline void endItem(void) { + os.put(SETITEM); + } + + inline void beginList() { + os.put(EMPTY_LIST); + os.put(BINPUT); + os.put(1); + os.put(MARK); + } + + inline void endList(void) { + os.put(APPENDS); + } + + inline void beginTuple() { + os.put(MARK); + } + + inline void endTuple(void) { + os.put(TUPLE); + } + + inline void writeString(const char *s, size_t length) { + if (!s) { + writeNone(); + return; + } + + if (length < 256) { + os.put(SHORT_BINSTRING); + os.put(length); + } else { + os.put(BINSTRING); + putInt32(length); + } + os.write(s, length); + + os.put(BINPUT); + os.put(1); + } + + inline void writeString(const char *s) { + if (!s) { + writeNone(); + return; + } + + writeString(s, strlen(s)); + } + + inline void writeString(const std::string &s) { + writeString(s.c_str(), s.size()); + } + + inline void writeNone(void) { + os.put(NONE); + } + + inline void writeBool(bool b) { + os.put(b ? NEWTRUE : NEWFALSE); + } + + inline void writeInt(uint8_t i) { + os.put(BININT1); + os.put(i); + } + + inline void writeInt(uint16_t i) { + if (i < 0x100) { + writeInt((uint8_t)i); + } else { + os.put(BININT2); + putInt16(i); + } + } + + inline void writeInt(int32_t i) { + if (0 <= i && i < 0x10000) { + writeInt((uint16_t)i); + } else { + os.put(BININT); + putInt32(i); + } + } + + inline void writeInt(uint32_t i) { + if (i < 0x8000000) { + writeInt((int32_t)i); + } else { + writeLong(i); + } + } + + inline void writeInt(long long i) { + if (-0x8000000 <= i && i < 0x8000000) { + writeInt((int32_t)i); + } else { + writeLong(i); + } + } + + inline void writeInt(unsigned long long i) { + if (i < 0x8000000) { + writeInt((int32_t)i); + } else { + writeLong(i); + } + } + + inline void writeFloat(double f) { + union { + double f; + char c[8]; + } u; + + assert(sizeof u.f == sizeof u.c); + u.f = f; + + os.put(BINFLOAT); + os.put(u.c[7]); + os.put(u.c[6]); + os.put(u.c[5]); + os.put(u.c[4]); + os.put(u.c[3]); + os.put(u.c[2]); + os.put(u.c[1]); + os.put(u.c[0]); + } + + inline void writeByteArray(const void *buf, size_t length) { + os.put(GLOBAL); + os << "__builtin__\nbytearray\n"; + os.put(BINPUT); + os.put(1); + writeString(static_cast(buf), length); + os.put(TUPLE1); + os.put(REDUCE); + } + +protected: + inline void putInt16(uint16_t i) { + os.put( i & 0xff); + os.put( i >> 8 ); + } + + inline void putInt32(uint32_t i) { + os.put( i & 0xff); + os.put((i >> 8) & 0xff); + os.put((i >> 16) & 0xff); + os.put( i >> 24 ); + } + + template< class T > + inline void writeLong(T l) { + os.put(LONG1); + + if (l == 0) { + os.put(0); + return; + } + + // Same as l >> (8 * sizeof l), but without the warnings + T sign; + if (std::numeric_limits::is_signed) { + sign = l < 0 ? ~0 : 0; + } else { + sign = 0; + } + + T sl = l; + unsigned c = 0; + do { + ++c; + } while (sl >>= 8 != sign); + + // Add an extra byte if sign bit doesn't match + if (((l >> (8 * c - 1)) & 1) != ((l >> (8 * sizeof l - 1)) & 1)) { + ++c; + } + os.put(c); + + for (unsigned i = 0; i < c; ++ i) { + os.put(l & 0xff); + l >>= 8; + } + } +}; + +#endif /* _Pickle_HPP_ */ diff --git a/common/image.cpp b/common/image.cpp deleted file mode 100644 index e692313..0000000 --- a/common/image.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * Copyright 2008-2010 VMware, Inc. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - **************************************************************************/ - - -#include -#include - -#include - -#include "image.hpp" - - -namespace image { - - -double Image::compare(Image &ref) -{ - if (width != ref.width || - height != ref.height || - channels < 3 || - ref.channels < 3) { - return 0.0; - } - - // Ignore missing alpha when comparing RGB w/ RGBA, but enforce an equal - // number of channels otherwise. - unsigned minChannels = std::min(channels, ref.channels); - if (channels != ref.channels && minChannels < 3) { - return 0.0; - } - - const unsigned char *pSrc = start(); - const unsigned char *pRef = ref.start(); - - unsigned long long error = 0; - for (unsigned y = 0; y < height; ++y) { - for (unsigned x = 0; x < width; ++x) { - // FIXME: Ignore alpha channel until we are able to pick a visual - // that matches the traces - for (unsigned c = 0; c < minChannels; ++c) { - int delta = pSrc[x*channels + c] - pRef[x*ref.channels + c]; - error += delta*delta; - } - } - - pSrc += stride(); - pRef += ref.stride(); - } - - double numerator = error*2 + 1; - double denominator = height*width*minChannels*255ULL*255ULL*2; - double quotient = numerator/denominator; - - // Precision in bits - double precision = -log(quotient)/log(2.0); - - return precision; -} - - -} /* namespace image */ diff --git a/common/image.hpp b/common/image.hpp deleted file mode 100644 index 7dd18c1..0000000 --- a/common/image.hpp +++ /dev/null @@ -1,123 +0,0 @@ -/************************************************************************** - * - * Copyright 2008-2010 VMware, Inc. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - **************************************************************************/ - -/* - * Image I/O. - */ - -#ifndef _IMAGE_HPP_ -#define _IMAGE_HPP_ - - -#include - - -namespace image { - - -class Image { -public: - unsigned width; - unsigned height; - unsigned channels; - - // Flipped vertically or not - bool flipped; - - // Pixels in RGBA format - unsigned char *pixels; - - inline Image(unsigned w, unsigned h, unsigned c = 4, bool f = false) : - width(w), - height(h), - channels(c), - flipped(f), - pixels(new unsigned char[h*w*c]) - {} - - inline ~Image() { - delete [] pixels; - } - - inline unsigned char *start(void) { - return flipped ? pixels + (height - 1)*width*channels : pixels; - } - - inline const unsigned char *start(void) const { - return flipped ? pixels + (height - 1)*width*channels : pixels; - } - - inline unsigned char *end(void) { - return flipped ? pixels - width*channels : pixels + height*width*channels; - } - - inline const unsigned char *end(void) const { - return flipped ? pixels - width*channels : pixels + height*width*channels; - } - - inline signed stride(void) const { - return flipped ? -(signed)(width*channels) : width*channels; - } - - bool writeBMP(const char *filename) const; - - void writePNM(std::ostream &os, const char *comment = NULL) const; - - inline bool writePNM(const char *filename, const char *comment = NULL) const { - std::ofstream os(filename, std::ofstream::binary); - if (!os) { - return false; - } - writePNM(os, comment); - return true; - } - - bool - writePNG(std::ostream &os) const; - - inline bool - writePNG(const char *filename) const { - std::ofstream os(filename, std::ofstream::binary); - if (!os) { - return false; - } - return writePNG(os); - } - - double compare(Image &ref); -}; - - -Image * -readPNG(const char *filename); - -const char * -readPNMHeader(const char *buffer, size_t size, unsigned *channels, unsigned *width, unsigned *height); - - -} /* namespace image */ - - -#endif /* _IMAGE_HPP_ */ diff --git a/common/image_bmp.cpp b/common/image_bmp.cpp deleted file mode 100644 index e0c6428..0000000 --- a/common/image_bmp.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * Copyright 2008-2010 VMware, Inc. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - **************************************************************************/ - - -#include -#include - -#include "image.hpp" - - -namespace image { - - -#pragma pack(push,2) -struct FileHeader { - uint16_t bfType; - uint32_t bfSize; - uint16_t bfReserved1; - uint16_t bfReserved2; - uint32_t bfOffBits; -}; -#pragma pack(pop) - -struct InfoHeader { - uint32_t biSize; - int32_t biWidth; - int32_t biHeight; - uint16_t biPlanes; - uint16_t biBitCount; - uint32_t biCompression; - uint32_t biSizeImage; - int32_t biXPelsPerMeter; - int32_t biYPelsPerMeter; - uint32_t biClrUsed; - uint32_t biClrImportant; -}; - -struct Pixel { - uint8_t rgbBlue; - uint8_t rgbGreen; - uint8_t rgbRed; - uint8_t rgbAlpha; -}; - - -bool -Image::writeBMP(const char *filename) const { - assert(channels == 4); - - struct FileHeader bmfh; - struct InfoHeader bmih; - unsigned x, y; - - bmfh.bfType = 0x4d42; - bmfh.bfSize = 14 + 40 + height*width*4; - bmfh.bfReserved1 = 0; - bmfh.bfReserved2 = 0; - bmfh.bfOffBits = 14 + 40; - - bmih.biSize = 40; - bmih.biWidth = width; - bmih.biHeight = height; - bmih.biPlanes = 1; - bmih.biBitCount = 32; - bmih.biCompression = 0; - bmih.biSizeImage = height*width*4; - bmih.biXPelsPerMeter = 0; - bmih.biYPelsPerMeter = 0; - bmih.biClrUsed = 0; - bmih.biClrImportant = 0; - - std::ofstream stream(filename, std::ofstream::binary); - - if (!stream) { - return false; - } - - stream.write((const char *)&bmfh, 14); - stream.write((const char *)&bmih, 40); - - unsigned stride = width*4; - - if (flipped) { - for (y = 0; y < height; ++y) { - const unsigned char *ptr = pixels + y * stride; - for (x = 0; x < width; ++x) { - struct Pixel pixel; - pixel.rgbRed = ptr[x*4 + 0]; - pixel.rgbGreen = ptr[x*4 + 1]; - pixel.rgbBlue = ptr[x*4 + 2]; - pixel.rgbAlpha = ptr[x*4 + 3]; - stream.write((const char *)&pixel, 4); - } - } - } else { - y = height; - while (y--) { - const unsigned char *ptr = pixels + y * stride; - for (x = 0; x < width; ++x) { - struct Pixel pixel; - pixel.rgbRed = ptr[x*4 + 0]; - pixel.rgbGreen = ptr[x*4 + 1]; - pixel.rgbBlue = ptr[x*4 + 2]; - pixel.rgbAlpha = ptr[x*4 + 3]; - stream.write((const char *)&pixel, 4); - } - } - } - - stream.close(); - - return true; -} - - -} /* namespace image */ diff --git a/common/image_png.cpp b/common/image_png.cpp deleted file mode 100644 index dba07d4..0000000 --- a/common/image_png.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * Copyright 2008-2010 VMware, Inc. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - **************************************************************************/ - - -#include -#include - -#include -#include -#include - -#include - -#include "image.hpp" - - -namespace image { - - -static const int png_compression_level = Z_BEST_SPEED; - - -static void -pngWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length) -{ - std::ostream *os = (std::ostream *) png_get_io_ptr(png_ptr); - os->write((const char *)data, length); -} - -bool -Image::writePNG(std::ostream &os) const -{ - png_structp png_ptr; - png_infop info_ptr; - int color_type; - - switch (channels) { - case 4: - color_type = PNG_COLOR_TYPE_RGB_ALPHA; - break; - case 3: - color_type = PNG_COLOR_TYPE_RGB; - break; - case 2: - color_type = PNG_COLOR_TYPE_GRAY_ALPHA; - break; - case 1: - color_type = PNG_COLOR_TYPE_GRAY; - break; - default: - assert(0); - goto no_png; - } - - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) - goto no_png; - - info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { - png_destroy_write_struct(&png_ptr, NULL); - goto no_png; - } - - if (setjmp(png_jmpbuf(png_ptr))) { - png_destroy_write_struct(&png_ptr, &info_ptr); - goto no_png; - } - - png_set_write_fn(png_ptr, &os, pngWriteCallback, NULL); - - png_set_IHDR(png_ptr, info_ptr, width, height, 8, - color_type, PNG_INTERLACE_NONE, - PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - - png_set_compression_level(png_ptr, png_compression_level); - - png_write_info(png_ptr, info_ptr); - - if (!flipped) { - for (unsigned y = 0; y < height; ++y) { - png_bytep row = (png_bytep)(pixels + y*width*channels); - png_write_rows(png_ptr, &row, 1); - } - } else { - unsigned y = height; - while (y--) { - png_bytep row = (png_bytep)(pixels + y*width*channels); - png_write_rows(png_ptr, &row, 1); - } - } - - png_write_end(png_ptr, info_ptr); - png_destroy_write_struct(&png_ptr, &info_ptr); - - return true; - -no_png: - return false; -} - - -Image * -readPNG(const char *filename) -{ - FILE *fp; - png_structp png_ptr; - png_infop info_ptr; - png_infop end_info; - Image *image; - - fp = fopen(filename, "rb"); - if (!fp) - goto no_fp; - - png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) - goto no_png; - - info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { - png_destroy_read_struct(&png_ptr, NULL, NULL); - goto no_png; - } - - end_info = png_create_info_struct(png_ptr); - if (!end_info) { - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - goto no_png; - } - - if (setjmp(png_jmpbuf(png_ptr))) { - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - goto no_png; - } - - png_init_io(png_ptr, fp); - - png_read_info(png_ptr, info_ptr); - - png_uint_32 width, height; - int bit_depth, color_type, interlace_type, compression_type, filter_method; - - png_get_IHDR(png_ptr, info_ptr, - &width, &height, - &bit_depth, &color_type, &interlace_type, - &compression_type, &filter_method); - - image = new Image(width, height); - if (!image) - goto no_image; - - /* Convert to RGBA8 */ - if (color_type == PNG_COLOR_TYPE_PALETTE) - png_set_palette_to_rgb(png_ptr); - if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) - png_set_expand_gray_1_2_4_to_8(png_ptr); - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) - png_set_tRNS_to_alpha(png_ptr); - if (bit_depth == 16) - png_set_strip_16(png_ptr); - - for (unsigned y = 0; y < height; ++y) { - png_bytep row = (png_bytep)(image->pixels + y*width*4); - png_read_row(png_ptr, row, NULL); - } - - png_read_end(png_ptr, info_ptr); - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); - fclose(fp); - return image; - -no_image: - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); -no_png: - fclose(fp); -no_fp: - return NULL; -} - - - -} /* namespace image */ diff --git a/common/image_pnm.cpp b/common/image_pnm.cpp deleted file mode 100644 index f9cd05d..0000000 --- a/common/image_pnm.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 Jose Fonseca - * Copyright 2008-2010 VMware, Inc. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - **************************************************************************/ - - -#include -#include -#include -#include - -#include "image.hpp" - - -namespace image { - -/** - * http://en.wikipedia.org/wiki/Netpbm_format - * http://netpbm.sourceforge.net/doc/ppm.html - */ -void -Image::writePNM(std::ostream &os, const char *comment) const { - assert(channels == 1 || channels >= 3); - - os << (channels == 1 ? "P5" : "P6") << "\n"; - if (comment) { - os << "#" << comment << "\n"; - } - os << width << " " << height << "\n"; - os << "255" << "\n"; - - const unsigned char *row; - - if (channels == 1 || channels == 3) { - for (row = start(); row != end(); row += stride()) { - os.write((const char *)row, width*channels); - } - } else { - unsigned char *tmp = new unsigned char[width*3]; - if (channels == 4) { - for (row = start(); row != end(); row += stride()) { - const uint32_t *src = (const uint32_t *)row; - uint32_t *dst = (uint32_t *)tmp; - unsigned x; - for (x = 0; x + 4 <= width; x += 4) { - /* - * It's much faster to access dwords than bytes. - * - * FIXME: Big-endian version. - */ - - uint32_t rgba0 = *src++ & 0xffffff; - uint32_t rgba1 = *src++ & 0xffffff; - uint32_t rgba2 = *src++ & 0xffffff; - uint32_t rgba3 = *src++ & 0xffffff; - uint32_t rgb0 = rgba0 - | (rgba1 << 24); - uint32_t rgb1 = (rgba1 >> 8) - | (rgba2 << 16); - uint32_t rgb2 = (rgba2 >> 16) - | (rgba3 << 8); - *dst++ = rgb0; - *dst++ = rgb1; - *dst++ = rgb2; - } - for (; x < width; ++x) { - tmp[x*3 + 0] = row[x*4 + 0]; - tmp[x*3 + 1] = row[x*4 + 1]; - tmp[x*3 + 2] = row[x*4 + 2]; - } - os.write((const char *)tmp, width*3); - } - } else if (channels == 2) { - for (row = start(); row != end(); row += stride()) { - const unsigned char *src = row; - unsigned char *dst = tmp; - for (unsigned x = 0; x < width; ++x) { - *dst++ = *src++; - *dst++ = *src++; - *dst++ = 0; - } - os.write((const char *)tmp, width*3); - } - } else { - assert(0); - } - delete [] tmp; - } -} - -const char * -readPNMHeader(const char *buffer, size_t bufferSize, unsigned *channels, unsigned *width, unsigned *height) -{ - *channels = 0; - *width = 0; - *height = 0; - - const char *currentBuffer = buffer; - const char *nextBuffer; - - // parse number of channels - int scannedChannels = sscanf(currentBuffer, "P%d\n", channels); - if (scannedChannels != 1) { // validate scanning of channels - // invalid channel line - return buffer; - } - // convert channel token to number of channels - *channels = (*channels == 5) ? 1 : 3; - - // advance past channel line - nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1; - bufferSize -= nextBuffer - currentBuffer; - currentBuffer = nextBuffer; - - // skip over optional comment - if (*currentBuffer == '#') { - // advance past comment line - nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1; - bufferSize -= nextBuffer - currentBuffer; - currentBuffer = nextBuffer; - } - - // parse dimensions of image - int scannedDimensions = sscanf(currentBuffer, "%d %d\n", width, height); - if (scannedDimensions != 2) { // validate scanning of dimensions - // invalid dimension line - return buffer; - } - - // advance past dimension line - nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1; - bufferSize -= nextBuffer - currentBuffer; - currentBuffer = nextBuffer; - - // skip over "255\n" at end of header - nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1; - - // return start of image data - return nextBuffer; -} - -} /* namespace image */ diff --git a/common/os_time.hpp b/common/os_time.hpp index 13cc733..3e4960e 100644 --- a/common/os_time.hpp +++ b/common/os_time.hpp @@ -73,7 +73,7 @@ namespace os { return counter.QuadPart; #elif defined(__linux__) struct timespec tp; - if (clock_gettime(CLOCK_REALTIME, &tp) == -1) { + if (clock_gettime(CLOCK_MONOTONIC, &tp) == -1) { return 0; } return tp.tv_sec * 1000000000LL + tp.tv_nsec; diff --git a/common/pickle.hpp b/common/pickle.hpp deleted file mode 100644 index 84b643a..0000000 --- a/common/pickle.hpp +++ /dev/null @@ -1,331 +0,0 @@ -/************************************************************************** - * - * Copyright 2012 Jose Fonseca - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - **************************************************************************/ - -/* - * Python pickle writer - */ - -#ifndef _PICKLE_HPP_ -#define _PICKLE_HPP_ - -#include -#include -#include - -#include -#include -#include - - -class PickleWriter -{ -private: - std::ostream &os; - - /* - * Python pickle opcodes. See pickle.py and pickletools.py from Python - * standard library for details. - */ - enum Opcode { - MARK = '(', - STOP = '.', - POP = '0', - POP_MARK = '1', - DUP = '2', - FLOAT = 'F', - INT = 'I', - BININT = 'J', - BININT1 = 'K', - LONG = 'L', - BININT2 = 'M', - NONE = 'N', - PERSID = 'P', - BINPERSID = 'Q', - REDUCE = 'R', - STRING = 'S', - BINSTRING = 'T', - SHORT_BINSTRING = 'U', - UNICODE = 'V', - BINUNICODE = 'X', - APPEND = 'a', - BUILD = 'b', - GLOBAL = 'c', - DICT = 'd', - EMPTY_DICT = '}', - APPENDS = 'e', - GET = 'g', - BINGET = 'h', - INST = 'i', - LONG_BINGET = 'j', - LIST = 'l', - EMPTY_LIST = ']', - OBJ = 'o', - PUT = 'p', - BINPUT = 'q', - LONG_BINPUT = 'r', - SETITEM = 's', - TUPLE = 't', - EMPTY_TUPLE = ')', - SETITEMS = 'u', - BINFLOAT = 'G', - - PROTO = '\x80', - NEWOBJ = '\x81', - EXT1 = '\x82', - EXT2 = '\x83', - EXT4 = '\x84', - TUPLE1 = '\x85', - TUPLE2 = '\x86', - TUPLE3 = '\x87', - NEWTRUE = '\x88', - NEWFALSE = '\x89', - LONG1 = '\x8a', - LONG4 = '\x8b', - }; - -public: - PickleWriter(std::ostream &_os) : - os(_os) { - } - - inline void begin() { - os.put(PROTO); - os.put(2); - } - - inline void end() { - os.put(STOP); - } - - inline void beginDict() { - os.put(EMPTY_DICT); - os.put(BINPUT); - os.put(1); - } - - inline void endDict() { - } - - inline void beginItem() { - } - - inline void beginItem(const char * name) { - writeString(name); - } - - inline void beginItem(const std::string &name) { - beginItem(name.c_str()); - } - - inline void endItem(void) { - os.put(SETITEM); - } - - inline void beginList() { - os.put(EMPTY_LIST); - os.put(BINPUT); - os.put(1); - os.put(MARK); - } - - inline void endList(void) { - os.put(APPENDS); - } - - inline void beginTuple() { - os.put(MARK); - } - - inline void endTuple(void) { - os.put(TUPLE); - } - - inline void writeString(const char *s, size_t length) { - if (!s) { - writeNone(); - return; - } - - if (length < 256) { - os.put(SHORT_BINSTRING); - os.put(length); - } else { - os.put(BINSTRING); - putInt32(length); - } - os.write(s, length); - - os.put(BINPUT); - os.put(1); - } - - inline void writeString(const char *s) { - if (!s) { - writeNone(); - return; - } - - writeString(s, strlen(s)); - } - - inline void writeString(const std::string &s) { - writeString(s.c_str(), s.size()); - } - - inline void writeNone(void) { - os.put(NONE); - } - - inline void writeBool(bool b) { - os.put(b ? NEWTRUE : NEWFALSE); - } - - inline void writeInt(uint8_t i) { - os.put(BININT1); - os.put(i); - } - - inline void writeInt(uint16_t i) { - if (i < 0x100) { - writeInt((uint8_t)i); - } else { - os.put(BININT2); - putInt16(i); - } - } - - inline void writeInt(int32_t i) { - if (0 <= i && i < 0x10000) { - writeInt((uint16_t)i); - } else { - os.put(BININT); - putInt32(i); - } - } - - inline void writeInt(uint32_t i) { - if (i < 0x8000000) { - writeInt((int32_t)i); - } else { - writeLong(i); - } - } - - inline void writeInt(long long i) { - if (-0x8000000 <= i && i < 0x8000000) { - writeInt((int32_t)i); - } else { - writeLong(i); - } - } - - inline void writeInt(unsigned long long i) { - if (i < 0x8000000) { - writeInt((int32_t)i); - } else { - writeLong(i); - } - } - - inline void writeFloat(double f) { - union { - double f; - char c[8]; - } u; - - assert(sizeof u.f == sizeof u.c); - u.f = f; - - os.put(BINFLOAT); - os.put(u.c[7]); - os.put(u.c[6]); - os.put(u.c[5]); - os.put(u.c[4]); - os.put(u.c[3]); - os.put(u.c[2]); - os.put(u.c[1]); - os.put(u.c[0]); - } - - inline void writeByteArray(const void *buf, size_t length) { - os.put(GLOBAL); - os << "__builtin__\nbytearray\n"; - os.put(BINPUT); - os.put(1); - writeString(static_cast(buf), length); - os.put(TUPLE1); - os.put(REDUCE); - } - -protected: - inline void putInt16(uint16_t i) { - os.put( i & 0xff); - os.put( i >> 8 ); - } - - inline void putInt32(uint32_t i) { - os.put( i & 0xff); - os.put((i >> 8) & 0xff); - os.put((i >> 16) & 0xff); - os.put( i >> 24 ); - } - - template< class T > - inline void writeLong(T l) { - os.put(LONG1); - - if (l == 0) { - os.put(0); - return; - } - - // Same as l >> (8 * sizeof l), but without the warnings - T sign; - if (std::numeric_limits::is_signed) { - sign = l < 0 ? ~0 : 0; - } else { - sign = 0; - } - - T sl = l; - unsigned c = 0; - do { - ++c; - } while (sl >>= 8 != sign); - - // Add an extra byte if sign bit doesn't match - if (((l >> (8 * c - 1)) & 1) != ((l >> (8 * sizeof l - 1)) & 1)) { - ++c; - } - os.put(c); - - for (unsigned i = 0; i < c; ++ i) { - os.put(l & 0xff); - l >>= 8; - } - } -}; - -#endif /* _Pickle_HPP_ */ diff --git a/common/trace_api.hpp b/common/trace_api.hpp index 4c896db..3d491fe 100644 --- a/common/trace_api.hpp +++ b/common/trace_api.hpp @@ -48,9 +48,7 @@ enum API { API_D3D7, API_D3D8, API_D3D9, - API_D3D10, - API_D3D10_1, - API_D3D11, + API_DXGI, // D3D10.x, D3D11.x }; diff --git a/common/trace_parser.cpp b/common/trace_parser.cpp index 7a8c4f9..f3aea7e 100644 --- a/common/trace_parser.cpp +++ b/common/trace_parser.cpp @@ -248,9 +248,10 @@ Parser::parse_function_sig(void) { api = trace::API_GL; } else if (n[0] == 'e' && n[1] == 'g' && n[2] == 'l' && n[3] >= 'A' && n[3] <= 'Z') { // egl[A-Z]* api = trace::API_EGL; - } else if (n[0] == 'D' && - ((n[1] == 'i' && n[2] == 'r' && n[3] == 'e' && n[4] == 'c' && n[5] == 't') || // Direct* - (n[1] == '3' && n[2] == 'D'))) { // D3D* + } else if ((n[0] == 'D' && + ((n[1] == 'i' && n[2] == 'r' && n[3] == 'e' && n[4] == 'c' && n[5] == 't') || // Direct* + (n[1] == '3' && n[2] == 'D'))) || // D3D* + (n[0] == 'C' && n[1] == 'r' && n[2] == 'e' && n[3] == 'a' && n[4] == 't' && n[5] == 'e')) { // Create* api = trace::API_DX; } else { /* TODO */ diff --git a/common/trace_resource.cpp b/common/trace_resource.cpp deleted file mode 100644 index 04ac327..0000000 --- a/common/trace_resource.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/********************************************************************* - * - * Copyright 2011 Intel Corporation - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, - * modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - *********************************************************************/ - - -#include - -#include "os_string.hpp" -#include "trace_tools.hpp" - - - -namespace trace { - -os::String -findProgram(const char*programFilename) -{ - os::String programPath; - - os::String processDir = os::getProcessName(); - processDir.trimFilename(); - - programPath = processDir; - programPath.join(programFilename); - if (programPath.exists()) { - return programPath; - } - -#ifndef _WIN32 - // Try absolute install directory - programPath = APITRACE_PROGRAMS_INSTALL_DIR; - programPath.join(programFilename); - if (programPath.exists()) { - return programPath; - } -#endif - - return ""; -} - -os::String -findWrapper(const char *wrapperFilename) -{ - os::String wrapperPath; - - os::String processDir = os::getProcessName(); - processDir.trimFilename(); - - // Try relative build directory - // XXX: Just make build and install directory layout match - wrapperPath = processDir; - wrapperPath.join("wrappers"); - wrapperPath.join(wrapperFilename); - if (wrapperPath.exists()) { - return wrapperPath; - } - - // Try relative install directory - wrapperPath = processDir; -#if defined(_WIN32) - wrapperPath.join("..\\lib\\wrappers"); -#elif defined(__APPLE__) - wrapperPath.join("../lib/wrappers"); -#else - wrapperPath.join("../lib/apitrace/wrappers"); -#endif - wrapperPath.join(wrapperFilename); - if (wrapperPath.exists()) { - return wrapperPath; - } - -#ifndef _WIN32 - // Try absolute install directory - wrapperPath = APITRACE_WRAPPERS_INSTALL_DIR; - wrapperPath.join(wrapperFilename); - if (wrapperPath.exists()) { - return wrapperPath; - } -#endif - - return ""; -} - -os::String -findScript(const char *scriptFilename) -{ - os::String scriptPath; - - os::String processDir = os::getProcessName(); - processDir.trimFilename(); - - // Try relative build directory - // XXX: Just make build and install directory layout match - scriptPath = processDir; - scriptPath.join("scripts"); - scriptPath.join(scriptFilename); - if (scriptPath.exists()) { - return scriptPath; - } - - // Try relative install directory - scriptPath = processDir; -#if defined(_WIN32) - scriptPath.join("..\\lib\\scripts"); -#elif defined(__APPLE__) - scriptPath.join("../lib/scripts"); -#else - scriptPath.join("../lib/apitrace/scripts"); -#endif - scriptPath.join(scriptFilename); - if (scriptPath.exists()) { - return scriptPath; - } - -#ifndef _WIN32 - // Try absolute install directory - scriptPath = APITRACE_SCRIPTS_INSTALL_DIR; - scriptPath.join(scriptFilename); - if (scriptPath.exists()) { - return scriptPath; - } -#endif - - std::cerr << "error: cannot find " << scriptFilename << " script\n"; - - return ""; -} - - -} /* namespace trace */ diff --git a/common/trace_resource.hpp b/common/trace_resource.hpp deleted file mode 100644 index 28c5727..0000000 --- a/common/trace_resource.hpp +++ /dev/null @@ -1,50 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 VMware, Inc. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - **************************************************************************/ - -#ifndef _TRACE_RESOURCE_HPP_ -#define _TRACE_RESOURCE_HPP_ - - -#include - -#include "os_string.hpp" -#include "trace_api.hpp" - - -namespace trace { - - -os::String -findProgram(const char*programFilename); - -os::String -findScript(const char *name); - -os::String -findWrapper(const char *wrapperFilename); - -} /* namespace trace */ - -#endif /* _TRACE_RESOURCE_HPP_ */ diff --git a/common/trace_tools.hpp b/common/trace_tools.hpp deleted file mode 100644 index ddfe888..0000000 --- a/common/trace_tools.hpp +++ /dev/null @@ -1,48 +0,0 @@ -/************************************************************************** - * - * Copyright 2011 VMware, Inc. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - **************************************************************************/ - -#ifndef _TRACE_TOOLS_HPP_ -#define _TRACE_TOOLS_HPP_ - - -#include - -#include "trace_api.hpp" - - -namespace trace { - - -int -traceProgram(API api, - char * const *argv, - const char *output = NULL, - bool verbose = false); - - - -} /* namespace trace */ - -#endif /* _TRACE_TOOLS_HPP_ */ diff --git a/common/trace_tools_trace.cpp b/common/trace_tools_trace.cpp deleted file mode 100644 index 4c0082d..0000000 --- a/common/trace_tools_trace.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/********************************************************************* - * - * Copyright 2011 Intel Corporation - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, - * modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - *********************************************************************/ - - -#include - -#include - -#include "os_string.hpp" -#include "os_process.hpp" -#include "trace_tools.hpp" -#include "trace_resource.hpp" - - - -namespace trace { - - -#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 const char *glWrappers[] = { - GL_TRACE_WRAPPER, - NULL -}; - -#ifdef EGL_TRACE_WRAPPER -static const char *eglWrappers[] = { - EGL_TRACE_WRAPPER, - NULL -}; -#endif - -#ifdef _WIN32 -static const char *d3d7Wrappers[] = { - "ddraw.dll", - NULL -}; - -static const char *d3d8Wrappers[] = { - "d3d8.dll", - NULL -}; - -static const char *d3d9Wrappers[] = { - "d3d9.dll", - NULL -}; - -static const char *dxgiWrappers[] = { - "dxgitrace.dll", - //"dxgi.dll", - "d3d10.dll", - "d3d10_1.dll", - "d3d11.dll", - NULL -}; -#endif - -int -traceProgram(API api, - char * const *argv, - const char *output, - bool verbose) -{ - const char **wrapperFilenames; - unsigned numWrappers; - int status = 1; - - /* - * TODO: simplify code - */ - - switch (api) { - case API_GL: - wrapperFilenames = glWrappers; - break; -#ifdef EGL_TRACE_WRAPPER - case API_EGL: - wrapperFilenames = eglWrappers; - break; -#endif -#ifdef _WIN32 - case API_D3D7: - wrapperFilenames = d3d7Wrappers; - break; - case API_D3D8: - wrapperFilenames = d3d8Wrappers; - break; - case API_D3D9: - wrapperFilenames = d3d9Wrappers; - break; - case API_D3D10: - case API_D3D10_1: - case API_D3D11: - wrapperFilenames = dxgiWrappers; - break; -#endif - default: - std::cerr << "error: unsupported API\n"; - return 1; - } - - numWrappers = 0; - while (wrapperFilenames[numWrappers]) { - ++numWrappers; - } - - unsigned i; - for (i = 0; i < numWrappers; ++i) { - const char *wrapperFilename = wrapperFilenames[i]; - - os::String wrapperPath = findWrapper(wrapperFilename); - - if (!wrapperPath.length()) { - std::cerr << "error: failed to find " << wrapperFilename << "\n"; - goto exit; - } - -#if defined(_WIN32) - /* On Windows copy the wrapper to the program directory. - */ - if (!copyWrapper(wrapperPath, argv[0], verbose)) { - goto exit; - } -#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) - assert(numWrappers == 1); - 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"; - } - - status = os::execute(argv); - -exit: -#if defined(TRACE_VARIABLE) - os::unsetEnvironment(TRACE_VARIABLE); -#endif -#if defined(_WIN32) - for (unsigned j = 0; j < i; ++j) { - const char *wrapperFilename = wrapperFilenames[j]; - os::String tmpWrapper(argv[0]); - tmpWrapper.trimFilename(); - tmpWrapper.join(wrapperFilename); - os::removeFile(tmpWrapper); - } -#endif - - if (output) { - os::unsetEnvironment("TRACE_FILE"); - } - - return status; - -} - - -} /* namespace trace */ diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 8968cb4..773d344 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -58,23 +58,40 @@ QT4_WRAP_UI(qapitrace_UIS_H ${qapitrace_UIS}) #add_app_icon(qapitrace_SRCS ../icons/hi*-qapitrace.png) link_directories(${LINK_DIRECTORIES} ${QJSON_LIBRARY_DIRS}) -include_directories(${QT_INCLUDES} ${QJSON_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/..) +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_SOURCE_DIR} + ${QJSON_INCLUDE_DIR} + ${QT_INCLUDES} +) + +if (WIN32) + # Use Windows subsystem (i.e., no console). + set (qapitrace_SUBSYSTEM "WIN32") +endif () -add_executable(qapitrace ${qapitrace_SRCS} ${qapitrace_UIS_H}) +add_executable(qapitrace ${qapitrace_SUBSYSTEM} ${qapitrace_SRCS} ${qapitrace_UIS_H}) target_link_libraries (qapitrace + image common - ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} ${SNAPPY_LIBRARIES} ${QJSON_LIBRARIES} ${QT_LIBRARIES} ) -# Recent builds of Qt no longer support i386 architecture if (APPLE) + # Recent builds of Qt no longer support i386 architecture set_target_properties (qapitrace PROPERTIES OSX_ARCHITECTURES x86_64) endif () +if (MSVC) + # When the Windows subsystem is chosen by default MSVC expects WinMain() + # entry point, but we rather use plain old main() everywhere. + set_target_properties (qapitrace PROPERTIES LINK_FLAGS "/ENTRY:mainCRTStartup") +endif () + ########### install files ############### diff --git a/gui/apitrace.cpp b/gui/apitrace.cpp index 6a8ebe2..a69ce2f 100644 --- a/gui/apitrace.cpp +++ b/gui/apitrace.cpp @@ -393,6 +393,9 @@ void ApiTrace::loaderSearchResult(const ApiTrace::SearchRequest &request, void ApiTrace::findFrameStart(ApiTraceFrame *frame) { + if (!frame) + return; + if (frame->isLoaded()) { emit foundFrameStart(frame); } else { @@ -402,6 +405,9 @@ void ApiTrace::findFrameStart(ApiTraceFrame *frame) void ApiTrace::findFrameEnd(ApiTraceFrame *frame) { + if (!frame) + return; + if (frame->isLoaded()) { emit foundFrameEnd(frame); } else { diff --git a/gui/graphing/histogramview.cpp b/gui/graphing/histogramview.cpp index 0b94577..2dd3d8e 100644 --- a/gui/graphing/histogramview.cpp +++ b/gui/graphing/histogramview.cpp @@ -228,8 +228,8 @@ void HistogramView::paintEvent(QPaintEvent *) qint64 HistogramView::itemAtPosition(QPoint pos) { double dvdx = m_viewWidth / (double)width(); - qint64 left = qFloor(dvdx) * (pos.x() - 1) + m_viewLeft; - qint64 right = qCeil(dvdx) * (pos.x() + 1) + m_viewLeft; + qint64 left = qFloor(dvdx * (pos.x() - 1)) + m_viewLeft; + qint64 right = qCeil(dvdx * (pos.x() + 1)) + m_viewLeft; qint64 longestIndex = 0; qint64 longestValue = 0; diff --git a/gui/main.cpp b/gui/main.cpp index 0ed50ed..471dec7 100644 --- a/gui/main.cpp +++ b/gui/main.cpp @@ -3,6 +3,9 @@ #include "apitrace.h" #include "apitracecall.h" +#include "os_string.hpp" +#include "os_process.hpp" + #include #include #include @@ -32,6 +35,15 @@ int main(int argc, char **argv) qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType >(); + +#ifndef Q_OS_WIN + os::String currentProcess = os::getProcessName(); + currentProcess.trimFilename(); + QString path = qgetenv("PATH"); + path = QLatin1String(currentProcess.str()) + QLatin1String(":") + path; + qputenv("PATH", path.toLatin1()); +#endif + QStringList args = app.arguments(); int i = 1; diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index caf9e37..d6ebd2f 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -48,6 +48,7 @@ MainWindow::MainWindow() m_nonDefaultsLookupEvent(0) { m_ui.setupUi(this); + updateActionsState(false); initObjects(); initConnections(); } @@ -228,11 +229,7 @@ void MainWindow::replayProfile() void MainWindow::replayStop() { m_retracer->quit(); - m_ui.actionStop->setEnabled(false); - m_ui.actionReplay->setEnabled(true); - m_ui.actionProfile->setEnabled(true); - m_ui.actionLookupState->setEnabled(true); - m_ui.actionShowThumbnails->setEnabled(true); + updateActionsState(true, true); } void MainWindow::newTraceFile(const QString &fileName) @@ -243,18 +240,11 @@ void MainWindow::newTraceFile(const QString &fileName) m_trace->setFileName(fileName); if (fileName.isEmpty()) { - m_ui.actionReplay->setEnabled(false); - m_ui.actionProfile->setEnabled(false); - m_ui.actionLookupState->setEnabled(false); - m_ui.actionShowThumbnails->setEnabled(false); + updateActionsState(false); setWindowTitle(tr("QApiTrace")); } else { + updateActionsState(true); QFileInfo info(fileName); - m_ui.actionReplay->setEnabled(true); - m_ui.actionProfile->setEnabled(true); - m_ui.actionLookupState->setEnabled(true); - m_ui.actionShowThumbnails->setEnabled(true); - m_ui.actionTrim->setEnabled(true); setWindowTitle( tr("QApiTrace - %1").arg(info.fileName())); } @@ -262,12 +252,7 @@ void MainWindow::newTraceFile(const QString &fileName) void MainWindow::replayFinished(const QString &message) { - m_ui.actionStop->setEnabled(false); - m_ui.actionReplay->setEnabled(true); - m_ui.actionProfile->setEnabled(true); - m_ui.actionLookupState->setEnabled(true); - m_ui.actionShowThumbnails->setEnabled(true); - + updateActionsState(true); m_progressBar->hide(); statusBar()->showMessage(message, 2000); m_stateEvent = 0; @@ -280,11 +265,7 @@ void MainWindow::replayFinished(const QString &message) void MainWindow::replayError(const QString &message) { - m_ui.actionStop->setEnabled(false); - m_ui.actionReplay->setEnabled(true); - m_ui.actionProfile->setEnabled(true); - m_ui.actionLookupState->setEnabled(true); - m_ui.actionShowThumbnails->setEnabled(true); + updateActionsState(true); m_stateEvent = 0; m_nonDefaultsLookupEvent = 0; @@ -936,6 +917,47 @@ void MainWindow::initConnections() this, SLOT(slotJumpTo(int))); } +void MainWindow::updateActionsState(bool traceLoaded, bool stopped) +{ + if (traceLoaded) { + /* Edit */ + m_ui.actionFind ->setEnabled(true); + m_ui.actionGo ->setEnabled(true); + m_ui.actionGoFrameStart ->setEnabled(true); + m_ui.actionGoFrameEnd ->setEnabled(true); + + /* Trace */ + if (stopped) { + m_ui.actionStop->setEnabled(false); + m_ui.actionReplay->setEnabled(true); + } + else { + m_ui.actionStop->setEnabled(true); + m_ui.actionReplay->setEnabled(false); + } + + m_ui.actionProfile ->setEnabled(true); + m_ui.actionLookupState ->setEnabled(true); + m_ui.actionShowThumbnails->setEnabled(true); + m_ui.actionTrim ->setEnabled(true); + } + else { + /* Edit */ + m_ui.actionFind ->setEnabled(false); + m_ui.actionGo ->setEnabled(false); + m_ui.actionGoFrameStart ->setEnabled(false); + m_ui.actionGoFrameEnd ->setEnabled(false); + + /* Trace */ + m_ui.actionReplay ->setEnabled(false); + m_ui.actionProfile ->setEnabled(false); + m_ui.actionStop ->setEnabled(false); + m_ui.actionLookupState ->setEnabled(false); + m_ui.actionShowThumbnails->setEnabled(false); + m_ui.actionTrim ->setEnabled(false); + } +} + void MainWindow::closeEvent(QCloseEvent * event) { m_profileDialog->close(); diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 2248127..78267ef 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -98,6 +98,7 @@ private slots: private: void initObjects(); void initConnections(); + void updateActionsState(bool traceLoaded, bool stopped = true); void newTraceFile(const QString &fileName); void replayTrace(bool dumpState, bool dumpThumbnails); void trimEvent(); diff --git a/gui/retracer.cpp b/gui/retracer.cpp index 213704b..2928ed6 100644 --- a/gui/retracer.cpp +++ b/gui/retracer.cpp @@ -3,7 +3,7 @@ #include "apitracecall.h" #include "thumbnail.h" -#include "image.hpp" +#include "image/image.hpp" #include "trace_profiler.hpp" @@ -137,19 +137,6 @@ Retracer::Retracer(QObject *parent) m_profilePixels(false) { qRegisterMetaType >(); - -#ifdef Q_OS_WIN - QString format = QLatin1String("%1;"); -#else - QString format = QLatin1String("%1:"); -#endif - QString buildPath = format.arg(APITRACE_BINARY_DIR); - m_processEnvironment = QProcessEnvironment::systemEnvironment(); - m_processEnvironment.insert("PATH", buildPath + - m_processEnvironment.value("PATH")); - - qputenv("PATH", - m_processEnvironment.value("PATH").toLatin1()); } QString Retracer::fileName() const @@ -271,9 +258,7 @@ void Retracer::run() case trace::API_D3D7: case trace::API_D3D8: case trace::API_D3D9: - case trace::API_D3D10: - case trace::API_D3D10_1: - case trace::API_D3D11: + case trace::API_DXGI: #ifdef Q_OS_WIN prog = QLatin1String("d3dretrace"); #else diff --git a/gui/retracer.h b/gui/retracer.h index e889d88..af1a3d9 100644 --- a/gui/retracer.h +++ b/gui/retracer.h @@ -65,8 +65,6 @@ private: bool m_profileGpu; bool m_profileCpu; bool m_profilePixels; - - QProcessEnvironment m_processEnvironment; }; #endif diff --git a/gui/tracedialog.cpp b/gui/tracedialog.cpp index fcfdf46..b8e438d 100644 --- a/gui/tracedialog.cpp +++ b/gui/tracedialog.cpp @@ -50,7 +50,7 @@ void TraceDialog::browse() tr("Find the application"), QDir::currentPath()); - if (isFileOk(fileName)) { + if (!fileName.isEmpty() && isFileOk(fileName)) { applicationEdit->setText(fileName); } } diff --git a/gui/traceprocess.cpp b/gui/traceprocess.cpp index 6f4d0b9..8d57e52 100644 --- a/gui/traceprocess.cpp +++ b/gui/traceprocess.cpp @@ -15,16 +15,6 @@ TraceProcess::TraceProcess(QObject *parent) this, SLOT(traceFinished())); connect(m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(traceError(QProcess::ProcessError))); - -#ifdef Q_OS_WIN - QString format = QLatin1String("%1;"); -#else - QString format = QLatin1String("%1:"); -#endif - QString buildPath = format.arg(APITRACE_BINARY_DIR); - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - env.insert("PATH", buildPath + env.value("PATH")); - qputenv("PATH", env.value("PATH").toLatin1()); } TraceProcess::~TraceProcess() diff --git a/gui/trimprocess.cpp b/gui/trimprocess.cpp index c23475d..34639c6 100644 --- a/gui/trimprocess.cpp +++ b/gui/trimprocess.cpp @@ -15,16 +15,6 @@ TrimProcess::TrimProcess(QObject *parent) this, SLOT(trimFinished())); connect(m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(trimError(QProcess::ProcessError))); - -#ifdef Q_OS_WIN - QString format = QLatin1String("%1;"); -#else - QString format = QLatin1String("%1:"); -#endif - QString buildPath = format.arg(APITRACE_BINARY_DIR); - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - env.insert("PATH", buildPath + env.value("PATH")); - qputenv("PATH", env.value("PATH").toLatin1()); } TrimProcess::~TrimProcess() diff --git a/gui/ui/mainwindow.ui b/gui/ui/mainwindow.ui index 7cbb3d1..06f4503 100644 --- a/gui/ui/mainwindow.ui +++ b/gui/ui/mainwindow.ui @@ -124,7 +124,7 @@ 0 - + about:blank @@ -493,9 +493,6 @@ - - false - :/resources/media-playback-start.png:/resources/media-playback-start.png @@ -508,9 +505,6 @@ - - false - :/resources/media-playback-stop.png:/resources/media-playback-stop.png @@ -520,9 +514,6 @@ - - false - :/resources/media-record.png:/resources/media-record.png @@ -535,9 +526,6 @@ - - false - Show &Thumbnails @@ -546,9 +534,6 @@ - - false - Tr&im @@ -567,7 +552,10 @@ :/resources/document-new.png:/resources/document-new.png - New + &New... + + + Ctrl+N @@ -576,7 +564,7 @@ :/resources/edit-find.png:/resources/edit-find.png - Find + &Find Ctrl+F @@ -638,9 +626,6 @@ - - false - &Profile diff --git a/gui/ui/tracedialog.ui b/gui/ui/tracedialog.ui index a3e5240..f6eccf4 100644 --- a/gui/ui/tracedialog.ui +++ b/gui/ui/tracedialog.ui @@ -47,7 +47,7 @@ - Browse + Browse... false diff --git a/image/CMakeLists.txt b/image/CMakeLists.txt new file mode 100644 index 0000000..ee4d98f --- /dev/null +++ b/image/CMakeLists.txt @@ -0,0 +1,14 @@ +include_directories ( + ${PNG_INCLUDE_DIR} +) + +add_library (image STATIC + image.cpp + image_bmp.cpp + image_png.cpp + image_pnm.cpp +) + +target_link_libraries (image + ${PNG_LIBRARIES} +) diff --git a/image/image.cpp b/image/image.cpp new file mode 100644 index 0000000..e692313 --- /dev/null +++ b/image/image.cpp @@ -0,0 +1,84 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * Copyright 2008-2010 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + +#include +#include + +#include + +#include "image.hpp" + + +namespace image { + + +double Image::compare(Image &ref) +{ + if (width != ref.width || + height != ref.height || + channels < 3 || + ref.channels < 3) { + return 0.0; + } + + // Ignore missing alpha when comparing RGB w/ RGBA, but enforce an equal + // number of channels otherwise. + unsigned minChannels = std::min(channels, ref.channels); + if (channels != ref.channels && minChannels < 3) { + return 0.0; + } + + const unsigned char *pSrc = start(); + const unsigned char *pRef = ref.start(); + + unsigned long long error = 0; + for (unsigned y = 0; y < height; ++y) { + for (unsigned x = 0; x < width; ++x) { + // FIXME: Ignore alpha channel until we are able to pick a visual + // that matches the traces + for (unsigned c = 0; c < minChannels; ++c) { + int delta = pSrc[x*channels + c] - pRef[x*ref.channels + c]; + error += delta*delta; + } + } + + pSrc += stride(); + pRef += ref.stride(); + } + + double numerator = error*2 + 1; + double denominator = height*width*minChannels*255ULL*255ULL*2; + double quotient = numerator/denominator; + + // Precision in bits + double precision = -log(quotient)/log(2.0); + + return precision; +} + + +} /* namespace image */ diff --git a/image/image.hpp b/image/image.hpp new file mode 100644 index 0000000..7dd18c1 --- /dev/null +++ b/image/image.hpp @@ -0,0 +1,123 @@ +/************************************************************************** + * + * Copyright 2008-2010 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + +/* + * Image I/O. + */ + +#ifndef _IMAGE_HPP_ +#define _IMAGE_HPP_ + + +#include + + +namespace image { + + +class Image { +public: + unsigned width; + unsigned height; + unsigned channels; + + // Flipped vertically or not + bool flipped; + + // Pixels in RGBA format + unsigned char *pixels; + + inline Image(unsigned w, unsigned h, unsigned c = 4, bool f = false) : + width(w), + height(h), + channels(c), + flipped(f), + pixels(new unsigned char[h*w*c]) + {} + + inline ~Image() { + delete [] pixels; + } + + inline unsigned char *start(void) { + return flipped ? pixels + (height - 1)*width*channels : pixels; + } + + inline const unsigned char *start(void) const { + return flipped ? pixels + (height - 1)*width*channels : pixels; + } + + inline unsigned char *end(void) { + return flipped ? pixels - width*channels : pixels + height*width*channels; + } + + inline const unsigned char *end(void) const { + return flipped ? pixels - width*channels : pixels + height*width*channels; + } + + inline signed stride(void) const { + return flipped ? -(signed)(width*channels) : width*channels; + } + + bool writeBMP(const char *filename) const; + + void writePNM(std::ostream &os, const char *comment = NULL) const; + + inline bool writePNM(const char *filename, const char *comment = NULL) const { + std::ofstream os(filename, std::ofstream::binary); + if (!os) { + return false; + } + writePNM(os, comment); + return true; + } + + bool + writePNG(std::ostream &os) const; + + inline bool + writePNG(const char *filename) const { + std::ofstream os(filename, std::ofstream::binary); + if (!os) { + return false; + } + return writePNG(os); + } + + double compare(Image &ref); +}; + + +Image * +readPNG(const char *filename); + +const char * +readPNMHeader(const char *buffer, size_t size, unsigned *channels, unsigned *width, unsigned *height); + + +} /* namespace image */ + + +#endif /* _IMAGE_HPP_ */ diff --git a/image/image_bmp.cpp b/image/image_bmp.cpp new file mode 100644 index 0000000..e0c6428 --- /dev/null +++ b/image/image_bmp.cpp @@ -0,0 +1,139 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * Copyright 2008-2010 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + +#include +#include + +#include "image.hpp" + + +namespace image { + + +#pragma pack(push,2) +struct FileHeader { + uint16_t bfType; + uint32_t bfSize; + uint16_t bfReserved1; + uint16_t bfReserved2; + uint32_t bfOffBits; +}; +#pragma pack(pop) + +struct InfoHeader { + uint32_t biSize; + int32_t biWidth; + int32_t biHeight; + uint16_t biPlanes; + uint16_t biBitCount; + uint32_t biCompression; + uint32_t biSizeImage; + int32_t biXPelsPerMeter; + int32_t biYPelsPerMeter; + uint32_t biClrUsed; + uint32_t biClrImportant; +}; + +struct Pixel { + uint8_t rgbBlue; + uint8_t rgbGreen; + uint8_t rgbRed; + uint8_t rgbAlpha; +}; + + +bool +Image::writeBMP(const char *filename) const { + assert(channels == 4); + + struct FileHeader bmfh; + struct InfoHeader bmih; + unsigned x, y; + + bmfh.bfType = 0x4d42; + bmfh.bfSize = 14 + 40 + height*width*4; + bmfh.bfReserved1 = 0; + bmfh.bfReserved2 = 0; + bmfh.bfOffBits = 14 + 40; + + bmih.biSize = 40; + bmih.biWidth = width; + bmih.biHeight = height; + bmih.biPlanes = 1; + bmih.biBitCount = 32; + bmih.biCompression = 0; + bmih.biSizeImage = height*width*4; + bmih.biXPelsPerMeter = 0; + bmih.biYPelsPerMeter = 0; + bmih.biClrUsed = 0; + bmih.biClrImportant = 0; + + std::ofstream stream(filename, std::ofstream::binary); + + if (!stream) { + return false; + } + + stream.write((const char *)&bmfh, 14); + stream.write((const char *)&bmih, 40); + + unsigned stride = width*4; + + if (flipped) { + for (y = 0; y < height; ++y) { + const unsigned char *ptr = pixels + y * stride; + for (x = 0; x < width; ++x) { + struct Pixel pixel; + pixel.rgbRed = ptr[x*4 + 0]; + pixel.rgbGreen = ptr[x*4 + 1]; + pixel.rgbBlue = ptr[x*4 + 2]; + pixel.rgbAlpha = ptr[x*4 + 3]; + stream.write((const char *)&pixel, 4); + } + } + } else { + y = height; + while (y--) { + const unsigned char *ptr = pixels + y * stride; + for (x = 0; x < width; ++x) { + struct Pixel pixel; + pixel.rgbRed = ptr[x*4 + 0]; + pixel.rgbGreen = ptr[x*4 + 1]; + pixel.rgbBlue = ptr[x*4 + 2]; + pixel.rgbAlpha = ptr[x*4 + 3]; + stream.write((const char *)&pixel, 4); + } + } + } + + stream.close(); + + return true; +} + + +} /* namespace image */ diff --git a/image/image_png.cpp b/image/image_png.cpp new file mode 100644 index 0000000..dba07d4 --- /dev/null +++ b/image/image_png.cpp @@ -0,0 +1,206 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * Copyright 2008-2010 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + +#include +#include + +#include +#include +#include + +#include + +#include "image.hpp" + + +namespace image { + + +static const int png_compression_level = Z_BEST_SPEED; + + +static void +pngWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length) +{ + std::ostream *os = (std::ostream *) png_get_io_ptr(png_ptr); + os->write((const char *)data, length); +} + +bool +Image::writePNG(std::ostream &os) const +{ + png_structp png_ptr; + png_infop info_ptr; + int color_type; + + switch (channels) { + case 4: + color_type = PNG_COLOR_TYPE_RGB_ALPHA; + break; + case 3: + color_type = PNG_COLOR_TYPE_RGB; + break; + case 2: + color_type = PNG_COLOR_TYPE_GRAY_ALPHA; + break; + case 1: + color_type = PNG_COLOR_TYPE_GRAY; + break; + default: + assert(0); + goto no_png; + } + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + goto no_png; + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_write_struct(&png_ptr, NULL); + goto no_png; + } + + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_write_struct(&png_ptr, &info_ptr); + goto no_png; + } + + png_set_write_fn(png_ptr, &os, pngWriteCallback, NULL); + + png_set_IHDR(png_ptr, info_ptr, width, height, 8, + color_type, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + png_set_compression_level(png_ptr, png_compression_level); + + png_write_info(png_ptr, info_ptr); + + if (!flipped) { + for (unsigned y = 0; y < height; ++y) { + png_bytep row = (png_bytep)(pixels + y*width*channels); + png_write_rows(png_ptr, &row, 1); + } + } else { + unsigned y = height; + while (y--) { + png_bytep row = (png_bytep)(pixels + y*width*channels); + png_write_rows(png_ptr, &row, 1); + } + } + + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, &info_ptr); + + return true; + +no_png: + return false; +} + + +Image * +readPNG(const char *filename) +{ + FILE *fp; + png_structp png_ptr; + png_infop info_ptr; + png_infop end_info; + Image *image; + + fp = fopen(filename, "rb"); + if (!fp) + goto no_fp; + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + goto no_png; + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, NULL, NULL); + goto no_png; + } + + end_info = png_create_info_struct(png_ptr); + if (!end_info) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + goto no_png; + } + + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + goto no_png; + } + + png_init_io(png_ptr, fp); + + png_read_info(png_ptr, info_ptr); + + png_uint_32 width, height; + int bit_depth, color_type, interlace_type, compression_type, filter_method; + + png_get_IHDR(png_ptr, info_ptr, + &width, &height, + &bit_depth, &color_type, &interlace_type, + &compression_type, &filter_method); + + image = new Image(width, height); + if (!image) + goto no_image; + + /* Convert to RGBA8 */ + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png_ptr); + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_expand_gray_1_2_4_to_8(png_ptr); + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(png_ptr); + if (bit_depth == 16) + png_set_strip_16(png_ptr); + + for (unsigned y = 0; y < height; ++y) { + png_bytep row = (png_bytep)(image->pixels + y*width*4); + png_read_row(png_ptr, row, NULL); + } + + png_read_end(png_ptr, info_ptr); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + fclose(fp); + return image; + +no_image: + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); +no_png: + fclose(fp); +no_fp: + return NULL; +} + + + +} /* namespace image */ diff --git a/image/image_pnm.cpp b/image/image_pnm.cpp new file mode 100644 index 0000000..f9cd05d --- /dev/null +++ b/image/image_pnm.cpp @@ -0,0 +1,163 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * Copyright 2008-2010 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + +#include +#include +#include +#include + +#include "image.hpp" + + +namespace image { + +/** + * http://en.wikipedia.org/wiki/Netpbm_format + * http://netpbm.sourceforge.net/doc/ppm.html + */ +void +Image::writePNM(std::ostream &os, const char *comment) const { + assert(channels == 1 || channels >= 3); + + os << (channels == 1 ? "P5" : "P6") << "\n"; + if (comment) { + os << "#" << comment << "\n"; + } + os << width << " " << height << "\n"; + os << "255" << "\n"; + + const unsigned char *row; + + if (channels == 1 || channels == 3) { + for (row = start(); row != end(); row += stride()) { + os.write((const char *)row, width*channels); + } + } else { + unsigned char *tmp = new unsigned char[width*3]; + if (channels == 4) { + for (row = start(); row != end(); row += stride()) { + const uint32_t *src = (const uint32_t *)row; + uint32_t *dst = (uint32_t *)tmp; + unsigned x; + for (x = 0; x + 4 <= width; x += 4) { + /* + * It's much faster to access dwords than bytes. + * + * FIXME: Big-endian version. + */ + + uint32_t rgba0 = *src++ & 0xffffff; + uint32_t rgba1 = *src++ & 0xffffff; + uint32_t rgba2 = *src++ & 0xffffff; + uint32_t rgba3 = *src++ & 0xffffff; + uint32_t rgb0 = rgba0 + | (rgba1 << 24); + uint32_t rgb1 = (rgba1 >> 8) + | (rgba2 << 16); + uint32_t rgb2 = (rgba2 >> 16) + | (rgba3 << 8); + *dst++ = rgb0; + *dst++ = rgb1; + *dst++ = rgb2; + } + for (; x < width; ++x) { + tmp[x*3 + 0] = row[x*4 + 0]; + tmp[x*3 + 1] = row[x*4 + 1]; + tmp[x*3 + 2] = row[x*4 + 2]; + } + os.write((const char *)tmp, width*3); + } + } else if (channels == 2) { + for (row = start(); row != end(); row += stride()) { + const unsigned char *src = row; + unsigned char *dst = tmp; + for (unsigned x = 0; x < width; ++x) { + *dst++ = *src++; + *dst++ = *src++; + *dst++ = 0; + } + os.write((const char *)tmp, width*3); + } + } else { + assert(0); + } + delete [] tmp; + } +} + +const char * +readPNMHeader(const char *buffer, size_t bufferSize, unsigned *channels, unsigned *width, unsigned *height) +{ + *channels = 0; + *width = 0; + *height = 0; + + const char *currentBuffer = buffer; + const char *nextBuffer; + + // parse number of channels + int scannedChannels = sscanf(currentBuffer, "P%d\n", channels); + if (scannedChannels != 1) { // validate scanning of channels + // invalid channel line + return buffer; + } + // convert channel token to number of channels + *channels = (*channels == 5) ? 1 : 3; + + // advance past channel line + nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1; + bufferSize -= nextBuffer - currentBuffer; + currentBuffer = nextBuffer; + + // skip over optional comment + if (*currentBuffer == '#') { + // advance past comment line + nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1; + bufferSize -= nextBuffer - currentBuffer; + currentBuffer = nextBuffer; + } + + // parse dimensions of image + int scannedDimensions = sscanf(currentBuffer, "%d %d\n", width, height); + if (scannedDimensions != 2) { // validate scanning of dimensions + // invalid dimension line + return buffer; + } + + // advance past dimension line + nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1; + bufferSize -= nextBuffer - currentBuffer; + currentBuffer = nextBuffer; + + // skip over "255\n" at end of header + nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1; + + // return start of image data + return nextBuffer; +} + +} /* namespace image */ diff --git a/inject/CMakeLists.txt b/inject/CMakeLists.txt new file mode 100644 index 0000000..489b819 --- /dev/null +++ b/inject/CMakeLists.txt @@ -0,0 +1,19 @@ +set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + +add_library (injectee MODULE + injectee.cpp +) +set_target_properties (injectee PROPERTIES + PREFIX "" + OUTPUT_NAME inject +) +install (TARGETS injectee LIBRARY DESTINATION bin) + +add_executable (injector + injector.cpp +) +set_target_properties (injector PROPERTIES + PREFIX "" + OUTPUT_NAME inject +) +install (TARGETS injector RUNTIME DESTINATION bin) diff --git a/inject/inject.h b/inject/inject.h new file mode 100644 index 0000000..058ada6 --- /dev/null +++ b/inject/inject.h @@ -0,0 +1,181 @@ +/************************************************************************** + * + * Copyright 2011-2012 Jose Fonseca + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + +/* + * Code for the DLL that will be injected in the target process. + * + * The injected DLL will manipulate the import tables to hook the + * modules/functions of interest. + * + * See also: + * - http://www.codeproject.com/KB/system/api_spying_hack.aspx + * - http://www.codeproject.com/KB/threads/APIHooking.aspx + * - http://msdn.microsoft.com/en-us/magazine/cc301808.aspx + */ + + +#include + + +static inline const char * +getSeparator(const char *szFilename) { + const char *p, *q; + p = NULL; + q = szFilename; + char c; + do { + c = *q++; + if (c == '\\' || c == '/' || c == ':') { + p = q; + } + } while (c); + return p; +} + + +static inline const char * +getBaseName(const char *szFilename) { + const char *pSeparator = getSeparator(szFilename); + if (!pSeparator) { + return szFilename; + } + return pSeparator; +} + + +static inline void +getDirName(char *szFilename) { + char *pSeparator = const_cast(getSeparator(szFilename)); + if (pSeparator) { + *pSeparator = '\0'; + } +} + + +static inline void +getModuleName(char *szModuleName, size_t n, const char *szFilename) { + char *p = szModuleName; + const char *q = getBaseName(szFilename); + char c; + while (--n) { + c = *q++; + if (c == '.' || c == '\0') { + break; + } + *p++ = c; + }; + *p++ = '\0'; +} + + +#define SHARED_MEM_SIZE 4096 + +static LPVOID pSharedMem = NULL; +static HANDLE hFileMapping = NULL; + + +static LPSTR +OpenSharedMemory(void) { + if (pSharedMem) { + return (LPSTR)pSharedMem; + } + + hFileMapping = CreateFileMapping( + INVALID_HANDLE_VALUE, // system paging file + NULL, // lpAttributes + PAGE_READWRITE, // read/write access + 0, // dwMaximumSizeHigh + SHARED_MEM_SIZE, // dwMaximumSizeLow + TEXT("injectfilemap")); // name of map object + if (hFileMapping == NULL) { + fprintf(stderr, "Failed to create file mapping\n"); + return NULL; + } + + BOOL bAlreadyExists = (GetLastError() == ERROR_ALREADY_EXISTS); + + pSharedMem = MapViewOfFile( + hFileMapping, + FILE_MAP_WRITE, // read/write access + 0, // dwFileOffsetHigh + 0, // dwFileOffsetLow + 0); // dwNumberOfBytesToMap (entire file) + if (pSharedMem == NULL) { + fprintf(stderr, "Failed to map view \n"); + return NULL; + } + + if (!bAlreadyExists) { + memset(pSharedMem, 0, SHARED_MEM_SIZE); + } + + return (LPSTR)pSharedMem; +} + + +static inline VOID +CloseSharedMem(void) { + if (!pSharedMem) { + return; + } + + UnmapViewOfFile(pSharedMem); + pSharedMem = NULL; + + CloseHandle(hFileMapping); + hFileMapping = NULL; +} + + +static inline VOID +SetSharedMem(LPCSTR lpszSrc) { + LPSTR lpszDst = OpenSharedMemory(); + if (!lpszDst) { + return; + } + + size_t n = 1; + while (*lpszSrc && n < SHARED_MEM_SIZE) { + *lpszDst++ = *lpszSrc++; + n++; + } + *lpszDst = '\0'; +} + + +static inline VOID +GetSharedMem(LPSTR lpszDst, size_t n) { + LPCSTR lpszSrc = OpenSharedMemory(); + if (!lpszSrc) { + return; + } + + while (*lpszSrc && --n) { + *lpszDst++ = *lpszSrc++; + } + *lpszDst = '\0'; +} + diff --git a/inject/injectee.cpp b/inject/injectee.cpp new file mode 100644 index 0000000..86992b8 --- /dev/null +++ b/inject/injectee.cpp @@ -0,0 +1,572 @@ +/************************************************************************** + * + * Copyright 2011-2012 Jose Fonseca + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + +/* + * Code for the DLL that will be injected in the target process. + * + * The injected DLL will manipulate the import tables to hook the + * modules/functions of interest. + * + * See also: + * - http://www.codeproject.com/KB/system/api_spying_hack.aspx + * - http://www.codeproject.com/KB/threads/APIHooking.aspx + * - http://msdn.microsoft.com/en-us/magazine/cc301808.aspx + */ + + +#include +#include +#include + +#include +#include + +#include "inject.h" + + +#define VERBOSITY 0 +#define NOOP 0 + + +static CRITICAL_SECTION Mutex = {(PCRITICAL_SECTION_DEBUG)-1, -1, 0, 0, 0, 0}; + + +static void +debugPrintf(const char *format, ...) +{ +#if VERBOSITY > 0 + static char buf[4096]; + + EnterCriticalSection(&Mutex); + + va_list ap; + va_start(ap, format); + _vsnprintf(buf, sizeof buf, format, ap); + va_end(ap); + + OutputDebugStringA(buf); + + LeaveCriticalSection(&Mutex); +#endif +} + + +static HMODULE WINAPI +MyLoadLibraryA(LPCSTR lpLibFileName); + +static HMODULE WINAPI +MyLoadLibraryW(LPCWSTR lpLibFileName); + +static HMODULE WINAPI +MyLoadLibraryExA(LPCSTR lpFileName, HANDLE hFile, DWORD dwFlags); + +static HMODULE WINAPI +MyLoadLibraryExW(LPCWSTR lpFileName, HANDLE hFile, DWORD dwFlags); + +static FARPROC WINAPI +MyGetProcAddress(HMODULE hModule, LPCSTR lpProcName); + + +static const char * +getImportDescriptionName(HMODULE hModule, const PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor) { + const char* szName = (const char*)((PBYTE)hModule + pImportDescriptor->Name); + return szName; +} + + +static PIMAGE_IMPORT_DESCRIPTOR +getImportDescriptor(HMODULE hModule, + const char *szModule, + const char *pszDllName) +{ + MEMORY_BASIC_INFORMATION MemoryInfo; + if (VirtualQuery(hModule, &MemoryInfo, sizeof MemoryInfo) != sizeof MemoryInfo) { + debugPrintf("%s: %s: VirtualQuery failed\n", __FUNCTION__, szModule); + return NULL; + } + if (MemoryInfo.Protect & (PAGE_NOACCESS | PAGE_EXECUTE)) { + debugPrintf("%s: %s: no read access (Protect = 0x%08x)\n", __FUNCTION__, szModule, MemoryInfo.Protect); + return NULL; + } + + PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule; + PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((PBYTE)hModule + pDosHeader->e_lfanew); + + PIMAGE_OPTIONAL_HEADER pOptionalHeader = &pNtHeaders->OptionalHeader; + + UINT_PTR ImportAddress = pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; + + if (!ImportAddress) { + return NULL; + } + + PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((PBYTE)hModule + ImportAddress); + + while (pImportDescriptor->FirstThunk) { + const char* szName = getImportDescriptionName(hModule, pImportDescriptor); + if (stricmp(pszDllName, szName) == 0) { + return pImportDescriptor; + } + ++pImportDescriptor; + } + + return NULL; +} + + +static BOOL +replaceAddress(LPVOID *lpOldAddress, LPVOID lpNewAddress) +{ + DWORD flOldProtect; + + if (*lpOldAddress == lpNewAddress) { + return TRUE; + } + + EnterCriticalSection(&Mutex); + + if (!(VirtualProtect(lpOldAddress, sizeof *lpOldAddress, PAGE_READWRITE, &flOldProtect))) { + LeaveCriticalSection(&Mutex); + return FALSE; + } + + *lpOldAddress = lpNewAddress; + + if (!(VirtualProtect(lpOldAddress, sizeof *lpOldAddress, flOldProtect, &flOldProtect))) { + LeaveCriticalSection(&Mutex); + return FALSE; + } + + LeaveCriticalSection(&Mutex); + return TRUE; +} + + +static LPVOID * +getOldFunctionAddress(HMODULE hModule, + PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor, + const char* pszFunctionName) +{ + PIMAGE_THUNK_DATA pOriginalFirstThunk = (PIMAGE_THUNK_DATA)((PBYTE)hModule + pImportDescriptor->OriginalFirstThunk); + PIMAGE_THUNK_DATA pFirstThunk = (PIMAGE_THUNK_DATA)((PBYTE)hModule + pImportDescriptor->FirstThunk); + + //debugPrintf(" %s\n", __FUNCTION__); + + while (pOriginalFirstThunk->u1.Function) { + PIMAGE_IMPORT_BY_NAME pImport = (PIMAGE_IMPORT_BY_NAME)((PBYTE)hModule + pOriginalFirstThunk->u1.AddressOfData); + const char* szName = (const char* )pImport->Name; + //debugPrintf(" %s\n", szName); + if (strcmp(pszFunctionName, szName) == 0) { + //debugPrintf(" %s succeeded\n", __FUNCTION__); + return (LPVOID *)(&pFirstThunk->u1.Function); + } + ++pOriginalFirstThunk; + ++pFirstThunk; + } + + //debugPrintf(" %s failed\n", __FUNCTION__); + + return NULL; +} + + +static void +replaceModule(HMODULE hModule, + const char *szModule, + PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor, + HMODULE hNewModule) +{ + PIMAGE_THUNK_DATA pOriginalFirstThunk = (PIMAGE_THUNK_DATA)((PBYTE)hModule + pImportDescriptor->OriginalFirstThunk); + PIMAGE_THUNK_DATA pFirstThunk = (PIMAGE_THUNK_DATA)((PBYTE)hModule + pImportDescriptor->FirstThunk); + + while (pOriginalFirstThunk->u1.Function) { + PIMAGE_IMPORT_BY_NAME pImport = (PIMAGE_IMPORT_BY_NAME)((PBYTE)hModule + pOriginalFirstThunk->u1.AddressOfData); + const char* szFunctionName = (const char* )pImport->Name; + debugPrintf(" hooking %s->%s!%s\n", szModule, + getImportDescriptionName(hModule, pImportDescriptor), + szFunctionName); + + PROC pNewProc = GetProcAddress(hNewModule, szFunctionName); + if (!pNewProc) { + debugPrintf(" warning: no replacement for %s\n", szFunctionName); + } else { + LPVOID *lpOldAddress = (LPVOID *)(&pFirstThunk->u1.Function); + replaceAddress(lpOldAddress, (LPVOID)pNewProc); + } + + ++pOriginalFirstThunk; + ++pFirstThunk; + } +} + + +static BOOL +hookFunction(HMODULE hModule, + const char *szModule, + const char *pszDllName, + const char *pszFunctionName, + LPVOID lpNewAddress) +{ + PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = getImportDescriptor(hModule, szModule, pszDllName); + if (pImportDescriptor == NULL) { + return FALSE; + } + LPVOID* lpOldFunctionAddress = getOldFunctionAddress(hModule, pImportDescriptor, pszFunctionName); + if (lpOldFunctionAddress == NULL) { + return FALSE; + } + + if (*lpOldFunctionAddress == lpNewAddress) { + return TRUE; + } + + if (VERBOSITY >= 3) { + debugPrintf(" hooking %s->%s!%s\n", szModule, pszDllName, pszFunctionName); + } + + return replaceAddress(lpOldFunctionAddress, lpNewAddress); +} + + +static BOOL +replaceImport(HMODULE hModule, + const char *szModule, + const char *pszDllName, + HMODULE hNewModule) +{ +#if NOOP + return TRUE; +#endif + + PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = getImportDescriptor(hModule, szModule, pszDllName); + if (pImportDescriptor == NULL) { + return TRUE; + } + + replaceModule(hModule, szModule, pImportDescriptor, hNewModule); + + return TRUE; +} + +static HMODULE g_hThisModule = NULL; + + +struct Replacement { + const char *szMatchModule; + HMODULE hReplaceModule; +}; + +static unsigned numReplacements = 0; +static Replacement replacements[32]; + + + +static void +hookModule(HMODULE hModule, + const char *szModule) +{ + if (hModule == g_hThisModule) { + return; + } + + for (unsigned i = 0; i < numReplacements; ++i) { + if (hModule == replacements[i].hReplaceModule) { + return; + } + } + + hookFunction(hModule, szModule, "kernel32.dll", "LoadLibraryA", (LPVOID)MyLoadLibraryA); + hookFunction(hModule, szModule, "kernel32.dll", "LoadLibraryW", (LPVOID)MyLoadLibraryW); + hookFunction(hModule, szModule, "kernel32.dll", "LoadLibraryExA", (LPVOID)MyLoadLibraryExA); + hookFunction(hModule, szModule, "kernel32.dll", "LoadLibraryExW", (LPVOID)MyLoadLibraryExW); + hookFunction(hModule, szModule, "kernel32.dll", "GetProcAddress", (LPVOID)MyGetProcAddress); + + const char *szBaseName = getBaseName(szModule); + for (unsigned i = 0; i < numReplacements; ++i) { + if (stricmp(szBaseName, replacements[i].szMatchModule) == 0) { + return; + } + } + + /* Don't hook internal dependencies */ + if (stricmp(szBaseName, "d3d10core.dll") == 0 || + stricmp(szBaseName, "d3d10level9.dll") == 0 || + stricmp(szBaseName, "d3d10sdklayers.dll") == 0 || + stricmp(szBaseName, "d3d10_1core.dll") == 0 || + stricmp(szBaseName, "d3d11sdklayers.dll") == 0 || + stricmp(szBaseName, "d3d11_1sdklayers.dll") == 0) { + return; + } + + for (unsigned i = 0; i < numReplacements; ++i) { + replaceImport(hModule, szModule, replacements[i].szMatchModule, replacements[i].hReplaceModule); + replaceImport(hModule, szModule, replacements[i].szMatchModule, replacements[i].hReplaceModule); + replaceImport(hModule, szModule, replacements[i].szMatchModule, replacements[i].hReplaceModule); + } +} + +static void +hookAllModules(void) +{ + HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId()); + if (hModuleSnap == INVALID_HANDLE_VALUE) { + return; + } + + MODULEENTRY32 me32; + me32.dwSize = sizeof me32; + + static bool first = true; + if (first) { + if (Module32First(hModuleSnap, &me32)) { + debugPrintf(" modules:\n"); + do { + debugPrintf(" %s\n", me32.szExePath); + } while (Module32Next(hModuleSnap, &me32)); + } + first = false; + } + + if (Module32First(hModuleSnap, &me32)) { + do { + hookModule(me32.hModule, me32.szExePath); + } while (Module32Next(hModuleSnap, &me32)); + } + + CloseHandle(hModuleSnap); +} + + + + +static HMODULE WINAPI +MyLoadLibrary(LPCSTR lpLibFileName, HANDLE hFile = NULL, DWORD dwFlags = 0) +{ + // To Send the information to the server informing that, + // LoadLibrary is invoked. + HMODULE hModule = LoadLibraryExA(lpLibFileName, hFile, dwFlags); + + //hookModule(hModule, lpLibFileName); + hookAllModules(); + + return hModule; +} + +static HMODULE WINAPI +MyLoadLibraryA(LPCSTR lpLibFileName) +{ + if (VERBOSITY >= 2) { + debugPrintf("%s(\"%s\")\n", __FUNCTION__, lpLibFileName); + } + + const char *szBaseName = getBaseName(lpLibFileName); + for (unsigned i = 0; i < numReplacements; ++i) { + if (stricmp(szBaseName, replacements[i].szMatchModule) == 0) { + debugPrintf("%s(\"%s\")\n", __FUNCTION__, lpLibFileName); +#ifdef __GNUC__ + void *caller = __builtin_return_address (0); + + HMODULE hModule = 0; + BOOL bRet = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + (LPCTSTR)caller, + &hModule); + assert(bRet); + char szCaller[256]; + DWORD dwRet = GetModuleFileNameA(hModule, szCaller, sizeof szCaller); + assert(dwRet); + debugPrintf(" called from %s\n", szCaller); +#endif + break; + } + } + + return MyLoadLibrary(lpLibFileName); +} + +static HMODULE WINAPI +MyLoadLibraryW(LPCWSTR lpLibFileName) +{ + if (VERBOSITY >= 2) { + debugPrintf("%s(L\"%S\")\n", __FUNCTION__, lpLibFileName); + } + + char szFileName[256]; + wcstombs(szFileName, lpLibFileName, sizeof szFileName); + + return MyLoadLibrary(szFileName); +} + +static HMODULE WINAPI +MyLoadLibraryExA(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) +{ + if (VERBOSITY >= 2) { + debugPrintf("%s(\"%s\")\n", __FUNCTION__, lpLibFileName); + } + return MyLoadLibrary(lpLibFileName, hFile, dwFlags); +} + +static HMODULE WINAPI +MyLoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) +{ + if (VERBOSITY >= 2) { + debugPrintf("%s(L\"%S\")\n", __FUNCTION__, lpLibFileName); + } + + char szFileName[256]; + wcstombs(szFileName, lpLibFileName, sizeof szFileName); + + return MyLoadLibrary(szFileName, hFile, dwFlags); +} + +static FARPROC WINAPI +MyGetProcAddress(HMODULE hModule, LPCSTR lpProcName) { + + if (VERBOSITY >= 99) { + /* XXX this can cause segmentation faults */ + debugPrintf("%s(\"%s\")\n", __FUNCTION__, lpProcName); + } + + assert(hModule != g_hThisModule); + for (unsigned i = 0; i < numReplacements; ++i) { + if (hModule == replacements[i].hReplaceModule) { + return GetProcAddress(hModule, lpProcName); + } + } + +#if !NOOP + char szModule[256]; + DWORD dwRet = GetModuleFileNameA(hModule, szModule, sizeof szModule); + assert(dwRet); + const char *szBaseName = getBaseName(szModule); + + for (unsigned i = 0; i < numReplacements; ++i) { + + if (stricmp(szBaseName, replacements[i].szMatchModule) == 0) { + debugPrintf(" %s(\"%s\", \"%s\")\n", __FUNCTION__, szModule, lpProcName); + FARPROC pProcAddress = GetProcAddress(replacements[i].hReplaceModule, lpProcName); + if (pProcAddress) { + if (VERBOSITY >= 2) { + debugPrintf(" replacing %s!%s\n", szBaseName, lpProcName); + } + return pProcAddress; + } else { + debugPrintf(" ignoring %s!%s\n", szBaseName, lpProcName); + break; + } + } + } +#endif + + return GetProcAddress(hModule, lpProcName); +} + + +EXTERN_C BOOL WINAPI +DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) +{ + const char *szNewDllName = NULL; + HMODULE hNewModule = NULL; + const char *szNewDllBaseName; + + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + debugPrintf("DLL_PROCESS_ATTACH\n"); + + g_hThisModule = hinstDLL; + + { + char szProcess[MAX_PATH]; + GetModuleFileNameA(NULL, szProcess, sizeof szProcess); + debugPrintf(" attached to %s\n", szProcess); + } + + /* + * Calling LoadLibrary inside DllMain is strongly discouraged. But it + * works quite well, provided that the loaded DLL does not require or do + * anything special in its DllMain, which seems to be the general case. + * + * See also: + * - http://stackoverflow.com/questions/4370812/calling-loadlibrary-from-dllmain + * - http://msdn.microsoft.com/en-us/library/ms682583 + */ + +#if 0 + szNewDllName = getenv("INJECT_DLL"); + if (!szNewDllName) { + debugPrintf("warning: INJECT_DLL not set\n"); + return FALSE; + } +#else + static char szSharedMemCopy[MAX_PATH]; + GetSharedMem(szSharedMemCopy, sizeof szSharedMemCopy); + szNewDllName = szSharedMemCopy; +#endif + debugPrintf(" injecting %s\n", szNewDllName); + + hNewModule = LoadLibraryA(szNewDllName); + if (!hNewModule) { + debugPrintf("warning: failed to load %s\n", szNewDllName); + return FALSE; + } + + szNewDllBaseName = getBaseName(szNewDllName); + if (stricmp(szNewDllBaseName, "dxgitrace.dll") == 0) { + replacements[numReplacements].szMatchModule = "dxgi.dll"; + replacements[numReplacements].hReplaceModule = hNewModule; + ++numReplacements; + + replacements[numReplacements].szMatchModule = "d3d10.dll"; + replacements[numReplacements].hReplaceModule = hNewModule; + ++numReplacements; + + replacements[numReplacements].szMatchModule = "d3d10_1.dll"; + replacements[numReplacements].hReplaceModule = hNewModule; + ++numReplacements; + + replacements[numReplacements].szMatchModule = "d3d11.dll"; + replacements[numReplacements].hReplaceModule = hNewModule; + ++numReplacements; + } else { + replacements[numReplacements].szMatchModule = szNewDllBaseName; + replacements[numReplacements].hReplaceModule = hNewModule; + ++numReplacements; + } + + hookAllModules(); + break; + + case DLL_THREAD_ATTACH: + break; + + case DLL_THREAD_DETACH: + break; + + case DLL_PROCESS_DETACH: + debugPrintf("DLL_PROCESS_DETACH\n"); + break; + } + return TRUE; +} diff --git a/inject/injector.cpp b/inject/injector.cpp new file mode 100644 index 0000000..04e0959 --- /dev/null +++ b/inject/injector.cpp @@ -0,0 +1,289 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + +/* + * Main program to start and inject a DLL into a process via a remote thread. + * + * For background see: + * - http://en.wikipedia.org/wiki/DLL_injection#Approaches_on_Microsoft_Windows + * - http://www.codeproject.com/KB/threads/completeinject.aspx + * - http://www.codeproject.com/KB/threads/winspy.aspx + * - http://www.codeproject.com/KB/DLL/DLL_Injection_tutorial.aspx + * - http://www.codeproject.com/KB/threads/APIHooking.aspx + * + * Other slightly different techniques: + * - http://www.fr33project.org/pages/projects/phook.htm + * - http://www.hbgary.com/loading-a-dll-without-calling-loadlibrary + * - http://securityxploded.com/ntcreatethreadex.php + */ + +#include + +#include +#include + +#include "inject.h" + + +/** + * Determine whether an argument should be quoted. + */ +static bool +needsQuote(const char *arg) +{ + char c; + while (true) { + c = *arg++; + if (c == '\0') { + break; + } + if (c == ' ' || c == '\t' || c == '\"') { + return true; + } + if (c == '\\') { + c = *arg++; + if (c == '\0') { + break; + } + if (c == '"') { + return true; + } + } + } + return false; +} + +static void +quoteArg(std::string &s, const char *arg) +{ + char c; + unsigned backslashes = 0; + + s.push_back('"'); + while (true) { + c = *arg++; + if (c == '\0') { + break; + } else if (c == '"') { + while (backslashes) { + s.push_back('\\'); + --backslashes; + } + s.push_back('\\'); + } else { + if (c == '\\') { + ++backslashes; + } else { + backslashes = 0; + } + } + s.push_back(c); + } + s.push_back('"'); +} + + +int +main(int argc, char *argv[]) +{ + + if (argc < 3) { + fprintf(stderr, "inject dllname.dll command [args] ...\n"); + return 1; + } + + const char *szDll = argv[1]; +#if 0 + SetEnvironmentVariableA("INJECT_DLL", szDll); +#else + SetSharedMem(szDll); +#endif + + PROCESS_INFORMATION processInfo; + HANDLE hProcess; + BOOL bAttach; + if (isdigit(argv[2][0])) { + bAttach = TRUE; + + BOOL bRet; + HANDLE hToken = NULL; + bRet = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken); + if (!bRet) { + fprintf(stderr, "error: OpenProcessToken returned %u\n", (unsigned)bRet); + return 1; + } + + LUID Luid; + bRet = LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &Luid); + if (!bRet) { + fprintf(stderr, "error: LookupPrivilegeValue returned %u\n", (unsigned)bRet); + return 1; + } + + TOKEN_PRIVILEGES tp; + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = Luid; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + bRet = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof tp, NULL, NULL); + if (!bRet) { + fprintf(stderr, "error: AdjustTokenPrivileges returned %u\n", (unsigned)bRet); + return 1; + } + + DWORD dwDesiredAccess = + PROCESS_CREATE_THREAD | + PROCESS_QUERY_INFORMATION | + PROCESS_QUERY_LIMITED_INFORMATION | + PROCESS_VM_OPERATION | + PROCESS_VM_WRITE | + PROCESS_VM_READ; + DWORD dwProcessId = atol(argv[2]); + hProcess = OpenProcess( + dwDesiredAccess, + FALSE /* bInheritHandle */, + dwProcessId); + if (!hProcess) { + DWORD dwLastError = GetLastError(); + fprintf(stderr, "error: failed to open process %lu (%lu)\n", dwProcessId, dwLastError); + return 1; + } + } else { + bAttach = FALSE; + std::string commandLine; + char sep = 0; + for (int i = 2; i < argc; ++i) { + const char *arg = argv[i]; + + if (sep) { + commandLine.push_back(sep); + } + + if (needsQuote(arg)) { + quoteArg(commandLine, arg); + } else { + commandLine.append(arg); + } + + sep = ' '; + } + + STARTUPINFO startupInfo; + memset(&startupInfo, 0, sizeof startupInfo); + startupInfo.cb = sizeof startupInfo; + + // Create the process in suspended state + if (!CreateProcessA( + NULL, + const_cast(commandLine.c_str()), // only modified by CreateProcessW + 0, // process attributes + 0, // thread attributes + TRUE, // inherit handles + CREATE_SUSPENDED, + NULL, // environment + NULL, // current directory + &startupInfo, + &processInfo)) { + fprintf(stderr, "error: failed to execute %s\n", commandLine.c_str()); + return 1; + } + + hProcess = processInfo.hProcess; + } + + /* + * XXX: Mixed architecture don't quite work. See also + * http://www.corsix.org/content/dll-injection-and-wow64 + */ + const char *szDllName; + szDllName = "inject.dll"; + + char szDllPath[MAX_PATH]; + GetModuleFileNameA(NULL, szDllPath, sizeof szDllPath); + getDirName(szDllPath); + strncat(szDllPath, szDllName, sizeof szDllPath - strlen(szDllPath) - 1); + + size_t szDllPathLength = strlen(szDllPath) + 1; + + // Allocate memory in the target process to hold the DLL name + void *lpMemory = VirtualAllocEx(hProcess, NULL, szDllPathLength, MEM_COMMIT, PAGE_READWRITE); + if (!lpMemory) { + fprintf(stderr, "error: failed to allocate memory in the process\n"); + TerminateProcess(hProcess, 1); + return 1; + } + + // Copy DLL name into the target process + if (!WriteProcessMemory(hProcess, lpMemory, szDllPath, szDllPathLength, NULL)) { + fprintf(stderr, "error: failed to write into process memory\n"); + TerminateProcess(hProcess, 1); + return 1; + } + + /* + * Get LoadLibraryA address from kernel32.dll. It's the same for all the + * process (XXX: but only within the same architecture). + */ + PTHREAD_START_ROUTINE lpStartAddress = + (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("KERNEL32"), "LoadLibraryA"); + + // Create remote thread in another process + HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, lpStartAddress, lpMemory, 0, NULL); + if (!hThread) { + fprintf(stderr, "error: failed to create remote thread\n"); + TerminateProcess(hProcess, 1); + return 1; + } + + // Wait for it to finish + WaitForSingleObject(hThread, INFINITE); + + DWORD hModule = 0; + GetExitCodeThread(hThread, &hModule); + if (!hModule) { + fprintf(stderr, "error: failed to inject %s\n", szDllPath); + TerminateProcess(hProcess, 1); + return 1; + } + + if (bAttach) { + return 0; + } + + // Start main process thread + ResumeThread(processInfo.hThread); + + // Wait for it to finish + WaitForSingleObject(hProcess, INFINITE); + + DWORD exitCode = ~0; + GetExitCodeProcess(hProcess, &exitCode); + + CloseHandle(hProcess); + CloseHandle(processInfo.hThread); + + return (int)exitCode; + +} diff --git a/retrace/CMakeLists.txt b/retrace/CMakeLists.txt index 5a2a608..c6a364f 100644 --- a/retrace/CMakeLists.txt +++ b/retrace/CMakeLists.txt @@ -6,6 +6,7 @@ include_directories ( ${CMAKE_SOURCE_DIR}/helpers ${CMAKE_BINARY_DIR}/dispatch ${CMAKE_SOURCE_DIR}/dispatch + ${CMAKE_SOURCE_DIR}/image ) add_definitions (-DRETRACE) @@ -39,8 +40,8 @@ add_library (retrace_common STATIC json.cpp ) target_link_libraries (retrace_common + image common - ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} ${SNAPPY_LIBRARIES} ${GETOPT_LIBRARIES} diff --git a/retrace/d3d10state.cpp b/retrace/d3d10state.cpp index 15be6c4..d5e41da 100644 --- a/retrace/d3d10state.cpp +++ b/retrace/d3d10state.cpp @@ -28,10 +28,8 @@ #include -#include "d3d11imports.hpp" -#include "json.hpp" -#include "d3dshader.hpp" -#include "d3dstate.hpp" +#include "d3d10imports.hpp" +#include "d3d10state.hpp" namespace d3dstate { @@ -41,46 +39,6 @@ const GUID GUID_D3DSTATE = {0x7D71CAC9,0x7F58,0x432C,{0xA9,0x75,0xA1,0x9F,0xCF,0xCE,0xFD,0x14}}; -template< class T > -inline void -dumpShader(JSONWriter &json, const char *name, T *pShader) { - if (!pShader) { - return; - } - - HRESULT hr; - - /* - * There is no method to get the shader byte code, so the creator is supposed to - * attach it via the SetPrivateData method. - */ - UINT BytecodeLength = 0; - char dummy; - hr = pShader->GetPrivateData(GUID_D3DSTATE, &BytecodeLength, &dummy); - if (hr != DXGI_ERROR_MORE_DATA) { - return; - } - - void *pShaderBytecode = malloc(BytecodeLength); - if (!pShaderBytecode) { - return; - } - - hr = pShader->GetPrivateData(GUID_D3DSTATE, &BytecodeLength, pShaderBytecode); - if (SUCCEEDED(hr)) { - IDisassemblyBuffer *pDisassembly = NULL; - hr = DisassembleShader(pShaderBytecode, BytecodeLength, &pDisassembly); - if (SUCCEEDED(hr)) { - json.beginMember(name); - json.writeString((const char *)pDisassembly->GetBufferPointer() /*, pDisassembly->GetBufferSize() */); - json.endMember(); - pDisassembly->Release(); - } - } - - free(pShaderBytecode); -} - static void dumpShaders(JSONWriter &json, ID3D10Device *pDevice) { diff --git a/retrace/d3d10state.hpp b/retrace/d3d10state.hpp new file mode 100644 index 0000000..b480bd6 --- /dev/null +++ b/retrace/d3d10state.hpp @@ -0,0 +1,84 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + +#ifndef _DXGISTATE_HPP_ +#define _DXGISTATE_HPP_ + + +#include + + +#include "json.hpp" +#include "d3dshader.hpp" +#include "d3dstate.hpp" + + +namespace d3dstate { + + +template< class T > +inline void +dumpShader(JSONWriter &json, const char *name, T *pShader) { + if (!pShader) { + return; + } + + HRESULT hr; + + /* + * There is no method to get the shader byte code, so the creator is supposed to + * attach it via the SetPrivateData method. + */ + UINT BytecodeLength = 0; + char dummy; + hr = pShader->GetPrivateData(GUID_D3DSTATE, &BytecodeLength, &dummy); + if (hr != DXGI_ERROR_MORE_DATA) { + return; + } + + void *pShaderBytecode = malloc(BytecodeLength); + if (!pShaderBytecode) { + return; + } + + hr = pShader->GetPrivateData(GUID_D3DSTATE, &BytecodeLength, pShaderBytecode); + if (SUCCEEDED(hr)) { + IDisassemblyBuffer *pDisassembly = NULL; + hr = DisassembleShader(pShaderBytecode, BytecodeLength, &pDisassembly); + if (SUCCEEDED(hr)) { + json.beginMember(name); + json.writeString((const char *)pDisassembly->GetBufferPointer() /*, pDisassembly->GetBufferSize() */); + json.endMember(); + pDisassembly->Release(); + } + } + + free(pShaderBytecode); +} + + +} /* namespace d3dstate */ + +#endif // _DXGISTATE_HPP_ diff --git a/retrace/d3d11state.cpp b/retrace/d3d11state.cpp index 05885f4..bc8f7c4 100644 --- a/retrace/d3d11state.cpp +++ b/retrace/d3d11state.cpp @@ -29,18 +29,62 @@ #include #include "d3d11imports.hpp" -#include "json.hpp" +#include "d3d10state.hpp" namespace d3dstate { +static void +dumpShaders(JSONWriter &json, ID3D11DeviceContext *pDeviceContext) +{ + json.beginMember("shaders"); + json.beginObject(); + + ID3D11VertexShader *pVertexShader = NULL; + pDeviceContext->VSGetShader(&pVertexShader, NULL, NULL); + if (pVertexShader) { + dumpShader(json, "VS", pVertexShader); + pVertexShader->Release(); + } + + ID3D11GeometryShader *pGeometryShader = NULL; + pDeviceContext->GSGetShader(&pGeometryShader, NULL, NULL); + if (pGeometryShader) { + dumpShader(json, "GS", pGeometryShader); + pGeometryShader->Release(); + } + + ID3D11PixelShader *pPixelShader = NULL; + pDeviceContext->PSGetShader(&pPixelShader, NULL, NULL); + if (pPixelShader) { + dumpShader(json, "PS", pPixelShader); + } + + json.endObject(); + json.endMember(); // shaders +} + + void dumpDevice(std::ostream &os, ID3D11DeviceContext *pDeviceContext) { JSONWriter json(os); /* TODO */ + json.beginMember("parameters"); + json.beginObject(); + json.endObject(); + json.endMember(); // parameters + + dumpShaders(json, pDeviceContext); + + json.beginMember("textures"); + json.beginObject(); + json.endObject(); + json.endMember(); // textures + + dumpFramebuffer(json, pDeviceContext); } diff --git a/retrace/d3d11state_images.cpp b/retrace/d3d11state_images.cpp index 9c6ff87..fda85b1 100644 --- a/retrace/d3d11state_images.cpp +++ b/retrace/d3d11state_images.cpp @@ -31,6 +31,7 @@ #include "image.hpp" #include "d3d11imports.hpp" +#include "d3d10state.hpp" namespace d3dstate { @@ -140,9 +141,10 @@ stageResource(ID3D11DeviceContext *pDeviceContext, } image::Image * -getRenderTargetImage(ID3D11DeviceContext *pDevice) { +getRenderTargetViewImage(ID3D11DeviceContext *pDevice, + ID3D11RenderTargetView *pRenderTargetView) { image::Image *image = NULL; - ID3D11RenderTargetView *pRenderTargetView = NULL; + ; D3D11_RENDER_TARGET_VIEW_DESC Desc; ID3D11Resource *pResource = NULL; ID3D11Resource *pStagingResource = NULL; @@ -154,9 +156,8 @@ getRenderTargetImage(ID3D11DeviceContext *pDevice) { const unsigned char *src; unsigned char *dst; - pDevice->OMGetRenderTargets(1, &pRenderTargetView, NULL); if (!pRenderTargetView) { - goto no_rendertarget; + return NULL; } pRenderTargetView->GetResource(&pResource); @@ -164,7 +165,8 @@ getRenderTargetImage(ID3D11DeviceContext *pDevice) { pRenderTargetView->GetDesc(&Desc); if (Desc.Format != DXGI_FORMAT_R8G8B8A8_UNORM && - Desc.Format != DXGI_FORMAT_R32G32B32A32_FLOAT) { + Desc.Format != DXGI_FORMAT_R32G32B32A32_FLOAT && + Desc.Format != DXGI_FORMAT_B8G8R8A8_UNORM) { std::cerr << "warning: unsupported DXGI format " << Desc.Format << "\n"; goto no_staging; } @@ -235,6 +237,13 @@ getRenderTargetImage(ID3D11DeviceContext *pDevice) { dst[4*x + 2] = ((float *)src)[4*x + 2] * scale; dst[4*x + 3] = ((float *)src)[4*x + 3] * scale; } + } else if (Desc.Format == DXGI_FORMAT_B8G8R8A8_UNORM) { + for (unsigned x = 0; x < Width; ++x) { + dst[4*x + 0] = src[4*x + 2]; + dst[4*x + 1] = src[4*x + 1]; + dst[4*x + 2] = src[4*x + 0]; + dst[4*x + 3] = src[4*x + 3]; + } } else { assert(0); } @@ -252,12 +261,57 @@ no_staging: if (pResource) { pResource->Release(); } + return image; +} + + + + +image::Image * +getRenderTargetImage(ID3D11DeviceContext *pDevice) { + ID3D11RenderTargetView *pRenderTargetView = NULL; + pDevice->OMGetRenderTargets(1, &pRenderTargetView, NULL); + + image::Image *image = NULL; if (pRenderTargetView) { + image = getRenderTargetViewImage(pDevice, pRenderTargetView); pRenderTargetView->Release(); } -no_rendertarget: + return image; } +void +dumpFramebuffer(JSONWriter &json, ID3D11DeviceContext *pDevice) +{ + json.beginMember("framebuffer"); + json.beginObject(); + + ID3D11RenderTargetView *pRenderTargetViews[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT]; + pDevice->OMGetRenderTargets(D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT, pRenderTargetViews, NULL); + + for (UINT i = 0; i < D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) { + if (!pRenderTargetViews[i]) { + continue; + } + + image::Image *image; + image = getRenderTargetViewImage(pDevice, pRenderTargetViews[i]); + if (image) { + char label[64]; + _snprintf(label, sizeof label, "RENDER_TARGET_%u", i); + json.beginMember(label); + json.writeImage(image, "UNKNOWN"); + json.endMember(); // RENDER_TARGET_* + } + + pRenderTargetViews[i]->Release(); + } + + json.endObject(); + json.endMember(); // framebuffer +} + + } /* namespace d3dstate */ diff --git a/retrace/d3d9state.cpp b/retrace/d3d9state.cpp index 99cb603..a90ee6d 100644 --- a/retrace/d3d9state.cpp +++ b/retrace/d3d9state.cpp @@ -30,6 +30,7 @@ #include "d3d9imports.hpp" #include "d3dshader.hpp" +#include "d3dstate.hpp" #include "json.hpp" @@ -100,9 +101,20 @@ dumpDevice(std::ostream &os, IDirect3DDevice9 *pDevice) { JSONWriter json(os); + /* TODO */ + json.beginMember("parameters"); + json.beginObject(); + json.endObject(); + json.endMember(); // parameters + dumpShaders(json, pDevice); - /* TODO */ + json.beginMember("textures"); + json.beginObject(); + json.endObject(); + json.endMember(); // textures + + dumpFramebuffer(json, pDevice); } diff --git a/retrace/d3d9state_images.cpp b/retrace/d3d9state_images.cpp index 855dede..81f7f7f 100644 --- a/retrace/d3d9state_images.cpp +++ b/retrace/d3d9state_images.cpp @@ -27,16 +27,18 @@ #include #include "image.hpp" +#include "json.hpp" #include "d3d9imports.hpp" +#include "d3dstate.hpp" namespace d3dstate { -image::Image * -getRenderTargetImage(IDirect3DDevice9 *pDevice) { +static image::Image * +getRenderTargetImage(IDirect3DDevice9 *pDevice, + IDirect3DSurface9 *pRenderTarget) { image::Image *image = NULL; - IDirect3DSurface9 *pRenderTarget = NULL; D3DSURFACE_DESC Desc; IDirect3DSurface9 *pStagingSurface = NULL; D3DLOCKED_RECT LockedRect; @@ -44,15 +46,17 @@ getRenderTargetImage(IDirect3DDevice9 *pDevice) { unsigned char *dst; HRESULT hr; - hr = pDevice->GetRenderTarget(0, &pRenderTarget); - if (FAILED(hr)) { - goto no_rendertarget; + if (!pRenderTarget) { + return NULL; } - assert(pRenderTarget); hr = pRenderTarget->GetDesc(&Desc); assert(SUCCEEDED(hr)); - assert(Desc.Format == D3DFMT_X8R8G8B8 || Desc.Format == D3DFMT_A8R8G8B8); + + if (Desc.Format != D3DFMT_X8R8G8B8 && Desc.Format != D3DFMT_A8R8G8B8) { + std::cerr << "warning: unsupported D3DFORMAT " << Desc.Format << "\n"; + goto no_staging; + } hr = pDevice->CreateOffscreenPlainSurface(Desc.Width, Desc.Height, Desc.Format, D3DPOOL_SYSTEMMEM, &pStagingSurface, NULL); if (FAILED(hr)) { @@ -91,10 +95,69 @@ no_image: no_rendertargetdata: pStagingSurface->Release(); no_staging: - pRenderTarget->Release(); -no_rendertarget: return image; } +image::Image * +getRenderTargetImage(IDirect3DDevice9 *pDevice) { + HRESULT hr; + + IDirect3DSurface9 *pRenderTarget = NULL; + hr = pDevice->GetRenderTarget(0, &pRenderTarget); + if (FAILED(hr)) { + return NULL; + } + assert(pRenderTarget); + + image::Image *image = NULL; + if (pRenderTarget) { + image = getRenderTargetImage(pDevice, pRenderTarget); + pRenderTarget->Release(); + } + + return image; +} + + +void +dumpFramebuffer(JSONWriter &json, IDirect3DDevice9 *pDevice) +{ + HRESULT hr; + + json.beginMember("framebuffer"); + json.beginObject(); + + D3DCAPS9 Caps; + pDevice->GetDeviceCaps(&Caps); + + for (UINT i = 0; i < Caps.NumSimultaneousRTs; ++i) { + IDirect3DSurface9 *pRenderTarget = NULL; + hr = pDevice->GetRenderTarget(i, &pRenderTarget); + if (FAILED(hr)) { + continue; + } + + if (!pRenderTarget) { + continue; + } + + image::Image *image; + image = getRenderTargetImage(pDevice, pRenderTarget); + if (image) { + char label[64]; + _snprintf(label, sizeof label, "RENDER_TARGET_%u", i); + json.beginMember(label); + json.writeImage(image, "UNKNOWN"); + json.endMember(); // RENDER_TARGET_* + } + + pRenderTarget->Release(); + } + + json.endObject(); + json.endMember(); // framebuffer +} + + } /* namespace d3dstate */ diff --git a/retrace/d3dstate.hpp b/retrace/d3dstate.hpp index 09e82a2..3766a91 100644 --- a/retrace/d3dstate.hpp +++ b/retrace/d3dstate.hpp @@ -53,6 +53,9 @@ extern const GUID GUID_D3DSTATE; image::Image * getRenderTargetImage(IDirect3DDevice9 *pDevice); +void +dumpFramebuffer(JSONWriter &json, IDirect3DDevice9 *pDevice); + void dumpDevice(std::ostream &os, IDirect3DDevice9 *pDevice); @@ -70,6 +73,9 @@ dumpDevice(std::ostream &os, ID3D10Device *pDevice); image::Image * getRenderTargetImage(ID3D11DeviceContext *pDeviceContext); +void +dumpFramebuffer(JSONWriter &json, ID3D11DeviceContext *pDeviceContext); + void dumpDevice(std::ostream &os, ID3D11DeviceContext *pDeviceContext); diff --git a/retrace/dxgiretrace.py b/retrace/dxgiretrace.py index a412c3a..8de3c98 100644 --- a/retrace/dxgiretrace.py +++ b/retrace/dxgiretrace.py @@ -74,14 +74,6 @@ createWindow(DXGI_SWAP_CHAIN_DESC *pSwapChainDesc) { if 'pSwapChainDesc' in function.argNames(): print r' createWindow(pSwapChainDesc);' - # Compensate for the fact we don't trace the software renderer - # module LoadLibrary call - if 'Software' in function.argNames(): - print r' if (Software) {' - print r' retrace::warning(call) << "using WARP for software device\n";' - print r' Software = LoadLibraryA("d3d10warp");' - print r' }' - # Compensate for the fact we don't trace DXGI object creation if function.name.startswith('D3D11CreateDevice'): print r' if (DriverType == D3D_DRIVER_TYPE_UNKNOWN && !pAdapter) {' @@ -89,52 +81,73 @@ createWindow(DXGI_SWAP_CHAIN_DESC *pSwapChainDesc) { print r' }' if function.name.startswith('D3D10CreateDevice'): + # Toggle debugging + print r' Flags &= ~D3D10_CREATE_DEVICE_DEBUG;' + print r' if (retrace::debug) {' + print r' if (LoadLibraryA("d3d10sdklayers")) {' + print r' Flags |= D3D10_CREATE_DEVICE_DEBUG;' + print r' }' + print r' }' + + # Force driver self.forceDriver('D3D10_DRIVER_TYPE') + if function.name.startswith('D3D11CreateDevice'): + # Toggle debugging + print r' Flags &= ~D3D11_CREATE_DEVICE_DEBUG;' + print r' if (retrace::debug) {' + print r' if (LoadLibraryA("d3d11sdklayers")) {' + print r' Flags |= D3D11_CREATE_DEVICE_DEBUG;' + print r' }' + print r' }' + + # Force driver self.forceDriver('D3D_DRIVER_TYPE') Retracer.invokeFunction(self, function) def forceDriver(self, enum): - print r' switch (retrace::driver) {' - print r' case retrace::DRIVER_HARDWARE:' - print r' DriverType = %s_HARDWARE;' % enum - print r' Software = NULL;' - print r' break;' - print r' case retrace::DRIVER_SOFTWARE:' - print r' pAdapter = NULL;' - print r' DriverType = %s_WARP;' % enum - print r' Software = NULL;' - print r' break;' - print r' case retrace::DRIVER_REFERENCE:' - print r' pAdapter = NULL;' - print r' DriverType = %s_REFERENCE;' % enum - print r' Software = NULL;' - print r' break;' - print r' case retrace::DRIVER_NULL:' - print r' pAdapter = NULL;' - print r' DriverType = %s_NULL;' % enum - print r' Software = NULL;' - print r' break;' - print r' case retrace::DRIVER_MODULE:' - print r' pAdapter = NULL;' - print r' DriverType = %s_SOFTWARE;' % enum - print r' Software = LoadLibraryA(retrace::driverModule);' - print r' if (!Software) {' - print r' retrace::warning(call) << "failed to load " << retrace::driverModule << "\n";' - print r' }' - print r' break;' - print r' default:' - print r' assert(0);' - print r' /* fall-through */' - print r' case retrace::DRIVER_DEFAULT:' - print r' if (DriverType == %s_SOFTWARE) {' % enum - print r' Software = LoadLibraryA("d3d10warp");' + # This can only work when pAdapter is NULL. For non-NULL pAdapter we + # need to override inside the EnumAdapters call below + print r' if (pAdapter == NULL) {' + print r' switch (retrace::driver) {' + print r' case retrace::DRIVER_HARDWARE:' + print r' DriverType = %s_HARDWARE;' % enum + print r' Software = NULL;' + print r' break;' + print r' case retrace::DRIVER_SOFTWARE:' + print r' DriverType = %s_WARP;' % enum + print r' Software = NULL;' + print r' break;' + print r' case retrace::DRIVER_REFERENCE:' + print r' DriverType = %s_REFERENCE;' % enum + print r' Software = NULL;' + print r' break;' + print r' case retrace::DRIVER_NULL:' + print r' DriverType = %s_NULL;' % enum + print r' Software = NULL;' + print r' break;' + print r' case retrace::DRIVER_MODULE:' + print r' DriverType = %s_SOFTWARE;' % enum + print r' Software = LoadLibraryA(retrace::driverModule);' print r' if (!Software) {' - print r' retrace::warning(call) << "failed to load d3d10warp.dll\n";' + print r' retrace::warning(call) << "failed to load " << retrace::driverModule << "\n";' print r' }' + print r' break;' + print r' default:' + print r' assert(0);' + print r' /* fall-through */' + print r' case retrace::DRIVER_DEFAULT:' + print r' if (DriverType == %s_SOFTWARE) {' % enum + print r' Software = LoadLibraryA("d3d10warp");' + print r' if (!Software) {' + print r' retrace::warning(call) << "failed to load d3d10warp.dll\n";' + print r' }' + print r' }' + print r' break;' print r' }' - print r' break;' + print r' } else {' + print r' Software = NULL;' print r' }' def invokeInterfaceMethod(self, interface, method): @@ -164,6 +177,34 @@ createWindow(DXGI_SWAP_CHAIN_DESC *pSwapChainDesc) { print r' pSharedResource = NULL;' print r' }' + # Force driver + if interface.name.startswith('IDXGIFactory') and method.name == 'EnumAdapters': + print r' const char *szSoftware = NULL;' + print r' switch (retrace::driver) {' + print r' case retrace::DRIVER_REFERENCE:' + print r' case retrace::DRIVER_SOFTWARE:' + print r' szSoftware = "d3d10warp.dll";' + print r' break;' + print r' case retrace::DRIVER_MODULE:' + print r' szSoftware = retrace::driverModule;' + print r' break;' + print r' default:' + print r' break;' + print r' }' + print r' HMODULE hSoftware = NULL;' + print r' if (szSoftware) {' + print r' hSoftware = LoadLibraryA(szSoftware);' + print r' if (!hSoftware) {' + print r' retrace::warning(call) << "failed to load " << szSoftware << "\n";' + print r' }' + print r' }' + print r' if (hSoftware) {' + print r' _result = _this->CreateSoftwareAdapter(hSoftware, ppAdapter);' + print r' } else {' + Retracer.invokeInterfaceMethod(self, interface, method) + print r' }' + return + Retracer.invokeInterfaceMethod(self, interface, method) # process events after presents diff --git a/retrace/glretrace_main.cpp b/retrace/glretrace_main.cpp index 1418ca3..d0298fc 100755 --- a/retrace/glretrace_main.cpp +++ b/retrace/glretrace_main.cpp @@ -43,9 +43,16 @@ namespace glretrace { bool insideList = false; bool insideGlBeginEnd = false; +enum { + GPU_START = 0, + GPU_DURATION, + OCCLUSION, + NUM_QUERIES, +}; + struct CallQuery { - GLuint ids[3]; + GLuint ids[NUM_QUERIES]; unsigned call; bool isDraw; GLuint program; @@ -109,26 +116,24 @@ checkGlError(trace::Call &call) { } } -static void -getCurrentTimes(int64_t& cpuTime, int64_t& gpuTime) { - GLuint query; - +static inline int64_t +getCurrentTime(void) { if (retrace::profilingGpuTimes && supportsTimestamp) { - glGenQueries(1, &query); - glQueryCounter(query, GL_TIMESTAMP); - glGetQueryObjecti64vEXT(query, GL_QUERY_RESULT, &gpuTime); + /* Get the current GL time without stalling */ + GLint64 timestamp = 0; + glGetInteger64v(GL_TIMESTAMP, ×tamp); + return timestamp; } else { - gpuTime = 0; - } - - if (retrace::profilingCpuTimes) { - cpuTime = os::getTime(); - } else { - cpuTime = 0; + return os::getTime(); } +} +static inline int64_t +getTimeFrequency(void) { if (retrace::profilingGpuTimes && supportsTimestamp) { - glDeleteQueries(1, &query); + return 1000000000; + } else { + return os::timeFrequency; } } @@ -140,17 +145,16 @@ completeCallQuery(CallQuery& query) { if (query.isDraw) { if (retrace::profilingGpuTimes) { if (supportsTimestamp) { - glGetQueryObjecti64vEXT(query.ids[0], GL_QUERY_RESULT, &gpuStart); + glGetQueryObjecti64vEXT(query.ids[GPU_START], GL_QUERY_RESULT, &gpuStart); } - glGetQueryObjecti64vEXT(query.ids[1], GL_QUERY_RESULT, &gpuDuration); + glGetQueryObjecti64vEXT(query.ids[GPU_DURATION], GL_QUERY_RESULT, &gpuDuration); } if (retrace::profilingPixelsDrawn) { - glGetQueryObjecti64vEXT(query.ids[2], GL_QUERY_RESULT, &pixels); + glGetQueryObjecti64vEXT(query.ids[OCCLUSION], GL_QUERY_RESULT, &pixels); } - glDeleteQueries(3, query.ids); } else { pixels = -1; } @@ -159,6 +163,8 @@ completeCallQuery(CallQuery& query) { cpuDuration = query.cpuEnd - query.cpuStart; } + glDeleteQueries(NUM_QUERIES, query.ids); + /* Add call to profile */ retrace::profiler.addCall(query.call, query.sig->name, query.program, pixels, gpuStart, gpuDuration, query.cpuStart, cpuDuration); } @@ -183,20 +189,20 @@ beginProfile(trace::Call &call, bool isDraw) { query.sig = call.sig; query.program = currentContext ? currentContext->activeProgram : 0; + glGenQueries(NUM_QUERIES, query.ids); + /* GPU profiling only for draw calls */ if (isDraw) { - glGenQueries(3, query.ids); - if (retrace::profilingGpuTimes) { if (supportsTimestamp) { - glQueryCounter(query.ids[0], GL_TIMESTAMP); + glQueryCounter(query.ids[GPU_START], GL_TIMESTAMP); } - glBeginQuery(GL_TIME_ELAPSED, query.ids[1]); + glBeginQuery(GL_TIME_ELAPSED, query.ids[GPU_DURATION]); } if (retrace::profilingPixelsDrawn) { - glBeginQuery(GL_SAMPLES_PASSED, query.ids[2]); + glBeginQuery(GL_SAMPLES_PASSED, query.ids[OCCLUSION]); } } @@ -204,18 +210,18 @@ beginProfile(trace::Call &call, bool isDraw) { /* CPU profiling for all calls */ if (retrace::profilingCpuTimes) { - callQueries.back().cpuStart = os::getTime(); + CallQuery& query = callQueries.back(); + query.cpuStart = getCurrentTime(); } } void endProfile(trace::Call &call, bool isDraw) { - GLint64 time = os::getTime(); /* CPU profiling for all calls */ if (retrace::profilingCpuTimes) { CallQuery& query = callQueries.back(); - query.cpuEnd = time; + query.cpuEnd = getCurrentTime(); } /* GPU profiling only for draw calls */ @@ -276,11 +282,9 @@ initContext() { /* Sync the gpu and cpu start times */ if (retrace::profilingCpuTimes || retrace::profilingGpuTimes) { if (!retrace::profiler.hasBaseTimes()) { - GLint64 gpuTime, cpuTime; - - getCurrentTimes(cpuTime, gpuTime); - retrace::profiler.setBaseCpuTime(cpuTime); - retrace::profiler.setBaseGpuTime(gpuTime); + GLint64 currentTime = getCurrentTime(); + retrace::profiler.setBaseCpuTime(currentTime); + retrace::profiler.setBaseGpuTime(currentTime); } } } @@ -291,23 +295,6 @@ frame_complete(trace::Call &call) { /* Complete any remaining queries */ flushQueries(); - /* GPU time drifts due to being relative times, not absolute and can be - * affected by the gpu switch between processes. - * - * To attempt to compensate we resynchronise on frame end however there is - * still noticeable drift within a single frame which we do not account for. - */ - if (retrace::profilingCpuTimes || retrace::profilingGpuTimes) { - int64_t cpuTime, gpuTime, error; - - getCurrentTimes(cpuTime, gpuTime); - cpuTime = cpuTime - retrace::profiler.getBaseCpuTime(); - gpuTime = gpuTime - retrace::profiler.getBaseGpuTime(); - error = gpuTime - cpuTime * (1.0E9 / os::timeFrequency); - - retrace::profiler.setBaseGpuTime(retrace::profiler.getBaseGpuTime() + error); - } - /* Indicate end of current frame */ retrace::profiler.addFrameEnd(); }