From: José Fonseca Date: Tue, 10 Jan 2012 20:20:37 +0000 (+0000) Subject: Merge branch 'trace-threads' X-Git-Url: https://git.cworth.org/git?a=commitdiff_plain;h=a9d7f8edfea904f8dc84f2ad472cd542437935f7;hp=8dfb0e569dff79a4d588bb65148046cffbb5d0d4;p=apitrace Merge branch 'trace-threads' --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f1315a..f25a5dd 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -423,7 +423,10 @@ elseif (APPLE) LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/wrappers ) - target_link_libraries (cgltrace dl) + target_link_libraries (cgltrace + pthread + dl + ) install (TARGETS cgltrace LIBRARY DESTINATION ${WRAPPER_INSTALL_DIR}) elseif (X11_FOUND) @@ -452,7 +455,11 @@ elseif (X11_FOUND) LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/wrappers ) - target_link_libraries (glxtrace dl ${X11_X11_LIB}) + target_link_libraries (glxtrace + ${X11_X11_LIB} + pthread + dl + ) install (TARGETS glxtrace LIBRARY DESTINATION ${WRAPPER_INSTALL_DIR}) endif () @@ -490,7 +497,10 @@ if (EGL_FOUND) LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/wrappers ) - target_link_libraries (egltrace dl) + target_link_libraries (egltrace + pthread + dl + ) install (TARGETS egltrace LIBRARY DESTINATION ${WRAPPER_INSTALL_DIR}) endif () diff --git a/TODO.markdown b/TODO.markdown index 3cd4e4e..2fd2383 100644 --- a/TODO.markdown +++ b/TODO.markdown @@ -9,12 +9,6 @@ Tracing * Allow clamping to a GL version or a number of extensions. -* Trace multiple threads: - - * `GetCurrentThreadId()` - - * `pthread_self()` - * Put (de)compression in a separate thread. * Trace TSCs diff --git a/cgltrace.py b/cgltrace.py index 2100723..e2e9874 100644 --- a/cgltrace.py +++ b/cgltrace.py @@ -45,7 +45,7 @@ if __name__ == '__main__': print '#include ' print '#include ' print - print '#include "trace_writer.hpp"' + print '#include "trace_writer_local.hpp"' print print '// To validate our prototypes' print '#define GL_GLEXT_PROTOTYPES' diff --git a/cli/cli_dump.cpp b/cli/cli_dump.cpp index f6c5129..adecb22 100644 --- a/cli/cli_dump.cpp +++ b/cli/cli_dump.cpp @@ -58,6 +58,7 @@ usage(void) " --colour= Colored syntax highlighting\n" " WHEN is 'auto', 'always', or 'never'\n" " --no-arg-names Don't dump argument names\n" + " --thread-ids Dump thread ids\n" ; } @@ -65,6 +66,7 @@ static int command(int argc, char *argv[]) { trace::DumpFlags dumpFlags = 0; + bool dumpThreadIds = false; int i; @@ -98,6 +100,8 @@ command(int argc, char *argv[]) color = COLOR_OPTION_NEVER; } else if (!strcmp(arg, "--no-arg-names")) { dumpFlags |= trace::DUMP_FLAG_NO_ARG_NAMES; + } else if (!strcmp(arg, "--thread-ids")) { + dumpThreadIds = true; } else { std::cerr << "error: unknown option " << arg << "\n"; usage(); @@ -130,6 +134,9 @@ command(int argc, char *argv[]) while ((call = p.parse_call())) { if (verbose || !(call->flags & trace::CALL_FLAG_VERBOSE)) { + if (dumpThreadIds) { + std::cout << std::hex << call->thread_id << std::dec << " "; + } trace::dump(*call, std::cout, dumpFlags); } delete call; diff --git a/common/os.hpp b/common/os.hpp index 6a3b8c8..caf9dc3 100644 --- a/common/os.hpp +++ b/common/os.hpp @@ -46,10 +46,6 @@ namespace os { -void acquireMutex(void); - -void releaseMutex(void); - void log(const char *format, ...) #ifdef __GNUC__ __attribute__ ((format (printf, 1, 2))) diff --git a/common/os_posix.cpp b/common/os_posix.cpp index 7dc2bb4..65c5404 100644 --- a/common/os_posix.cpp +++ b/common/os_posix.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -58,24 +57,6 @@ namespace os { -static pthread_mutex_t -mutex = PTHREAD_MUTEX_INITIALIZER; - - -void -acquireMutex(void) -{ - pthread_mutex_lock(&mutex); -} - - -void -releaseMutex(void) -{ - pthread_mutex_unlock(&mutex); -} - - String getProcessName(void) { diff --git a/common/os_thread.hpp b/common/os_thread.hpp new file mode 100644 index 0000000..b17563f --- /dev/null +++ b/common/os_thread.hpp @@ -0,0 +1,163 @@ +/************************************************************************** + * + * 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. + * + **************************************************************************/ + +/* + * Simple OS abstraction. + * + * Mimics C++11 / boost threads. + */ + +#ifndef _OS_THREAD_HPP_ +#define _OS_THREAD_HPP_ + + +#ifdef _WIN32 +#include +#else +#include +#endif + +namespace os { + + + class recursive_mutex + { + public: +#ifdef _WIN32 + typedef CRITICAL_SECTION native_handle_type; +#else + typedef pthread_mutex_t native_handle_type; +#endif + + recursive_mutex(void) { +#ifdef _WIN32 + InitializeCriticalSection(&_native_handle); +#else + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&_native_handle, NULL); + pthread_mutexattr_destroy(&attr); +#endif + } + + ~recursive_mutex() { +#ifdef _WIN32 + DeleteCriticalSection(&_native_handle); +#else + pthread_mutex_destroy(&_native_handle); +#endif + } + + inline void + lock(void) { +#ifdef _WIN32 + EnterCriticalSection(&_native_handle); +#else + pthread_mutex_lock(&_native_handle); +#endif + } + + inline void + unlock(void) { +#ifdef _WIN32 + LeaveCriticalSection(&_native_handle); +#else + pthread_mutex_unlock(&_native_handle); +#endif + } + + private: + native_handle_type _native_handle; + }; + + + template + class thread_specific_ptr + { + private: +#ifdef _WIN32 + DWORD dwTlsIndex; +#else + pthread_key_t key; + + static void destructor(void *ptr) { + delete static_cast(ptr); + } +#endif + + public: + thread_specific_ptr(void) { +#ifdef _WIN32 + dwTlsIndex = TlsAlloc(); +#else + pthread_key_create(&key, &destructor); +#endif + } + + ~thread_specific_ptr() { +#ifdef _WIN32 + TlsFree(dwTlsIndex); +#else + pthread_key_delete(key); +#endif + } + + T* get(void) const { + void *ptr; +#ifdef _WIN32 + ptr = TlsGetValue(dwTlsIndex); +#else + ptr = pthread_getspecific(key); +#endif + return static_cast(ptr); + } + + T* operator -> (void) const + { + return get(); + } + + T& operator * (void) const + { + return *get(); + } + + void reset(T* new_value=0) { + T * old_value = get(); +#ifdef _WIN32 + TlsSetValue(dwTlsIndex, new_value); +#else + pthread_setspecific(key, new_value); +#endif + if (old_value) { + delete old_value; + } + } + }; + +} /* namespace os */ + +#endif /* _OS_THREAD_HPP_ */ diff --git a/common/os_win32.cpp b/common/os_win32.cpp index 0b72433..b35346e 100644 --- a/common/os_win32.cpp +++ b/common/os_win32.cpp @@ -38,29 +38,6 @@ namespace os { -/* - * Trick from http://locklessinc.com/articles/pthreads_on_windows/ - */ -static CRITICAL_SECTION -criticalSection = { - (PCRITICAL_SECTION_DEBUG)-1, -1, 0, 0, 0, 0 -}; - - -void -acquireMutex(void) -{ - EnterCriticalSection(&criticalSection); -} - - -void -releaseMutex(void) -{ - LeaveCriticalSection(&criticalSection); -} - - String getProcessName(void) { @@ -258,21 +235,56 @@ abort(void) } -static LPTOP_LEVEL_EXCEPTION_FILTER prevExceptionFilter = NULL; +static PVOID prevExceptionFilter = NULL; static void (*gCallback)(void) = NULL; -static LONG WINAPI -unhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo) +static LONG CALLBACK +unhandledExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) { - if (gCallback) { - gCallback(); + PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord; + + /* + * Ignore OutputDebugStringA exception. + */ + if (pExceptionRecord->ExceptionCode == DBG_PRINTEXCEPTION_C) { + return EXCEPTION_CONTINUE_SEARCH; + } + + /* + * Ignore C++ exceptions + * + * http://support.microsoft.com/kb/185294 + * http://blogs.msdn.com/b/oldnewthing/archive/2010/07/30/10044061.aspx + */ + if (pExceptionRecord->ExceptionCode == 0xe06d7363) { + return EXCEPTION_CONTINUE_SEARCH; } - if (prevExceptionFilter) { - return prevExceptionFilter(pExceptionInfo); + // Clear direction flag +#ifdef _MSC_VER +#ifndef _WIN64 + __asm { + cld + }; +#endif +#else + asm("cld"); +#endif + + log("apitrace: warning: caught exception 0x%08lx\n", pExceptionRecord->ExceptionCode); + + static int recursion_count = 0; + if (recursion_count) { + fprintf(stderr, "apitrace: warning: recursion handling exception\n"); } else { - return EXCEPTION_CONTINUE_SEARCH; + if (gCallback) { + ++recursion_count; + gCallback(); + --recursion_count; + } } + + return EXCEPTION_CONTINUE_SEARCH; } void @@ -285,19 +297,17 @@ setExceptionCallback(void (*callback)(void)) assert(!prevExceptionFilter); - /* - * TODO: Unfortunately it seems that the CRT will reset the exception - * handler in certain circumnstances. See - * http://www.codeproject.com/KB/winsdk/crash_hook.aspx - */ - prevExceptionFilter = SetUnhandledExceptionFilter(unhandledExceptionFilter); + prevExceptionFilter = AddVectoredExceptionHandler(0, unhandledExceptionHandler); } } void resetExceptionCallback(void) { - gCallback = NULL; + if (gCallback) { + RemoveVectoredExceptionHandler(prevExceptionFilter); + gCallback = NULL; + } } diff --git a/common/trace_format.hpp b/common/trace_format.hpp index 5aade00..4e1d9a9 100644 --- a/common/trace_format.hpp +++ b/common/trace_format.hpp @@ -66,8 +66,11 @@ namespace trace { * * - version 3: * - enums signatures are recorded for the a whole set of values (not as individual values) + * + * - version 4: + * - call enter events include thread ID */ -#define TRACE_VERSION 3 +#define TRACE_VERSION 4 /* @@ -75,7 +78,7 @@ namespace trace { * * trace = event* EOF * - * event = EVENT_ENTER call_sig call_detail+ + * event = EVENT_ENTER thread_id call_sig call_detail+ * | EVENT_LEAVE call_no call_detail+ * * call_sig = sig_id ( name arg_names )? diff --git a/common/trace_model.hpp b/common/trace_model.hpp index 4f150bc..076ff09 100644 --- a/common/trace_model.hpp +++ b/common/trace_model.hpp @@ -410,6 +410,7 @@ enum { class Call { public: + unsigned thread_id; unsigned no; const FunctionSig *sig; std::vector args; @@ -417,7 +418,8 @@ public: CallFlags flags; - Call(FunctionSig *_sig, const CallFlags &_flags) : + Call(FunctionSig *_sig, const CallFlags &_flags, unsigned _thread_id) : + thread_id(_thread_id), sig(_sig), args(_sig->num_args), ret(0), diff --git a/common/trace_parser.cpp b/common/trace_parser.cpp index f3e623a..88afa98 100644 --- a/common/trace_parser.cpp +++ b/common/trace_parser.cpp @@ -390,9 +390,17 @@ BitmaskSig *Parser::parse_bitmask_sig() { void Parser::parse_enter(Mode mode) { + unsigned thread_id; + + if (version >= 4) { + thread_id = read_uint(); + } else { + thread_id = 0; + } + FunctionSigFlags *sig = parse_function_sig(); - Call *call = new Call(sig, sig->flags); + Call *call = new Call(sig, sig->flags, thread_id); call->no = next_call_no++; diff --git a/common/trace_writer.cpp b/common/trace_writer.cpp index 1679989..5708e50 100644 --- a/common/trace_writer.cpp +++ b/common/trace_writer.cpp @@ -134,8 +134,9 @@ inline bool lookup(std::vector &map, size_t index) { } } -unsigned Writer::beginEnter(const FunctionSig *sig) { +unsigned Writer::beginEnter(const FunctionSig *sig, unsigned thread_id) { _writeByte(trace::EVENT_ENTER); + _writeUInt(thread_id); _writeUInt(sig->id); if (!lookup(functions, sig->id)) { _writeString(sig->name); diff --git a/common/trace_writer.hpp b/common/trace_writer.hpp index f89eb65..e012a9b 100644 --- a/common/trace_writer.hpp +++ b/common/trace_writer.hpp @@ -58,7 +58,7 @@ namespace trace { bool open(const char *filename); void close(void); - unsigned beginEnter(const FunctionSig *sig); + unsigned beginEnter(const FunctionSig *sig, unsigned thread_id); void endEnter(void); void beginLeave(unsigned call); @@ -105,46 +105,6 @@ namespace trace { }; - extern const FunctionSig memcpy_sig; - extern const FunctionSig malloc_sig; - extern const FunctionSig free_sig; - extern const FunctionSig realloc_sig; - - /** - * A specialized Writer class, mean to trace the current process. - * - * In particular: - * - it creates a trace file based on the current process name - * - uses mutexes to allow tracing from multiple threades - * - flushes the output to ensure the last call is traced in event of - * abnormal termination - */ - class LocalWriter : public Writer { - protected: - int acquired; - - public: - /** - * Should never called directly -- use localWriter singleton below instead. - */ - LocalWriter(); - ~LocalWriter(); - - void open(void); - - unsigned beginEnter(const FunctionSig *sig); - void endEnter(void); - - void beginLeave(unsigned call); - void endLeave(void); - - void flush(void); - }; - - /** - * Singleton. - */ - extern LocalWriter localWriter; -} +} /* namespace trace */ #endif /* _TRACE_WRITER_HPP_ */ diff --git a/common/trace_writer_local.cpp b/common/trace_writer_local.cpp index 0730150..a3ab720 100644 --- a/common/trace_writer_local.cpp +++ b/common/trace_writer_local.cpp @@ -31,9 +31,10 @@ #include #include "os.hpp" +#include "os_thread.hpp" #include "os_string.hpp" #include "trace_file.hpp" -#include "trace_writer.hpp" +#include "trace_writer_local.hpp" #include "trace_format.hpp" @@ -123,25 +124,39 @@ LocalWriter::open(void) { #endif } +static unsigned next_thread_id = 0; +static os::thread_specific_ptr thread_id_specific_ptr; + unsigned LocalWriter::beginEnter(const FunctionSig *sig) { - os::acquireMutex(); + mutex.lock(); ++acquired; if (!m_file->isOpened()) { open(); } - return Writer::beginEnter(sig); + unsigned *thread_id_ptr = thread_id_specific_ptr.get(); + unsigned thread_id; + if (thread_id_ptr) { + thread_id = *thread_id_ptr; + } else { + thread_id = next_thread_id++; + thread_id_ptr = new unsigned; + *thread_id_ptr = thread_id; + thread_id_specific_ptr.reset(thread_id_ptr); + } + + return Writer::beginEnter(sig, thread_id); } void LocalWriter::endEnter(void) { Writer::endEnter(); --acquired; - os::releaseMutex(); + mutex.unlock(); } void LocalWriter::beginLeave(unsigned call) { - os::acquireMutex(); + mutex.lock(); ++acquired; Writer::beginLeave(call); } @@ -149,23 +164,28 @@ void LocalWriter::beginLeave(unsigned call) { void LocalWriter::endLeave(void) { Writer::endLeave(); --acquired; - os::releaseMutex(); + mutex.unlock(); } void LocalWriter::flush(void) { /* * Do nothing if the mutex is already acquired (e.g., if a segfault happen - * while writing the file) to prevent dead-lock. + * while writing the file) as state could be inconsistent, therefore yield + * inconsistent trace files and/or repeated segfaults till infinity. */ - if (!acquired) { - os::acquireMutex(); + mutex.lock(); + if (acquired) { + os::log("apitrace: ignoring exception while tracing\n"); + } else { + ++acquired; if (m_file->isOpened()) { os::log("apitrace: flushing trace due to an exception\n"); m_file->flush(); } - os::releaseMutex(); + --acquired; } + mutex.unlock(); } diff --git a/common/trace_writer_local.hpp b/common/trace_writer_local.hpp new file mode 100644 index 0000000..e54142f --- /dev/null +++ b/common/trace_writer_local.hpp @@ -0,0 +1,87 @@ +/************************************************************************** + * + * Copyright 2007-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. + * + **************************************************************************/ + +/* + * Trace writing functions. + */ + +#ifndef _TRACE_WRITER_LOCAL_HPP_ +#define _TRACE_WRITER_LOCAL_HPP_ + + +#include "os_thread.hpp" +#include "trace_writer.hpp" + + +namespace trace { + + extern const FunctionSig memcpy_sig; + extern const FunctionSig malloc_sig; + extern const FunctionSig free_sig; + extern const FunctionSig realloc_sig; + + /** + * A specialized Writer class, mean to trace the current process. + * + * In particular: + * - it creates a trace file based on the current process name + * - uses mutexes to allow tracing from multiple threades + * - flushes the output to ensure the last call is traced in event of + * abnormal termination + */ + class LocalWriter : public Writer { + protected: + /** + * We need a recursive mutex so that it doesn't dead lock when a segfault happens when the mutex is held. + */ + os::recursive_mutex mutex; + int acquired; + + public: + /** + * Should never called directly -- use localWriter singleton below instead. + */ + LocalWriter(); + ~LocalWriter(); + + void open(void); + + unsigned beginEnter(const FunctionSig *sig); + void endEnter(void); + + void beginLeave(unsigned call); + void endLeave(void); + + void flush(void); + }; + + /** + * Singleton. + */ + extern LocalWriter localWriter; + +} /* namespace trace */ + +#endif /* _TRACE_WRITER_LOCAL_HPP_ */ diff --git a/common/trace_writer_model.cpp b/common/trace_writer_model.cpp index ccd7b22..cc3af28 100644 --- a/common/trace_writer_model.cpp +++ b/common/trace_writer_model.cpp @@ -101,7 +101,7 @@ public: } void visit(Call *call) { - unsigned call_no = writer.beginEnter(call->sig); + unsigned call_no = writer.beginEnter(call->sig, call->thread_id); for (unsigned i = 0; i < call->args.size(); ++i) { if (call->args[i]) { writer.beginArg(i); diff --git a/d3d10trace.py b/d3d10trace.py index edcf38a..b94527c 100644 --- a/d3d10trace.py +++ b/d3d10trace.py @@ -29,7 +29,7 @@ from trace import DllTracer if __name__ == '__main__': - print '#include "trace_writer.hpp"' + print '#include "trace_writer_local.hpp"' print '#include "os.hpp"' print print '#include ' diff --git a/d3d8trace.py b/d3d8trace.py index 65e13ed..d3754e5 100644 --- a/d3d8trace.py +++ b/d3d8trace.py @@ -44,7 +44,7 @@ if __name__ == '__main__': print '#include ' print '#include "d3dshader.hpp"' print - print '#include "trace_writer.hpp"' + print '#include "trace_writer_local.hpp"' print '#include "os.hpp"' print tracer = D3D8Tracer('d3d8.dll') diff --git a/d3d9trace.py b/d3d9trace.py index e6bf000..27fbc35 100644 --- a/d3d9trace.py +++ b/d3d9trace.py @@ -40,7 +40,7 @@ class D3D9Tracer(DllTracer): if __name__ == '__main__': - print '#include "trace_writer.hpp"' + print '#include "trace_writer_local.hpp"' print '#include "os.hpp"' print print '#include "d3d9imports.hpp"' diff --git a/ddrawtrace.py b/ddrawtrace.py index fb8f2a5..7022e13 100644 --- a/ddrawtrace.py +++ b/ddrawtrace.py @@ -74,7 +74,7 @@ if __name__ == '__main__': #endif ''' - print '#include "trace_writer.hpp"' + print '#include "trace_writer_local.hpp"' print '#include "os.hpp"' print tracer = DDrawTracer('ddraw.dll') diff --git a/egltrace.py b/egltrace.py index de90799..8fa313a 100644 --- a/egltrace.py +++ b/egltrace.py @@ -76,7 +76,7 @@ if __name__ == '__main__': print '#include ' print '#include ' print - print '#include "trace_writer.hpp"' + print '#include "trace_writer_local.hpp"' print print '// To validate our prototypes' print '#define GL_GLEXT_PROTOTYPES' diff --git a/glxtrace.py b/glxtrace.py index c9e9518..9adcaaf 100644 --- a/glxtrace.py +++ b/glxtrace.py @@ -58,7 +58,7 @@ if __name__ == '__main__': print '#endif' print '#include ' print - print '#include "trace_writer.hpp"' + print '#include "trace_writer_local.hpp"' print print '// To validate our prototypes' print '#define GL_GLEXT_PROTOTYPES' diff --git a/wgltrace.py b/wgltrace.py index d5a5248..9dab406 100644 --- a/wgltrace.py +++ b/wgltrace.py @@ -66,7 +66,7 @@ if __name__ == '__main__': print '#include ' print '#include ' print - print '#include "trace_writer.hpp"' + print '#include "trace_writer_local.hpp"' print '#include "os.hpp"' print print '// To validate our prototypes'