d3d8.cpp
d3d9.cpp
ddraw.cpp
-dump
dxsdk
glproc.hpp
glretrace
glretrace_gl.cpp
glretrace_state.cpp
glxtrace.cpp
+tracedump
traces
wgltrace.cpp
# Enable math constants defines
add_definitions (-D_USE_MATH_DEFINES)
+ # No min/max macros
+ add_definitions (-DNOMINMAX)
+
# Adjust warnings
add_definitions (-D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS)
add_definitions (-D_SCL_SECURE_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS)
add_library (trace trace_model.cpp trace_parser.cpp trace_write.cpp ${os})
-add_executable (dump dump.cpp)
-target_link_libraries (dump trace)
+add_executable (tracedump tracedump.cpp)
+target_link_libraries (tracedump trace)
add_custom_command (
OUTPUT glretrace_gl.cpp
* View the trace with
- /path/to/dump application.trace
+ /path/to/tracedump application.trace
* Replay the trace with
* View the trace with
- /path/to/dump application.trace
+ /path/to/tracedump application.trace
* Replay the trace with
* Add option to include call stack frames in the trace
+* Call gzflush() only when there is a signal/exception, except of doing it on
+ every call.
+
Retracing:
+++ /dev/null
-/**************************************************************************
- *
- * Copyright 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.
- *
- **************************************************************************/
-
-
-/*
- * Simple utility to dump a trace to standard output.
- */
-
-
-#include "trace_parser.hpp"
-
-
-int main(int argc, char **argv)
-{
- for (int i = 1; i < argc; ++i) {
- Trace::Parser p;
- if (p.open(argv[i])) {
- Trace::Call *call;
- call = p.parse_call();
- while (call) {
- std::cout << *call;
- delete call;
- call = p.parse_call();
- }
- }
- }
- return 0;
-}
}
-#ifdef WIN32
+#ifdef _WIN32
#include <windows.h>
inline Formatter *defaultFormatter(void) {
-#ifdef WIN32
+#ifdef _WIN32
return new WindowsFormatter;
#else
return new AnsiFormatter;
#ifndef _GLIMPORTS_HPP_
#define _GLIMPORTS_HPP_
-#ifdef WIN32
+#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
-#else /* !WIN32 */
+#else /* !_WIN32 */
#include <X11/Xlib.h>
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
#include <GL/gl.h>
#define GL_TEXTURE_INDEX_SIZE_EXT 0x80ED
#endif
-#ifdef WIN32
+#ifdef _WIN32
#include <GL/wglext.h>
#endif /* __MINGW32__ */
-#else /* !WIN32 */
+#else /* !_WIN32 */
#include <GL/glx.h>
#include <GL/glext.h>
/* Prevent collision with Trace::Bool */
#undef Bool
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
#endif /* _GLIMPORTS_HPP_ */
def header(self):
print '#ifdef RETRACE'
- print '# ifdef WIN32'
+ print '# ifdef _WIN32'
print '# define __getPrivateProcAddress(name) wglGetProcAddress(name)'
print '# else'
print '# define __getPrivateProcAddress(name) glXGetProcAddressARB((const GLubyte *)(name))'
print '# endif'
print '# define __abort() OS::Abort()'
print '#else /* !RETRACE */'
- print '# ifdef WIN32'
+ print '# ifdef _WIN32'
print '# define __getPrivateProcAddress(name) __wglGetProcAddress(name)'
print ' static inline PROC __stdcall __wglGetProcAddress(const char * lpszProc);'
print '# else'
- print '# define __getPublicProcAddress(name) dlsym(RTLD_NEXT, name)'
+ print ' static void *libgl_handle = RTLD_NEXT;'
+ print '# define __getPublicProcAddress(name) dlsym(libgl_handle, name)'
print '# define __getPrivateProcAddress(name) __glXGetProcAddressARB((const GLubyte *)(name))'
print ' static inline __GLXextFuncPtr __glXGetProcAddressARB(const GLubyte * procName);'
print '# endif'
print
dispatcher = GlDispatcher()
dispatcher.header()
- print '#ifdef WIN32'
+ print '#ifdef _WIN32'
print
dispatcher.dispatch_api(wglapi)
- print '#else /* !WIN32 */'
+ print '#else /* !_WIN32 */'
print
dispatcher.dispatch_api(glxapi)
- print '#endif /* !WIN32 */'
+ print '#endif /* !_WIN32 */'
print
dispatcher.dispatch_api(glapi)
print
is_draw_elements = function.name in self.draw_elements_function_names
if is_array_pointer or is_draw_array or is_draw_elements:
- print ' if (Trace::Parser::version < 1) {'
+ print ' if (glretrace::parser.version < 1) {'
if is_array_pointer or is_draw_array:
print ' GLint __array_buffer = 0;'
case GL_DEPTH_COMPONENT:
case GL_STENCIL_INDEX:
return 1;
- break;
case GL_LUMINANCE_ALPHA:
+ case GL_RG:
return 2;
- break;
case GL_RGB:
case GL_BGR:
return 3;
- break;
case GL_RGBA:
case GL_BGRA:
return 4;
- break;
default:
OS::DebugMessage("warning: %s: unexpected format GLenum 0x%04X\n", __FUNCTION__, format);
return 0;
- break;
}
}
("glGet", X, 1, "GL_PROXY_TEXTURE_1D"), # 0x8063
("glGet", X, 1, "GL_PROXY_TEXTURE_2D"), # 0x8064
("glGet", X, 1, "GL_TEXTURE_TOO_LARGE_EXT"), # 0x8065
- ("glGetTexParameter", I, 1, "GL_TEXTURE_PRIORITY"), # 0x8066
+ ("glGetTexParameter", F, 1, "GL_TEXTURE_PRIORITY"), # 0x8066
("glGetTexParameter", B, 1, "GL_TEXTURE_RESIDENT"), # 0x8067
("glGet", I, 1, "GL_TEXTURE_BINDING_1D"), # 0x8068
("glGet", I, 1, "GL_TEXTURE_BINDING_2D"), # 0x8069
glGetVertexAttrib = StateGetter('glGetVertexAttrib', {I: 'iv', F: 'fv', D: 'dv', P: 'Pointerv'})
glGetTexParameter = StateGetter('glGetTexParameter', {I: 'iv', F: 'fv'})
+glGetTexLevelParameter = StateGetter('glGetTexLevelParameter', {I: 'iv', F: 'fv'})
class JsonWriter(Visitor):
def dump(self):
print '#include <string.h>'
print '#include <iostream>'
+ print '#include <algorithm>'
print
print '#include "json.hpp"'
print '#include "glimports.hpp"'
print ' }'
print
print ' json.beginObject();'
- print
- print ' GLfloat param;'
- for function, type, count, name in parameters:
- if function != 'glGetTexParameter' or count != 1:
- continue
- print ' glGetTexParameterfv(target, %s, ¶m);' % name
- print ' json.beginMember("%s");' % name
- JsonWriter().visit(type, 'param')
- print ' json.endMember();'
- print
- print
print ' json.beginMember("levels");'
print ' json.beginArray();'
print ' GLint level = 0;'
print ' json.writeNumber(texture);'
print ' json.endMember();'
print
- # TODO: Generalize this
- for function, type, count, name in parameters:
- if function != 'glGetTexLevelParameter' or count != 1:
- continue
- print ' glGetTexLevelParameterfv(target, level, %s, ¶m);' % name
- print ' json.beginMember("%s");' % name
- JsonWriter().visit(type, 'param')
- print ' json.endMember();'
- print
print ' json.beginMember("image");'
print ' writeTextureImage(json, target, level);'
print ' json.endMember();'
self.dump_atoms(glGet)
self.dump_vertex_attribs()
+ self.dump_texture_parameters()
print ' json.endObject();'
print ' json.endMember(); // parameters'
print
def dump_vertex_attribs(self):
- print ' json.beginMember("GL_VERTEX_ATTRIB");'
- print ' json.beginArray();'
print ' GLint max_vertex_attribs = 0;'
print ' __glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_vertex_attribs);'
print ' for (GLint index = 0; index < max_vertex_attribs; ++index) {'
+ print ' char name[32];'
+ print ' snprintf(name, sizeof name, "GL_VERTEX_ATTRIB_ARRAY%i", index);'
+ print ' json.beginMember(name);'
print ' json.beginObject();'
self.dump_atoms(glGetVertexAttrib, 'index')
print ' json.endObject();'
+ print ' json.endMember(); // GL_VERTEX_ATTRIB_ARRAYi'
print ' }'
print
- print ' json.endArray();'
- print ' json.endMember(); // GL_VERTEX_ATTRIB'
+
+ def dump_texture_parameters(self):
+ print ' {'
+ print ' GLint active_texture = GL_TEXTURE0;'
+ print ' glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);'
+ print ' GLint max_texture_coords = 0;'
+ print ' glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);'
+ print ' GLint max_combined_texture_image_units = 0;'
+ print ' glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_combined_texture_image_units);'
+ print ' GLint max_units = std::max(max_combined_texture_image_units, max_texture_coords);'
+ print ' for (GLint unit = 0; unit < max_units; ++unit) {'
+ print ' char name[32];'
+ print ' snprintf(name, sizeof name, "GL_TEXTURE%i", unit);'
+ print ' json.beginMember(name);'
+ print ' glActiveTexture(GL_TEXTURE0 + unit);'
+ print ' json.beginObject();'
+ print ' GLint texture;'
+ print
+ for target, binding in texture_targets:
+ print ' // %s' % target
+ print ' texture = 0;'
+ print ' glGetIntegerv(%s, &texture);' % binding
+ print ' if (glIsEnabled(%s) || texture) {' % target
+ print ' json.beginMember("%s");' % target
+ print ' json.beginObject();'
+ self.dump_atoms(glGetTexParameter, target)
+ # We only dump the first level parameters
+ self.dump_atoms(glGetTexLevelParameter, target, "0")
+ print ' json.endObject();'
+ print ' json.endMember(); // %s' % target
+ print ' }'
+ print
+ print ' json.endObject();'
+ print ' json.endMember(); // GL_TEXTUREi'
+ print ' }'
+ print ' glActiveTexture(active_texture);'
+ print ' }'
print
def dump_current_program(self):
print
def dump_textures(self):
- print ' json.beginMember("textures");'
- print ' json.beginArray();'
- print ' GLint active_texture = GL_TEXTURE0;'
- print ' glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);'
- print ' GLint max_texture_coords = 0;'
- print ' glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);'
- print ' for (GLint unit = 0; unit < max_texture_coords; ++unit) {'
- print ' glActiveTexture(GL_TEXTURE0 + unit);'
- print ' json.beginObject();'
+ print ' {'
+ print ' json.beginMember("textures");'
+ print ' json.beginArray();'
+ print ' GLint active_texture = GL_TEXTURE0;'
+ print ' glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);'
+ print ' GLint max_texture_coords = 0;'
+ print ' glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);'
+ print ' GLint max_combined_texture_image_units = 0;'
+ print ' glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_combined_texture_image_units);'
+ print ' GLint max_units = std::max(max_combined_texture_image_units, max_texture_coords);'
+ print ' for (GLint unit = 0; unit < max_units; ++unit) {'
+ print ' glActiveTexture(GL_TEXTURE0 + unit);'
+ print ' json.beginObject();'
for target, binding in texture_targets:
- print ' json.beginMember("%s");' % target
- print ' writeTexture(json, %s, %s);' % (target, binding)
- print ' json.endMember();'
- print ' json.endObject();'
+ print ' json.beginMember("%s");' % target
+ print ' writeTexture(json, %s, %s);' % (target, binding)
+ print ' json.endMember();'
+ print ' json.endObject();'
+ print ' }'
+ print ' glActiveTexture(active_texture);'
+ print ' json.endArray();'
+ print ' json.endMember(); // texture'
print ' }'
- print ' glActiveTexture(active_texture);'
- print ' json.endArray();'
- print ' json.endMember(); // texture'
print
def dump_framebuffer(self):
enable_name = 'GL_%s_ARRAY' % uppercase_name
binding_name = 'GL_%s_ARRAY_BUFFER_BINDING' % uppercase_name
print ' // %s' % function_name
+ self.array_prolog(api, uppercase_name)
print ' if (__glIsEnabled(%s)) {' % enable_name
print ' GLint __binding = 0;'
print ' __glGetIntegerv(%s, &__binding);' % binding_name
print ' if (!__binding) {'
+ self.array_cleanup(api, uppercase_name)
print ' return true;'
print ' }'
print ' }'
+ self.array_epilog(api, uppercase_name)
print
print ' // glVertexAttribPointer'
function = api.get_function_by_name(function_name)
print ' // %s' % function.name
- print ' if (__glIsEnabled(%s)) {;' % enable_name
+ self.array_trace_prolog(api, uppercase_name)
+ self.array_prolog(api, uppercase_name)
+ print ' if (__glIsEnabled(%s)) {' % enable_name
print ' GLint __binding = 0;'
print ' __glGetIntegerv(%s, &__binding);' % binding_name
print ' if (!__binding) {'
print ' size_t __size = __%s_size(%s, maxindex);' % (function.name, arg_names)
# Emit a fake function
+ self.array_trace_intermezzo(api, uppercase_name)
print ' unsigned __call = Trace::BeginEnter(__%s_sig);' % (function.name,)
for arg in function.args:
assert not arg.output
print ' Trace::EndLeave();'
print ' }'
print ' }'
+ self.array_epilog(api, uppercase_name)
+ self.array_trace_epilog(api, uppercase_name)
print
# Samething, but for glVertexAttribPointer
print '}'
print
+ #
+ # Hooks for glTexCoordPointer, which is identical to the other array
+ # pointers except the fact that it is indexed by glClientActiveTexture.
+ #
+
+ def array_prolog(self, api, uppercase_name):
+ if uppercase_name == 'TEXTURE_COORD':
+ print ' GLint client_active_texture = 0;'
+ print ' __glGetIntegerv(GL_CLIENT_ACTIVE_TEXTURE, &client_active_texture);'
+ print ' GLint max_texture_coords = 0;'
+ print ' __glGetIntegerv(GL_MAX_TEXTURE_COORDS, &max_texture_coords);'
+ print ' for (GLint unit = 0; unit < max_texture_coords; ++unit) {'
+ print ' GLenum texture = GL_TEXTURE0 + unit;'
+ print ' __glClientActiveTexture(texture);'
+
+ def array_trace_prolog(self, api, uppercase_name):
+ if uppercase_name == 'TEXTURE_COORD':
+ print ' bool client_active_texture_dirty = false;'
+
+ def array_epilog(self, api, uppercase_name):
+ if uppercase_name == 'TEXTURE_COORD':
+ print ' }'
+ self.array_cleanup(api, uppercase_name)
+
+ def array_cleanup(self, api, uppercase_name):
+ if uppercase_name == 'TEXTURE_COORD':
+ print ' __glClientActiveTexture(client_active_texture);'
+
+ def array_trace_intermezzo(self, api, uppercase_name):
+ if uppercase_name == 'TEXTURE_COORD':
+ print ' if (texture != client_active_texture || client_active_texture_dirty) {'
+ print ' client_active_texture_dirty = true;'
+ self.fake_glClientActiveTexture_call(api, "texture");
+ print ' }'
+
+ def array_trace_epilog(self, api, uppercase_name):
+ if uppercase_name == 'TEXTURE_COORD':
+ print ' if (client_active_texture_dirty) {'
+ self.fake_glClientActiveTexture_call(api, "client_active_texture");
+ print ' }'
+
+ def fake_glClientActiveTexture_call(self, api, texture):
+ function = api.get_function_by_name('glClientActiveTexture')
+ self.fake_call(function, [texture])
+
+ def fake_call(self, function, args):
+ print ' unsigned __fake_call = Trace::BeginEnter(__%s_sig);' % (function.name,)
+ for arg, instance in zip(function.args, args):
+ assert not arg.output
+ print ' Trace::BeginArg(%u);' % (arg.index,)
+ dump_instance(arg.type, instance)
+ print ' Trace::EndArg();'
+ print ' Trace::EndEnter();'
+ print ' Trace::BeginLeave(__fake_call);'
+ print ' Trace::EndLeave();'
+
+
+
+
+
print
print '#include <stdlib.h>'
print '#include <string.h>'
+ print
+ print '#ifndef _GNU_SOURCE'
+ print '#define _GNU_SOURCE // for dladdr'
+ print '#endif'
print '#include <dlfcn.h>'
print
print '#include "trace_write.hpp"'
print ' return procPtr;'
print '}'
print
+ print r'''
+
+/*
+ * Several applications, such as Quake3, use dlopen("libGL.so.1"), but
+ * LD_PRELOAD does not intercept symbols obtained via dlopen/dlsym, therefore
+ * we need to intercept the dlopen() call here, and redirect to our wrapper
+ * shared object.
+ */
+void *dlopen(const char *filename, int flag)
+{
+ typedef void * (*PFNDLOPEN)(const char *, int);
+ static PFNDLOPEN dlopen_ptr = NULL;
+ void *handle;
+
+ if (!dlopen_ptr) {
+ dlopen_ptr = (PFNDLOPEN)dlsym(RTLD_NEXT, "dlopen");
+ if (!dlopen_ptr) {
+ OS::DebugMessage("error: dlsym(RTLD_NEXT, \"dlopen\") failed\n");
+ return NULL;
+ }
+ }
+
+ handle = dlopen_ptr(filename, flag);
+
+ if (filename && handle) {
+ if (0) {
+ OS::DebugMessage("warning: dlopen(\"%s\", 0x%x)\n", filename, flag);
+ }
+
+ // FIXME: handle absolute paths and other versions
+ if (strcmp(filename, "libGL.so") == 0 ||
+ strcmp(filename, "libGL.so.1") == 0) {
+ // Use the true libGL.so handle instead of RTLD_NEXT from now on
+ libgl_handle = handle;
+
+ // Get the file path for our shared object, and use it instead
+ static int dummy = 0xdeedbeef;
+ Dl_info info;
+ if (dladdr(&dummy, &info)) {
+ OS::DebugMessage("apitrace: redirecting dlopen(\"%s\", 0x%x)\n", filename, flag);
+ handle = dlopen_ptr(info.dli_fname, flag);
+ } else {
+ OS::DebugMessage("warning: dladdr() failed\n");
+ }
+ }
+ }
+
+ return handle;
+}
+
+'''
print '} /* extern "C" */'
template<class T>
inline void writeNumber(T n) {
separator();
- os << std::dec << n;
+ os << std::dec << std::setprecision(9) << n;
value = true;
space = ' ';
}
#include <stdarg.h>
#include <stdio.h>
-#ifdef WIN32
+#ifdef _WIN32
#ifndef snprintf
#define snprintf _snprintf
#endif
#define vsnprintf _vsnprintf
#endif
#define PATH_SEP '\\'
-#else /* !WIN32 */
+#else /* !_WIN32 */
#define PATH_SEP '/'
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
#ifndef PATH_MAX
#define PATH_MAX 1024
set -e
-DUMP=${DUMP:-`dirname "$0"`/dump}
+TRACEDUMP=${TRACEDUMP:-`dirname "$0"`/tracedump}
-$DUMP
+$TRACEDUMP
stripdump () {
# http://www.theeggeadventure.com/wikimedia/index.php/Linux_Tips#Use_sed_to_remove_ANSI_colors
- $DUMP "$1" \
+ $TRACEDUMP "$1" \
| sed \
-e 's/\x1b\[[0-9]\{1,2\}\(;[0-9]\{1,2\}\)\{0,2\}m//g' \
-e 's/\r$//g' \
namespace Trace {
-unsigned long long Parser::version = 0;
-
-
Parser::Parser() {
file = NULL;
next_call_no = 0;
+ version = 0;
}
#include <iostream>
-#include <map>
#include <list>
#include <string>
unsigned next_call_no;
public:
- static unsigned long long version;
+ unsigned long long version;
Parser();
} /* namespace Trace */
-#ifdef WIN32
+#ifdef _WIN32
#if 0
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
--- /dev/null
+/**************************************************************************
+ *
+ * Copyright 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.
+ *
+ **************************************************************************/
+
+
+/*
+ * Simple utility to dump a trace to standard output.
+ */
+
+
+#include "trace_parser.hpp"
+
+
+int main(int argc, char **argv)
+{
+ for (int i = 1; i < argc; ++i) {
+ Trace::Parser p;
+ if (p.open(argv[i])) {
+ Trace::Call *call;
+ call = p.parse_call();
+ while (call) {
+ std::cout << *call;
+ delete call;
+ call = p.parse_call();
+ }
+ }
+ }
+ return 0;
+}