C++(Qt)软件调试---gdb调试入门用法(12)

gdb调试—入门用法(1)

文章目录

  • gdb调试---入门用法(1)
    • 1、前言
      • 1.1 什么是GDB
      • 1.2 为什么要学习GDB
      • 1.3 主要内容
      • 1.4 GDB资料
    • 2、C/C++开发调试环境准备
    • 3、gdb启动调试
      • 1.1 启动调试并传入参数
      • 1.2 附加到进程
      • 1.3 过程执行
      • 1.4 退出调试
    • 4、gdb断点管理
      • 1.1 设置断点
      • 1.2 管理断点
    • 5、gdb的图形化界面功能
    • 6、gdb查看和修改变量
    • 7、gdb查看和修改内存的值
    • 8、查看修改寄存器
    • 9、gdb源代码查看、管理
    • 10、搜索源代码
    • 11、函数调用栈管理
    • 12、观察点
    • 13、gdb调试捕获点
    • 14、gdb生成core dump文件
    • 15、gdb调试core dump

更多精彩内容
个人内容分类汇总
C++软件调试、异常定位
GDB官方教程文档(英文)
100个gdb小技巧

1、前言

1.1 什么是GDB

GDB是GNU调试器的缩写,是一种用于调试程序的工具。

它可以帮助程序员在程序运行时检查程序的状态,查找程序中的错误和问题,并提供一些调试工具来帮助程序员更好地理解程序的行为。

GDB支持多种编程语言,包括C、C++、Go、Fortran和汇编语言等。

它可以在命令行界面或者图形界面下使用,并且可以在多种操作系统上运行,包括Linux、Unix、Windows等。

GDB的主要作用包括以下几个方面:

  1. 检查程序状态:GDB可以帮助程序员在程序运行时检查程序的状态,包括变量的值、函数的调用栈、程序的执行流程等。
  2. 查找程序错误和问题:GDB可以帮助程序员更快地找到程序中的错误和问题,提高程序的质量和稳定性。
  3. 设置断点:GDB可以设置断点,让程序在特定的位置停下来,以便程序员检查程序的状态。
  4. 提供调试工具:GDB还提供了一些调试工具,如单步执行、查看内存、查看寄存器等,帮助程序员更好地理解程序的行为。

总之,GDB是一种非常强大的调试工具,可以帮助程序员更快地找到程序中的错误和问题,提高程序的质量和稳定性。

1.2 为什么要学习GDB

学习GDB可以帮助程序员更好地调试程序,找到程序中的错误和问题,提高程序的质量和稳定性。

在开发大型软件时,程序中可能存在许多错误和问题,这些问题可能会导致程序崩溃或者出现不可预期的行为。

使用GDB可以帮助程序员更快地找到这些问题,并且提供一些调试工具来帮助程序员更好地理解程序的行为。

此外,学习GDB还可以提高程序员的调试能力,让他们更加熟练地使用调试工具,提高工作效率。

因此,学习GDB是非常有必要的。

1.3 主要内容

  1. linux C/C++开发调试环境准备;
  2. GDB调试基本功能介绍;
    • 调试执行;
    • 断点管理;
    • 线程管理;
    • 调用栈管理;
    • 查看修改变量;
    • 其它基本功能;
  3. 多线程死锁调试;
  4. 动态库调试;
  5. 内存检查
    • 内存泄漏;
    • 堆栈溢出;
    • 删除后继续使用;
  6. 远程调试;
  7. Core dump调试分析;
  8. 发行版调试。

1.4 GDB资料

GDB:GNU 工程调试器 (sourceware.org)

顶部(使用 GDB 调试) (sourceware.org)

2、C/C++开发调试环境准备

  • 在ubuntu中使用下 列命令安装C++开发调试环境

    sudo apt install g++ gcc make gdb
    

3、gdb启动调试

