OglDump

An OpenGL Analysis Tool
Authored by Don Burns

Introduction

OglDump is an Open Source (GPL) tool for debugging OpenGL based software.  It is useful for gaining insight into functionality of OpenGL applications, or applications written with libraries built on top of OpenGL.

OglDump is intended for use by programmers that are familiar with a development environment, the C/C++ programming language and OpenGL.  Current functionality has been tested with :

Description

OglDump base code consists of a parser which generates code to overload all functions defined in the system header file gl.h and glx.h (usually /usr/include/GL/gl.h and /usr/include/GL/glx.h).  The parser uses partially written source files and adds definitions specific to the version of OpenGL in use.
 
 

Applications may then link dynamically to this libGL.so (rather than system libGL.so), trap and view commands, by either using the built in functionality via environmental variables, or altered _gl.c code.

Building OglDump

The OglDump root directory contains the following files and subdirectories:
 
Makefile
Builds parser, libGL.so or both.
Parser
Contains source code for the parser
doc
A directory containing documentation
include
Some include directories used by the parser
srcfiles
Source files for building final ogldump code.
vectors.h
Used for internal parsing of OpenGL vector arguments

Typing 'make' in this directory attempts to build the parser, generate code for libGL.so and compile libGL.so.  You may build just the parser by typing 'make parser' or changing to the Parser directory and typing 'make'.

A 'make' session on an Irix 6.5/OpenGL 1.2 follows:
 

don_burns@peru 49% make
        cd Parser; make
        CC -g -c parser.C
        yacc -d parse.y
1 rules never reduced

conflicts: 4 reduce/reduce
        cc  -O -c y.tab.c
        rm -f y.tab.c
        mv -f y.tab.o parse.o
        lex  lex.l
        cc  -O -c lex.yy.c
        rm -f lex.yy.c; mv -f lex.yy.o lex.o
        CC -g parser.o parse.o lex.o  -ll -o ../parser

     (Possible C++ prelinker info here)
 

At this point the parser is made and resides in the OglDump root directory.  The historical C preprocessor is then invoked to produce "pure" C code as input to the parser's stdin.
 
        /lib/cpp -D_MIPS_SZINT=32 -D_LANGUAGE_C -P \ -I./include \ /usr/include/GL/glx.h  | parser
At this point, _gl.c, _gl.h, ogldump.c and ogldump.h have been generated from source files in the srcfiles directory and definitions in the system glx.h header file.  Note that glx.h is used but not gl.h, since glx.h normally includes gl.h.  Also note that if you want to make code changes to the generated files that persist beyond future parsing of the system glx.h, then you must make this changes either in the corresponding file in srcfiles/ file or in Parser/parser.C.

Note that -I./include is passed to the preprocessor.  This directory contains some lightweight versions of X11 header files, containing only the needed definitions for the purpose of OglDump.  Using the standard system X11 files would result in a need for an unecessarily complex parser due to the large scope.

        cc -g -c _gl.c
        cc -g -c ogldump.c
        cc -g -g -shared _gl.o ogldump.o -o libGL.so


Due to the light weight parsing approach of the parser, and differences in systems, it is likely that there will be problems at this point.  If you are able to ascertain these problems and fix them, please send the author an update and an email regarding your fixes.
 

Using OglDump for analysis

To use OglDump, your application must link with the libGL.so you've just generated.  The method of doing this differs between Irix and Linux.

For Linux

You may use the method described below for Irix, but often there are  conflicts or mutual dependencies in libGL.so and libGLcore.so on some  systems.  Best results are obtained by setting the environmental variable:
setenv LD_PRELOAD `pwd`/libGL.so.1

For Irix

Simply set the environmental variable LD_LIBRARY_PATH to the directory libGL.so resides in :
setenv LD_LIBRARY_PATH `pwd`
It is entirely probable that your application is not linked directly to libGL.so, but rather to something like libGL.so.1.  Assuming your executable is named myexecutable, test this with ldd.  For example
don_burns@peru 60% pwd
/DonsDisk/OglDump
don_burns@peru 61% ldd myexecutable
        libGLU.so  =>    /usr/lib32/libGLU.so
        libGL.so  =>     /DonsDisk/OglDump/libGL.so
        libX11.so.1  =>  /usr/lib32/libX11.so.1
        libm.so  =>      /usr/lib32/libm.so
        libimage.so  =>  /usr/lib32/libimage.so
        libCsup.so  =>   /usr/lib32/libCsup.so
        libC.so.2  =>    /usr/lib32/libC.so.2
        libc.so.1  =>    /usr/lib32/libc.so.1
In the above case, myexecutable is, indeed linked to the libGL.so in the current directory.  If however, the ldd command had output :

        libGL.so.1  =>     /usr/lib32/libGL.so.1

then, link libGL.so with libGL.so.1

ln -s libGL.so libGL.so.1
All of this assumes that LD_LIBRARY_PATH is set to the current directory.

All Systems

Now, start your executable.  A message should be printed to the shell that looks something like:
 
 
don_burns@peru 62% myexecutable
**************************************************
                      WARNING
             USING Ogldump libGL.so library

                     Don Burns
                    (Some Date)
**************************************************

This is there to remind you (who the author is and) that your are linked to the analysis library.  There is a performance penalty for running with the analysis library and it is best that it not be silent.  Other than the message printed to the shell, the program should run normally.

Two environmental variables invoke built in functionality:

