GDB调试技巧

工具程序gdb是GNU调试器。为了能调试程序,编译时必须指定GCC的-g选项将源码与可执行代码联系起来。

将程序载入调试器

fibonacci.c

int current;
int next;
int nextnext;

void setstart(void) {
    current = 0;
    next = 1;
}

void calcnext(void) {
    nextnext = current + next;
    current = next;
    next = nextnext;
}

int main(int argc, char *argv[]) {
    int i;
    setstart();
    for (i = 0; i < 20; i++) {
        printf("%2d: %d\n", i+1, current);
        calcnext();
    }
    return(0);
}
$ gcc -g fibonacci.c -o fibonacci \\ -g选项使得编译后的程序包含调试信息

breakpoints(断点)

当进程执行时,会中断在断点对应的行上。可根据行号、程序名、内存地址设置断点。

(gdb) break 20             // 设置断点(在行号为20处)
            Breakpoint 17 at 0x8048479: file fibonacci.c, line 20.
(gdb) break main           // 设置断点(在main函数开始处)
            Breakpoint 18 at 0x804846a: file fibonacci.c, line 18.
(gdb) info address main    // 查看地址(在main函数开始处)
            Symbol "main" is a function at address 0x8048461.
(gdb) break *0x8048461     // 设置断点(在0x8048461地址处)
            Breakpoint 19 at 0x8048461: file fibonacci.c, line 16.
(gdb) info breakpoints     // 查看断点
            Num     Type            Disp Enb Address    What
            17      breakpoint      keep y   0x08048479 in main at fibonacci.c:20
            18      breakpoint      keep y   0x0804846a in main at fibonacci.c:18
            19      breakpoint      keep y   0x08048461 in main at fibonacci.c:16
(gdb) ignore 5 3           // 忽略断点(忽略Num=5的断点3次触发)  
            Will ignore next 3 crossings of breakpoint 5.
(gdb) delete 5             // 删除断点(断点列表中Num=5)
(gdb) clear main           // 清除断点(main函数开始处)
(gdb) clear 20             // 清除断点(在行号为20处)    
(gdb) enable 5             // 激活断点(断点列表中Num=5)    
(gdb) disable 5            // 禁止断点(断点列表中Num=5) 

watchpoints(观察点)

当进程执行时,某个变量或内存地址被读或写了,就中断进程。

(gdb) info address current // 查看地址(current变量)
            Symbol "current" is static storage at address 0x804a02c.
(gdb) watch *0x804a02c     // 设置观察点(当写0x804a02c地址时)
            Hardware watchpoint 14: *0x804a02c
(gdb) rwatch current       // 设置观察点(当读到current变量时)
            Hardware read watchpoint 15: current
(gdb) awatch current       // 设置观察点(当读或写到current变量时)
            Hardware access (read/write) watchpoint 16: current
(gdb) info watchpoints     // 查看观察点
            Num     Type            Disp Enb Address    What
            14      hw watchpoint   keep y              *0x804a02c
            15      read watchpoint keep y              current
            16      acc watchpoint  keep y              current
(gdb) delete 2             // 删除观察点(观察点列表中Num=2)
(gdb) enable 2             // 激活观察点(观察点列表中Num=2)    
(gdb) disable 2            // 禁止观察点(观察点列表中Num=2) 

checkpoints(快照)

当进程执行到某一时刻中断后,调用checkpoint命令创建(fork)此进程的子进程,此时子进程相当于是父进程此时刻的快照。

(gdb) checkpoint           // 创建快照(调用fork创建子进程)
            checkpoint: fork returned pid 8684.
(gdb) info checkpoints     // 查看快照(checkpoint和PID一一对应)
            1 process 8684 at 0x8048479, file fibonacci.c, line 20
         *' 0 process 8680 (main process) at 0x8048479, file fibonacci.c, line 20
(gdb) restart 1            // 载入快照(将快照列表中id=1的进程载入调试器)

catchpoints(捕捉事件)

