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

news2024/11/19 23:27:20

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<iostream>
    
    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 <executable> <arguments>
    • 在启动 GDB 调试器时指定程序及其参数;
    • “gdb --args” 是 GNU Debugger (gdb) 命令的一部分,用于在调试程序时指定要调试的可执行文件和其参数
    • 使用此命令可以将可执行文件和参数作为一个整体传递给 gdb。
    • 其中,“” 是要调试的可执行文件的路径和名称,“” 是要传递给可执行文件的命令行参数,多个参数之间以空格分隔。
  • set args <args>
    • gdb --args类似,不过是在 GDB 内部执行的命令(gdb启动后),用于修改当前正在调试的程序的命令行参数。
    • 使用 gdb set args 可以多次修改参数,而使用 gdb --args 只能在启动时设置一次。
    • 如果要调试的程序中包含空格或其他特殊字符,则必须使用引号或转义字符来正确指定参数。
  • r <args>
    • r是run的缩写,run命令用于启动程序;
    • r 用于启动程序并传递命令行参数,类似于set args + run。

在这里插入图片描述

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

1.2 附加到进程

  • 什么是附加到进程调试

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

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

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

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

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

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

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

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

    • gdb attach <PID>
    • gdb --pid=<PID>
    • gdb -p <PID>
  • 测试代码

    #include <iostream>
    
    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<iostream>
    
    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;
    }
    
  • 演示

    在这里插入图片描述

1.4 退出调试

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

4、gdb断点管理

  • 测试代码

    #include <iostream>
    
    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:用于设置跟踪点。
      • 跟踪点是一种特殊的断点,它可以在程序执行到指定的函数或行号时触发,并显示函数的参数和返回值。
      • 跟踪点可以帮助开发人员更详细地了解程序的执行过程,以便更好地调试和分析代码。
  • 演示

    在这里插入图片描述

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命令可以查看当前设置的所有断点的详细信息,包括断点号、断点类型、断点位置等。
  • 演示

    在这里插入图片描述

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 <win_name> [+ | -]count”命令调整窗口大小(winheight缩写为winwin_name可以是srccmdasmregs)。
    • 例如:win src -5

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

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

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

    在这里插入图片描述

6、gdb查看和修改变量

  • 测试代码

    #include<iostream>
    
    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":修改字符串的值,需要注意内存越界;
  • 演示

    在这里插入图片描述

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

  • 测试代码

    #include <iostream>
    
    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:需要设置的值。
  • 演示

    在这里插入图片描述

8、查看修改寄存器

  • 测试代码

    #include <iostream>
    
    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第六个参数
  • 演示:没有调试符号时可以通过查看寄存器来进行调试。

    在这里插入图片描述

  • 修改寄存器命令

  • 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这一行代码。

    在这里插入图片描述

9、gdb源代码查看、管理

  • 测试代码

    • main.cpp

      /********************************************************************************
      * 文件名:   main.cpp
      * 创建时间: 2023-07-26 20:14:04
      * 开发者:   MHF
      * 邮箱:     1603291350@qq.com
      * 功能:     
      *********************************************************************************/
      #include<iostream>
      #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:设置每次显示的代码行数。
  • 演示

    在这里插入图片描述

10、搜索源代码

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

11、函数调用栈管理

  • 测试代码

    #include <iostream>
    
    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的栈帧信息
  • 演示

    在这里插入图片描述

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 <iostream>
    
    using namespace std;
    
    int main()
    {
        int a = 10;
        char c = 'a';
    
        return 0;
    }
    
    
  • 演示

    在这里插入图片描述

13、gdb调试捕获点

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

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

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

    事件说明
    catch assertCatch在引发时失败的Ada断言。
    catch catch当程序捕获异常时触发捕获点。可以用来跟踪异常的捕获过程。
    catch exceptionCatch 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<iostream>
    
    using namespace std;
    
    void fun()
    {
        int a = 1;
        cout << a << endl;
        throw a;
    }
    
    int main()
    {
        try
        {
            fun();
        }
        catch(int a)
        {
            cout << "捕获:" << a << endl;
        }
    }
    
    
  • 演示

    在这里插入图片描述

