• gdb 基本知识


    一 单步执行和跟踪函数调用

    先给出一段代码,可以用下面介绍的例子。
    #include <stdio.h>
    int add_range(int low, int high)
    {
        int i, sum;
        for (i = low; i <= high; i++)
        sum = sum + i;
        return sum;
    }
     
    int main(void)
    {
        int result[100];
        result[0] = add_range(1, 10);
        result[1] = add_range(1, 100);
        printf("result[0]=%d
    result[1]=%d
    ", result[0],
        result[1]);
        return 0;
    }
    1.  在编译时要加上-g选项,生成的可执行文件才能用gdb进行源码级调试。
        $ gcc -g main.c -o main
        $ gdb main
     
        当然在加-tui 在格式上会比较直观好看一些
        $ gcc -g main.c -o main
        $ gdb -tui main
     
      gdb加-g编译时并不是把整个源文件嵌入到可执行文件中,所以在调试时必须保证gdb能找到源文件。
    2. 帮助命令
        (gdb) help
        
        进一步查看某一类别中有哪些命令,例如查看files类别下有哪些命令可用:
        (gdb) help files
        
    3.  list命令
        (gdb) list
       
         一次只列10行。
         gdb的很多常用命令有简写形式,例如list命令可以写成l,要列一个函数的源代码也可以用函数名做参数:
        (gdb) l add_range
        
    4. 退出gdb的环境
        (gdb) quit 或 q
     
    5. start命令开始执行程序
        (gdb) start    
        Breakpoint 1 at 0x80483ad: file main.c, line 14.
        Starting program: /home/akaedu/main 
        main () at main.c:14
        14 result[0] = add_range(1, 10);
        (gdb)
     
    6. next命令(简写为n)控制这些语句一条一条地执行。
        (gdb) n
        15 result[1] = add_range(1, 100);
        (gdb) (直接回车)    
        16 printf("result[0]=%d result[1]=%d ", result[0], 
        result[1]);
        (gdb) (直接回车)
        result[0]=55
        result[1]=5105
        17 return 0;
     
    7. 用step命令(简写为s) 进入某一个模块。钻进add_range函数中去跟踪执行
        (gdb) s    
        add_range (low=1, high=10) at main.c:6
        6 for (i = low; i <= high; i++)
     
    8. backtrace命令(简写为bt)可以查看函数调用的栈帧
        (gdb) bt    
        #0 add_range (low=1, high=10) at main.c:6
        #1 0x080483c1 in main () at main.c:14
     
    9. info命令(简写为i)查看add_range函数局部变量的值:
        (gdb) i locals
        i = 0
        sum = 0
        
    10. frame命令(简写为f)选择1号栈帧然后再查看局部变量:
        (gdb) f 1
        #1 0x080483c1 in main () at main.c:14
        14 result[0] = add_range(1, 10);
        (gdb) i locals 
        result = {0, 0, 0, 0, 0, 0, 134513196, 225011984, -1208685768, -1081160480, 
        ...
        -1208623680}
     
    11. 用print命令(简写为p)打印出变量sum的值:
        gdb) (直接回车)
        6 for (i = low; i <= high; i++)
        (gdb) p sum
        $1 = 3
     
    12. finish命令让程序一直运行到从当前函数返回为止:
        (gdb) finish
        Run till exit from #0 add_range (low=1, high=10) at main.c:6
        0x080483c1 in main () at main.c:14
        14 result[0] = add_range(1, 10);
        Value returned is $2 = 55
     
     
    二  断点
    实际运用中用的比较最多,可能就是断点了。在程序某一个地方设置一个断点,当程序运行到这人一行时,就会在此停止,此时你可以查看一些变量的情况 。
    例子源码:
    #include <stdio.h>
    int main(void)
    {
        int sum = 0, i = 0;
        char input[5];
        while (1)
        {
            scanf("%s", input);
            for (i = 0; input[i] != ''; i++)
                sum = sum*10 + input[i] - '0';
           printf("input=%d
    ", sum);
        }
        return 0;
    }
    1. display命令使得每次停下来的时候都显示当前sum的值,然后继续往下走。
    2. undisplay命令可以取消跟踪显示,变量sum的编号是1,可以用undisplay 1命令取消它的跟踪显示
        (gdb) display sum
        1: sum = -1208103488
        (gdb) n
        9 scanf("%s", input);
        1: sum = 0
        (gdb) 
        123
        10 for (i = 0; input[i] != ''; i++)
        1: sum = 0
     
    2. break命令的参数也可以是函数名,表示在某个函数开头设断点。现在用continue命令(简写为c)连续运行而非单步运行,程序到达断点会自动停下来,这样就可以停在下一次循环的开头。
        (gdb) l
        5 int sum = 0, i;
        6 char input[5];
        7 
        8 while (1) {
        9 scanf("%s", input);
        10 for (i = 0; input[i] != ''; i++)
        11 sum = sum*10 + input[i] - '0';
        12 printf("input=%d ", sum);
        13 }
        14 return 0;
        (gdb) b 9
        Breakpoint 2 at 0x80483bc: file main.c, line 9.
        (gdb) c
        Continuing.
        input=123
        Breakpoint 2, main () at main.c:9
        9 scanf("%s", input);
        1: sum = 123
     
    3. 用info命令可以查看已经设置的断点
        Breakpoint 3 at 0x8048411: file main.c, line 12.
        (gdb) i breakpoints
        Num Type Disp Enb Address What
        2 breakpoint keep y 0x080483c3 in main at main.c:9
        breakpoint already hit 1 time
        3 breakpoint keep y 0x08048411 in main at main.c:12
      
    4. 每个断点都有一个编号,可以用编号指定删除某个断点:  
        (gdb) delete breakpoints 2
        (gdb) i breakpoints 
        Num Type Disp Enb Address What
        3 breakpoint keep y 0x08048411 in main at main.c:12
     
    5. 有时候一个断点暂时不用可以禁用掉而不必删除,这样以后想用的时候可以直接启用,而不必重新
       从代码里找应该在哪一行设断点:
        (gdb) disable breakpoints 3
        (gdb) i breakpoints     
        Num Type Disp Enb Address What
        3 breakpoint keep n 0x08048411 in main at main.c:12
        (gdb) enable 3
        (gdb) i breakpoints     
        Num Type Disp Enb Address What
        3 breakpoint keep y 0x08048411 in main at main.c:12
        (gdb) delete breakpoints     
        Delete all breakpoints? (y or n) y
        (gdb) i breakpoints    
        No breakpoints or watchpoints.
     
    6. gdb的断点功能非常灵活,还可以设置断点在满足某个条件时才激活。
      条件断点是语法是:break  [where] if [condition],
      这种断点真是非常管用尤其是在一个循环或递归中,或是要监视某个变量。
      注意,这个设置是在gdb中的,只不过每经过那个断点时gdb会帮你检查一下条件是否满足。
      例如我们仍然在循环开头设置断点,但是仅当sum不等于0时才中断,然后用run命令(简写为r)重新从程序开头连续运行:
        (gdb) break 9 if sum != 0    
        Breakpoint 5 at 0x80483c3: file main.c, line 9.
        (gdb) i breakpoints 
        Num Type Disp Enb Address What
        5 breakpoint keep y 0x080483c3 in main at main.c:9
        stop only if sum != 0
        (gdb) r
        The program being debugged has been started already.
        Start it from the beginning? (y or n) y
        Starting program: /home/akaedu/main 
        123
        input=123
        Breakpoint 5, main () at main.c:9
        9 
     
     三 观察点
    (gdb) watch input[5]
    Hardware watchpoint 2: input[5]
    (gdb) i watchpoints 
    Num Type Disp Enb Address What
    2 hw watchpoint keep y input[5]
     
     四 段错误
    如果程序运行时出现段错误,用gdb可以很容易定位到究竟是哪一行引发的段错误,例如这个小程
    序:
     
    #include <stdio.h>
    int main(void)
    {
        int man = 0;
        scanf("%d", man);
        return 0;
    }
    调试过程如下:
    $ gdb main
    ...
    (gdb) r
    Starting program: /home/akaedu/main 
    123
    Program received signal SIGSEGV, Segmentation fault.
    0xb7e1404b in _IO_vfscanf () from /lib/tls/i686/cmov/libc.so.6
    (gdb) bt
    #0 0xb7e1404b in _IO_vfscanf () from /lib/tls/i686/cmov/libc.so.6
    #1 0xb7e1dd2b in scanf () from /lib/tls/i686/cmov/libc.so.6
    #2 0x0804839f in main () at main.c:6
     
    # 五 调试宏
    在gdb下,我们无法print宏定义,因为宏是预编译的.所以要在gcc编译程序的时候,加上-ggdb3参数 就可以了。
    另外,你可以使用下述的gdb的宏调试命令 来查看相关的宏。
    info macro – 你可以查看这个宏在哪些文件里被引用了,以及宏定义是什么样的。
    macro – 你可以查看宏展开的样子。
     
     
     
     
  • 相关阅读:
    LCA问题的离线处理Tarjan算法模版
    匈牙利算法 模版
    poj 1190 dfs
    poj 1376 bfs
    划分树模版
    让innerHTML的脚本也可以运行起来
    Keycode对照表
    Javascript 操作XML简单介绍
    Webdings和Wingdings字符码对应表
    动态加载JS脚本的4种方法
  • 原文地址:https://www.cnblogs.com/fangshenghui/p/4687932.html
Copyright © 2020-2023  润新知