OGLDUMP_ENABLE
Turns on printing of all OpenGL calls, with their arguments and values (where possible).  However, this is still not very useful since it is difficult to read all commands as they fly by, even if you are a speed reader.  It is more interesting to take the output of a single frame.  The assumption is made here that the OpenGL application is double buffered, such that a frame boundary is defined by glXSwapBuffers().  Use, then,
OGLDUMP_FRAME=n
where n is the number of the frame you want to dump to the stdout.  n may not be less than 1, and n=1 is the first frame of execution.  Alternately,you may specify a range of frames, as in:

    OGLDUMP_FRAME=30,80

A simple example follows:

don_burns@peru 63% setenv OGLDUMP_FRAME 60

don_burns@peru 64% ./myexecutable
**************************************************
                      WARNING
             USING Ogldump libGL.so library

                     Don Burns
                    Mar 8, 2002
**************************************************
--------------  OGLDUMP Frame 60  ----------------
    glViewport( x = 0, y = 0, width = 640, height = 480 )
    glClearColor( red =   0.0000, green =   0.0000, blue =   0.0000, alpha =   1.0000 )
    glClear( mask = GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT )
    glMatrixMode( mode = GL_PROJECTION )
    glLoadIdentity()
    glMultMatrixd({
                        {  1.2990  0.0000  0.0000  0.0000 }
                        {  0.0000  1.7321  0.0000  0.0000 }
                        {  0.0000  0.0000 -1.0020 -1.0000 }
                        {  0.0000  0.0000 -2.0020  0.0000 }
                         )
    glMatrixMode( mode = GL_MODELVIEW )
    glLoadIdentity()
    glMultMatrixf({
                        {  1.0000  0.0000  0.0000  0.0000 }
                        {  0.0000  1.0000  0.0000  0.0000 }
                        {  0.0000  0.0000  1.0000  0.0000 }
                        {  0.0000  0.0000  0.0000  1.0000 }
                         )
    glTranslated( x =   0.0000, y =   0.0000, z =  -7.0000 )
    glMultMatrixf({
                        {  1.0000  0.0000  0.0000  0.0000 }
                        {  0.0000  1.0000  0.0000  0.0000 }
                        {  0.0000  0.0000  1.0000  0.0000 }
                        {  0.0000  0.0000  0.0000  1.0000 }
                         )
    glCallList( list = 1 )
    glXSwapBuffers( dpy = 0x1001a128, drawable = 0x6400004 )


This example displays the output of a program that draws a simple, display listed cube and the frame ends at glXSwapBuffers.  Complex and larger output can be redirected to a file, then browsed for interesting information.

Caliper Analysis

Ogldump supports a mode in which it's tracing can be turned on/off at any point during execution. This mode is know as caliper mode, and is controlled using signals. Instructions are presented to the user at startup, but the general principle is presented below:

To enable the caliper:

% setenv OGLDUMP_CALIPER
Launch your app as in a typical ogldump invocation. Then, to start tracing:
% kill -USR1 <pid>
where <pid> is the process id of your running executable. To stop tracing:
% kill -USR2 <pid>
The caliper can be started and stopped as many times as necessary. Caliper readings automatically sync to the next swapbuffers, and begin and end on frame boundaries.

Detailed Analysis

For more specific analysis, _gl.c may be edited and recompiled.  The main contents of _gl.c (after some initalization) contains an overloaded version of all of the OpenGL functions defined in the system gl.h.

For example, you may want to test to see how many tex binds occur during run time.  Declare the following variables at the top of the file:

static int frame = 0;
static int ntexbinds = 0;


Alter glBindTexture() and glBindTextureEXT():

void  glBindTexture( GLenum  target, GLuint  texture)
{
    inited || init();

    ntexbinds++;

    ....
}

void  glBindTextureEXT( GLenum  target, GLuint  texture)
{
    inited || init();

    ntexbinds++;

    ....
}
 

Then, monitor in glXSwapBuffers() :
 
void  glXSwapBuffers( Display * dpy, GLXDrawable  drawable)
{
    inited || init();

    ++nframe;

    printf( "\033[0;0H\n" );
    printf( "       Frame number : %4d\n", frame );
    printf( "Number of Tex Binds : %4d\n", ntexbinds );

    ntexbinds = 0;

    ...
}

Conclusion

OglDump can be a powerful tool to gain insight into the functionality of OpenGL programs, isolate performance bottlenecks or determine many other problems which are either not obvious from source code, or are hidden from the programmer by a higher level API.  It may also be used to learn the nuts and bolts behind certain "magic", like multipass rendering, or other special effects.

OglDump is open-sourced in hopes that it can receive contributions from its users.  Please email the author with your comments, questions, enhancements or problems.

Notes and Known Problems

There is a known problem with Mesa 3.4.  In gl.h Mesa defines :
GLAPI void GLAPIENTRY glGetPointerv( GLenum pname, void **params );
GLAPI void GLAPIENTRY glGetPointervEXT( GLenum pname, void **params );
These functions declare the params variable to be void ** instead of GLvoid **.  This is a small anomaly that trips up the parser.  Rather than tweaking the parser to handle an otherwise trivial case, a bug has been opened with the Mesa folks and I'm asking users of Mesa3.4 to change the declaration of these functions to :
GLAPI void GLAPIENTRY glGetPointerv( GLenum pname, GLvoid **params );
GLAPI void GLAPIENTRY glGetPointervEXT( GLenum pname, GLvoid **params );