Differences between revisions 2 and 3
Revision 2 as of 2009-10-17 21:33:09
Size: 4953
Editor: AS-40816
Comment: First chapter, "quick start", of process record tutorial
Revision 3 as of 2009-10-17 21:55:48
Size: 11888
Editor: AS-40816
Comment:
Deletions are marked like this. Additions are marked like this.
Line 148: Line 148:

== Continue, reverse-continue, and breakpoints ==

If your gdb session from the "quick start" is still running, please quit and start again. We will use the same program to debug. This time, let's start debugging from the very first instruction (at the symbol "_start").

{{{
(gdb) break _start
(gdb) run
(gdb) record
}}}

This time, let's set our breakpoints before continuing, and then run up to the last breakpoint at "bar".

{{{
(gdb) break main
(gdb) break foo
(gdb) break bar
(gdb) continue
(gdb) continue
Continuing.

Breakpoint 2, main ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:34
34 xyz = 0; /* break in main */
(gdb) continue
Continuing.

Breakpoint 3, foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:28
28 xyz = 1; /* break in foo */
(gdb) continue
Continuing.

Breakpoint 4, bar ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:22
22 xyz = 2; /* break in bar */
(gdb) backtrace
#0 bar ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:22
#1 0x08048366 in foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:29
#2 0x08048393 in main ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:35
}}}

We're now actually debugging the live program, just as we usually do, except that the program's execution is being recorded by process record.

Let's imagine that we get to this point, and then realize that we really were interested in what happens at the beginning of foo. Normally, we would have to start the whole debugging session over from the beginning -- but with process record and reverse debugging, we can simply "reverse-continue" the program back to the breakpoint at foo.

{{{
(gdb) reverse-continue
Continuing.

Breakpoint 3, foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:28
28 xyz = 1; /* break in foo */
(gdb)
}}}

Another "reverse-continue" would take us back to the next earlier breakpoint, at main. But rather than actually do that, let's imagine that we've finished debugging "foo" and are now ready to resume where we left off.

We can go directly to the point at which the recording currently ends, by disabling all our breakpoints and then telling gdb to continue. Process record will replay until it reaches the current end of the recording, and then stop.

{{{
(gdb) disable breakpoints
(gdb) continue
Continuing.

No more reverse-execution history.
bar ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:22
22 xyz = 2; /* break in bar */
(gdb)
}}}

Now we are back where we were, at the beginning of "bar". We can simply continue debugging, and process record will append the new execution log to the end of the old one.

{{{
(gdb) finish
Run till exit from #0 bar ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:22
foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:30
30 }
Value returned is $1 = 1
(gdb) finish
Run till exit from #0 foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:30
main ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:36
36 return (xyz == 2 ? 0 : 1);
Value returned is $2 = 1
(gdb) finish
"finish" not meaningful in the outermost frame.
(gdb) step
37 } /* end of main */
(gdb) step
0x00531de3 in __libc_start_main () from /lib/tls/libc.so.6
(gdb)
}}}

And now, just to show that our execution log is still there and is complete:

{{{
(gdb) reverse-continue
Continuing.

Breakpoint 4, bar ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:22
22 xyz = 2; /* break in bar */
(gdb)
Continuing.

Breakpoint 3, foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:28
28 xyz = 1; /* break in foo */
(gdb)
Continuing.

Breakpoint 2, main ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:34
34 xyz = 0; /* break in main */
(gdb)
Continuing.

No more reverse-execution history.
0x08048298 in _start ()
(gdb)
}}}

== Step and reverse-step ==

Let's quit gdb and start a new session:

{{{
(gdb) break _start
(gdb) run
(gdb) record
(gdb) break main
(gdb) continue

Breakpoint 2, main ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:34
34 xyz = 0; /* break in main */
(gdb)
}}}

Now let's do some normal single stepping.

{{{
(gdb) step
foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:28
28 xyz = 1; /* break in foo */
(gdb) step
29 return bar ();
(gdb) step
bar ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:22
22 xyz = 2; /* break in bar */
(gdb) step
23 return 1;
(gdb) step
24 }
(gdb) step
foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:30
30 }
(gdb) step
main ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:36
36 return (xyz == 2 ? 0 : 1);
(gdb) step
37 } /* end of main */
}}}

And now let's just do that in reverse.

