Difference between revisions of "Complete roguelike tutorial using C++ and libtcod - extra 2: debugging"

From RogueBasin
Jump to navigation Jump to search
Line 45: Line 45:


Ok, now our game is bigger, probably slightly slower, but we can see what it's doing while running.
Ok, now our game is bigger, probably slightly slower, but we can see what it's doing while running.
==Smashing the bug==
Let's load the game into GNU's debugger :
> gdb ./tuto
...
Reading symbols from tuto.exe...done.
(gdb)
Now we have gdb's prompt. Let's run the game (type 'r' or 'run')
(gdb) r
Starting program: tuto.exe
24 bits font.
key color : 0 0 0
character for ascii code 255 is colored
Using SDL renderer...
Program received signal SIGSEGV, Segmentation fault.
0x00401cc9 in Map::createRoom (this=0x9149240, first=true, x1=47, y1=14,
          x2=56, y2=22) at src/Map.cpp:75
          engine.player->x=(x1+x2)/2;
First we can see some messages printed by libtcod on the game's standard output (24 bits font and so on). Then it happens.
Program received signal SIGSEGV, Segmentation fault.
This scary SIGSEGV word means that the application received a SIGnal because there was a SEGmentation Violation. Seems pretty bad. Segmentation violation is a cuss word to say that you tried to access some part of the memory you are not allowed to. How is that possible ? Well, at this stage you should know that C has pointers, integer variables storing a memory address. Since you can put any value in a pointer variable, it can point to any part of the memory, including parts that do not belong to your program or that do not exist. For example :
int *pi = NULL;
*pi = 0;
Here we're trying to put the value 0 at address NULL (== 0). The very first byte of memory. No need to say, this part of the memory is locked by the OS and no user program can write there. This code will trigger a SIGSEGV.
Back to our game. gdb kindly provides the faulty line :
0x00401cc9 in Map::createRoom (this=0x9149240, first=true, x1=47, y1=14,
x2=56, y2=22) at src/Map.cpp:75
It happened in Map.cpp in the function createRoom at line 75 :
engine.player->x=(x1+x2)/2;
Using a pointer dereference like (*ptr) or ptr->... is a typical case where a SIGSEGV can occur. Let's see the value of the player pointer :
(gdb) print engine.player
$1 = (Actor *) 0x0
0x0 is not a smiley. It's the hexadecimal value for 0. Our pointer is NULL. No surprise trying to dereference it leads to a crash. Remember that when we build the map, we place the player in the center of the first room. The problem is that since we moved the map creation before the player creation, we can't do that anymore.
Ok let's revert our change.
player = new Actor(40,25,'@',TCODColor::white);
actors.push(player);
map = new Map(80,45);




[[Category:Developing]]
[[Category:Developing]]

Revision as of 13:53, 21 October 2015

Complete roguelike tutorial using C++ and libtcod
-originally written by Jice
Text in this tutorial was released under the Creative Commons Attribution-ShareAlike 3.0 Unported and the GNU Free Documentation License (unversioned, with no invariant sections, front-cover texts, or back-cover texts) on 2015-09-21.


Sooner or later (and always sooner than expected), you will do something wrong in your code and the result will be a sudden, brutal, impolite crash of your application.

This article will show you how to debug the application with a command line debugger. I know it's easier to use an IDE and I might add a dedicated article on debugging with CodeLite. But you don't always have an IDE available. Your favorite IDE might not be ported to some obscure platform you want your game to run on. gdb is always a useful addition in any C/C++ developer's swiss army knife.

Let's crash

For example, let's do some minor change to the Engine constructor and move the map creation before the player creation :

map = new Map(80,45);
player = new Actor(40,25,'@',TCODColor::white);
actors.push(player);

This seems totally inoffensive. Let's recompile the game as usual :

Windows :

> g++ src/*.cpp -o tuto -Iinclude -Llib -ltcod-mingw
-static-libgcc -static-libstdc++

Linux :

> g++ src/*.cpp -o tuto -Iinclude -L. -ltcod -ltcodxx -Wl,-rpath=.

Now if you run the game, you should see the game window disappearing as soon as it opens. No error message, no nice stack trace. What the hell ? Well welcome to native application coding.

Adding debugging information

To be able to understand what's happening, we need a debugger. And for the debugger to be able to track what the program is doing, we need to add information in the .exe. With g++ you do that with the -g flag.

We'll also go further and use the libtcod library that also contains debugging information. That means adding -debug to the library names in the compilation command :

Let's recompile with debugging information :

Windows :

> g++ src/*.cpp -o tuto -Iinclude -Llib -ltcod-mingw-debug
-static-libgcc -static-libstdc++ -g

Linux :

> g++ src/*.cpp -o tuto -Iinclude -L. -ltcod_debug
-ltcodxx_debug -Wl,-rpath=. -g

Ok, now our game is bigger, probably slightly slower, but we can see what it's doing while running.

Smashing the bug

Let's load the game into GNU's debugger :

> gdb ./tuto
...
Reading symbols from tuto.exe...done.
(gdb)

Now we have gdb's prompt. Let's run the game (type 'r' or 'run')

(gdb) r
Starting program: tuto.exe
24 bits font.
key color : 0 0 0
character for ascii code 255 is colored
Using SDL renderer...

Program received signal SIGSEGV, Segmentation fault.
0x00401cc9 in Map::createRoom (this=0x9149240, first=true, x1=47, y1=14,
          x2=56, y2=22) at src/Map.cpp:75
          engine.player->x=(x1+x2)/2;

First we can see some messages printed by libtcod on the game's standard output (24 bits font and so on). Then it happens.

Program received signal SIGSEGV, Segmentation fault. This scary SIGSEGV word means that the application received a SIGnal because there was a SEGmentation Violation. Seems pretty bad. Segmentation violation is a cuss word to say that you tried to access some part of the memory you are not allowed to. How is that possible ? Well, at this stage you should know that C has pointers, integer variables storing a memory address. Since you can put any value in a pointer variable, it can point to any part of the memory, including parts that do not belong to your program or that do not exist. For example :

int *pi = NULL;
*pi = 0;

Here we're trying to put the value 0 at address NULL (== 0). The very first byte of memory. No need to say, this part of the memory is locked by the OS and no user program can write there. This code will trigger a SIGSEGV.

Back to our game. gdb kindly provides the faulty line :

0x00401cc9 in Map::createRoom (this=0x9149240, first=true, x1=47, y1=14,
x2=56, y2=22) at src/Map.cpp:75

It happened in Map.cpp in the function createRoom at line 75 :

engine.player->x=(x1+x2)/2;

Using a pointer dereference like (*ptr) or ptr->... is a typical case where a SIGSEGV can occur. Let's see the value of the player pointer :

(gdb) print engine.player
$1 = (Actor *) 0x0

0x0 is not a smiley. It's the hexadecimal value for 0. Our pointer is NULL. No surprise trying to dereference it leads to a crash. Remember that when we build the map, we place the player in the center of the first room. The problem is that since we moved the map creation before the player creation, we can't do that anymore.

Ok let's revert our change.

player = new Actor(40,25,'@',TCODColor::white);
actors.push(player);
map = new Map(80,45);