1.1 启动调试并传入参数

  • 测试代码

    #include
    
    using namespace std;
    
    int func(int x)
    {
        int sum = 0;
        for(int i = 0; i < x; i++)
        {
    	sum += i;
        }
        return sum;
    }
    
    int main(int argc, char** argv)
    {
        for(int i = 1; i < argc; i++)
        {
    	      cout << "传入参数:" << i <<" " << argv[i] << endl;
        }
        int a = 0;
        int x = 0;
        if(argc > 1)
        {
            x = atoi(argv[1]);
        }
        a = func(x);
        cout << x << " " << a << endl;
    
        return 0;
    }
    
    
  • gdb --args
    • 在启动 GDB 调试器时指定程序及其参数;
    • “gdb --args” 是 GNU Debugger (gdb) 命令的一部分,用于在调试程序时指定要调试的可执行文件和其参数
    • 使用此命令可以将可执行文件和参数作为一个整体传递给 gdb。
    • 其中,“” 是要调试的可执行文件的路径和名称,“” 是要传递给可执行文件的命令行参数,多个参数之间以空格分隔。
  • set args
    • gdb --args类似,不过是在 GDB 内部执行的命令(gdb启动后),用于修改当前正在调试的程序的命令行参数。
    • 使用 gdb set args 可以多次修改参数,而使用 gdb --args 只能在启动时设置一次。
    • 如果要调试的程序中包含空格或其他特殊字符,则必须使用引号或转义字符来正确指定参数。
  • r
    • r是run的缩写,run命令用于启动程序;
    • r 用于启动程序并传递命令行参数,类似于set args + run。

C++(Qt)软件调试---gdb调试入门用法(12)_第1张图片

  • 命令中的-q是用于关闭启动时的提示信息。

1.2 附加到进程

  • 什么是附加到进程调试

    附加到进程调试是一种调试技术,它允许开发人员在程序运行时观察和分析程序的内部状态。

    通常,在调试过程中,开发人员会在代码中设置断点,以便在程序执行到特定位置时停止并检查其状态。但是,有时候问题可能只在程序运行的特定环境中出现,或者只在特定条件下才能复现。这时,附加到进程调试就非常有用了。

    附加到进程调试的过程通常包括以下几个步骤:

    1. 打开调试器:首先,开发人员需要打开一个调试器,例如Visual Studio、GDB或LLDB等。调试器是一个用于观察和控制程序执行的工具。
    2. 选择进程:开发人员需要选择要附加调试的进程。这可以是正在运行的本地程序,也可以是远程计算机上的程序。
    3. 设置断点:开发人员可以在代码中设置断点,以便在程序执行到特定位置时停止。断点可以设置在函数调用、条件语句、循环等位置。
    4. 开始调试:一旦选择了进程并设置了断点,开发人员就可以开始调试了。调试器会监视程序的执行,并在断点处停止程序。
    5. 观察和分析:当程序停止在断点处时,开发人员可以观察和分析程序的内部状态,例如变量的值、函数的调用堆栈等。这有助于理解程序的行为和找出问题所在。
    6. 继续执行:在观察和分析完程序的内部状态后,开发人员可以选择继续执行程序,直到下一个断点或程序结束。

    附加到进程调试是一种强大的调试技术,可以帮助开发人员快速定位和解决程序中的问题。它在软件开发和故障排除过程中非常常见和重要。

  • 下面三条命令的主要区别在于语法的不同,但它们的功能是相同的。你可以使用gdb的各种命令来控制调试过程,例如"break"设置断点,"run"开始执行程序,"next"单步执行等等。

  • 请注意,附加到正在运行的进程可能会对其产生一些影响,因此请谨慎使用。此外,附加到进程可能需要root权限或者对进程的所有者具有适当的权限。

  • 是想要附加的进程的进程ID。附加成功后,gdb将会连接到该进程,并允许你使用gdb的调试功能来检查进程的状态、设置断点、单步执行等操作。

    • gdb attach
    • gdb --pid=
    • gdb -p
  • 测试代码

    #include 
    
    int main() 
    {
        int num;
        std::cout << "请输入一个整数:";
        std::cin >> num;
        int a;
        a = num + 1;
        int b;
        b = num * 10;
        std::cout << a <<" " << b << std::endl;
        std::cout << "您输入的整数是:" << num << std::endl;
        return 0;
    }
    
    
  • 演示如下

    1. 使用命令g++ test2.cpp -g编译代码,加上-g生成包含调试详细的可执行程序;
    2. 运行可执行程序;
    3. 使用命令cat -n test2.cpp 查看源代码;
    4. 使用命令ps -aux | grep a.out查看名称为a.out的进程的pid号;
    5. 进程的pid号;
    6. 使用命令 sudo gdb -q -p 9834将gdb附加到pid号为9834的进程进行调试,需要使用超级用户权限;
    7. 使用命令b test2.cpp:8在test2.cpp文件的第8行打一个断点;
    8. 继续执行gdb,c命令是continue的缩写,用于继续执行程序直到下一个断点或程序结束;
    9. 单步执行gdb,n命令是next的缩写,用于执行下一行代码。当程序被暂停在某个断点处时,可以使用n命令来执行下一行代码,而不会进入函数调用。这个命令通常用于逐行执行程序,以便观察程序的执行流程。