设置catchpoint用于捕捉到事件后中断进程。catch命令设置事件,tcatch命令设置临时事件(事件执行一次后对应catchpoint自动删除)。

可捕获的事件类型如下
catch/tcatch assert -- Catch failed Ada assertions
catch/tcatch catch -- Catch an exception
catch/tcatch exception -- Catch Ada exceptions
catch/tcatch exec -- Catch calls to exec
catch/tcatch fork -- Catch calls to fork
catch/tcatch load -- Catch loads of shared libraries
catch/tcatch rethrow -- Catch an exception
catch/tcatch signal -- Catch signals by their names and/or numbers
catch/tcatch syscall -- Catch system calls by their names and/or numbers
catch/tcatch throw -- Catch an exception
catch/tcatch unload -- Catch unloads of shared libraries
catch/tcatch vfork -- Catch calls to vfork

(gdb) catch load           // 设置捕获载入库事件
(gdb) info breakpoints     // 查看catchpoints 
            Num     Type           Disp Enb Address    What
            7       catchpoint     keep y              load of library
(gdb) run                  // 从开始位置运行程序
            Starting program: /home/fibonacci
            Catchpoint 7
              Inferior loaded /lib/i386-linux-gnu/libc.so.6
            __GI__dl_debug_state () at dl-debug.c:74
(gdb) info breakpoints     // 查看catchpoints
            Num     Type           Disp Enb Address    What
            7       catchpoint     keep y              load of library
                    catchpoint already hit 1 time

display(监视)

每次程序中断时,显示指定变量的值。

(gdb) display current      // 设置display(全局)
(gdb) display i            // 设置display(临时)
(gdb) info display         // 查看display
            Auto-display expressions now in effect:
            Num Enb Expression
            2:   y  i
            1:   y  current