14、gdb生成core dump文件

  • 测试代码

  • 使用命令

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

    在这里插入图片描述

15、gdb调试core dump

  • 测试代码

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

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

  • 演示

    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/886022.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Debian11 Crontab

Crontab用户命令 可执行文件 crontab命令的可执行文件在哪儿&#xff1f; $ which -a crontab /usr/bin/crontab /bin/crontabcrontab命令的可执行文件有2个&#xff1a;/usr/bin/crontab 和 /bin/crontab $ diff /usr/bin/crontab /bin/crontab $diff 发现这两个文件并无区…

学习pytorch4 transforms的使用

学习pytorch4 transforms用法 常用类ToTensor1. ToTensor如何使用2. 为什么我们需要tensor数据类型PIL数据类型![在这里插入图片描述](https://img-blog.csdnimg.cn/f642055ddbfc4c228066331fc3cd53bf.png)tensor数据类型 代码tensorboard 启动命令 B站小土堆视频学习 常用类T…

Ubuntu20 ctrl+alt+T无法打开终端

事情是这样的&#xff0c;某天改了下python版本&#xff0c;发现linux默认打开终端的快捷键ctrlaltT寄了&#xff0c;网上给出的都是修改快捷键不出意外肯定没用 但是幸好我们是会分析的&#xff0c;我看到&#xff0c;很多回答说新增一个快捷键运行的命令是gnome-terminal&…

pycharm上传项目到github,版本管理

前提&#xff1a;下载git 设置Git路径 登录Github 此时自动打开浏览器&#xff0c;并打开连接页面&#xff0c;点击 Authorize GitHub。登录&#xff1a; 创建本地仓库 提交到Github 填写初始提交相关信息 origin&#xff0c;它们只是远程服务器的一个别名&#xff0c;否则你就…

计算机竞赛 python+大数据校园卡数据分析

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于yolov5的深度学习车牌识别系统实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;4分工作量&#xff1a;4分创新点&#xff1a;3分 该项目较为新颖&am…

IC流程中 DFT 学习笔记(1)

引言 DFT是ASIC芯片设计流程中不可或缺的环节。其主要目的是在芯片前端设计验证完成后插入一些诸如寄存器链等可供测试的逻辑&#xff0c;算是IC后端设计的范畴。主要是在ASIC芯片流片完成后&#xff0c;通过这些已插入的逻辑&#xff0c;检测流片得到的芯片的制造质量。检测一…

聚簇索引是如何产生的 ?聚簇索引和非聚簇索引有什么区别 ?

目录 1. 聚簇索引是如何产生的 2. 聚簇索引和非聚簇索引有什么区别 1. 聚簇索引是如何产生的 首先聚簇索引和非聚簇索引是 InnoDB 里面的叫法&#xff0c;其次呢&#xff0c;一张表它一定有聚簇索引。 它产生的过程如下&#xff1a; 表中有无有主键索引&#xff0c;如果有&…

拦截器和过滤器的区别

&#x1f600;前言 本篇博文是关于拦截器VS 过滤器的分享&#xff0c;希望你能够喜欢&#x1f60a; &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的满意是我…

day10 11 12-牛客67道剑指offer-JZ83、18、84、69、44、11、56、85、84、89、68

文章目录 1. JZ83 剪绳子&#xff08;进阶版&#xff09;2. JZ18 删除链表的节点3. JZ69 跳台阶4. JZ44 数字序列中某一位的数字5. JZ11 旋转数组的最小数字6. JZ56 数组中只出现一次的两个数字统计频率位运算 7. JZ85 连续子数组的最大和(二)8. JZ84 二叉树中和为某一值的路径…

set_output_delay的本质

set_output_delay是对模块output信号在模块外部延迟的约束&#xff0c;本质上EDA工具会根据约束调整内部器件&#xff08;UFF0&#xff09;的类型&#xff0c;摆放位置以及组合逻辑&#xff08;C1&#xff09;以满足约束要求&#xff0c;即EDA工具保证模块DUA的UFF0的Tclk2qTc1…

复数

文章目录 复数虚数 i i i几何解释 复数复数的性质复数的加法1. 加实部2. 加虚部3. 组合实部和虚部复数加法的几何解释总结 复数减法1. 减实部2. 减虚部3. 组合实部和虚部复数减法的几何解释总结 复数乘法1. 展开乘法2. 使用 i 2 − 1 i^2 -1 i2−13. 合并实部和虚部复数乘法…

100 道 Python 经典面试题超强汇总(三):Python 对象内部

《Python Cookbook》的作者David Beazley的课程PPT开源了&#xff0c;目标用户是希望从编写基础脚本过渡到编写更复杂程序的高级 Python 程序员&#xff0c;课程主题侧重于流行库和框架中使用的编程技术&#xff0c;主要目的是更好地理解 Python 语言本身&#xff0c;以便阅读他…

(JavaScript笔记摘要)一次性搞定原型和原型链

一、前言 学完JavaScript ES5基础语法&#xff08;核心语法&#xff09;后&#xff0c;发现根本不够用&#xff0c;于是选择继续精进JavaScript功底 学到原型和原型链时&#xff0c;发现有点卡壳儿&#xff0c;于是投入了一定精力&#xff0c;进行了汇总整理和吸收&#xff0c…

知识体系总结(九)设计原则、设计模式、分布式、高性能、高可用

文章目录 架构设计为什么要进行技术框架的设计 六大设计原则一、单一职责原则二、开闭原则三、依赖倒置原则四、接口分离原则五、迪米特法则&#xff08;又称最小知道原则&#xff09;六、里氏替换原则案例诠释 常见设计模式构造型单例模式工厂模式简单工厂工厂方法 生成器模式…

【Kubernetes】Kubernetes之配置资源管理

Secret 和 ConfigMap 一、Secret1. Secret 的作用2. Secret 的类型3. Secret 使用方式及应用场景4. Secret 的实例4.1 创建 Secret4.2 使用方式将 Secret 挂载到 Volume 中&#xff0c;以 Volume 的形式挂载到 Pod 的某个目录下将 Secret 导出到环境变量中 二、 ConfigMap1. Co…

由于找不到mfc140u.dll,无法继续执行代码怎么修复?

当我在使用某个应用程序时遇到了mfc140u.dll缺失的错误提示时&#xff0c;我意识到这是由于该动态链接库文件丢失或损坏所引起的。mfc140u.dll是MFC的一部分&#xff0c;它包含了许多与用户界面、窗口管理、控件等相关的函数和类。这个文件通常用于支持使用MFC开发的应用程序的…

高效服务器DDoS防护,F5安全架构不可少

数字化转型&#xff0c;特别是现代化应用正面临着严峻挑战。如何确保应用的用户体验&#xff0c;用户现有安全架构如何适应现代化应用防护需求&#xff0c;以及 API 安全直接关乎现代化应用的普及和用户的体验&#xff0c;甚至关系到企业数字化能否转型成功。当企业网站或服务器…

HD Supply EDI 项目测试流程

在此前的文章 HD Supply EDI 项目案例中&#xff0c;我们已经为大家介绍了 HD Supply EDI 项目案例&#xff0c;了解了 HD Supply 业务测试场景&#xff0c;本文将为大家介绍 EDI 项目测试流程。 HD Supply EDI 项目连接测试 在HD Supply EDI 项目中&#xff0c;CommerceHub为…

[oneAPI] 手写数字识别-BiLSTM

[oneAPI] 手写数字识别-BiLSTM 手写数字识别参数与包加载数据模型训练过程结果 oneAPI 比赛&#xff1a;https://marketing.csdn.net/p/f3e44fbfe46c465f4d9d6c23e38e0517 Intel DevCloud for oneAPI&#xff1a;https://devcloud.intel.com/oneapi/get_started/aiAnalyticsToo…

[obs] 编译记录

2023.7 obs 最近编译方式经常改。本次使用的是最新的方式编译 2023/7月份版本&#xff0c;记录一下遇到的坑 obs 最新版默认使用 vs2022 才能编译&#xff0c;如果想用 vs2019 编译&#xff0c;改下面这个地方就好了 CMakePresets.json 文件的obs aja 编译有问题 解决方案&a…