#include <iostream>
using namespace std;
int main()
{
// We are setting this pointer to not point at anything
int* bar = 0;
// Now try to print out what is in the bad pointer
cout << *bar << endl;
// And now try to set the bad pointer
*bar = 42;
}
This program will certainly crash (hopefully you can see why. If not,
please go re-read the chapter of your text regarding pointers.)
So we compile it as normal and see what happens:
> g++ -Wall -Werror -W -pedantic test1.cc -o test1 > ./test1 Segmentation FaultWe have a problem (as expected), so lets see what the debugger says:
> gdb test1 NU gdb Red Hat Linux (5.2.1-4) Copyright 2002 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-redhat-linux"... (gdb)Now the debugger waits for instructions. To get it to run our program and see what happened, give it the instruction "run".
(gdb) run Starting program: /home/dsheldon/cs12/test1 Program received signal SIGSEGV, Segmentation fault. 0x080485fd in main () (gdb) bt #0 0x080485fd in main () #1 0x401124ad in __libc_start_main () from /lib/libc.so.6 (gdb)Which is not terribly helpful. We are now told the memory location of the instruction that killed the program, and the fact that the program died in the function "main ()." This is not news (we only defined main, of course that is where the problem is!).
> g++ -g -Wall -Werror -W -pedantic test1.cc -o test1and rerun the debugger (hit Ctrl+D to terminate gdb, and let it terminate your program).
(gdb) run Starting program: /home/dsheldon/cs12/test1 Program received signal SIGSEGV, Segmentation fault. 0x080485fd in main () at t.cc:13 13 cout << *bar << endl; (gdb)Ooh, that's much better. Not only do we know what happened, we even know what line it happened on. Very useful. Let's look at another (contrived) example:
#include <iostream>
using namespace std;
int fac(int n)
{
if (n == 1)
{
int* bar = 0;
cout << *bar << endl;
}
return n * fac(n - 1);
}
int main()
{
cout << fac(5) << endl;
}
Now we want to see what gdb will let us do with regard to functions.
Compile as before (with the -g flag), and run the debugger on the program.
> gdb test2 GNU gdb Red Hat Linux (5.2.1-4) Copyright 2002 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-redhat-linux"... (gdb) run Starting program: /home/dsheldon/cs12/test2 Program received signal SIGSEGV, Segmentation fault. 0x080485f9 in fac(int) (n=1) at test2.cc:10 10 cout << *bar << endl; (gdb) bt #0 0x080485f9 in fac(int) (n=1) at test2.cc:10 #1 0x0804861e in fac(int) (n=2) at test2.cc:12 #2 0x0804861e in fac(int) (n=3) at test2.cc:12 #3 0x0804861e in fac(int) (n=4) at test2.cc:12 #4 0x0804861e in fac(int) (n=5) at test2.cc:12 #5 0x0804864d in main () at test2.cc:17 #6 0x401124ad in __libc_start_main () from /lib/libc.so.6 (gdb)(Note: bt stands for "back trace".) Ooh, this is nice. It will keep track of all the stack activation frames, their parameters, and where in your code they are listed. With this it is pretty easy to tell how your program got into the state it was in. But now, how do we see what that state really is? On the same run of gdb, let's peek around at the internals of the program.
(gdb) print bar $1 = (int *) 0x0 (gdb) p n $2 = 1The command "print", which can be abbreviated as just "p", shows the current type and value of the variable (or expression) requested. This lets us examine what all the variables were just as the program died. What if we want to see what the value of something in an earlier function (lower on the stack) is?
(gdb) up #1 0x0804861e in fac(int) (n=2) at test2.cc:12 12 return n * fac(n - 1); (gdb) p bar No symbol "bar" in current context (gdb) p n $4 = 2The commands "up" and "down" let you move through the function stack. Slightly annoyingly, "up" moves you lower in the stack and "down" moves you higher (so you must think of the function stack as growing down, rather than growing up.) Here we went up to the next higher recursive call, and tried to print out "bar." Bar was defined locally to the scope of the "if (n == 1)" statement, so it doesn't exist in this part of the function (remember scoping rules!) But the variable "n", which exists in both scopes (as a different local variable, remember), shows the correct value for this activation frame. With just these tools, you can do quite a lot of debugging of broken code.
GNU gdb Red Hat Linux (5.2.1-4) Copyright 2002 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-redhat-linux"... (gdb) break 16 Breakpoint 1 at 0x8048628: file test2.cc, line 16. (gdb)The command "break" allows us to set a breakpoint on a specific line of our source code. For programs that have only a single source file, this can be done by simply giving the line number. For programs with multiple source files (like the third homework assignment!), this can be done by specifying the name of the file, a colon, and the line number (like "maze.cc:20"). So we set a breakpoint at the beginning of main (line 16), let us also set one at line 9, where the bad pointer in our factorial function is defined.
(gdb) break 9 Breakpoint 2 at 0x80485e4: file test2.cc, line 9. (gdb)Now we can run our program as before.
(gdb) run
Starting program: /home/dsheldon/cs12/test2
Breakpoint 1, main () at test2.cc:16
16 {
(gdb) step
main () at test2.cc:17
17 cout << fac(5) << endl;
(gdb) step
fac(int) (n=5) at test2.cc:7
7 if (n == 1)
(gdb) step
12 return n * fac(n - 1);
(gdb) step
fac(int) (n=4) at test2.cc:7
7 if (n == 1)
(gdb) continue
Continuing.
Breakpoint 2, fac(int) (n=1) at test2.cc:9
9 int* bar = 0;
The "run" command is stopped when it reaches the first breakpoint. We
can continue executing line-by-line using the "step" command (to step
through the code.) If we tire of using "step", we can continue the
execution using the "continue" command, which runs from the current
point in the execution until the program crashes or it hits a
breakpoint (much like "run" only without restarting the program.)
#include <iostream>
using namespace std;
int main()
{
int n;
int temp = 5;
string foo = "Hello world!";
int myArray[n];
cout << foo << temp << endl;
}
#include <iostream>
using namespace std;
struct Point
{
int x;
int y;
};
int main()
{
Point* p = new Point();
Point* q = p;
p->x = 10;
q->y = 15;
delete q;
delete p;
}
© 2003 UC Riverside Department of Computer Science & Engineering. All rights reserved.