From 65ced474536bad23ee204170918f56eb8f8c4bf9 Mon Sep 17 00:00:00 2001 From: Brian Paul Date: Fri, 2 Jun 2006 14:50:28 +0000 Subject: Thomas Sondergaard's API tracer --- progs/tools/trace/Makefile | 30 ++++++ progs/tools/trace/README | 23 +++++ progs/tools/trace/gltrace | 82 +++++++++++++++ progs/tools/trace/gltrace.py | 189 ++++++++++++++++++++++++++++++++++ progs/tools/trace/gltrace_support.cc | 190 +++++++++++++++++++++++++++++++++++ progs/tools/trace/gltrace_support.h | 65 ++++++++++++ 6 files changed, 579 insertions(+) create mode 100644 progs/tools/trace/Makefile create mode 100644 progs/tools/trace/README create mode 100755 progs/tools/trace/gltrace create mode 100644 progs/tools/trace/gltrace.py create mode 100644 progs/tools/trace/gltrace_support.cc create mode 100644 progs/tools/trace/gltrace_support.h (limited to 'progs/tools/trace') diff --git a/progs/tools/trace/Makefile b/progs/tools/trace/Makefile new file mode 100644 index 00000000000..3f7bdcbc935 --- /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 00000000000..7b3141dba7b --- /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 00000000000..d386912cf25 --- /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 + +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 00000000000..973881ac94d --- /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 + +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 + */ +""" + #self.printVisibility( "HIDDEN", "hidden" ) + #self.printFastcall() + #self.printNoinline() + + print """ +#include +#include +#include +#include +#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(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 00000000000..fb0404c450e --- /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 +#include +#include +#include +#include +#include + +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 00000000000..de28669a989 --- /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 +#include +#include + +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 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 + + -- cgit v1.2.3