GDB: Automatic 'Next'ing?

Here's something's that's such a hack I'm a bit embarrassed to post it. But if you need just a one-off, it might do well enough to let you get the information you want. There really should be a better way.

You can define a stupid little gdb script that executes the step or next command a certain number of times:

# file: step_mult.gdb

define step_mult
    set $step_mult_max = 1000
    if $argc >= 1
        set $step_mult_max = $arg0
    end

    set $step_mult_count = 0
    while ($step_mult_count < $step_mult_max)
        set $step_mult_count = $step_mult_count + 1
        printf "step #%d\n", $step_mult_count
        step
    end
end

(I used step instead of next for no particularly good reason; just change it to whatever you need.)

Then you can run that command (with an optional count), and it'll display each step or next nicely.

Here's a sample program that'll crash when it tries to dereference a NULL pointer:

#include<stdio.h>

int x[] = {
    0, 1, 2, 3, 4, 5, 6, 7, 8,9, 10 
};

int* p[11];

int main()
{
    int i;

    for (i = 0; i < 11; ++i) {
        p[i] = &x[i];
    }

    p[5] = 0;

    for (i = 0; i < 11; ++i) {
        printf( "*p[%d] == %d\n", i, *p[i]);
    }

    return 0;
}

Here's a gdb session (on Windows) debugging that program and using the step_mult script:

C:\temp>gdb test.exe
GNU gdb (GDB) 7.2
...
Reading symbols from C:\temp/test.exe...done.

(gdb) source c:/temp/step_mult.gdb
(gdb) start

Temporary breakpoint 1 at 0x401385: file C:\temp\test.c, line 23.
Starting program: C:\temp/test.exe
[New Thread 5396.0x1638]

Temporary breakpoint 1, main () at C:\temp\test.c:23
23          for (i = 0; i < 11; ++i) {

(gdb) step_mult 70

step #1
24              p[i] = &x[i];
step #2
23          for (i = 0; i < 11; ++i) {
step #3
24              p[i] = &x[i];
step #4
23          for (i = 0; i < 11; ++i) {
step #5
24              p[i] = &x[i];
step #6
23          for (i = 0; i < 11; ++i) {
step #7
24              p[i] = &x[i];
step #8
23          for (i = 0; i < 11; ++i) {
step #9
24              p[i] = &x[i];
step #10
23          for (i = 0; i < 11; ++i) {
step #11
24              p[i] = &x[i];
step #12
23          for (i = 0; i < 11; ++i) {
step #13
24              p[i] = &x[i];
step #14
23          for (i = 0; i < 11; ++i) {
step #15
24              p[i] = &x[i];
step #16
23          for (i = 0; i < 11; ++i) {
step #17
24              p[i] = &x[i];
step #18
23          for (i = 0; i < 11; ++i) {
step #19
24              p[i] = &x[i];
step #20
23          for (i = 0; i < 11; ++i) {
step #21
24              p[i] = &x[i];
step #22
23          for (i = 0; i < 11; ++i) {
step #23
27          p[5] = 0;
step #24
29          for (i = 0; i < 11; ++i) {
step #25
30              printf( "*p[%d] == %d\n", i, *p[i]);
step #26
*p[0] == 0
29          for (i = 0; i < 11; ++i) {
step #27
30              printf( "*p[%d] == %d\n", i, *p[i]);
step #28
*p[1] == 1
29          for (i = 0; i < 11; ++i) {
step #29
30              printf( "*p[%d] == %d\n", i, *p[i]);
step #30
*p[2] == 2
29          for (i = 0; i < 11; ++i) {
step #31
30              printf( "*p[%d] == %d\n", i, *p[i]);
step #32
*p[3] == 3
29          for (i = 0; i < 11; ++i) {
step #33
30              printf( "*p[%d] == %d\n", i, *p[i]);
step #34
*p[4] == 4
29          for (i = 0; i < 11; ++i) {
step #35
30              printf( "*p[%d] == %d\n", i, *p[i]);
step #36

Program received signal SIGSEGV, Segmentation fault.
0x004013d2 in main () at C:\temp\test.c:30
30              printf( "*p[%d] == %d\n", i, *p[i]);
step #37

Program received signal SIGSEGV, Segmentation fault.
0x004013d2 in main () at C:\temp\test.c:30
30              printf( "*p[%d] == %d\n", i, *p[i]);
step #38

Program exited with code 030000000005.
step #39
The program is not being run.
(gdb)

Unfortunately, since the script doesn't stop when the segfault happens, gdb decides to simply stop debugging the program, so you can't make any further useful inquiries. But the log might still be useful.

I'm sure there are numerous ways one might make the script more intelligent. Unfortunately, I have no idea how to do that, and the user level docs for GDB don't seem too helpful for those details. The best way would be if the script could detect the segfault or signal had happened and just stop then instead of relying on some arbitrary count. I imagine the gdb/MI interface, or probably even the Python scripting interface might have a good mechanism, but I don't know anything about those.

After your first run you could use the count displayed (37 in my example) and restart the program and give a count that's just shy of where it crashed before and take control manually.

Like I said, it's not particularly pretty - but it might get you there.

Tags:

C

Debugging

Gdb