1.3 过程执行

  • 常用过程执行命令

    • start:启动程序并暂停main函数的第一行,让程序在调试器中开始执行,而不是直接运行。
    • next(简写为n):执行下一行代码,并将控制权移动到下一行,遇见函数调用会跳过,如果调用的函数中有断点则会进入。
      • n:简写形式,执行下一行代码。
      • next:执行下一行代码。
      • next n:执行下n行代码。
      • nexti:执行下一条机器指令。
      • nexti n:执行下n条机器指令。
    • step [count](简写为s):执行下一行代码,并将控制权移动到下一行。如果当前行是函数调用,会进入该函数并在第一行暂停。
      • 其中,count是一个可选参数,表示要执行的行数。如果不指定count,则默认为1,即执行一行。
    • finish:执行完当前函数的剩余代码,并将控制权返回到调用该函数的地方
    • continue(简写为c):继续执行程序,直到遇到下一个断点或程序结束
  • 测试代码

    #include
    
    using namespace std;
    
    int sumFun(int size)
    {
        int sum = 0;
        for(int i = 0; i < size; i++)
        {
            sum += i;
        }
        return sum;
    }
    
    int main()
    {
        int size = 10;
        int sum = 0;
        sum = sumFun(size);
        cout << "计算从0到" << size <<" 的和为:" << endl;
        cout << sum << endl;
    
        size = 100;
        sum = sumFun(size);
        cout << "计算从0到" << size <<" 的和为:" << endl;
        cout << sum << endl;
    
        return 0;
    }
    
  • 演示

    C++(Qt)软件调试---gdb调试入门用法(12)_第2张图片

1.4 退出调试

  • 退出调试方式
    • quit:缩写q这是最常用的退出命令,它会立即终止gdb会话并退出到终端。
    • Ctrl + D:这是一个快捷键组合,按下Ctrl和D键后,gdb会话会立即终止并退出到终端。
    • detach:用于将gdb与正在调试的程序分离,使得程序可以在后台继续运行,而不受gdb的控制。这在某些情况下非常有用,比如当你想让程序在调试过程中继续执行一段时间,而不需要gdb的干预。

4、gdb断点管理

  • 测试代码

    #include 
    
    using namespace std;
    
    int fun1()
    {
        int a = 10;
        int b = 20;
        throw a;
    
        return b;
    }
    
    int fun2(int a)
    {
        int b = 0;
        b = a + 10;
        return b;
    }
    int main()
    {
        cout <<"调用函数2" << endl;
        int a = 10;
        cout << fun2(a) << endl;
    
        cout << "调用函数1" << endl;
        try
        {
            fun1();
        }
        catch(...)
        {
            cout <<"出现异常" << endl;
        }
        return 0;
    }
    