{{{
37 } /* end of main */
(gdb) reverse-step
36 return (xyz == 2 ? 0 : 1);
(gdb) reverse-step
foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:30
30 }
(gdb) reverse-step
bar ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:24
24 }
(gdb) reverse-step
23 return 1;
(gdb) reverse-step
22 xyz = 2; /* break in bar */
(gdb) reverse-step
foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:29
29 return bar ();
(gdb) reverse-step
28 xyz = 1; /* break in foo */
(gdb) reverse-step
main ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:35
35 foo ();
(gdb) reverse-step

Breakpoint 2, main ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:34
34 xyz = 0; /* break in main */
}}}

Just as we saw when using breakpoints, we see the exact same events going backward as going
forward, except that the events occur in reverse order.

Process Record Tutorial

This is a short tutorial on how to use gdb's Process Record feature.

Process Record lets you:

  • Record a program's execution and play it back.
  • Debug a program in reverse (see Reverse Debugging).

Quick Start

To get started using process record, all you need is gdb version 7.0 or later, and an x86 computer running Linux. And, of course, a program to debug. Here's a simple one:

int xyz;

int bar ()
{
  xyz = 2; /* break in bar */
  return 1;
}

int foo ()
{
  xyz = 1; /* break in foo */
  return bar ();
}

int main ()
{
  xyz = 0;      /* break in main */
  foo ();
  return (xyz == 2 ? 0 : 1);
}               /* end of main */

Compile this program (with -g of course), and load it into gdb, then do the following.

(gdb) break main
(gdb) run
(gdb) record

This will turn on process recording, which will now record all subsequent instructions executed by the program being debugged.

Note that you can start process recording at any point (not just at main). You may choose to start it later, or even earlier. The only restriction is that your program has to be running (so you have to type "run" before "record"). If you want to start recording from the very first instruction of your program, you can do it like this:

(gdb) break _start
(gdb) run
(gdb) record

run to the end of the program

Now let's make a common mistake -- say "continue" without having set any breakpoints. Your program will run, but process record will detect when it exits, and ask you whether you want to stop it before exiting. Say yes.

(gdb) continue
Continuing.
The next instruction is syscall exit_group.  It will make the program exit.  Do you want to stop the program?([y] or n) yes
Process record: inferior program stopped.

Program received signal SIGTRAP, Trace/breakpoint trap.
0x005037a0 in _dl_sysinfo_int80 () from /lib/ld-linux.so.2
(gdb)

reverse-continue

You are now at the very last instruction before your program will exit. However, the execution of your program has been recorded, and you can now return to the beginning and replay it.

To return to the beginning, just use the new "reverse-continue" command. This will execute your program in reverse, and since there are still no breakpoints, it will run all the way back to the start of the recording, and then stop.

(gdb) reverse-continue
Continuing.

No more reverse-execution history.
main ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:34
34        xyz = 0;      /* break in main */
(gdb)

forward replay

This time, let's set some breakpoints.

(gdb) break foo
(gdb) break bar
(gdb) continue
Continuing.

Breakpoint 2, foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:28
28        xyz = 1; /* break in foo */
(gdb) continue
Continuing.

Breakpoint 3, bar ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:22
22        xyz = 2; /* break in bar */
(gdb) backtrace
#0  bar ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:22
#1  0x08048366 in foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:29
#2  0x08048393 in main ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:35
(gdb)

Looks just like an ordinary debugging session -- but you are now replaying the execution log that was made earlier when your program ran without any breakpoints.

backward replay

To finish out this quick start, let's play to the end and then run backwards.

(gdb) continue
Continuing.

No more reverse-execution history.
0x005037a0 in _dl_sysinfo_int80 () from /lib/ld-linux.so.2
(gdb) reverse-continue
Continuing.

Breakpoint 3, bar ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:22
22        xyz = 2; /* break in bar */
(gdb) reverse-continue
Continuing.

Breakpoint 2, foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:28
28        xyz = 1; /* break in foo */
(gdb) reverse-continue
Continuing.

No more reverse-execution history.
main ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:34
34        xyz = 0;      /* break in main */
(gdb)

You'll notice that in replaying the program backwards, we hit the same breakpoints, but in the opposite order. That's the key observation for this quick start -- that you can replay the recorded execution log forward and backward, and that when replaying backward, the same events occur but in the opposite order.

Continue, reverse-continue, and breakpoints

If your gdb session from the "quick start" is still running, please quit and start again. We will use the same program to debug. This time, let's start debugging from the very first instruction (at the symbol "_start").

(gdb) break _start
(gdb) run
(gdb) record

This time, let's set our breakpoints before continuing, and then run up to the last breakpoint at "bar".

