Thomas Sondergaard's API tracer
diff --git a/progs/tools/trace/Makefile b/progs/tools/trace/Makefile
new file mode 100644
index 0000000..3f7bdcb
--- /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 0000000..7b3141d
--- /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 0000000..d386912
--- /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 0000000..973881a
--- /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 0000000..fb0404c
--- /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 0000000..de28669
--- /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
+
+