Search code examples
cdebugginggdb

GDB: Automatic 'Next'ing?


Quick one this time.

Is it possible (other than pressing enter forever) for gdb to continually next through a program line by line to find where a bug is happening?

Edit: continue isn't what I would like; I would like to see effectively the complete program execution, line by line, as you would get from nexting over and over.


Solution

  • 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.