GDB调试

概念

  • GDB 是由 GNU 软件系统社区提供的调试工具,同 GCC 配套组成了一套完整的开发环境,GDB 是 Linux 和许多类 Unix 系统中的标准开发环境

  • 一般来说,GDB 主要帮助你完成下面四个方面的功能

    • 启动程序,可以按照自定义的要求随心所欲的运行程序
    • 可让被调试的程序在所指定的调置的断点处停住(断点可以是条件表达式)
    • 当程序被停住时,可以检查此时程序中所发生的事
    • 可以改变程序,将一个 BUG 产生的影响修正从而测试其他 BUG
  • 在GDB(GNU Debugger)中,命令的缩写是为了提高用户的效率和便捷性而设计的。GDB允许用户使用命令的前几个字符作为缩写,只要这些缩写是唯一的并且不与其他命令冲突。这种设计使得用户在调试过程中可以更快速地输入命令,提高工作效率。

    1. 命令缩写的规则

      • GDB命令可以被缩写为其前几个字符,只要这些字符足以唯一地标识该命令。
      • 例如,list 命令可以缩写为 lli,因为在GDB中没有其他命令以 lli 开头。
    2. 命令示例

      • list 命令用于显示源代码,可以缩写为 lli
      • info break 命令用于显示断点信息,可以缩写为 i b
    3. 具体例子

      1. list 命令

        • 完整命令:list
        • 缩写形式:lli
        • 解释:在GDB中,没有其他命令以 lli 开头,因此 lli 都可以唯一地标识 list 命令。
      2. info break 命令

        • 完整命令:info break
        • 缩写形式:i b
        • 解释:info 命令有很多子命令,但 break 子命令可以缩写为 b,因此 info break 可以缩写为 i b

准备工作

  • 安装gdb

    • sudo apt update
    • sudo apt install gdb
  • 安装后查看gdb是否安装成功

    • gdb -v
  • 使用以下命令编译:gcc -g -Wall program.c -o program

    • 通常,在为调试而编译时,我们会关掉编译器的优化选项-O), 并打开调试选项-g)。另外,-Wall在尽量不影响程序行为的情况下选项打开所有warning,也可以发现许多问题,避免一些不必要的 BUG
    • -g 选项的作用是在可执行文件中加入源代码的信息,比如可执行文件中第几条机器指令对应源代码的第几行,但并不是把整个源文件嵌入到可执行文件中,所以在调试时必须保证 gdb 能找到源文件
  • 注:当在 gdb 中直接使用回车时,会默认执行上一条命令

常用命令

说明

  • program1.c:源文件如下
点击查看代码
#include <stdio.h>

int sum(int a,int b);

int main(int argc, char *argv[])
{
    for(int i = 0; i < argc; i++){
        printf("argv[%d]: %s\n", i, argv[i]);
    }
    printf("Hello world\n");

    for(int i = 1; i < 5; i++){
        printf("%d\n", i);
    }

    int a = 1, b = 2;
    printf("a+b = %d\n", sum(a,b));
    return 0;
}

int sum(int a, int b){
    return a+b;
}
  • program2.c:源文件如下
点击查看代码
int main()
{
    pid_t pid = fork();
    if(pid > 0){
        for(int i = 0; i < 5; i++){
            printf("I am parent process, pid = %d\n", getpid());
            sleep(1);
        }
    }else if(pid == 0){
        for(int i = 0; i < 5; i++){
            printf("I am child process, pid = %d\n", getpid());
            sleep(1);
        }
    }else{
        perror("fork() failed");
        exit(1);
    }
    return 0;
}

启动与退出

  • 启动:gdb 可执行程序 或者 gdb file [filename]

    • 方法一
    • 方法二
  • 退出:quit/q

给程序设置参数/获取设置参数

  • 设置参数:set args 10 20 或者 gdb --args program1 10 20

    • 方法一
    • 方法二
  • 获取设置参数:show args

GDB使用帮助

  • help

查看当前文件代码

  • 默认位置显示,下翻:list/l

  • 默认位置显示,上翻:list/l -

  • 从指定的行显示:list/l 行号

  • 从指定的函数显示:list/l 函数名

  • 注:查看时会显示前后文

