On certain operating systems4, GDB is able to save a snapshot of a program’s state, called a checkpoint, and come back to it later.
Returning to a checkpoint effectively undoes everything that has
happened in the program since the checkpoint
was saved. This
includes changes in memory, registers, and even (within some limits)
system state. Effectively, it is like going back in time to the
moment when the checkpoint was saved.
Thus, if you’re stepping through a program and you think you’re getting close to the point where things go wrong, you can save a checkpoint. Then, if you accidentally go too far and miss the critical statement, instead of having to restart your program from the beginning, you can just go back to the checkpoint and start again from there.
This can be especially useful if it takes a lot of time or steps to reach the point where you think the bug occurs.
To use the checkpoint
/restart
method of debugging:
checkpoint
Save a snapshot of the debugged program’s current execution state.
The checkpoint
command takes no arguments, but each checkpoint
is assigned a small integer id, similar to a breakpoint id.
info checkpoints
List the checkpoints that have been saved in the current debugging session. For each checkpoint, the following information will be listed:
Checkpoint ID
Active state indicator
Process ID
Code Address
Source line, or label
Checkpoint IDs will be displayed as either a non-negative integer or
in the form i.n, where i is the inferior number, a
positive integer, as shown by the command info inferiors
, and
n, a non-negative integer, is the checkpoint number for that
inferior. The single non-negative integer form is used when
there is only one inferior. The i.n form is used when
there are multiple inferiors.
The active state indicator is a single letter, either ‘y’ or
‘n’, indicating yes or no. Only one checkpoint per inferior may
be active at once. The active checkpoint in the current inferior is
also shown by a ‘*’ at the start of the line. Checkpoints whose
active state is ‘n’ can be switched to using the restart
command or deleted using the delete checkpoint
command.
restart checkpoint-id
Restore the program state that was saved as checkpoint number
checkpoint-id. All program variables, registers, stack frames
etc. will be returned to the values that they had when the checkpoint
was saved. In essence, gdb will “wind back the clock” to the point
in time when the checkpoint was saved. The checkpoint number
checkpoint-id is specified in the same form as that output by the
info checkpoints
command.
Note that breakpoints, GDB variables, command history etc. are not affected by restoring a checkpoint. In general, a checkpoint only restores things that reside in the program being debugged, not in the debugger.
delete checkpoint checkpoint-id
Delete the previously-saved checkpoint identified by checkpoint-id.
Returning to a previously saved checkpoint will restore the user state of the program being debugged, plus a significant subset of the system (OS) state, including file pointers. It won’t “un-write” data from a file, but it will rewind the file pointer to the previous location, so that the previously written data can be overwritten. For files opened in read mode, the pointer will also be restored so that the previously read data can be read again.
Of course, characters that have been sent to a printer (or other external device) cannot be “snatched back”, and characters received from eg. a serial device can be removed from internal program buffers, but they cannot be “pushed back” into the serial pipeline, ready to be received again. Similarly, the actual contents of files that have been changed cannot be restored (at this time).
However, within those constraints, you actually can “rewind” your program to a previously saved point in time, and begin debugging it again — and you can change the course of events so as to debug a different execution path this time.
Finally, there is one bit of internal program state that will be different when you return to a checkpoint — the program’s process id. Each checkpoint will have a unique process id (or pid), and each will be different from the program’s original pid. If your program has saved a local copy of its process id, this could potentially pose a problem.
On some systems such as GNU/Linux, address space randomization is performed on new processes for security reasons. This makes it difficult or impossible to set a breakpoint, or watchpoint, on an absolute address if you have to restart the program, since the absolute location of a symbol will change from one execution to the next.
A checkpoint, however, is an identical copy of a process. Therefore if you create a checkpoint at (eg.) the start of main, and simply return to that checkpoint instead of restarting the process, you can avoid the effects of address randomization and your symbols will all stay in the same place.