summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Paul <brian.paul@tungstengraphics.com>2006-06-02 14:50:28 +0000
committerBrian Paul <brian.paul@tungstengraphics.com>2006-06-02 14:50:28 +0000
commit65ced474536bad23ee204170918f56eb8f8c4bf9 (patch)
tree737457c8263b5fcf8bcc0756f0e61b2578f32774
parent21fbdb14e9a189272fd7398be525e087dbf017dc (diff)
Thomas Sondergaard's API tracer
-rw-r--r--progs/tools/trace/Makefile30
-rw-r--r--progs/tools/trace/README23
-rwxr-xr-xprogs/tools/trace/gltrace82
-rw-r--r--progs/tools/trace/gltrace.py189
-rw-r--r--progs/tools/trace/gltrace_support.cc190
-rw-r--r--progs/tools/trace/gltrace_support.h65
6 files changed, 579 insertions, 0 deletions
diff --git a/progs/tools/trace/Makefile b/progs/tools/trace/Makefile
new file mode 100644
index 0000000000..3f7bdcbc93
--- /dev/null
+++ b/progs/tools/trace/Makefile
@@ -0,0 +1,30 @@
+# Makefile for Thomas Sondergaard's API tracer
+
+TOP = ../../..
+
+include $(TOP)/configs/current
+
+
+OBJECTS = gltrace.o gltrace_support.o
+
+TRACER = gltrace.so
+
+.cc.o:
+ $(CXX) -c $(INCDIRS) $(CXXFLAGS) $< -o $@
+
+
+default: $(TRACER)
+
+$(TRACER): $(OBJECTS)
+ $(TOP)/bin/mklib -o $(TRACER) -noprefix -cplusplus \
+ $(MKLIB_OPTIONS) $(OBJECTS)
+
+gltrace.cc: gltrace.py
+ PYTHONPATH=$(TOP)/src/mesa/glapi python gltrace.py -f $(TOP)/src/mesa/glapi/gl_API.xml > gltrace.cc
+
+
+clean:
+ rm -f $(OBJECTS)
+ rm -f $(TRACER)
+ rm -f *~
+ rm -f gltrace.cc
diff --git a/progs/tools/trace/README b/progs/tools/trace/README
new file mode 100644
index 0000000000..7b3141dba7
--- /dev/null
+++ b/progs/tools/trace/README
@@ -0,0 +1,23 @@
+NAME
+ gltrace - trace opengl calls
+
+SYNOPSIS
+ gltrace [OPTION] command [arg ...]
+
+DESCRIPTION
+ -h help (this text)
+ -c log gl calls
+ -t time stamp log entries
+ -e check for and log errors. errors occurring between
+ glBegin() and glEnd() are checked at glEnd()
+ -v verbose. Shows configuration settings passed to
+ gltrace.so
+ -l LOGFILE logfile. Default is stderr
+
+PROBLEMS
+ Not all OpenGL extensions are known and traced by gltrace. Extension
+ functions not initialized using glXGetProcAddress(ARB) will not be
+ traced.
+
+AUTHOR
+ Thomas Sondergaard (ts_news1 'at' sondergaard.cc)
diff --git a/progs/tools/trace/gltrace b/progs/tools/trace/gltrace
new file mode 100755
index 0000000000..d386912cf2
--- /dev/null
+++ b/progs/tools/trace/gltrace
@@ -0,0 +1,82 @@
+#!/bin/bash
+
+# Copyright (C) 2006 Thomas Sondergaard
+# 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
+# on the rights to use, copy, modify, merge, publish, distribute, sub
+# license, 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 (including the next
+# paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+# IBM AND/OR ITS SUPPLIERS 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.
+#
+# Authors:
+# Thomas Sondergaard <ts@medical-insight.com>
+
+usage="usage: $0 [ -hctev ] [-l LOGFILE] program [args...]\n\t-h\t\thelp (this text)\n\t-c\t\tlog gl calls\n\t-t\t\ttime stamp log entries\n\t-e\t\tcheck for and log errors. errors occurring between\n\t\t\tglBegin() and glEnd() are checked at glEnd()\n\t-v\t\tverbose. Shows configuration settings passed to\n\t\t\tgltrace.so\n\t-l LOGFILE\tlogfile. Default is stderr"
+
+# Path to gltrace.so - must not be relative
+#GLTRACE_SO=/home/ts/Mesa_gltrace/src/mesa/glapi/gltrace.so
+# This seems to work:
+GLTRACE_SO=./gltrace.so
+
+# Set options from command line
+
+VERBOSE=0
+GLTRACE_LOG_CALLS=0
+GLTRACE_LOG_TIME=0
+GLTRACE_CHECK_ERRORS=0
+export GLTRACE_LOG_CALLS GLTRACE_LOG_TIME GLTRACE_CHECK_ERRORS
+
+if [ $# -eq 0 ]; then
+ echo -e $usage
+ exit
+fi
+
+while getopts "hctevl:" options; do
+ case $options in
+ h) echo -e $usage
+ exit 1;;
+ c) GLTRACE_LOG_CALLS=1;;
+ t) GLTRACE_LOG_TIME=1;;
+ e) GLTRACE_CHECK_ERRORS=1;;
+ l) GLTRACE_LOGFILE=$OPTARG
+ export GLTRACE_LOGFILE;;
+ v) VERBOSE=1;;
+ *) echo -e $usage
+ exit 1;;
+ esac
+done
+
+# Remove the parsed args
+shift $(($OPTIND-1))
+
+if [ ! -r $GLTRACE_SO ]; then
+ echo "Error: The gltrace.so file '$GLTRACE_SO' is missing!"
+ exit 1
+fi
+
+export LD_PRELOAD=$GLTRACE_SO
+
+if [ $VERBOSE -eq 1 ]; then
+ echo GLTRACE_LOG_CALLS=$GLTRACE_LOG_CALLS
+ echo GLTRACE_LOG_TIME=$GLTRACE_LOG_TIME
+ echo GLTRACE_CHECK_ERRORS=$GLTRACE_CHECK_ERRORS
+ echo GLTRACE_LOGFILE=$GLTRACE_LOGFILE
+ echo LD_PRELOAD=$LD_PRELOAD
+ echo command=$*
+fi
+
+exec $*
diff --git a/progs/tools/trace/gltrace.py b/progs/tools/trace/gltrace.py
new file mode 100644
index 0000000000..973881ac94
--- /dev/null
+++ b/progs/tools/trace/gltrace.py
@@ -0,0 +1,189 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2006 Thomas Sondergaard
+# 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
+# on the rights to use, copy, modify, merge, publish, distribute, sub
+# license, 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 (including the next
+# paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+# IBM AND/OR ITS SUPPLIERS 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.
+#
+# Authors:
+# Thomas Sondergaard <ts@medical-insight.com>
+
+import gl_XML, glX_XML, glX_proto_common, license
+import sys, getopt, copy, string
+
+def create_argument_string(parameters):
+ """Create a parameter string from a list of gl_parameters."""
+
+ list = []
+ for p in parameters:
+ list.append( p.name )
+ #if len(list) == 0: list = ["void"]
+
+ return string.join(list, ", ")
+
+def create_logfunc_string(func, name):
+ """Create a parameter string from a list of gl_parameters."""
+
+ list = []
+ list.append('"gl' + name + '("')
+ sep = None
+ for p in func.parameters:
+ if (sep):
+ list.append(sep)
+ list.append( p.name )
+ sep = '", "'
+ list.append('");"')
+ #if len(list) == 0: list = ["void"]
+
+ return "if (config.logCalls) GLTRACE_LOG(" + string.join(list, " << ")+");";
+
+class PrintGltrace(glX_proto_common.glx_print_proto): #(gl_XML.gl_print_base):
+ def __init__(self):
+ gl_XML.gl_print_base.__init__(self)
+
+ self.name = "gltrace.py"
+ self.license = license.bsd_license_template % ( \
+"""Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas.
+(C) Copyright IBM Corporation 2004""", "PRECISION INSIGHT, IBM")
+ #self.header_tag = "_INDIRECT_H_"
+
+ self.last_category = ""
+ return
+
+
+ def printRealHeader(self):
+ print """/**
+ * \\file
+ * gl and glX wrappers for tracing
+ *
+ * \\author Thomas Sondergaard <ts@medical-insight.com>
+ */
+"""
+ #self.printVisibility( "HIDDEN", "hidden" )
+ #self.printFastcall()
+ #self.printNoinline()
+
+ print """
+#include <GL/gl.h>
+#include <GL/glx.h>
+#include <GL/glu.h>
+#include <dlfcn.h>
+#include "gltrace_support.h"
+
+using namespace gltrace;
+
+static GLenum real_glGetError() {
+ static GLenum (*real_func)(void) = 0;
+ if (!real_func) real_func = (GLenum (*)(void)) dlsym(RTLD_NEXT, "glGetError");
+ return real_func();
+}
+
+bool betweenGLBeginEnd = false;
+
+extern "C" {
+
+
+__GLXextFuncPtr real_glXGetProcAddressARB(const GLubyte *func_name) {
+ static __GLXextFuncPtr (*real_func)(const GLubyte *func_name) = 0;
+ if (!real_func) real_func = (__GLXextFuncPtr (*)(const GLubyte *func_name)) dlsym(RTLD_NEXT, "glXGetProcAddressARB");
+
+ return real_func(func_name);
+}
+
+__GLXextFuncPtr glXGetProcAddressARB(const GLubyte *func_name_ubyte) {
+ std::string func_name =
+ std::string("gltrace_")+reinterpret_cast<const char*>(func_name_ubyte);
+
+ __GLXextFuncPtr f = (__GLXextFuncPtr) dlsym(RTLD_DEFAULT, func_name.c_str());
+ if (!f) {
+ GLTRACE_LOG("warning: Could not resolve '" << func_name << "' - function will not be intercepted");
+ return real_glXGetProcAddressARB(func_name_ubyte);
+ }
+ return f;
+}
+
+"""
+
+ def printRealFooter(self):
+ print "} // Extern \"C\""
+
+ def printBody(self, api):
+ for func in api.functionIterateGlx():
+ for func_name in func.entry_points:
+ functionPrefix = ""
+ use_dlsym = True
+ if (api.get_category_for_name(func.name)[1] != None):
+ functionPrefix = "gltrace_"
+ use_dlsym = False
+
+ print '%s %sgl%s(%s) {' % (func.return_type, functionPrefix, func_name, func.get_parameter_string())
+ if (use_dlsym):
+ print ' static %s (*real_func)(%s) = 0;' % (func.return_type, func.get_parameter_string())
+ print ' if (!real_func) real_func = (%s (*)(%s)) dlsym(RTLD_NEXT, "gl%s");' % (func.return_type, func.get_parameter_string(), func_name)
+ else: # use glXGetProcAddressArb
+ print ' static %s (*real_func)(%s) = 0;' % (func.return_type, func.get_parameter_string())
+ print ' if (!real_func) real_func = (%s (*)(%s)) real_glXGetProcAddressARB((GLubyte *)"gl%s");' % (func.return_type, func.get_parameter_string(), func_name)
+ print ' ' + create_logfunc_string(func, func_name)
+ if (func.return_type == "void"):
+ print ' real_func(%s);' % (create_argument_string(func.parameters))
+ else:
+ print ' %s retval = real_func(%s);' % (func.return_type, create_argument_string(func.parameters))
+ if (func.name == "Begin"):
+ print ' betweenGLBeginEnd = true;'
+ elif (func.name == "End"):
+ print ' betweenGLBeginEnd = false;'
+ print ' if (!betweenGLBeginEnd && config.checkErrors) {'
+ print ' GLenum res;'
+ print ' while ((res = real_glGetError ()) != GL_NO_ERROR) '
+ print ' GLTRACE_LOG("OpenGL Error (" << res << "): <" << gluErrorString(res) << "> at " << gltrace::getStackTrace());'
+ print ' }'
+ if (func.return_type != "void"):
+ print " return retval;"
+ print '}'
+
+
+def show_usage():
+ print "Usage: %s [-f input_file_name] [-m output_mode] [-d]" % sys.argv[0]
+ print " -m output_mode Output mode can be one of 'proto', 'init_c' or 'init_h'."
+ print " -d Enable extra debug information in the generated code."
+ sys.exit(1)
+
+
+if __name__ == '__main__':
+ file_name = "gl_API.xml"
+
+ try:
+ (args, trail) = getopt.getopt(sys.argv[1:], "f:d")
+ except Exception,e:
+ show_usage()
+
+ debug = 0
+ for (arg,val) in args:
+ if arg == "-f":
+ file_name = val
+ elif arg == "-d":
+ debug = 1
+
+ printer = PrintGltrace()
+
+ printer.debug = debug
+ api = gl_XML.parse_GL_API( file_name, glX_XML.glx_item_factory() )
+
+ printer.Print( api )
diff --git a/progs/tools/trace/gltrace_support.cc b/progs/tools/trace/gltrace_support.cc
new file mode 100644
index 0000000000..fb0404c450
--- /dev/null
+++ b/progs/tools/trace/gltrace_support.cc
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2006 Thomas Sondergaard 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
+ * BRIAN PAUL 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 "gltrace_support.h"
+#include <sstream>
+#include <fstream>
+#include <iomanip>
+#include <execinfo.h>
+#include <cxxabi.h>
+#include <sys/time.h>
+
+namespace {
+
+ const char *
+ demangle (const char * mangled) throw()
+ {
+ static char buf[4096];
+ int status;
+ unsigned int length = sizeof(buf)-1;
+
+ memset (buf, 0, sizeof(buf));
+
+ if (!mangled)
+ return 0;
+
+ char * demangled = __cxxabiv1::__cxa_demangle(mangled,
+ buf,
+ &length,
+ &status);
+ if (demangled && !status)
+ return demangled;
+ else
+ return mangled;
+ }
+
+ void
+ printStackTrace (void **stackframes,
+ int stackframe_size,
+ std::ostream & out )
+ {
+ char **strings = 0;
+ std::stringstream ss;
+
+ // this might actually fail if memory is tight or we are in a
+ // signal handler
+ strings = backtrace_symbols (stackframes, stackframe_size);
+
+ ss << "Backtrace :";
+
+ if (stackframe_size == gltrace::MAX_STACKFRAMES)
+ ss << "(possibly incomplete maximal number of frames exceeded):" << std::endl;
+ else
+ ss << std::endl;
+
+ out << ss.str();
+
+ // the first frame is the constructor of the exception
+ // the last frame always seem to be bogus?
+ for (int i = 0; strings && i < stackframe_size-1; ++i) {
+ char libname[257], funcname[2049];
+ unsigned int address=0, funcoffset = 0x0;
+
+ memset (libname,0,sizeof(libname));
+ memset (funcname,0,sizeof(funcname));
+
+ strcpy (funcname,"??");
+ strcpy (libname, "??");
+
+ int scanned = sscanf (strings[i], "%256[^(] ( %2048[^+] + %x ) [ %x ]",
+ libname,
+ funcname,
+ &funcoffset,
+ &address);
+
+ /* ok, so no function was mentioned in the backtrace */
+ if (scanned < 4) {
+ scanned = sscanf (strings[i], "%256[^([] [ %x ]",
+ libname,
+ &address);
+ }
+
+ if (funcname[0] == '_') {
+ const char * demangled;
+ if ((demangled = demangle(funcname) ) != funcname) {
+ strncpy (funcname, demangled, sizeof(funcname)-1);
+ }
+ }
+ else
+ strcat (funcname," ()");
+
+ out << "\t#" << i << std::hex << " 0x" << address << " in " << funcname
+ << " at 0x" << funcoffset << " (from " << libname << ")" << std::endl;
+ }
+
+ free (strings);
+ }
+
+
+} // anon namespace
+
+namespace gltrace {
+
+ std::string getStackTrace(int count, int first) {
+ ++first;
+ std::stringstream ss;
+ const int BA_MAX = 1000;
+ assert(count + first <= BA_MAX);
+ void *ba[BA_MAX];
+ int n = backtrace(ba, count+first);
+
+ printStackTrace( &ba[first], n-first, ss);
+
+ return ss.str();
+ }
+
+ std::ostream &timeNow(std::ostream &os) {
+
+ struct timeval now;
+ struct tm t;
+ static char *months[12] =
+ {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ };
+
+ gettimeofday (&now, 0);
+ localtime_r ((time_t*) &now.tv_sec, &t);
+
+ os
+ << months[t.tm_mon] << " "
+ << std::setw(2) << t.tm_mday << " "
+ << std::setw(2) << t.tm_hour << ":"
+ << std::setw(2) << t.tm_min << ":"
+ << std::setw(2) << t.tm_sec << "."
+ << std::setw(3) << now.tv_usec/1000;
+ return os;
+ }
+
+ logstream::logstream(const char *filename) {
+ if (!filename)
+ init(std::cerr.rdbuf());
+ else {
+ file_os.reset(new std::ofstream(filename));
+ if (file_os->good())
+ init(file_os->rdbuf());
+ else {
+ std::cerr << "ERROR: gltrace: Failed to open '" << filename
+ << "' for writing. Falling back to stderr." << std::endl;
+ init(std::cerr.rdbuf());
+ }
+ }
+ *this << std::setfill('0'); // setw used in timeNow
+ }
+
+
+ Config::Config() :
+ logCalls(true),
+ checkErrors(true),
+ logTime(true),
+ log(getenv("GLTRACE_LOGFILE")) {
+ if (const char *v = getenv("GLTRACE_LOG_CALLS"))
+ logCalls = strncmp("1", v, 1) == 0;
+ if (const char *v = getenv("GLTRACE_CHECK_ERRORS"))
+ checkErrors = strncmp("1", v, 1) == 0;
+ if (const char *v = getenv("GLTRACE_LOG_TIME"))
+ logTime = strncmp("1", v, 1) == 0;
+ }
+
+ // *The* config
+ Config config;
+
+} // namespace gltrace
diff --git a/progs/tools/trace/gltrace_support.h b/progs/tools/trace/gltrace_support.h
new file mode 100644
index 0000000000..de28669a98
--- /dev/null
+++ b/progs/tools/trace/gltrace_support.h
@@ -0,0 +1,65 @@
+// -*- c++ -*- (emacs c++ mode)
+/*
+ * Copyright (C) 2006 Thomas Sondergaard 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
+ * BRIAN PAUL 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 GLTRACE_SUPPORT_H
+#define GLTRACE_SUPPORT_H
+
+#include <string>
+#include <iostream>
+#include <memory>
+
+namespace gltrace {
+
+ const int MAX_STACKFRAMES = 100;
+
+ /// Returns the stack trace of the current thread
+ std::string getStackTrace(int count = MAX_STACKFRAMES, int first = 0);
+
+ std::ostream &timeNow(std::ostream &os);
+
+ struct logstream : public std::ostream {
+
+ /// Opens a logstream - if filename is null, stderr will be used
+ logstream(const char *filename = 0);
+
+ private:
+ std::auto_ptr<std::ofstream> file_os;
+ };
+
+ struct Config {
+ bool logCalls;
+ bool checkErrors;
+ bool logTime;
+ logstream log;
+
+ Config();
+ };
+
+ extern Config config;
+
+} // namespace gltrace
+
+#define GLTRACE_LOG(x) \
+ { if (config.logTime) config.log << timeNow << ": "; config.log << x << "\n"; }
+
+#endif // GLTRACE_SUPPORT_H
+
+