1.1 设置断点

  • 设置断点命令

    1. break:设置断点,缩写为b
      • 行号断点:使用break命令加上源代码的行号可以在指定行上设置断点。例如,break 10会在第10行设置一个断点;
      • 函数断点:使用break命令加上函数名可以在指定函数入口处设置断点。例如,break myFunction会在myFunction函数的入口处设置一个断点,如果是类或者命名空间则需要加上作用域;
      • 条件断点:使用break命令加上条件可以在满足条件时设置断点。例如,break myFunction if i == 10会在myFunction函数的入口处设置一个断点,但只有当变量i的值等于10时才会触发断点;
      • 内存地址断点:使用break命令加上内存地址可以在指定内存地址处设置断点。例如,break *0x12345678会在内存地址0x12345678处设置一个断点,当调试汇编程序,或者没有调试信息的程序时,经常需要在程序地址上打断点,可以使用disassemble命令查看程序的反汇编后的地址;
      • 指令断点:使用break命令加上指令地址可以在指定指令处设置断点。例如,break *0x12345678会在指令地址0x12345678处设置一个断点;
    2. tbreak:命令用于设置临时断点,缩写为tb。与break命令不同,临时断点只会在程序执行到该断点时触发一次,然后自动被删除。您可以使用tbreak命令后跟函数或行号来设置临时断点。
    3. rbreak:设置正则表达式断点,缩写为rb
      • 语法rbreak [regexp],其中,regexp是一个正则表达式,用于匹配要设置断点的函数名。rbreak命令会在所有可执行文件中搜索匹配的函数名,并在每个匹配的函数入口处设置断点;
      • 设置所有函数名以"foo"开头的断点: rbreak ^foo
      • 设置所有函数名以"bar"结尾的断点: rbreak bar$
      • 设置所有函数名中包含"baz"的断点: rbreak baz
    4. catch:catch命令用于设置异常断点。
      • 异常断点会在程序抛出异常时触发。您可以使用catch命令后跟异常类型来设置异常断点;
      • 语法catch [exception] [command]
      • exception:是要捕获的异常类型,可以是以下几种:
        • throw:捕获由C++程序中的throw语句引发的异常。
        • catch:捕获由C++程序中的catch语句处理的异常。
        • exec:捕获由被调试程序中的exec调用引发的异常。
        • fork:捕获由被调试程序中的fork调用引发的异常。
        • vfork:捕获由被调试程序中的vfork调用引发的异常。
        • syscall:捕获由被调试程序中的系统调用引发的异常。
      • command:是在捕获到异常时要执行的命令。如果省略command,则gdb会在捕获到异常时停止程序的执行并显示相关信息。
    5. trace:用于设置跟踪点。
      • 跟踪点是一种特殊的断点,它可以在程序执行到指定的函数或行号时触发,并显示函数的参数和返回值。
      • 跟踪点可以帮助开发人员更详细地了解程序的执行过程,以便更好地调试和分析代码。
  • 演示

    C++(Qt)软件调试---gdb调试入门用法(12)_第3张图片

