Thomas Sondergaard's API tracer
authorBrian Paul <brian.paul@tungstengraphics.com>
Fri, 2 Jun 2006 14:50:28 +0000 (14:50 +0000)
committerBrian Paul <brian.paul@tungstengraphics.com>
Fri, 2 Jun 2006 14:50:28 +0000 (14:50 +0000)
progs/tools/trace/Makefile [new file with mode: 0644]
progs/tools/trace/README [new file with mode: 0644]
progs/tools/trace/gltrace [new file with mode: 0755]
progs/tools/trace/gltrace.py [new file with mode: 0644]
progs/tools/trace/gltrace_support.cc [new file with mode: 0644]
progs/tools/trace/gltrace_support.h [new file with mode: 0644]

diff --git a/progs/tools/trace/Makefile b/progs/tools/trace/Makefile
new file mode 100644 (file)
index 0000000..3f7bdcb
--- /dev/null
@@ -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 (file)
index 0000000..7b3141d
--- /dev/null
@@ -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 (executable)
index 0000000..d386912
--- /dev/null
@@ -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 (file)
index 0000000..973881a
--- /dev/null
@@ -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 (file)
index 0000000..fb0404c
--- /dev/null
@@ -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 (file)
index 0000000..de28669
--- /dev/null
@@ -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
+
+