http://www.openlogic.com/wazi/bid/351471/more-gdb-tips-and-tricks
The GNU Debugger (GDB) is a powerful tool for developers. In an earlier article I talked about how to use breakpoints and watchpoints, and how to auto-display values and call user-defined and system functions. This time, let's see how to use GDB to examine memory and debug macros and signal handlers. To create the examples here, I used GDB 7.6.1 and GCC 4.8.1, and compiled the C code using the
The syntax of the command is
To understand how to use the
GDB doesn't know anything about macros by default either, but you can enable macro debugging in the debugger using compile-time flags such as
You can also use the
To alter this behavior, use the
Run the
The GNU Debugger (GDB) is a powerful tool for developers. In an earlier article I talked about how to use breakpoints and watchpoints, and how to auto-display values and call user-defined and system functions. This time, let's see how to use GDB to examine memory and debug macros and signal handlers. To create the examples here, I used GDB 7.6.1 and GCC 4.8.1, and compiled the C code using the
-ggdb
option.Examine memory
Use GDB'sx
command to examine memory. The command offers several formatting options that let you control the number of bytes to display and the way you'd like to display them.The syntax of the command is
x/FMT ADDRESS
, where FMT specifies the output format and ADDRESS is the memory address to be examined. FMT consists of three types of information: the repeat count, the display format, and the unit size. For example: x/3uh 0x786757
is a request to display three halfwords (h) of memory, formatted as unsigned decimal integers (u), starting at address 0x786757. Available format letters are o (octal), x (hex), d (decimal), u (unsigned decimal), t (binary), f (float), a (address), i (instruction), c (char) and s (string), while available unit size letters are b (byte), h (halfword), w (word), g (giant, 8 bytes). And it doesn't matter whether the unit size or format comes first; either order works.To understand how to use the
x
command, consider the following code:#includeSuppose the main() and the func() functions are in different modules, and while debugging a problem you want to examine the value pointed to by the
#include
void func(char *ptr)
{
char tmp[] = "someOtherString";
char *new_ptr = ptr+3;
if(strncmp(tmp, new_ptr, sizeof(tmp)))
{
/*
* Some processing
*/
}
}
int main(void)
{
func("1. Openlogic");
return 0;
}
new_ptr
pointer. Load the program using GDB, put a breakpoint at the strncmp
line, then run the program. Once the program hits the breakpoint, you'll get a prompt:$ gdb testRun
Reading symbols from /home/himanshu/practice/test...done.
(gdb) break 8
Breakpoint 1 at 0x80484a9: file test.c, line 8.
(gdb) run
Starting program: /home/himanshu/practice/test
Breakpoint 1, func (ptr=0x8048590 "1. Openlogic") at test.c:8
8 if(strncmp(tmp, new_ptr, sizeof(tmp)))
(gdb)
x/s new_ptr
at this prompt, and you'll get the following output:(gdb) x/s new_ptrThe
0x8048593: "Openlogic"
(gdb)
x
command with the s
format letter displays the value stored at the memory address pointed by new_ptr
as a string. To display the value in character format, change the format specifier to c
:(gdb) x/9c new_ptr
0x8048593: 79 'O' 112 'p' 101 'e' 110 'n' 108 'l' 111 'o' 103 'g' 105 'i'
0x804859b: 99 'c'
Debugging macros
One of the biggest reasons developers prefer inline functions over macros is that debuggers tend to be better at dealing with the former. For example, consider the following code:#includeThe cube of 4 is 64, but watch what happens when you run the program:
#define CUBE(x) x*x*x
int main(void)
{
int a =3;
printf("\n The cube of [%d] is [%d]\n", a+1, CUBE(a+1));
return 0;
}
$ ./testIt can be a nightmare to pinpoint the cause of this kind of problem in a project with a large code base, as most debuggers simply aren't good at debugging macros.
The cube of [4] is [10]
GDB doesn't know anything about macros by default either, but you can enable macro debugging in the debugger using compile-time flags such as
-gdwarf-2
and -g3
; for more info on these options, read the man page of the GCC compiler. Once the program is compiled with the aforementioned command-line options, load and run it with GDB in the standard way. Put in a breakpoint so that you can debug the macro while the program is running:$ gdb testWhen the program hits the breakpoint, run the following command to see how the macro is expanded:
Reading symbols from /home/himanshu/practice/test...done.
(gdb) break 9
Breakpoint 1 at 0x8048459: file test.c, line 9.
(gdb) run
Starting program: /home/himanshu/practice/test
The cube of [4] is [10]
Breakpoint 1, main () at test.c:9
9 return 0;
(gdb)
(gdb) macro expand CUBE(3+1)That expansion is equivalent to:
expands to: 3+1*3+1*3+1
3 + ( 1 * 3 ) + ( 1 * 3 ) + 1
, or 10. Once you know the problem, you can easily correct it by redefining the macro as #define CUBE(x) (x)*(x)*(x)
.You can also use the
info macro
command to find out more about a macro. For example:(gdb) info macro CUBERead this document to learn more on how to debug macros using GDB.
Defined at /home/himanshu/practice/test.c:3
#define CUBE(x) x*x*x
Debugging signal handlers
Debugging signal handlers is not as easy as debugging normal functions with GDB. For example, consider the following program:#includeThe program defines a signal handler for
#include
#include
void sighandler(int signum)
{
printf("\n Caught SIGNIT - : %d", signum);
}
int main()
{
signal(SIGINT, (void*)sighandler);
while (1)
{
printf("\n Waiting for user action...");
sleep(1);
}
}
SIGINT
, which is usually generated when someone presses Ctrl-C. Load the program using GDB, put a breakpoint at the sighandler()
function, as you would do for any other normal function that you want to debug, then run the program:$ gdb testNow generate the
Reading symbols from /home/himanshu/practice/test...done.
(gdb) break sighandler
Breakpoint 1 at 0x8048483: file test.c, line 7.
(gdb) run
Starting program: /home/himanshu/practice/test
Waiting for user action...
Waiting for user action...
Waiting for user action...
Waiting for user action...
SIGINT
signal by pressing Ctrl-C, and you'll see the problem:Waiting for user action...Instead of the breakpoint being hit, the signal is intercepted by the debugger.
Waiting for user action...
^C
Program received signal SIGINT, Interrupt.
0xb7fdd424 in __kernel_vsyscall ()
(gdb)
To alter this behavior, use the
handle
command. It expects a list of signals, along with the actions to be applied on them. In this case, the actions nostop
and pass
are of interest. The former makes sure that GDB doesn't stop the program when the signal happens, while the latter makes sure that GDB allows the program to see this signal.Run the
handle
command after setting the breakpoint:$ gdb testMake sure you answer in the affirmative the question asked by the debugger. Now when you run the program and press Ctrl-C to generate and send SIGINT, the breakpoint is hit:
Reading symbols from /home/himanshu/practice/test...done.
(gdb) break sighandler
Breakpoint 1 at 0x8048483: file test.c, line 7.
(gdb) handle SIGINT nostop pass
SIGINT is used by the debugger.
Are you sure you want to change it? (y or n) y
Signal Stop Print Pass to program Description
SIGINT No Yes Yes Interrupt
(gdb)
(gdb) run
Starting program: /home/himanshu/practice/test
Waiting for user action...
Waiting for user action...
^C
Program received signal SIGINT, Interrupt.
Breakpoint 1, sighandler (signum=2) at test.c:7
7 printf("\n Caught SIGNIT - : %d", signum);
(gdb)