查看非当前文件代码

  • 编译运行并使用gdb program

    • 注意:要查看其他文件,需要在生成可执行文件program时,确保所有源文件都被编译并链接
    • 增加swap.h:源文件如下
    点击查看代码
    #ifndef __SWAP_H
    #define __SWAP_H
    
    void swap(int *a, int *b){
        *a ^= *b;
        *b ^= *a;
        *a ^= *b;
    }
    
    #endif
    
    • 修改program1.c:源代码如下
    点击查看代码
    #include <stdio.h>
    #include "swap.h"
    
    int sum(int a,int b);
    
    int main(int argc, char *argv[])
    {
        for(int i = 0; i < argc; i++){
            printf("argv[%d]: %s\n", i, argv[i]);
        }
        printf("Hello world\n");
    
        for(int i = 1; i < 5; i++){
            printf("%d\n", i);
        }
    
        int a = 1, b = 2;
        printf("before swap: a=%d, b=%d\n", a, b);
        printf("a+b = %d\n", sum(a,b));
        swap(&a, &b);
        printf("after swap: a=%d, b=%d\n", a, b);
        return 0;
    }
    
    int sum(int a, int b){
        return a+b;
    }
    
    • 编译链接:gcc -g -Wall program1.c -o program1
  • 显示当前调试会话中已加载的源文件列表:info sources

  • 从指定文件指定的行显示:list/l 文件名:行号

  • 从指定文件指定的函数显示:list/l 文件名:函数名

  • 查看及设置显示的行数

    • 查看显示的行数:show list/listsize
    • 设置显示的行数:set list/listsize

断点操作

  • 查看断点:i/info b/break

  • 设置一般断点

    • b/break 行号
    • b/break 函数名
    • b/break 文件名:行号
    • b/break 文件名:函数

  • 设置条件断点(一般用在循环的位置):b/break 10 if i==5

  • 删除断点:d/del/delete 断点编号

  • 设置断点无效:dis/disable 断点编号

  • 设置断点生效:ena/enable 断点编号

  • 忽略断点n次ignore 断点编号 忽略次数

调试操作

  • 现有断点

  • 运行 GDB 程序 (重新调试程序)

    • 程序停在第一行:start

    • 遇到断点才停:run

  • 继续运行,到下一个断点停:c/continue

  • 向下执行一行代码(不会进入函数体):n/next

  • 向下单步调试(遇到函数进入函数体)

    • s/step

    • 跳出函数体:finish

变量操作/监视

  • 打印变量值:p/print 变量名/表达式

  • 打印变量类型:pt/ptype 变量名/表达式

  • 自动变量操作

    • 自动打印指定变量的值:display 变量名

    • 查看自动变量:i/info dis/display

    • 取消自动变量:undis/undisplay 编号

其它操作

  • 设置变量值:set var 变量名=变量值 (循环中用的较多)

  • 跳出循环:until 希望跳转到的行号

多线程操作

  • 说明:使用gdb调试时,gdb默认只能跟踪一个进程,可以在fork函数调用前,通过指令设置gdb调试工具来跟踪父进程或子进程,默认gdb跟踪父进程

  • 调试program2可执行程序

  • 查看调试父进程或子进程:show follow-fork-mode

  • 设置调试父进程或子进程:set follow-fork-mode [parent(default) | child

  • 调试模式

    • 说明:默认为on,表示调试当前进程时,其余进程继续运行,如果为off,则调试当前进程时,其余进程被gdb挂起

    • 查看当前调试模式:show detach-on-fork

    • 设置on:set detach-on-fork on

    • 设置off:set detach-on-fork off

    • 查看当前调试的进程:i/info i/inferiors

      • 当设置调试子进程,其余进程为挂起状态时
    • 切换当前进程的调试:inferior id

      • 切换到父进程
    • 使进程脱离gdb调试,不再挂起:detach inferiors id

      • 子进程脱离挂起状态
posted @ 2024-08-30 13:32  JoShamash  阅读(108)  评论(0编辑  收藏  举报