1.2 管理断点

  • gdb断点管理常用命令

    1. delete:删除断点。会从断点列表中删除指定的断点,但不会清除程序中的断点位置。如果再次运行程序,断点仍然会被触发。

      • 缩写为d

      • 删除所有断点:使用delete命令后不加任何参数,即可删除所有已设置的断点。

      • 删除指定编号的断点:每个断点都有一个唯一的编号,可以使用delete命令后加上断点编号来删除指定的断点。例如delete 1

      • 删除所有在指定函数中设置的断点:可以使用delete命令后加上函数名来删除所有在该函数中设置的断点。例如 delete function_name

      • 删除所有在指定文件中设置的断点:可以使用delete命令后加上文件名来删除所有在该文件中设置的断点。例如:delete file_name

      • 删除所有在指定行号处设置的断点:可以使用delete命令后加上行号来删除所有在该行号处设置的断点。例如delete line_number

      • 需要注意的是,删除断点时需要确保当前正在调试的程序处于暂停状态,否则删除操作可能无效。可以使用info breakpoints命令来查看当前已设置的断点,并获取它们的编号以及其他相关信息。

    2. clear:清除断点。会从程序中清除指定的断点位置,这意味着即使再次运行程序,断点也不会被触发。

      • 清除所有断点:使用clear命令后不跟任何参数,可以清除所有已设置的断点。
      • 清除指定行号的断点:使用clear命令后跟上要清除断点的源文件名行号,可以清除指定行号的断点。例如,clear main.cpp:10可以清除main.cp文件中第10行的断点。
      • 清除指定函数的断点:使用clear命令后跟上要清除断点的函数名,可以清除指定函数的断点。例如clear my_function可以清除名为my_function的函数的断点。
      • 清除指定源文件的断点:使用clear命令后跟上要清除断点的源文件名,可以清除指定源文件的所有断点。例如,clear main.cpp可以清除main.cp文件中的所有断点。
    3. disable:禁用断点、观察点或线程。

      • 语法:disable [breakpoint|watchpoint|thread] <编号>

        • breakpoint表示断点;
        • watchpoint表示观察点;
        • thread表示线程;
        • <编号>是要禁用的断点、观察点或线程的编号。
      • 可以使用disable命令后跟断点号来禁用特定的断点,例如disable 1

    4. enable:用于启用断点。

      • 可以使用enable命令后跟断点号来启用特定的断点,例如enable 1
    5. info breakpoints:显示当前设置的所有断点。

      • 缩写为i b
      • 使用info breakpoints命令可以查看当前设置的所有断点的详细信息,包括断点号、断点类型、断点位置等。
  • 演示

    C++(Qt)软件调试---gdb调试入门用法(12)_第4张图片

5、gdb的图形化界面功能

gdb中的tui是指Text User Interface,它是gdb的一个可选功能,用于在终端中以图形化的方式显示源代码、汇编代码和调试信息。

tui提供了一个类似于文本编辑器的界面,可以在调试过程中更方便地查看和操作代码。

使用tui可以在终端中同时显示源代码和调试信息,以及当前执行的代码行。

它可以帮助开发人员更直观地理解代码的执行流程,快速定位问题。

启动tui功能的方式:

  1. 启动gdb时使用命令gdb -tui 可执行程序
  2. 在gdb命令行中输入命令tui enable
  3. 在gdb命令行中使用快捷键Ctrl x a

关闭tui功能的方式:在gdb命令行中输入命令tui disable

启用tui后,可以使用以下命令来操作:

  • layout src:显示源代码窗口。
  • layout asm:显示汇编代码窗口。
  • layout regs:显示寄存器窗口。
  • layout split:将源代码窗口和汇编代码窗口分割显示。
  • layout next:切换到下一个布局。
  • layout prev:切换到上一个布局。
  • focus next:将焦点切换到下一个窗口。
  • focus prev:将焦点切换到上一个窗口。
  • 使用gdb图形化调试界面时,可以使用“winheight [+ | -]count”命令调整窗口大小(winheight缩写为winwin_name可以是srccmdasmregs)。
    • 例如:win src -5

此外,还可以使用其他gdb命令来设置断点、单步执行代码等。

需要注意的是,gdb的tui功能在不同的终端和操作系统上可能会有一些差异,具体的使用方法和快捷键可能会有所不同。可以通过在gdb命令行中输入help tui来获取更详细的帮助信息。

  • 演示:使用命令打的断点在tui窗口中可以实时看见。

    C++(Qt)软件调试---gdb调试入门用法(12)_第5张图片

