Hi,

I stumbled upon mesa's gl_API.xml and the python scripts to process it to generate glx indirect rendering c-code. I've used these bits to generate an opengl tracer, that I thought might be useful for other people than me. The tracer is used like this:

LD_PRELOAD=/path/to/gltrace.so glxgears

The tracer can be configured using environment variables to log all opengl calls to a logfile or to call glGetError after all OpenGL calls and print the error and the stack trace when errors occur. Of course there is a significant overhead involved in snooping all OpenGL calls, but it is good support for debugging.

The attached gltrace.py generates c++ source that depends on a few logging macros and a function for obtaining the current stack trace that is not included, but they are mainly convenience wrappers around c++ iostreams and backtrace_symbols from <execinfo.h>.

I thought gltrace might be a candidate for inclusion as a utility in Mesa. If there is interest I could polish it up so other people can compile it than me :-)


Best regards,

Thomas


PS! Here's some sample output:

May 15 13:21:17.120 argon camloader[10292]: debug 12: glX_tracer.cc.2427: glEnable(2929) May 15 13:21:18.464 argon camloader[10292]: debug 12: glX_tracer.cc.2416: glDisable(3089) May 15 13:21:18.465 argon camloader[10292]: debug 12: glX_tracer.cc.3421: glViewport(0, 0, 1076, 910) May 15 13:21:18.465 argon camloader[10292]: debug 12: glX_tracer.cc.2328: glClearColor(0, 0, 0, 1) May 15 13:21:18.470 argon camloader[10292]: debug 12: glX_tracer.cc.2350: glClearDepth(1) May 15 13:21:18.471 argon camloader[10292]: debug 12: glX_tracer.cc.2295: glClear(16640) May 15 13:21:18.472 argon camloader[10292]: debug 12: glX_tracer.cc.2427: glEnable(3089) May 15 13:21:18.473 argon camloader[10292]: debug 12: glX_tracer.cc.1997: glScissor(0, 0, 1076, 910) May 15 13:21:18.473 argon camloader[10292]: debug 12: glX_tracer.cc.3421: glViewport(0, 0, 1076, 910) May 15 13:21:18.473 argon camloader[10292]: debug 12: glX_tracer.cc.3289: glMatrixMode(5888) May 15 13:21:18.473 argon camloader[10292]: debug 12: glX_tracer.cc.3344: glPushMatrix() May 15 13:21:18.473 argon camloader[10292]: debug 12: glX_tracer.cc.3256: glLoadIdentity() May 15 13:21:18.474 argon camloader[10292]: debug 12: glX_tracer.cc.3289: glMatrixMode(5889) May 15 13:21:18.474 argon camloader[10292]: debug 12: glX_tracer.cc.3344: glPushMatrix() May 15 13:21:18.474 argon camloader[10292]: debug 12: glX_tracer.cc.3256: glLoadIdentity() May 15 13:21:18.474 argon camloader[10292]: debug 12: glX_tracer.cc.214: glColor3fv(0xa520b600) May 15 13:21:18.475 argon camloader[10292]: debug 12: glX_tracer.cc.2416: glDisable(2852) May 15 13:21:18.476 argon camloader[10292]: debug 12: glX_tracer.cc.2416: glDisable(2929) May 15 13:21:18.477 argon camloader[10292]: debug 12: glX_tracer.cc.2416: glDisable(2896) May 15 13:21:18.477 argon camloader[10292]: debug 12: glX_tracer.cc.2416: glDisable(3042) May 15 13:21:18.477 argon camloader[10292]: debug 12: glX_tracer.cc.1909: glLineWidth(2) May 15 13:21:18.477 argon camloader[10292]: debug 12: glX_tracer.cc.136: glBegin(2) May 15 13:21:18.477 argon camloader[10292]: debug 12: glX_tracer.cc.1469: glVertex2f(-1, -1)


#!/usr/bin/env python

# (C) Copyright IBM Corporation 2004, 2005
# 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:
#    Ian Romanick <[EMAIL PROTECTED]>

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('") cmdlen="')
        list.append('cmdlen')
        list.append('" totalcmdbytes="')
        list.append('totalcmdbytes')
        #if len(list) == 0: list = ["void"]

        return "EV_DEBUG_S(12, " + 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 <[EMAIL PROTECTED]>
 */
"""
                #self.printVisibility( "HIDDEN", "hidden" )
                #self.printFastcall()
                #self.printNoinline()

                print """
#define EV_LOGFILE_LOCAL_SECTION "gltrace"
#define __GLX_PAD(a) (((a)+3) & ~3)

#include <GL/gl.h>
#include <GL/glx.h>
#include <GL/glu.h>
#include <dlfcn.h>
#include <evbase/LogFile.h>
#include <evbase/Exception.h>
#include <glx/x11/indirect_size.h>
static GLenum my_glGetError() {
  static GLenum (*real_func)(void) = 0;
  if (!real_func) real_func = (GLenum (*)(void)) dlsym(RTLD_NEXT, "glGetError");
  return real_func();
}

bool betweenGLBeginEnd = false;
static unsigned totalcmdbytes = 0;

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("ts_")+reinterpret_cast<const char*>(func_name_ubyte);
  
  __GLXextFuncPtr f = (__GLXextFuncPtr) dlsym(RTLD_DEFAULT, func_name.c_str());
  if (!f) {
    EV_WARNING_S("Could not resolve '" << func_name << "' - function will not 
be intercepted");
    return real_glXGetProcAddressARB(func_name_ubyte);
  }
  return f;
}

extern GLint __glImageSize(GLint, GLint, GLint, GLenum, GLenum, GLenum);
"""

        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 = "ts_"
                                        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)
                                self.emit_packet_size_calculation(func, 0)
                                print '  totalcmdbytes += cmdlen;'
                                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 && 
EV_CHECK_DEBUG(11)) {'
                                print '    GLenum res;'
                                print '    while ((res = my_glGetError ()) != 
GL_NO_ERROR) '
                                print '      EV_ERROR_S ("OpenGL Error (" << 
res << "): <" << gluErrorString(res) << "> at " << evbase::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 )

Reply via email to