| Previous: Memory Layout And The Stack | Next: Initialization, Listing, And Running |
As of SDL 1.2.11, it appears that SDL_SetVideoMode() no longer generates SIGFPE when passed SDL_OPENGL. This means you can use GDB to debug spinning_cube. However, this is still an excellent example of:
In the last section we looked at how a program is laid out in memory. Knowing this is not only useful for debugging with GDB, but it's also useful for debugging without GDB. In this interlude, guest written by my close friend, Mark Kim, we'll see how.
Compile and run spinning_cube.tar.bz2. A spinning cube is displayed with images of Geordi (white) and Juliette (calico), me on a New York City subway, and where I work.
However, when you press a key, some of the cube's textures mysteriously vanish. My first instinct was to use GDB to find the problem, but I discovered that SDL programs that use OpenGL can't be debugged via GDB. Upon investigation, I found that when you pass the flag SDL_OPENGL to the function SDL_SetVideoMode(), a SIGFPE is generated which terminates the program. If you try to handle the SIGFPE, you'll find that SDL_SetVideoMode() never returns, so GDB is left in a hung state.
I had just spent over 40 hours programming over the last 3 days and was getting punch-drunk. Not having GDB available pushed me over the edge and I sent an exasperated email to Mark for help. I got a reply within 10 minutes.
Before continuing you'll want to:
Spend 10 minutes trying to fix the bug. This will make Mark's email all the more impressive. As you read Mark's email, pay particular attention to steps 6, 7B, and 7C for particular examples of sheer debugging brilliance!
Hey Peter,
The problem was there was an overlapping memory area between the debugging
variables and the texture variabes. In video.[hc], the "texture[2]" array
should have been declared "texture[NUM_TEXURES]" instead. Attached is a
patch file.
The debugging process went like this:
1. Try Debug() -- indeed it makes some textures disappear.
2. Try debug_for_reals() into an empty function -- same happens,
so that's not the problem.
3. Try removing each line of Debug() macro. This revealed that
writing values into the "die_*" variables cause the texture
to disappear.
4. So instead of calling Debug(), try writing some values into
the "die_*" variables -- the textures disappear again.
5. Check if any other code is using those variables by changing
variable names and looking out for compilation errors --
nothing significant showed up.
6. Perhaps someone is using the same memory space as the "die_*"
variables unintentionally. I tried shifting the memory locations of
the "die_*" variables down by putting an array in front of them,
like this:
yerror.c:
...
#include "yerror.h"
+ char buffer[1024];
// Global Debugging/Dying Variables
const char *die_filename;
const char *die_function;
int die_line;
bool debug = true;
which fixed the problem. So now it's a matter of finding the
overlapping memory.
7. Tracking down the problem needs some narrowing down of the
possiblilities, so I made the following assumptions:
A. I know a problem like this occurs most often when an array
size is declared too short at another place, so there's probably
an array out there that's declared too short, and the "die_*"
variables, placed in memory right after that array, is probably
getting overwritten by some code expecting the array to be
longer.
It could also be a pointer combined with malloc() but at this
point I'm just thinking about one problem at a time.
B. The problem must be with either a global or static variable
since it's overlapping with another global variable
in the heap space. So I'm looking for an array declared in
global or static scope. That narrows down my search quite a bit.
BTW, the fact that I'm looking for a variable that overlaps with
a global variable probably discounts malloc() from our potential
list of problems since malloc(), if the way I view the memory is
correct, should allocate memory only *after* all global
variables, and it's unlikely code accidentally writes to
a memory location before a pointer rather than an after
(though it's certainly possible to write to memory before
a pointer.) But again, this is all an afterthought... I'm just
thinking about another global array at this point.
C. I know the global array I'm looking for must be somehow linked
to a texture operation since that's what's being interfered by
writing to the "die_*" variables. So I'm looking for a global
array that does something with textures, probably one that stores
textures or pointers to textures or index to textures or
something like that.
8. And that's what I looked for. texture[2] looked a little suspicious
so I tried expanding its size and that fixed the problem. Just to
make sure, I looked for the code that writes to texture with index
greater than 1 and found init.c:127 and several places in render.c.
Hope that helps!
-Mark