6、gdb查看和修改变量

  • 测试代码

    #include
    
    using namespace std;
    
    int g_a = 10;
    char g_b = 'a';
    
    int func(int x)
    {
        int sum = 0;
        for(int i = 0; i < x; i++)
        {
    	sum += i;
        }
        return sum;
    }
    
    struct Test{
        int a = 10;
        char b = 'c';
        double c = 123.321;
    };
    
    int main(int argc, char** argv)
    {
        for(int i = 1; i < argc; i++)
        {
    	      cout << "传入参数:" << i <<" " << argv[i] << endl;
        }
        int a = 0;
        int x = 0;
        char str[] = "hello";
        if(argc > 1)
        {
            x = atoi(argv[1]);
        }
        a = func(x);
        cout << x << " " << a << endl;
        Test t;
        cout << "结构体" << endl;
        return 0;
    }
    
    
  • 常用命令

    1. info locals:显示当前栈帧的局部变量的值。
    2. info args:gdb将会显示出当前函数的参数列表及其对应的值。这些参数包括函数的形参名称以及实际传递给函数的值。
    3. info variables:这个命令将显示当前作用域中所有变量的列表,包括全局变量。
    4. print variable_name:这个命令将显示指定变量的值。(缩写为p)
    5. set print null-stop:设置字符串的显示规则,显示字符串时遇见\0就停止输出;
    6. set print pretty:显示结构体,结构体换行输出;
    7. set print array on:显示数组;
    8. p 变量名=value:修改变量值为value;
    9. set var 变量名=value:修改变量值为value;
    10. set main::str = "hello":修改字符串的值,需要注意内存越界;
  • 演示

    C++(Qt)软件调试---gdb调试入门用法(12)_第6张图片

7、gdb查看和修改内存的值

  • 测试代码

    #include 
    
    using namespace std;
    
    int main()
    {
        int a = 10;
        char c = 'a';
    
        return 0;
    }
    
    
  • 查看内存

  • x/[格式 长度] 地址

  • 格式字母有:

    • o(八进制)
    • x(十六进制)
    • d(十进制)
    • u(无符号十进制)
    • t(二进制)
    • f(浮点)
    • a(地址)
    • i(指令)
    • c(字符)
    • s(字符串)
    • z(十六进制,左边加零)。
  • 大小字母有

    • b:以字节为单位显示数据。
    • h:以半字(2字节)为单位显示数据。
    • w:以字(4字节)为单位显示数据。
    • g:以双字(8字节)为单位显示数据。
  • 设置内存

    • set {type}address=value
    • address:存储地址;
    • type:变量类型;
    • value:需要设置的值。
  • 演示

    C++(Qt)软件调试---gdb调试入门用法(12)_第7张图片

8、查看修改寄存器

  • 测试代码

    #include 
    
    using namespace std;
    
    int fun(const char* name, int age)
    {
        cout << name <<" " << age << endl;
        return 0;
    }
    
    int main()
    {
        fun("hello", 123);
    
        return 0;
    }
    
    
  • 使用命令

    • info registers:用于显示当前正在被调试程序的寄存器状态。它会列出各个寄存器的名称和当前的值。
    • info r rdi:查看单个寄存器rdi;
  • 函数参数的寄存器

    寄存器 函数参数
    rdi 第一个参数
    rsi 第二个参数
    rdx 第三个参数
    rcx 第四个参数
    r8 第五个参数
    r9 第六个参数
  • 演示:没有调试符号时可以通过查看寄存器来进行调试。

    C++(Qt)软件调试---gdb调试入门用法(12)_第8张图片

  • 修改寄存器命令

  • set var $pc=value:修改pc寄存器,pc寄存器是一种用于存储即将被执行的指令地址的寄存器。它通常用于计算机的中央处理器(CPU)中,在执行程序时起到指示下一条要执行的指令的作用。

  • 演示

    • 使用b fun在fun函数打一个断点;
    • 使用r命令运行程序,停在断点处;
    • 使用info line 8命令查看第8行代码的地址;
    • 使用set var &pc=0x5555555551e3将pc寄存器执行的下一条指令修改为第8行代码;
    • 使用n命令单步执行,程序直接跳转到第8行,跳过了int a = 10这一行代码。

    C++(Qt)软件调试---gdb调试入门用法(12)_第9张图片

