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)
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
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}
)
add_subdirectory (dispatch)
add_subdirectory (helpers)
add_subdirectory (wrappers)
+add_subdirectory (image)
add_subdirectory (retrace)
# CLI
if (ENABLE_CLI)
+ if (WIN32)
+ add_subdirectory (inject)
+ endif ()
add_subdirectory (cli)
endif ()
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
---------------------------------
+# 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
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}
#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
#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
#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.";
file1 = argv[optind];
file2 = argv[optind + 1];
- os::String command = trace::findScript("jsondiff.py");
+ os::String command = findScript("jsondiff.py");
char *args[5];
--- /dev/null
+/*********************************************************************
+ *
+ * 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 <iostream>
+
+#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 "";
+}
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <stdlib.h>
+
+#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_ */
#include "os_process.hpp"
#include "trace_parser.hpp"
-#include "trace_resource.hpp"
+#include "cli_resources.hpp"
#include "cli.hpp"
#include "cli_retrace.hpp"
{
trace::Parser p;
if (!p.open(filename)) {
+ exit(1);
return trace::API_UNKNOWN;
}
trace::Call *call;
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:
}
std::vector<const char *> command;
- os::String retracePath = trace::findProgram(retraceName);
+ os::String retracePath = findProgram(retraceName);
if (retracePath.length()) {
command.push_back(retracePath);
} else {
#include <assert.h>
#include <string.h>
+#include <stdlib.h>
#include <getopt.h>
#include <iostream>
+#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<const char *> args;
+ int status = 1;
+
+ /*
+ * TODO: simplify code
+ */
+
+ bool useInject = false;
+ switch (api) {
+ case trace::API_GL:
+ wrapperFilename = GL_TRACE_WRAPPER;
+ break;
+#ifdef EGL_TRACE_WRAPPER
+ case trace::API_EGL:
+ wrapperFilename = EGL_TRACE_WRAPPER;
+ break;
+#endif
+#ifdef _WIN32
+ case trace::API_D3D7:
+ wrapperFilename = "ddraw.dll";
+ break;
+ case trace::API_D3D8:
+ wrapperFilename = "d3d8.dll";
+ break;
+ case trace::API_D3D9:
+ wrapperFilename = "d3d9.dll";
+ break;
+ case trace::API_DXGI:
+ wrapperFilename = "dxgitrace.dll";
+ useInject = true;
+ break;
+#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.";
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();
}
assert(argv[argc] == 0);
- return trace::traceProgram(api, argv + optind, output, verbose);
+ return traceProgram(api, argv + optind, output, verbose);
}
const Command trace_command = {
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <ostream>
+#include <string>
+#include <limits>
+
+
+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<const char *>(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<T>::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_ */
+++ /dev/null
-/**************************************************************************
- *
- * 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 <assert.h>
-#include <math.h>
-
-#include <algorithm>
-
-#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 */
+++ /dev/null
-/**************************************************************************
- *
- * 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 <fstream>
-
-
-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_ */
+++ /dev/null
-/**************************************************************************
- *
- * 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 <assert.h>
-#include <stdint.h>
-
-#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 */
+++ /dev/null
-/**************************************************************************
- *
- * 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 <zlib.h>
-#include <png.h>
-
-#include <assert.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-#include <fstream>
-
-#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 */
+++ /dev/null
-/**************************************************************************
- *
- * 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 <assert.h>
-#include <string.h>
-#include <stdint.h>
-#include <stdio.h>
-
-#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 */
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;
+++ /dev/null
-/**************************************************************************
- *
- * 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 <assert.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#include <ostream>
-#include <string>
-#include <limits>
-
-
-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<const char *>(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<T>::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_ */
API_D3D7,
API_D3D8,
API_D3D9,
- API_D3D10,
- API_D3D10_1,
- API_D3D11,
+ API_DXGI, // D3D10.x, D3D11.x
};
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 */
+++ /dev/null
-/*********************************************************************
- *
- * 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 <iostream>
-
-#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 */
+++ /dev/null
-/**************************************************************************
- *
- * 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 <stdlib.h>
-
-#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_ */
+++ /dev/null
-/**************************************************************************
- *
- * 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 <stdlib.h>
-
-#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_ */
+++ /dev/null
-/*********************************************************************
- *
- * 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 <stdlib.h>
-
-#include <iostream>
-
-#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 */
#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 ###############
void ApiTrace::findFrameStart(ApiTraceFrame *frame)
{
+ if (!frame)
+ return;
+
if (frame->isLoaded()) {
emit foundFrameStart(frame);
} else {
void ApiTrace::findFrameEnd(ApiTraceFrame *frame)
{
+ if (!frame)
+ return;
+
if (frame->isLoaded()) {
emit foundFrameEnd(frame);
} else {
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;
#include "apitrace.h"
#include "apitracecall.h"
+#include "os_string.hpp"
+#include "os_process.hpp"
+
#include <QApplication>
#include <QMetaType>
#include <QVariant>
qRegisterMetaType<ApiTrace::SearchResult>();
qRegisterMetaType<ApiTrace::SearchRequest>();
qRegisterMetaType<QList<QImage> >();
+
+#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;
m_nonDefaultsLookupEvent(0)
{
m_ui.setupUi(this);
+ updateActionsState(false);
initObjects();
initConnections();
}
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)
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()));
}
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;
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;
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();
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();
#include "apitracecall.h"
#include "thumbnail.h"
-#include "image.hpp"
+#include "image/image.hpp"
#include "trace_profiler.hpp"
m_profilePixels(false)
{
qRegisterMetaType<QList<ApiTraceError> >();
-
-#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
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
bool m_profileGpu;
bool m_profileCpu;
bool m_profilePixels;
-
- QProcessEnvironment m_processEnvironment;
};
#endif
tr("Find the application"),
QDir::currentPath());
- if (isFileOk(fileName)) {
+ if (!fileName.isEmpty() && isFileOk(fileName)) {
applicationEdit->setText(fileName);
}
}
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()
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()
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="url" stdset="0">
+ <property name="url">
<url>
<string>about:blank</string>
</url>
</property>
</action>
<action name="actionReplay">
- <property name="enabled">
- <bool>false</bool>
- </property>
<property name="icon">
<iconset resource="../qapitrace.qrc">
<normaloff>:/resources/media-playback-start.png</normaloff>:/resources/media-playback-start.png</iconset>
</property>
</action>
<action name="actionStop">
- <property name="enabled">
- <bool>false</bool>
- </property>
<property name="icon">
<iconset resource="../qapitrace.qrc">
<normaloff>:/resources/media-playback-stop.png</normaloff>:/resources/media-playback-stop.png</iconset>
</property>
</action>
<action name="actionLookupState">
- <property name="enabled">
- <bool>false</bool>
- </property>
<property name="icon">
<iconset resource="../qapitrace.qrc">
<normaloff>:/resources/media-record.png</normaloff>:/resources/media-record.png</iconset>
</property>
</action>
<action name="actionShowThumbnails">
- <property name="enabled">
- <bool>false</bool>
- </property>
<property name="text">
<string>Show &Thumbnails</string>
</property>
</property>
</action>
<action name="actionTrim">
- <property name="enabled">
- <bool>false</bool>
- </property>
<property name="text">
<string>Tr&im</string>
</property>
<normaloff>:/resources/document-new.png</normaloff>:/resources/document-new.png</iconset>
</property>
<property name="text">
- <string>New</string>
+ <string>&New...</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+N</string>
</property>
</action>
<action name="actionFind">
<normaloff>:/resources/edit-find.png</normaloff>:/resources/edit-find.png</iconset>
</property>
<property name="text">
- <string>Find</string>
+ <string>&Find</string>
</property>
<property name="shortcut">
<string>Ctrl+F</string>
</property>
</action>
<action name="actionProfile">
- <property name="enabled">
- <bool>false</bool>
- </property>
<property name="text">
<string>&Profile</string>
</property>
<item>
<widget class="QPushButton" name="browseButton">
<property name="text">
- <string>Browse</string>
+ <string>Browse...</string>
</property>
<property name="flat">
<bool>false</bool>
--- /dev/null
+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}
+)
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <assert.h>
+#include <math.h>
+
+#include <algorithm>
+
+#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 */
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <fstream>
+
+
+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_ */
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <assert.h>
+#include <stdint.h>
+
+#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 */
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <zlib.h>
+#include <png.h>
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <fstream>
+
+#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 */
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <assert.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#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 */
--- /dev/null
+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)
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <windows.h>
+
+
+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<char *>(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';
+}
+
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <assert.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <windows.h>
+#include <tlhelp32.h>
+
+#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;
+}
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <string>
+
+#include <windows.h>
+#include <stdio.h>
+
+#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<char *>(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;
+
+}
${CMAKE_SOURCE_DIR}/helpers
${CMAKE_BINARY_DIR}/dispatch
${CMAKE_SOURCE_DIR}/dispatch
+ ${CMAKE_SOURCE_DIR}/image
)
add_definitions (-DRETRACE)
json.cpp
)
target_link_libraries (retrace_common
+ image
common
- ${PNG_LIBRARIES}
${ZLIB_LIBRARIES}
${SNAPPY_LIBRARIES}
${GETOPT_LIBRARIES}
#include <iostream>
-#include "d3d11imports.hpp"
-#include "json.hpp"
-#include "d3dshader.hpp"
-#include "d3dstate.hpp"
+#include "d3d10imports.hpp"
+#include "d3d10state.hpp"
namespace d3dstate {
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)
{
--- /dev/null
+/**************************************************************************
+ *
+ * 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 <windows.h>
+
+
+#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_
#include <iostream>
#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<ID3D11DeviceChild>(json, "VS", pVertexShader);
+ pVertexShader->Release();
+ }
+
+ ID3D11GeometryShader *pGeometryShader = NULL;
+ pDeviceContext->GSGetShader(&pGeometryShader, NULL, NULL);
+ if (pGeometryShader) {
+ dumpShader<ID3D11DeviceChild>(json, "GS", pGeometryShader);
+ pGeometryShader->Release();
+ }
+
+ ID3D11PixelShader *pPixelShader = NULL;
+ pDeviceContext->PSGetShader(&pPixelShader, NULL, NULL);
+ if (pPixelShader) {
+ dumpShader<ID3D11DeviceChild>(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);
}
#include "image.hpp"
#include "d3d11imports.hpp"
+#include "d3d10state.hpp"
namespace d3dstate {
}
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;
const unsigned char *src;
unsigned char *dst;
- pDevice->OMGetRenderTargets(1, &pRenderTargetView, NULL);
if (!pRenderTargetView) {
- goto no_rendertarget;
+ return NULL;
}
pRenderTargetView->GetResource(&pResource);
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;
}
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);
}
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 */
#include "d3d9imports.hpp"
#include "d3dshader.hpp"
+#include "d3dstate.hpp"
#include "json.hpp"
{
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);
}
#include <assert.h>
#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;
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)) {
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 */
image::Image *
getRenderTargetImage(IDirect3DDevice9 *pDevice);
+void
+dumpFramebuffer(JSONWriter &json, IDirect3DDevice9 *pDevice);
+
void
dumpDevice(std::ostream &os, IDirect3DDevice9 *pDevice);
image::Image *
getRenderTargetImage(ID3D11DeviceContext *pDeviceContext);
+void
+dumpFramebuffer(JSONWriter &json, ID3D11DeviceContext *pDeviceContext);
+
void
dumpDevice(std::ostream &os, ID3D11DeviceContext *pDeviceContext);
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) {'
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):
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
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;
}
}
-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;
}
}
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;
}
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);
}
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]);
}
}
/* 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 */
/* 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);
}
}
}
/* 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();
}