GDB allows you to examine what is happening inside a program
while it is running. It lets you execute programs in a
controlled
manner and view and set the values of variables.
With GDB you can do the following things:
In your previous programming experience, you may have managed
without using a debugger. You might have been able to find
the
mistakes in your programs by printing things on the screen or
simply reading through your code. Beware, however, that
OS/161
[the operating system developed in the CS161 course] is a
large and complex body of code, much more so than you may have
worked on in the past. To make matters worse, much of it was
written by someone other than you. A debugger is an
essential
tool
in this environment.
We would not lie if we said that there has
never been a student in CS161 who has survived this class without
using GDB. You should, therefore, take the time to learn GDB
and
make it your best friend (or rather your second best friend; your
best friend should be your partner).
This guide will explain to you
how to get started debugging OS/161, describe the most common GDB
commands, and suggest some helpful debugging techniques.
Debugging tips
Tip #1: Check your beliefs about the program
So how do you actually approach debugging? When you have a
bug
in a program, it means that you have a particular belief about how
your program should behave, and somewhere in the program this
belief is violated. For example, you may believe that a
certain
variable should always be 0 when you start a "for" loop, or a
particular pointer can never be NULL in a certain "if
statement".
To check such beliefs, set a breakpoint in the debugger at a line
where you can check the validity of your belief. And when
your
program hits the breakpoint, ask the debugger to display the value
of the variable in question.
Tip #2: Narrow down your search
If you have a situation where a variable does not have the value you expect, and you want to find a place where it is modified, instead of walking through the entire program line by line, you can check the value of the variable at several points in the program and narrow down the location of the misbehaving code.
Tip #3: Walk through your code
Steve Maguire (the author of Writing Solid Code) recommends using the debugger to step through every new line of code you write, at least once, in order to understand exactly what your code is doing. It helps you visually verify that your program is behaving more or less as intended. With judicious use, the step, next and finish commands can help you trace through complex code quickly and make it possible to examine key data structures as they are built.
Tip #4: Use good tools
Using GDB with a visual front-end can be very helpful. For
example, using GDB inside the emacs
editor puts you
in a
split-window mode, where in one of the windows you run your GDB
session, and in the other window the GDB moves an arrow through
the
lines of your source file as they are executed. To use GDB
through
emacs
do the following:
Run gdb (like this): gdb
vim
editor in a similar
way.Tip #5: Beware of printfs!
A lot of programmers like to find mistakes in their programs by inserting "printf" statements that display the values of the variables. If you decide to resort to this technique, you have to keep in mind two things: First, because adding printfs requires a recompile, printf debugging may take longer overall than using a debugger.
More subtly, if you are debugging a multi-threaded program, such as an OS kernel, the order in which the instructions are executed depends on how your threads are scheduled, and some bugs may or may not manifest themselves under a particular execution scenario. Because printf outputs to the console, and the console is a serial device that isn't extraordinarily fast, an extra call to printf may alter the timing and scheduling considerably. This can make bugs hide or appear to come and go, which makes your debugging job much more difficult.
... (Tips 6 and 7 are specific to assembly languages and OS/161)Tip #8: Other tricks and caveats
If you have a void *
in GDB and you know what type
it
actually
is, you can cast it when printing, using the usual C expression
syntax.
(end of excerpts from Harvard)
Some output from GDB on Linux, using a server with AMD processors
% gdb
GNU gdb Red Hat Linux (6.3.0.0-1.162.el4rh)
Copyright 2004 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-gnu".
(gdb) help
List of classes of commands:
aliases -- Aliases of other commands
breakpoints -- Making program stop at certain points
data -- Examining data
files -- Specifying and examining files
internals -- Maintenance commands
obscure -- Obscure features
running -- Running the program
stack -- Examining the stack
status -- Status inquiries
support -- Support facilities
tracepoints -- Tracing of program execution without stopping the
program
user-defined -- User-defined commands
Type "help" followed by a class name for a list of commands in
that
class.
Type "help" followed by command name for full documentation.
Command name abbreviations are allowed if unambiguous.
(gdb) quit
% make gdbdemo
gcc -g -Wall -Wextra -o h1.x h1.c
h1.c:22: warning: unused parameter 'argc'
-g
option so the compiler puts
symbol
table information into the executable. This allows the
debugger
to make sense of the stack, find global variables and function
definitions, etc.% h1.x
Segmentation fault
% h1.x 1
1 f1
main out
% h1.x 2
2 f1
main out
% h1.x 3
3 f2 ***
main out
% gdb h1.x
GNU gdb Red Hat Linux (6.3.0.0-1.162.el4rh)
Copyright 2004 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-gnu"...Using host
libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) run
Starting program: .../h1.x
Program received signal SIGSEGV, Segmentation fault.
0x0071addc in ____strtol_l_internal () from /lib/tls/libc.so.6
r
also works....
indicates the full pathname, which we
edited out.(gdb) bt
#0 0x0071addc in ____strtol_l_internal () from
/lib/tls/libc.so.6
#1 0x0071ab6f in __strtol_internal () from
/lib/tls/libc.so.6
#2 0x00718136 in atoi () from /lib/tls/libc.so.6
#3 0x0804840b in main (argc=1, argv=0xbfe6b4e4) at h1.c:27
(gdb) l h1.c:27
22 int main(/*@unused@*/ int argc, char
*argv[]) /*@globals n@*/ /*@modifies n@*/
23 {
24 void (*func)(void) = f1;
25 uintptr_t A[2];
26
27 A[n = atoi(argv[1])] = (uintptr_t) f2;
28
29 (*func)();
30
31 printf("main out\n");
l
file-name:line-number
,
but
if
there
is
only one source file, the line number is sufficient.l function-name
will show the beginning of the
function definition.(gdb) b 26
Breakpoint 1 at 0x80483fb: file h1.c, line 26.
b function-name
will stop at the beginning of the
function execution.(gdb) c
Continuing.
Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.
(gdb) disp n
n
will be displayed at
each
breakpoint. Cancel this with undisplay.disp /FMT EXPR
x
as the FMT
for
hexadecimal; EXPR
can be any expression that makes sense in the current context.(gdb) r
Starting program: .../h1.x
Breakpoint 1, main (argc=1, argv=0xbff1ed74) at h1.c:27
27 A[n = atoi(argv[1])] = (uintptr_t) f2;
1: n = 0
(gdb) p A
$1 = {134518080, 8495092}
(gdb) p /x A
$2 = {0x8049540, 0x819ff4}
printf()
function.(gdb) quit
The program is running. Exit anyway? (y or n) y
% gdb h1.x
...
(gdb) r
Starting program: .../h1.x
Program received signal SIGSEGV, Segmentation fault.
0x0071addc in ____strtol_l_internal () from /lib/tls/libc.so.6
(gdb) bt
#0 0x0071addc in ____strtol_l_internal () from
/lib/tls/libc.so.6
#1 0x0071ab6f in __strtol_internal () from
/lib/tls/libc.so.6
#2 0x00718136 in atoi () from /lib/tls/libc.so.6
#3 0x0804840b in main (argc=1, argv=0xbff8cd64) at h1.c:27
(gdb)
l
h1.c:27
22 int main(/*@unused@*/ int argc, char
*argv[]) /*@globals n@*/ /*@modifies n@*/
23 {
24 void (*func)(void) = f1;
25 uintptr_t A[2];
26
27 A[n = atoi(argv[1])] = (uintptr_t) f2;
28
29 (*func)();
30
31 printf("main out\n");
(gdb) b 23
Breakpoint 1 at 0x80483d8: file h1.c, line 23.
(gdb) disp argc
No symbol "argc" in current context.
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: .../h1.x
Breakpoint 1, main (argc=1, argv=0xbfe8f0b4) at h1.c:23
23 {
(gdb) disp n
1: n = 0
(gdb) disp argc
2: argc = 1
(gdb) disp argv
3: argv = (char **) 0xbfea7024
(gdb) disp argv[]
A syntax error in expression, near `]'.
(gdb) disp argv[0]
4: argv[0] = 0xbff5372c ".../h1.x"
(gdb) disp argv[1]
5: argv[1] = 0x0
(gdb) disp f1
6: f1 = {void (void)} 0x804839c <f1>
(gdb) disp f2
7: f2 = {void (void)} 0x80483ba <f2>
(gdb) disp func
8: func = (void (*)(void)) 0x6ebca0 <_rtld_local_ro>
(gdb) disp /x A
9: /x A = {0x8049540, 0x819ff4}
(gdb) n
24 void (*func)(void) = f1;
9: /x A = {0x8049540, 0x819ff4}
8: func = (void (*)(void)) 0x6ebca0 <_rtld_local_ro>
7: f2 = {void (void)} 0x80483ba <f2>
6: f1 = {void (void)} 0x804839c <f1>
5: argv[1] = 0x0
4: argv[0] = 0xbff5372c ".../h1.x"
3: argv = (char **) 0xbfea7024
2: argc = 1
1: n = 0
s
command (step) to stop
inside
a function call.n
evaluates function
calls
entirely, while s
stops just after calling the
function.(gdb) n
27 A[n = atoi(argv[1])] = (uintptr_t) f2;
9: /x A = {0x8049540, 0x819ff4}
8: func = (void (*)(void)) 0x804839c <f1>
7: f2 = {void (void)} 0x80483ba <f2>
6: f1 = {void (void)} 0x804839c <f1>
5: argv[1] = 0x0
4: argv[0] = 0xbff5372c ".../h1.x"
3: argv = (char **) 0xbfea7024
2: argc = 1
1: n = 0
(gdb) n
Program received signal SIGSEGV, Segmentation fault.
0x0071addc in ____strtol_l_internal () from /lib/tls/libc.so.6
7: f2 = {void (void)} 0x80483ba <f2>
6: f1 = {void (void)} 0x804839c <f1>
1: n = 0
(gdb) n
Single stepping until exit from function ____strtol_l_internal,
which has no line number information.
Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.
(gdb) r 3
Starting program: .../h1.x 3
Breakpoint 1, main (argc=2, argv=0xbfe33094) at h1.c:23
23 {
9: /x A = {0x8049540, 0x819ff4}
8: func = (void (*)(void)) 0x6ebca0 <_rtld_local_ro>
7: f2 = {void (void)} 0x80483ba <f2>
6: f1 = {void (void)} 0x804839c <f1>
5: argv[1] = 0xbff19768 "3"
4: argv[0] = 0xbff1972a ".../h1.x"
3: argv = (char **) 0xbfe33094
2: argc = 2
1: n = 0
(gdb) n
24 void (*func)(void) = f1;
9: /x A = {0x8049540, 0x819ff4}
8: func = (void (*)(void)) 0x6ebca0 <_rtld_local_ro>
7: f2 = {void (void)} 0x80483ba <f2>
6: f1 = {void (void)} 0x804839c <f1>
5: argv[1] = 0xbff19768 "3"
4: argv[0] = 0xbff1972a ".../h1.x"
3: argv = (char **) 0xbfe33094
2: argc = 2
1: n = 0
(gdb) n
27 A[n = atoi(argv[1])] = (uintptr_t) f2;
9: /x A = {0x8049540, 0x819ff4}
8: func = (void (*)(void)) 0x804839c <f1>
7: f2 = {void (void)} 0x80483ba <f2>
6: f1 = {void (void)} 0x804839c <f1>
5: argv[1] = 0xbff19768 "3"
4: argv[0] = 0xbff1972a ".../h1.x"
3: argv = (char **) 0xbfe33094
2: argc = 2
1: n = 0
(gdb) n
29 (*func)();
9: /x A = {0x8049540, 0x819ff4}
8: func = (void (*)(void)) 0x80483ba <f2>
7: f2 = {void (void)} 0x80483ba <f2>
6: f1 = {void (void)} 0x804839c <f1>
5: argv[1] = 0xbff19768 "3"
4: argv[0] = 0xbff1972a ".../h1.x"
3: argv = (char **) 0xbfe33094
2: argc = 2
1: n = 3
(gdb) n
3 f2 ***
31 printf("main out\n");
9: /x A = {0x8049540, 0x819ff4}
8: func = (void (*)(void)) 0x80483ba <f2>
7: f2 = {void (void)} 0x80483ba <f2>
6: f1 = {void (void)} 0x804839c <f1>
5: argv[1] = 0xbff19768 "3"
4: argv[0] = 0xbff1972a ".../h1.x"
3: argv = (char **) 0xbfe33094
2: argc = 2
1: n = 3
(gdb) where
#0 main (argc=2, argv=0xbfe33094) at h1.c:31
(gdb) info frame
Stack level 0, frame at 0xbfe33010:
eip = 0x8048425 in main (h1.c:31); saved eip 0x703df3
source language c.
Arglist at 0xbfe33008, args: argc=2, argv=0xbfe33094
Locals at 0xbfe33008, Previous frame's sp is 0xbfe33010
Saved registers:
ebp at 0xbfe33008, eip at 0xbfe3300c
info breakpoints
info registers
(gdb) p f1
$1 = {void (void)} 0x804839c <f1>
(gdb) p f2
$2 = {void (void)} 0x80483ba <f2>
(gdb) p /x A
$8 = {0x8049540, 0x819ff4}
(gdb) p &A
$2 = (uintptr_t (*)[2]) 0xbffbae88
(gdb) p &func
$3 = (void (**)(void)) 0xbffbae94
(gdb) p &A[0]
$4 = (uintptr_t *) 0xbffbae88
(gdb) p &A[1]
$5 = (uintptr_t *) 0xbffbae8c
(gdb) p &A[2]
$6 = (uintptr_t *) 0xbffbae90
(gdb) p &A[3]
$7 = (uintptr_t *) 0xbffbae94
(gdb) quit
The program is running. Exit anyway? (y or n) y
%
.gdbinit
and they will be run automatically at startup.