9、gdb源代码查看、管理

  • 测试代码

    • main.cpp

      /********************************************************************************
      * 文件名:   main.cpp
      * 创建时间: 2023-07-26 20:14:04
      * 开发者:   MHF
      * 邮箱:     [email protected]
      * 功能:     
      *********************************************************************************/
      #include
      #include "test.h"
      
      using namespace std;
      
      int main()
      {
          int a = 10;
          int b = 20;
      
          int sum = 0;
          test t;
          t.setValue(a, b);
          sum = t.getSum();
      
          cout << sum << endl;
          return 0;
      }
      
    • test.h

      #pragma one
      
      class test
      {
      private:
          int m_a = 0;
          int m_b = 0;
      
      public:
          test(/* args */);
          ~test();
      
          void setValue(int a, int b);
          int getSum();
      };
      
      
    • test.cpp

      #include "test.h"
      
      test::test(/* args */)
      {
      }
      
      test::~test()
      {
      }
      
      
      void test::setValue(int a, int b)
      {
          m_a = a;
          m_b = b;
      }
      
      int test::getSum()
      {
          return m_a + m_b;
      }
      
  • 使用命令

    • list:缩写为l,它用于显示源代码,并帮助程序员在调试过程中理解代码的执行流程。
    • 语法:list [function] [start-line [end-line]]
      • function(可选):指定要显示源代码的函数名或方法名。如果不指定,则显示当前默认的函数或方法的源代码。
      • start-line(可选):指定要从哪一行开始显示源代码。如果不指定,则默认从当前上下文中的下一行开始显示。
      • end-line(可选):指定要到哪一行结束显示源代码。如果不指定,则默认显示从起始行开始的十行代码。
    • 使用示例
      • list:显示当前函数或方法的源代码,默认从下一行开始,显示十行代码。
      • list main:显示名为"main"的函数的源代码,默认从下一行开始,显示十行代码。
      • list main 10:显示名为"main"的函数的源代码,从第10行开始,显示十行代码。
      • list main.cpp:15:显示指定文件的指定行代码。
      • list file:fun:显示指定文件中的指定函数代码;
      • list class:fun:显示指定类的成员函数代码。
    • set listsize xx:设置每次显示的代码行数。
  • 演示

    C++(Qt)软件调试---gdb调试入门用法(12)_第10张图片

10、搜索源代码

  • 使用命令
    • search str:在当前文件中查找包含str字符串的行;
    • forward-search str:向后查找;
    • reverse-search str:向前查找。

11、函数调用栈管理

  • 测试代码

    #include 
    
    using namespace std;
    
    void fun1()
    {
        int a = 10;
        int* b = nullptr;
        *b = a;
    }
    
    void fun2()
    {
        char c = 'a';
        cout << c << endl;
        fun1();
    }
    
    int main()
    {
        fun2();
        return 0;
    }
    
    
  • 使用命令

    1. backtrace命令:显示当前的函数调用栈。可以在程序崩溃或中止时使用,用来追踪函数调用链以确定程序执行到哪里。缩写为bt
    2. updown命令:在当前函数调用链中向上或向下切换。
      • up : 切换到调用当前函数的函数
      • down:切换回当前函数
      • up n :向上选择函数堆栈帧,其中n是层数
      • down n:切向下选择函数堆栈帧,其中n是层数
    3. frame命令:切换到指定的栈帧(Stack Frame)。
      • 先使用backtrace查看栈帧列表
      • 在使用frame 2 切换到第3个栈帧
    4. info frame命令:显示当前栈帧的详细信息,包括函数调用点、参数、局部变量等。
      • info frame :查看当前栈帧信息
      • info frame 3 :查看id为3的栈帧信息
  • 演示

    C++(Qt)软件调试---gdb调试入门用法(12)_第11张图片

