Today I had the opportunity to use an amazing feature in GDB -- "reverse debugging". Actually, 'amazing' is probably an understatement.
Think of the following scenarios in a GDB debugging session (and forgive the dramatic prose):
You're battling against an elusive little bug, the kind that appears only some times when you're running your code, crashes it, and goes its merry way, only to strike again a few compile-run cycles later. So how do you get rid of that bug? The usual answer: you pain-stakingly go through session upon GDB session, hoping to catch that bug in action and find out what's causing it. Isn't there supposed to be an easier way?
You're in a middle of a particularly time-consuming debugging session, having set up the perfect combination of breakpoints and watchpoints after hunting around to find the approximate location of the bug's origin. So you
runthe code, and then carefully start issuing
nextcommands, navigating the code, examining variables along the way. Then you realize, somehow, that you've let the code run just a little too far and missed your chance. What now, you start over?
Back to the Future
Reverse debugging in GDB could be a neat solution to the problems above. By allowing you to re-simulate a particular instance of execution of your program any number of times, it gives you the ability to catch bugs easily and test them to your heart's content. Here's how you can handle problems #1 and #2 described above using this technique:
Instead of carefully setting up and plodding through multiple GDB sessions in the hope that the bug will appear, you can simply set a few important breakpoints in the suspicious area of code, and keep issuing
continue, and if Mr. Bug doesn't show up,
runit again. Keep doing this until the bug rears its ugly head, at which point you issue a
reverse-continue, which takes you back to the last breakpoint hit. This reversal restores everything to the state it was in before you'd issued your last
continue-- but now you know the bug is going to appear shortly, so you set more breakpoints before the next one and narrow down your problem to a smaller area. Things don't look so good for the cornered prick.
The answer to this should be pretty obvious now. Whenever you realize you've come too far in the code, you can go back using
reverse-continue(or some other reverse command). Just make sure you set "recording" on (using
record) at the point till which you want to able to reverse execution, preferrably right at the start of the program (it's pretty easy, take a look).