Thomas Sondergaard's API tracer

This commit is contained in:
Brian Paul 2006-06-02 14:50:28 +00:00
parent 21fbdb14e9
commit 65ced47453
6 changed files with 579 additions and 0 deletions

View file

@ -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

23
progs/tools/trace/README Normal file
View file

@ -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)

82
progs/tools/trace/gltrace Executable file
View file

@ -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 $*

View file

@ -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 )

View file

@ -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

View file

@ -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