Reverse Debugging using GDB
The GNU Debugger is a feature-rich open source debugging suite. It's console based and can easily be extended using integrated development environments like Eclipse or Netbeans or with dedicated graphical interfaces like ddd.
But also Emacs, THE all-round text-based editor for all appliances, provides a nice interface to communicate with GDB in an effective way (once you have pushed all the shortcuts in your head ;) ). This article goes beyond next, single-step and continue (GDB commands, don't worry if you are not familiar with them) and shows you the reverse-debugging technique, gives you some background and shows you how to use it.
Reverse debugging basics
What is reverse debugging
Reverse debugging is a technique where you cannot just move forward in your program, you can also move backward. Moving backward sounds simple, but GDB completely restores the state as it was when the code was executed in 'forward-debugging-mode'.
Is it helpful for me?
I think that this can be helpful for everyone using GDB.
Imagine a program that crashes sometime. GDB catches the e.g. segmentation fault for you and jumps to the source. You can now examine the cause of the program crash, maybe because some value is wrong but you can not say where the faulty value was written. This is where reverse debugging comes in. Using reverse debugging you can step through your program in reverse order and examine where the value was written.
How to use it
The GDB command set gets extended by seven commands and a single variable explained below:
- record: Start the reverse-debugging-record-recording. Needs to be called once for every debugging session to start recording the state of the program for every instruction.
- reverse-continue (short form: rc): Same as continue, but runs the program to the next event, e.g. breakpoint, end-(or should i say begin?)-of program or begin of reverse recording
- reverse-finish: Execute backward until the current stack frame is called
- reverse-next (short form: rn): Step program backward, and stop at the previous sourceline in the innermost stack frame
- reverse-nexti (short form: rni): Step backward for exactly one instruction, if the call is a subroutine call it breaks before the subroutine call and does not step into subroutine calls
- reverse-step (short form: rs): Step program backward until it reaches the beginning of a previous source line, this prevents multiple stops on a single line of code
- reverse-stepi: Step backward for exactly one instruction
- Var: exec_direction (forward/reverse): Seting this to reverse turns the normal step/next/continue/... commands to its reverse counterparts. eg. set exec-direction reverse
How does it work, not an Emacs tutorial
I will use Emacs to demonstrate how to use this feature, but this is not an Emacs Tutorial, i will show you the basics of the GDB mode for Emacs and how to use reverse debugging. If you are not familiar with the Emacs basics take a look at one of the following tutorials:
- And last but not least an Emacs cheat-sheet ;)
Reverse debugging is of course not enabled in each of your debugging sessions, because it generates some overhead. So if you want to use reverse-debugging you need to tell GDB. You can do this right after you start your program or anytime later. But keep in mind that you only can go back to the point where you started the reverse-debugging-record-recording.
Before we start make sure that your gdb supports reverse debugging. It is supported since version 7.0 so check using
if you are on a newer version. It should give you something like
GNU gdb (GDB) 7.2
Copyright (C) 2010 Free Software Foundation, Inc.
Next check if your emacs supports reverse-debugging, i think it's supported since version 23
GNU Emacs 23.1.1
Copyright (C) 2009 Free Software Foundation, Inc.
Once the requirements are met load our example file (attached):
and Emacs will appear and show you something like this:
If you have a splitted screen with multiple windows click on the screen with the source code and hit CTRL+x 1 in series.
Next we need to compile our application, we can do that right here in emacs, just hit META-x and type compile
META-x compile <ret>
Emacs will then recommend make -k, but you can only use that if you created a Makefile for the source so we will enter the compile command on our own
gcc -g -o gdb_reverse_debugging_test gdb_reverse_debugging_test.c <ret>
Emacs will show you the result of the compile process in a split view, if everything succeeded you can switch back to the single-window-view by clicking in the source-code window and hitting
C-x 1 </ret>
Next is to invoke the gdb mode of Emacs with the following command:
META-x gdb <ret>
check the suggested gdb-start-command and confirm by pressing
Now a gdb prompt will show up, but we want to see the source while debugging, so run the following command:
Now your Emacs window should look like the following:
Looks like your IDE isn't it? ;)
For this debugging session we will now set three breakpoints, one in each subroutine. Activate the gdb sub-window (top left) and enter the following
You will notice that in the lower right window labeled Breakpoints/Threads the three breakpoints appeared.
Another way of setting a breakpoint is to activate the source code sub-window, navigate to the source line of interest and hit
Once the breakpoints are set let gdb run the application by typing run in the gdb sub-window
Starting program: gdb_reverse_debugging_test
Breakpoint 1, main () at gdb_reverse_debugging_test.c:17
We already hit the breakpoint in the main subroutine, the first thing we want to do now is to activate the reverse-debugging-recording by typing record in the gdb sub-window
That's all, reverse-debugging is enabled now.
I would say we just continue twice now, till we reach the breakpoint in the bar sub-routine and do one more step then:
Breakpoint 3, bar () at gdb_reverse_debugging_test.c:5
The debugger now is at the return source-line of the bar sub-routine, and we just assigned '2' to the variable xyz
lets check if that is correct, type the following in your gdb sub-window:
(gdb) display xyz
1: xyz = 2
Not that suprisingly, now let's view the backtrace:
#0 bar () at gdb_reverse_debugging_test.c:6
#1 0x00000000004004f1 in foo () at gdb_reverse_debugging_test.c:12
#2 0x000000000040050b in main () at gdb_reverse_debugging_test.c:18
We will need this backtrace later
Now it's getting more exciting, let's go back for a single step:
On the current source line xyz get's assigned, you still remember that the value of xyz was '2'?
(gdb) display xyz
1: xyz = 1
We see hat the changes made by this source-line where undone.
Now go back even further, to the breakpoint in the foo subroutine and take a look a the backtrace:
#0 foo () at gdb_reverse_debugging_test.c:11
#1 0x000000000040050b in main () at gdb_reverse_debugging_test.c:18
Still remember the last backtrace?
Play around a bit, and discover the power of reverse-debugging, i think that is really great.
Problems with reverse debugging
- I think (correct me if im wrong) that it is not possible to effectively debug multi-threaded applications
- Another problem is that only a few architectures (i386-linux, amd64-linux) and some remote targets are supported. For me it is of special interest to support arm-targets.
- For other problems and future work visit the GDB reverse debugging page