(gdb) break main
(gdb) break foo
(gdb) break bar
(gdb) continue
(gdb) continue
Continuing.

Breakpoint 2, main ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:34
34        xyz = 0;      /* break in main */
(gdb) continue
Continuing.

Breakpoint 3, foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:28
28        xyz = 1; /* break in foo */
(gdb) continue
Continuing.

Breakpoint 4, bar ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:22
22        xyz = 2; /* break in bar */
(gdb) backtrace
#0  bar ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:22
#1  0x08048366 in foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:29
#2  0x08048393 in main ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:35

We're now actually debugging the live program, just as we usually do, except that the program's execution is being recorded by process record.

Let's imagine that we get to this point, and then realize that we really were interested in what happens at the beginning of foo. Normally, we would have to start the whole debugging session over from the beginning -- but with process record and reverse debugging, we can simply "reverse-continue" the program back to the breakpoint at foo.

(gdb) reverse-continue
Continuing.

Breakpoint 3, foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:28
28        xyz = 1; /* break in foo */
(gdb) 

Another "reverse-continue" would take us back to the next earlier breakpoint, at main. But rather than actually do that, let's imagine that we've finished debugging "foo" and are now ready to resume where we left off.

We can go directly to the point at which the recording currently ends, by disabling all our breakpoints and then telling gdb to continue. Process record will replay until it reaches the current end of the recording, and then stop.

(gdb) disable breakpoints
(gdb) continue
Continuing.

No more reverse-execution history.
bar ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:22
22        xyz = 2; /* break in bar */
(gdb)

Now we are back where we were, at the beginning of "bar". We can simply continue debugging, and process record will append the new execution log to the end of the old one.

(gdb) finish
Run till exit from #0  bar ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:22
foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:30
30      }
Value returned is $1 = 1
(gdb) finish
Run till exit from #0  foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:30
main ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:36
36        return (xyz == 2 ? 0 : 1);
Value returned is $2 = 1
(gdb) finish
"finish" not meaningful in the outermost frame.
(gdb) step
37      }               /* end of main */
(gdb) step
0x00531de3 in __libc_start_main () from /lib/tls/libc.so.6
(gdb)

And now, just to show that our execution log is still there and is complete:

(gdb) reverse-continue
Continuing.

Breakpoint 4, bar ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:22
22        xyz = 2; /* break in bar */
(gdb)
Continuing.

Breakpoint 3, foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:28
28        xyz = 1; /* break in foo */
(gdb)
Continuing.

Breakpoint 2, main ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:34
34        xyz = 0;      /* break in main */
(gdb)
Continuing.

No more reverse-execution history.
0x08048298 in _start ()
(gdb)

Step and reverse-step

Let's quit gdb and start a new session:

(gdb) break _start
(gdb) run
(gdb) record
(gdb) break main
(gdb) continue

Breakpoint 2, main ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:34
34        xyz = 0;      /* break in main */
(gdb)

Now let's do some normal single stepping.

(gdb) step
foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:28
28        xyz = 1; /* break in foo */
(gdb) step
29        return bar ();
(gdb) step
bar ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:22
22        xyz = 2; /* break in bar */
(gdb) step
23        return 1;
(gdb) step
24      }
(gdb) step
foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:30
30      }
(gdb) step
main ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:36
36        return (xyz == 2 ? 0 : 1);
(gdb) step
37      }               /* end of main */

And now let's just do that in reverse.

37      }               /* end of main */
(gdb) reverse-step
36        return (xyz == 2 ? 0 : 1);
(gdb) reverse-step
foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:30
30      }
(gdb) reverse-step
bar ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:24
24      }
(gdb) reverse-step
23        return 1;
(gdb) reverse-step
22        xyz = 2; /* break in bar */
(gdb) reverse-step
foo ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:29
29        return bar ();
(gdb) reverse-step
28        xyz = 1; /* break in foo */
(gdb) reverse-step
main ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:35
35        foo ();
(gdb) reverse-step

Breakpoint 2, main ()
    at /data/home/msnyder/cvs/localhost/dumpload/gdb/testsuite/gdb.reverse/break-reverse.c:34
34        xyz = 0;      /* break in main */

Just as we saw when using breakpoints, we see the exact same events going backward as going forward, except that the events occur in reverse order.

None: ProcessRecord/Tutorial (last edited 2009-10-17 23:01:13 by AS-40816)

All content (C) 2008 Free Software Foundation. For terms of use, redistribution, and modification, please see the WikiLicense page.