Debugging C++ programs is often extremely challenging. The direct pointer manipulations permitted by the language give rise to bugs that can't happen in most other computer languages. Often, these bugs manifest themselves in strange ways, such as the program printing interesting messages like "core dump" or "bus error" with no additional information. This is the price you pay for the efficiency and low-level control that the C++ language provides.
Debugging is a big subject, and we can only scratch the surface here. In general, here are three approaches you can use for debugging:
assert statements so that when something goes wrong
the program halts right away instead of continuing.
To use gdb, do the following:
-g command-line option e.g.
g++ -g -Wall myprog.cc -o myprog
The "-g" option puts debugging information into the executable. Most importantly, it puts the text of the source code file into the executable so you can examine it as the program executes (we'll see how below).
gdb myprog (for the example above). This will start the
interactive debugger. It's basically a shell-like environment in which you
can run your program and do useful debugging tasks as well.
When in the debugger, you have a choice of lots of commands. Do "info" to get a list of commands. Here are some of the most important ones:
run: runs the program
where: tells you where you are in the program when
you have stopped at some point. Also tells you the calling history of the
program up to that point (i.e. which functions have been called to get
you where you are).
p <variable>: prints the value of
<variable>. p is short for "print".
break <file>:<line>: causes the program
to stop at a particular line in a particular source code file.
break <function>: causes the program to stop
when entering a particular function.
n: executes the next statement and then stops. This
command will not enter a new function while you're inside a function.
Instead, it goes to the next statement in the current function.
n is short for "next".
s: executes the next statement, possibly entering a
new function, and then stops. s is short for "step".
l: lists lines in a source code file.
l is short for "list".
c: continues executing the program.
c is short for "continue".
q: exits (quits) gdb.
q is short for "quit".
help
cmdname at the gdb prompt, where
cmdname is the name of the command listed above.
segmentation violation (core dumped) or maybe just
segmentation fault. First, let's identify what that cryptic
phrase means. A "segmentation violation" means that your program tried to
access memory that it wasn't allowed to. Since Unix is a multitasking
operating system, each process lives in its own little world, with its own
little hunk of memory that it's allowed to play with. The operating system
knows what hunk belongs to your process and what doesn't; if your process try
to access memory that it doesn't have the right to access, then it violates
the (memory) segment boundaries and you get a segmentation violation, which
(normally) causes your program to abort. A "core dump" refers to the fact
that by default, a "core" file will be "dumped" into the directory from which
you ran your program. The file is actually called "core" and can be very
large (several megabytes or more). That's because it's a dump of what the
memory contained when your program crashed. It is possible to use the core
file to debug your program, but there are much easier ways to debug, so we
won't cover that here. Most shells allow you to put in a statement that
restricts the size of core dumps (ideally to zero bytes, in which case no
core file is dumped); ask your local Unix guru for more information on this.
OK. Now what you need to know is where the segmentation violation
occurred. To do this, compile your program with the -g
option described above, start up gdb, and type run myprog
(where "myprog" is the name of your program). Alternatively you can invoke
gdb as gdb myprog and then just type
run at the gdb prompt. [NOTE: if your program
needs command-line arguments, you should supply them after the
run or run myprog statement
e.g. run myprog arg1 arg2 arg3.] This will run
your program until the segmentation violation occurs. Gdb will tell you that
the segmentation violation occurred and then wait for your command.
For instance, consider this little program:
#include <iostream>
using namespace std;
void print_array(int *arr, int size)
{
for (int i = 0; i < size; i++)
{
cout << arr[i] << endl;
}
}
int main()
{
int *foo = new int[10];
print_array(foo, 1000);
delete [] foo;
return 0;
}
There is an obvious bug here: the program tries to print 1000 elements from a
10-element array. This will probably lead to a core dump. Let's say we save
this as myprog.cc and compile it using the -g
option. When we type gdb myprog and then type run
at the gdb prompt the program prints a bunch of zeroes and then we get this:
Program received signal SIGSEGV, Segmentation fault.
0x0804866f in print_array(int*, int) (arr=0x804a830, size=1000) at myprog.cc:9
9 cout << arr[i] << endl;
This means that the segmentation violation (also known as a segmentation
fault or segfault for short) occurred in the function "print_array" at line 9
in the file "myprog.cc".
Type where and you will get a stack backtrace. This is
probably the single most useful thing you can have when something goes wrong.
A stack backtrace is a list of function names in your program and associated
data. The stack is a data structure which holds information about
functions which have partially finished executing. When a function calls
another function, information about the new function being called is "pushed"
onto a stack. This includes information such as the arguments to the
function, the contents of local variables, etc. This information is referred
to as a stack frame. When the function is finished its work it "pops"
the frame off the stack and returns to the previous stack frame, which
belongs to the function that called it.
Typing where at the gdb prompt gives us this:
(gdb) where
#0 0x0804866f in print_array(int*, int) (arr=0x804a830, size=1000) at myprog.cc:9
#1 0x080486c2 in main () at myprog.cc:16
#2 0x40113a51 in __libc_start_main () from /lib/libc.so.6
In the above backtrace, we see that the function
__libc_start_main called main which called
print_array. In this case, the function
__libc_start_main is a C++ library function which you didn't
write. main is the good old main function that you write in
every C++ program. The segmentation violation occurred in the
print_array() function, and gdb even tells you what line it
happened on (which is what the "-g" option does for you). If it isn't
obvious what is going wrong, you can set a breakpoint there:
break myprog.cc:9
Now, when you run the program again, gdb will stop it on that line, and you
will be able to print out the values of any relevant variables before
the line is executed. Typing c will continue execution
from that point.
There is much, much more to debugging than we have time to go into here, but this should get you started. Reading the gdb info documentation (type "info gdb" at the shell prompt) will be a good place to go for more information, as will asking your Unix guru friends.
gdb.
The one we recommend is called ddd (for Data
Display Debugger). Learning a graphical debugger can take
time, but it's well worth it because it makes it much easier to interact with
your program (setting breakpoints, looking at code as it executes, etc.).
Describing ddd in detail is beyond the scope of this document,
but if you're interested, do a "man ddd" or visit this link for much more
information.