GDB reverse debugging
Let's start with a simple C++ program in the file gdb-test.cpp:
#include <iostream> const unsigned int SIZE = 10; int main() { int b[SIZE]; // intitialize for(int i=0; i <= SIZE; i++) { b[i]=0; } std::cout << "done!" << std::endl; return 0; }
Let's comple and run it.
$ g++ -g gdb-test.cpp -o gdb-test $ ./gdb-test
??? It doesn't stop!
Let's load it into gdb.
$ gdb ./gdb-test GNU gdb (GDB) 7.1-ubuntu Copyright (C) 2010 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> [...] Reading symbols from /home/larsr/gdb-test...done. (gdb)
Now run it:
(gdb) r Starting program: /home/larsr/gdb-test
Still dosn't stop! Stop it with ctrl-c:
^C
Program received signal SIGINT, Interrupt.
0x08048704 in main () at gdb-test.cpp:11
11 for(int i=0; i <= SIZE; i++) {
(gdb)Let's look at the variable i.
(gdb) print i $1 = 4 (gdb)
Looks normal enough! But strange that it didn't get further than 4... Well, let's run some more, break, and look at i.
(gdb) continue
Continuing.
^C
Program received signal SIGINT, Interrupt.
0x080486fc in main () at gdb-test.cpp:11
11 for(int i=0; i <= SIZE; i++) {
(gdb) print i
$2 = 7
(gdb) c
Continuing.
^C
Program received signal SIGINT, Interrupt.
0x080486f8 in main () at gdb-test.cpp:11
11 for(int i=0; i <= SIZE; i++) {
(gdb) print i
$4 = 6
(gdb)What? First i was 7 and then it was 6! How did that happen? Lets start to record, and run some more!
(gdb) record
(gdb) c
Continuing.
^C
Program received signal SIGINT, Interrupt.
main () at gdb-test.cpp:11
11 for(int i=0; i <= SIZE; i++) {
(gdb) print i
$6 = 2
(gdb)Something has modified i to become smaller! When did this happen? Let's watch i and go backwards.
(gdb) reverse-continue
Continuing.
Hardware watchpoint 1: i
Old value = 2
New value = 1
main () at gdb-test.cpp:11
11 for(int i=0; i <= SIZE; i++) {This makes sense. We are walking backswards, and in that direction we had a value of 2 which turned into 1. Let's keep going backwards.
(gdb) reverse-continue
Continuing.
Hardware watchpoint 1: i
Old value = 1
New value = 0
main () at gdb-test.cpp:11
11 for(int i=0; i <= SIZE; i++) {
(gdb) reverse-continue
Continuing.
Hardware watchpoint 1: i
Old value = 0
New value = 10
0x080486eb in main () at gdb-test.cpp:12
12 b[i]=0;
(gdb)Here we see that as we went backwards, we went from 0 to 10. That means that when we ran forward, i went from 10 to 0, and it happened when we executed
b[i]=0;
Could it be that writing to b[10] overwrites the memory of i? Where are they located in memory?
(gdb) print &i $7 = (int *) 0xbffff7bc (gdb) print &b[i] $8 = (int *) 0xbffff7bc (gdb)
Yup, they are at the same address. Why? Doh! An array indexing error! b[10] is not within the array bounds! The last element is b[9].
Line 11 should use < instead of <= and read
for(int i=0; i < SIZE; i++) {Case closed.
An ugly observation is that programs like this bug out silently and "work" if you change the program to be initialized to 100 (or something bigger than SIZE).