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)
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 ()
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 ()
* 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
print '#include <stdlib.h>'
print '#include <string.h>'
print
- print '#include "trace_writer.hpp"'
+ print '#include "trace_writer_local.hpp"'
print
print '// To validate our prototypes'
print '#define GL_GLEXT_PROTOTYPES'
" --colour=<WHEN> 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"
;
}
command(int argc, char *argv[])
{
trace::DumpFlags dumpFlags = 0;
+ bool dumpThreadIds = false;
int i;
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();
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;
namespace os {
-void acquireMutex(void);
-
-void releaseMutex(void);
-
void log(const char *format, ...)
#ifdef __GNUC__
__attribute__ ((format (printf, 1, 2)))
#include <unistd.h>
#include <sys/time.h>
#include <sys/wait.h>
-#include <pthread.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
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)
{
--- /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.
+ *
+ **************************************************************************/
+
+/*
+ * Simple OS abstraction.
+ *
+ * Mimics C++11 / boost threads.
+ */
+
+#ifndef _OS_THREAD_HPP_
+#define _OS_THREAD_HPP_
+
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <pthread.h>
+#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 <typename T>
+ class thread_specific_ptr
+ {
+ private:
+#ifdef _WIN32
+ DWORD dwTlsIndex;
+#else
+ pthread_key_t key;
+
+ static void destructor(void *ptr) {
+ delete static_cast<T *>(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<T*>(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_ */
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)
{
}
-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
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;
+ }
}
*
* - 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
/*
*
* 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 )?
class Call
{
public:
+ unsigned thread_id;
unsigned no;
const FunctionSig *sig;
std::vector<Value *> args;
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),
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++;
}
}
-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);
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);
};
- 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_ */
#include <string.h>
#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"
#endif
}
+static unsigned next_thread_id = 0;
+static os::thread_specific_ptr<unsigned> 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);
}
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();
}
--- /dev/null
+/**************************************************************************
+ *
+ * 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_ */
}
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);
if __name__ == '__main__':
- print '#include "trace_writer.hpp"'
+ print '#include "trace_writer_local.hpp"'
print '#include "os.hpp"'
print
print '#include <windows.h>'
print '#include <d3d8.h>'
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')
if __name__ == '__main__':
- print '#include "trace_writer.hpp"'
+ print '#include "trace_writer_local.hpp"'
print '#include "os.hpp"'
print
print '#include "d3d9imports.hpp"'
#endif
'''
- print '#include "trace_writer.hpp"'
+ print '#include "trace_writer_local.hpp"'
print '#include "os.hpp"'
print
tracer = DDrawTracer('ddraw.dll')
print '#include <string.h>'
print '#include <dlfcn.h>'
print
- print '#include "trace_writer.hpp"'
+ print '#include "trace_writer_local.hpp"'
print
print '// To validate our prototypes'
print '#define GL_GLEXT_PROTOTYPES'
print '#endif'
print '#include <dlfcn.h>'
print
- print '#include "trace_writer.hpp"'
+ print '#include "trace_writer_local.hpp"'
print
print '// To validate our prototypes'
print '#define GL_GLEXT_PROTOTYPES'
print '#include <string.h>'
print '#include <windows.h>'
print
- print '#include "trace_writer.hpp"'
+ print '#include "trace_writer_local.hpp"'
print '#include "os.hpp"'
print
print '// To validate our prototypes'