(gdb) next                 // 继续运行1行源码(不跟进函数内)
            main (argc=1, argv=0xbffff694) at fibonacci.c:19
            19              for (i = 0; i < 20; i++) {
            2: i = 11
            1: current = 144
(gdb) undisplay 1          // 取消display(display列表中Num=1)
(gdb) info display         // 查看display
            Auto-display expressions now in effect:
            Num Enb Expression
            2:   y  i

调试示例

$ gdb 
(gdb) file fibonacci       // 载入fibonacci可执行文件
            Reading symbols from fibonacci...done.
(gdb) break main           // 设置断点(在main函数开始处)
            Breakpoint 1 at 0x804846a: file fibonacci.c, line 18.
(gdb) run                  // 启动进程(从开始位置运行)
            Starting program: /home/ssyang/00_test/old/fibonacci
            Breakpoint 1, main (argc=1, argv=0xbffff694) at fibonacci.c:18
            18              setstart();
(gdb) backtrace            // 打印栈调用(包含当前位置信息)
            #0  main (argc=1, argv=0xbffff694) at fibonacci.c:18        
(gdb) list                 // 列出源码(当前行附近10行)
            13              next = nextnext;
            14      }
            15
            16      int main(int argc, char *argv[]) {
            17              int i;
            18              setstart();
            19              for (i = 0; i < 20; i++) {
            20                      printf("%2d: %d\n", i+1, current);
            21                      calcnext();
            22              }
(gdb) next                 // 继续运行1行源码(不跟进函数内)
            19              for (i = 0; i < 20; i++) {
(gdb) next 2 
             1: 0  (current全局变量——[写次数]: [上次值])
            21                      calcnext();
(gdb) step                 // 继续运行1行源码(跟进函数内)
            calcnext () at fibonacci.c:11
            11              nextnext = current + next;
(gdb) backtrace 
            #0  calcnext () at fibonacci.c:11
            #1  0x0804849e in main (argc=1, argv=0xbffff694) at fibonacci.c:21
(gdb) watch current        // 设置观察点
            Hardware watchpoint 2: current
(gdb) continue             // 继续运行直到被中断
            Continuing.
            Hardware watchpoint 2: current
            Old value = 0
            New value = 1
            calcnext () at fibonacci.c:13
            13              next = nextnext;
(gdb) next 5  
             2: 1  (current全局变量——[写次数]: [上次值])
            19              for (i = 0; i < 20; i++) {
(gdb) Quit(Ctrl+C)         // 按下Ctrl+C中断当前进程
(gdb) finish               // 继续运行直到当前函数返回
            Run till exit from #0  calcnext () at fibonacci.c:13
            main (argc=1, argv=0xbffff694) at fibonacci.c:19
            19              for (i = 0; i < 20; i++) {      
(gdb) print i              // 显示i变量的值
            $3 = 2
(gdb) ptype/whatis i       // 显示i变量的类型
            type = int
......
(gdb) step
            calcnext () at fibonacci.c:11
            11              nextnext = current + next;
(gdb) return               // 强制从当前函数立即返回
            Make calcnext return now? (y or n) y
            #0  main (argc=1, argv=0xbffff694) at fibonacci.c:19
            19              for (i = 0; i < 20; i++) {
(gdb) set current=100      // 设置current变量值为100
(gdb) p current            // 显示current变量的值
            $9 = 100
(gdb) quit                 // 退出GDB
$      

执行事后检查

在UNIX系统中,程序崩溃时的内容可以完全复制到文件core中。ulimit命令用于shell启动进程所占用的资源。

ulimit -a : 查看当前shell所有限制
ulimit -c 0 : 阻止当前shell生成core文件
ulimit -c 1024 : 限制当前shell生成core文件的最大大小为1024KB

falldown.c

char **nowhere;

void setbad(void) {
    nowhere = 0;
    *nowhere = "This is a string\n";
}

int main(int argc, char *argv[]) {
    setbad();
    printf("%s\n", *nowhere);
}
$ gcc -g falldown.c -o falldown
$ ulimit -c 1024   
$ ./falldown
            Segmentation fault (core dumped)
$ ll
            -rw-------  1 test test   212992  3月  2 15:11 core
            -rwxrwxr-x  1 test test     8290  3月  2 14:49 falldown*
            -rw-rw-r--  1 test test      163  3月  2 14:49 falldown.c
$ gdb falldown core
            Reading symbols from falldown...done.
            [New LWP 14136]
            Core was generated by `./falldown'.
            Program terminated with signal SIGSEGV, Segmentation fault.
            #0  0x0804842f in setbad () at falldown.c:5
            5               *nowhere = "This is a string\n";
            (gdb) print nowhere
            $2 = (char **) 0x0

将调试器附带到运行程序上

looper.c

void goaround(int counter) {
    int i = 0;
    while (i < counter) {
        if (i++ == 17)
            i = 10;
    }
}

int main(int argc, char *argv[]) {
    printf("started\n");
    goaround(20);
    printf("done\n");
    return 0;
}
$ gcc -g looper.c -o looper
$ ./looper &
            [1] 14308
$ ps -a
              PID TTY          TIME CMD
            14308 pts/6    00:00:09 looper
            14309 pts/6    00:00:00 ps
$ sudo gdb looper 14308
            Reading symbols from looper...done.
            Attaching to program: /home/ssyang/00_test/looper, process 14308
            Reading symbols from /lib/i386-linux-gnu/libc.so.6...
            Reading symbols from /usr/lib/debug//lib/i386-linux-gnu/libc-2.19.so...done.
            done.
            Loaded symbols for /lib/i386-linux-gnu/libc.so.6
            Reading symbols from /usr/lib/debug//lib/i386-linux-gnu/ld-2.19.so...done.
            done.
            Loaded symbols for /lib/ld-linux.so.2
            goaround (counter=20) at looper.c:3
            3               while (i < counter) {
            (gdb) display i
            1: i = 15
            (gdb) step
            step      stepi     stepping
            (gdb) step
            4                       if (i++ == 17)
            1: i = 15
            (gdb)
            3               while (i < counter) {
            1: i = 16
            (gdb)
            4                       if (i++ == 17)
            1: i = 16

你可能感兴趣的:(▷,Linux)