12、观察点

  • watch: watch 命令允许您监视一个变量或一个表达式的值,并在其值发生更改时暂停程序的执行。您可以使用 watch 命令来跟踪特定变量的变化,以便在发生错误或特定事件时检查它们的值。

    • watch a
    • watch a == 10:观察点可以带有条件。条件可以使观察点仅在满足特定条件时触发。例如,要在变量x的值为10时触发观察点;
    • watch a thread 1:指定只有线程1写变量a时触发;
  • rwatch:rwatch 命令也是用于监视变量或表达式的值,但它只在读取(而不是写入)该值时触发断点。这对于跟踪对某个变量的读取很有用,以确认某些代码段是否访问了它。

  • awatch: awatch 命令可用于监视变量或表达式的读取写入操作。当变量或表达式的值发生变化时,程序将在读取或写入操作上暂停执行。

  • info watch:查看观察点;

  • delete:删除观察点;

  • disable:禁用观察点;

  • enable:弃用观察点;

  • 测试代码

    #include 
    
    using namespace std;
    
    int main()
    {
        int a = 10;
        char c = 'a';
    
        return 0;
    }
    
    
  • 演示

    C++(Qt)软件调试---gdb调试入门用法(12)_第12张图片

13、gdb调试捕获点

  • 捕获点是一个特殊的断点,命令语法为:catch event

  • 即捕获到event这个事件的时候,程序就会中断下类;

  • 支持的捕获事件有:(可以在gdb中使用help catch命令查看)

    事件 说明
    catch assert Catch在引发时失败的Ada断言。
    catch catch 当程序捕获异常时触发捕获点。可以用来跟踪异常的捕获过程。
    catch exception Catch Ada异常,当引发时。
    catch exec 捕获对exec的调用。
    catch fork 捕获对fork的调用。
    catch handlers 处理时捕获Ada异常。
    catch load 捕获共享库的负载。
    catch rethrow 重新引发时捕获异常。
    catch signal 通过信号的名称和/或编号捕捉信号。
    catch syscall 通过系统调用的名称、组和/或号码捕获系统调用。
    catch throw 抛出时捕获异常
    catch unload 捕获共享库的卸载。
    catch vfork 捕获对vfork的调用。
  • 测试代码

    #include
    
    using namespace std;
    
    void fun()
    {
        int a = 1;
        cout << a << endl;
        throw a;
    }
    
    int main()
    {
        try
        {
            fun();
        }
        catch(int a)
        {
            cout << "捕获:" << a << endl;
        }
    }
    
    
  • 演示

    C++(Qt)软件调试---gdb调试入门用法(12)_第13张图片

14、gdb生成core dump文件

  • 测试代码

  • 使用命令

    • generate-core-file:gcore 类似,generate-core-file 命令也用于在 gdb 中生成核心转储文件,可以单独使用命令,也可以在generate-core-file后跟文件路径文件名称。
    • gcore:在正在运行的进程中,使用 gcore 命令可以生成一个称为核心转储文件(core dump)的文件。这个核心转储文件包含了进程在崩溃时的内存状态、寄存器信息等,有助于开发人员分析程序崩溃的原因。
  • 使用示例

    C++(Qt)软件调试---gdb调试入门用法(12)_第14张图片

15、gdb调试core dump

  • 测试代码

  • 方法1:使用gdb 可执行程序 core文件命令加载调试core dump;

  • 方法2:使用gdb 可执行程序命令进入gdb后,使用core core文件名命令指定core文件,进行调试;

  • 演示

    C++(Qt)软件调试---gdb调试入门用法(12)_第15张图片

你可能感兴趣的:(#,C++软件调试,c++,qt,gdb)