MYF

[RA] Ch37.2 Debugger: gdb

Reading Assignment: All of Programming Ch37.2 Debugger: gdb

Useful Reference:

Debugging is the process of identifying precisely what is wrong with your code and fixing it. The debugger helps you gather information about what is going on in your code.

Getting Started

The first step in using gdb is to compile the code with debugging symbols–extra information to help a debugging tool understand what the layout of the code and data in memory is–included in the binary. The -g option to gcc requests that it include this debugging information, but if you are using gdb in particular, you should use -ggdb3, which requests the maximum amount of debug information.

To run gdb inside emacs, use the command M-x gdb. At this point, emacs should prompt you for how you want to run gdb and provide a proposed command line.

The first commands are listed below:

  • start: Begin the program’s execution.
  • run: This command runs the program.
  • step: Advance the program one “step”, in much the same way that we would advance the execution arrow when executing code by hand. It can be abbreviated as s.
  • next: Advance the program one line of code. Unlike step, if current line of code is a function call, gdb will execute the entire called function without stopping. It can be abbreviated as n.
  • print: It takes an expression as an argument, evaluates that expression and prints the results. If you do print x = 3, it will set x to 3, then print 3. You can put /x after print to get the result printed in hex format. It can be abbreviated as p or p/x to print in hex. Every time you print the value of an expression, gdb will remember the value in its own internal variables which are named $1, $2.... You can use these $ variables in other expressions if you want to make use of these values later. gdb also has a feature to let you print multiple elements from an array–if you put @number after an lvalue, gdb will print number values starting at the location you named. For example, if a is an array, you can do p a[0]@5 to print the first 5 elements of a;
  • display: This command takes an expression as an argument, and displays its value every time gdb stops and displays prompt. For example, display i will evaluate and print i before each (gdb) prompt. You can abbreviate this command as disp.

If you hit enter without entering any command, gdb will repeat the last command you entered. This feature is most useful when you want to use step or next multiple times in a row.

video 37.2.1 illustrates the use of the basic gdb commands that we just discussed.

Investigating the State of Your Program

One useful features is the ability to inspect the current set of stack frames, and move up and down within them. The backtrace command lists all of the stack frames.

Sometimes, you might want to inspect variables in other frames further up the stack, You can instruct gdb to select different frame with up and down, which move up and down the stack specifically. One particularly common use of up is when your program stops in a failed assert. At this time, gdb will go deep inside the C library, in the code that handles assert. However, you will want to get back to your own code, which is a few stack frames up. You can use up a few times until gdb returns to a frame corresponding to your code.

You can also get information about various aspects of the program with the info command, which has various subcommands. For example, info frame will describe the memory layout of the current frame, info types will describe the types that are in the current program. There are a variety of info commands, you can use help info to see them all.

Controlling Execution

One of the most useful ways to control the execution of our program is to set a breakpoint on a particular line. A breakpoint instructes gdb to stop execution whenever the program reaches that particular line. you can set a breakpoint with the break line_number command. In emacs, you can also press C-x space to set a breakpoint at the point. You can even set a breakpoint in memory address.

Once we have set a breakpoint, we can run the program, it will execute until the breakpoint is encountered, and gdb will return control to us at a prompt, allowing for other commands.

We can set conditional breakpoints sometime. For example, if we want to make a breakpoint on line size for the condition i == 25000, we could tell gdb: (gdb) break 7 if i==25000. Alternatively, if the breakpoint already existed, for example, as breakpoint 1, we could write cond 1 i==25000. If we write a cond command with no expression, then it makes a breakpoint unconditional.

We can also enable or disable or delete breakpoints by its numeric id. You can use the info breakpoints command to see the status of current breakpoints.

Two other useful commands to control the execution of the program are until, which causes a loop to execute until it finished, and finish, which finishes the current function.

Watchpoints

Watchpoint can have gdb stop when the value of particular expression changes. For example, we can write watch i, which will cause gdb to stop whenever the value of i changes. When gdb stops in response to a watchpoint, it will print the old value of the expression and the new value.

It can be powerful when you have pointer-related problems.

Signals

Whenever your program receives a signal, gdb will stop the program and give you control. There are three particularly common signals.

  • SIGSEGV, indicating a segmentation fault. Running it in gdb can help you gather a lot of information about what is happening.
  • SIGABRT happens when your program calls abort() or fails an assert. If your code is failing asserts, then running it in gdb can be incredibly useful–you will get the control of the program at the point where assert causes the program to abort, and see exactly what was going on when the problem happened.
  • SIGINT happens when the program is interrupted. It is useful when your program is getting stuck in an infinite loop.

Notes

1
2
3
ptype # print type of a variable
set args flag1 flag2 input.txt # ./a.out flag1 flag2 input.txt
show args