告别代码Bug,GDB调试工具详解

在软件开发的漫漫长路上,Bug 就像隐藏在黑暗中的 “小怪兽”,时不时跳出来给开发者们制造麻烦。曾经,欧洲航天局(ESA)首次发射阿丽亚娜 5 号火箭,这本是太空探索史上的重要时刻,却因一行代码导致灾难性故障,价值近 5 亿欧元的火箭在发射 37 秒后爆炸 。经过调查,原来是制导系统存在软件缺陷,一段源于阿丽亚娜 4号的死代码中,64 位浮点变量转换为 16 位带符号整数时出现整数溢出问题,最终导致火箭自毁。这样的故事告诉我们,一个看似不起眼的 Bug,可能会引发难以估量的后果。

当我们在开发中遇到程序崩溃、结果异常等问题时,是不是常常感到无从下手?别担心,今天要给大家介绍的 GDB 调试工具,就是我们战胜这些 “小怪兽” 的有力武器,它能帮助我们深入程序内部,揪出隐藏的 Bug,让程序乖乖听话。

一、GDB是什么?

1.1GDB概述

GDB,全称 GNU Debugger,是 GNU 开源组织发布的一款功能强大的程序调试工具。自 1986 年由理查德・斯托曼(Richard Stallman)编写以来,它不断发展和完善,如今已成为 Linux 系统下调试程序的首选工具 ,在整个 Linux 生态系统中占据着举足轻重的地位。它就像是一位经验丰富的侦探,深入程序的 “案发现场”,帮助开发者们找到隐藏在代码中的 “罪犯”——Bug。

GDB 支持多种编程语言,包括但不限于 C、C++、Fortran、Ada、Objective-C、Go、D 等,能够与 GCC、Clang、LLVM 等一系列主流编译器无缝集成。这意味着无论你使用哪种编程语言进行开发,GDB 都能为你提供高效的调试支持,在桌面应用程序、服务器端服务,还是嵌入式系统的开发中,都能以其强大的功能和灵活的交互方式,为开发者提供无与伦比的调试体验。

  • GDB官网:https://www.gnu.org/software/gdb/(https://www.gnu.org/software/gdb/)

  • GDB适用的编程语言: Ada / C / C++ / objective-c / Pascal 等。

  • GDB的工作方式: 本地调试和远程调试。

目前release的最新版本为8.0,GDB可以运行在Linux 和Windows 操作系统上。

1.2GDB 的优势

功能丰富:GDB 提供了全面的调试功能,如设置断点(包括普通断点、条件断点)、单步执行(step 和 next )、查看变量值(print)、观察内存(x 命令)、回溯函数调用栈(backtrace)等。这些功能可以帮助开发者深入分析程序的运行状态,快速定位问题。比如,在调试一个复杂的 C++ 程序时,我们可以通过设置条件断点,当某个变量满足特定条件时程序暂停,从而精准地捕捉到问题出现的时刻;利用回溯函数调用栈,清晰地了解函数的调用顺序和各层调用间的上下文关系,快速定位问题发生在哪个函数调用链路中。

跨平台支持:它支持广泛的操作系统和平台,包括 Linux、Windows(通过 MinGW 或 Cygwin)、macOS 以及多种嵌入式平台(如 ARM、RISC-V 等)。无论你是在开发桌面应用、移动应用还是嵌入式系统,GDB 都能发挥作用。在远程调试时,GDB 非常灵活,可以与不同架构的系统进行连接,适用于跨平台和多架构的调试。比如,开发一款同时在 Linux 和 Windows 系统上运行的软件,使用 GDB 可以在不同系统下进行统一的调试操作,提高开发效率。

强大的扩展性:GDB 支持插件机制,可以通过安装第三方插件增强其功能,如内存分析、性能剖析、远程调试等。用户还可以通过 Python 脚本扩展 GDB 的功能,进行定制化调试操作。这对于需要在调试过程中进行复杂计算或自动化分析的场景非常有用。例如,在进行大规模数据处理程序的调试时,可以编写 Python 脚本来自动化分析程序运行过程中产生的大量数据,快速发现潜在问题。

开源免费:作为一款开源软件,GDB 拥有庞大的社区支持,开发者们可以自由获取、使用和修改它的源代码。这不仅降低了开发成本,还使得 GDB 能够不断吸收社区的智慧和力量,持续进化和完善。同时,丰富的社区资源,如文档、教程、论坛等,也为开发者们学习和使用 GDB 提供了便利。

与一些集成开发环境(IDE)自带的调试工具相比,GDB 虽然没有华丽的图形界面,但它更加轻量级、灵活,且无依赖,不依赖于任何复杂的图形界面或大型库,这使得它非常适合在资源受限的环境中使用,比如嵌入式开发 。在服务器或远程开发环境中,GDB 不需要图形化界面,可以直接通过 SSH 连接到目标机器进行调试。而且,GDB 能够提供比大多数 IDE 更低级别的控制和调试能力,例如,它可以直接操作内存、寄存器,甚至直接修改程序的执行流,这对于一些高级调试需求至关重要。

二、GDB基础操作

2.1安装与启动GDB

(1)安装GDB

  • gdb -v 检查是否安装成功,未安装成功则安装(必须确保编译器已经安装,如 gcc) 。

  • 启动 gdb

  • gdb test_file.exe 来启动 gdb 调试, 即直接指定需要调试的可执行文件名

  • 直接输入 gdb 启动,进入 gdb 之后采用命令 file test_file.exe 来指定文件名

  • 如果目标执行文件要求出入参数(如 argv[] 接收参数),则可以通过三种方式指定参数:

  • 在启动 gdb 时,gdb --args text_file.exe

  • 在进入gdb 之后,运行 set args param_1

  • 在 进入 gdb 调试以后,run param_1 或者 start para_1

(2)启动 GDB

在使用 GDB 调试程序之前,我们需要先编译程序并生成包含调试信息的可执行文件。以 C 语言程序为例,使用 GCC 编译器时,通过在编译命令中添加 -g 参数来实现,例如:

gcc -g -o my_program my_program.c

这样生成的 my_program 可执行文件就包含了调试所需的符号信息,这些符号信息就像是程序中的 “地图标记”,能够帮助 GDB 在调试时准确地定位到代码中的变量、函数和行号等关键位置。

启动 GDB 有多种方式,以下是几种常见的方法:

调试新程序:最直接的方式是在终端中输入 gdb 加上可执行文件名,例如:

gdb my_program

这种方式适用于我们需要从程序的初始状态开始调试,GDB 会加载程序的调试信息,并准备好接受调试命令。

附加到正在运行的进程:当程序已经在运行,并且我们想要调试这个正在运行的实例时,可以使用 attach 命令。首先,通过 ps -ef | grep my_program 命令获取程序的进程 ID(PID),然后使用以下命令将 GDB 附加到该进程:

gdb
(gdb) attach <PID>

这种方式在程序出现运行时错误,需要在不重启程序的情况下进行调试时非常有用,它可以让我们直接查看程序当前的运行状态,分析问题出现的原因 。

使用 core 文件调试:如果程序在运行过程中崩溃并生成了 core 文件(系统默认情况下可能不会生成 core 文件,需要通过 ulimit -c unlimited 命令设置允许生成 core 文件 ),我们可以使用 GDB 加载 core 文件进行调试。命令如下:

gdb my_program core

Core 文件就像是程序崩溃时的 “快照”,记录了程序崩溃时的内存状态、寄存器值等关键信息,通过分析 core 文件,我们可以找到导致程序崩溃的原因,比如空指针引用、数组越界等问题。

2.2gdb的使用

运行程序

run(r)运行程序,如果要加参数,则是run arg1 arg2 ...

查看源代码

list(l):查看最近十行源码
list fun:查看fun函数源代码
list file:fun:查看flie文件中的fun函数源代码

设置断点与观察断点

break 行号/fun设置断点。
break file:行号/fun设置断点。
break if<condition>:条件成立时程序停住。
info break(缩写:i b):查看断点。
watch expr:一旦expr值发生改变,程序停住。
delete n:删除断点。

单步调试

continue(c):运行至下一个断点。
step(s):单步跟踪,进入函数,类似于VC中的step in。
next(n):单步跟踪,不进入函数,类似于VC中的step out。
finish:运行程序,知道当前函数完成返回,并打印函数返回时的堆栈地址和返回值及参数值等信息。
until:当厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序知道退出循环体。

查看运行时数据

print(p):查看运行时的变量以及表达式。
ptype:查看类型。
print array:打印数组所有元素。
print *array@len:查看动态内存。len是查看数组array的元素个数。
print x=5:改变运行时数据。

2.3常用命令详解

⑴设置断点(break):断点是调试中最常用的工具之一,它就像是在程序的执行路径上设置的 “路障”,当程序执行到断点处时会暂停,以便我们检查程序的状态。设置断点的基本命令是 break,可以简写为 b。例如,要在 main 函数的入口处设置断点,可以使用以下命令:

(gdb) b main

也可以在指定行号处设置断点,假设我们的代码文件是 my_program.c,要在第 20 行设置断点,可以这样操作:

(gdb) b my_program.c:20

此外,还可以设置条件断点,只有当条件满足时,断点才会生效。比如,当变量 i 的值等于 10 时暂停程序:

(gdb) b my_program.c:30 if i == 10

⑵运行程序(run):设置好断点后,使用 run 命令(简写为 r)来启动程序。如果程序需要传入命令行参数,可以在 run 命令后面直接添加参数,例如:

(gdb) run arg1 arg2

run 命令会使程序从起始位置开始执行,直到遇到第一个断点或者程序结束。

继续运行(continue):当程序在断点处暂停后,如果我们想让程序继续执行,直到下一个断点或程序结束,可以使用 continue 命令,简写为 c:

(gdb) c

这个命令非常实用,在我们检查完当前断点处的程序状态后,继续程序的执行,以观察后续的运行情况。

单步执行(next、step)

next:next 命令(简写为 n)用于单步执行程序,每次执行一行代码,但当遇到函数调用时,不会进入函数内部,而是将函数调用视为一行代码直接执行过去。例如:

(gdb) n

假设我们有一个函数调用 result = add_numbers(a, b),使用 next 命令会直接执行完这个函数调用,并停在下一行代码,而不会进入 add_numbers 函数内部查看其执行过程。

step:step 命令(简写为 s)同样是单步执行,但当遇到函数调用时,会进入函数内部,在函数的第一行代码处暂停。比如:

(gdb) s

使用 step 命令遇到上述的 add_numbers 函数调用时,会进入 add_numbers 函数内部,方便我们查看函数内部的执行逻辑,检查每一步的变量变化和计算结果,对于调试函数内部的问题非常有效。

⑸ 打印变量值(print):在调试过程中,我们经常需要查看变量的值,这时就可以使用 print 命令(简写为 p)。例如,要查看变量 i 的值,可以使用以下命令:

(gdb) p i

如果变量是一个复杂的数据结构,比如结构体或对象,print 命令也能完整地显示其成员信息。此外,还可以对表达式进行求值,例如:

(gdb) p a + b

这条命令会计算 a + b 的值并显示出来。

查看断点信息(info break):使用 info break 命令(简写为 i b)可以查看当前设置的所有断点的信息,包括断点的编号、位置、条件等。例如:

(gdb) i b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00000000004005c8 in main at my_program.c:10breakpoint already hit 1 time
2       breakpoint     keep y   0x00000000004005e0 in main at my_program.c:20 if i == 10

通过这些信息,我们可以清楚地了解断点的设置情况,方便对断点进行管理,比如删除或禁用某些断点。

2.4程序错误

  • 编译错:编写程序的时候没有符合语言规范导致编译错误。比如:语法错误。

  • 运行时错误:编译器检查不出这种错误,但在运行时候可能会导致程序崩溃。比如:内存地址非法访问。

  • 逻辑错误:编译和运行都很顺利,但是程序没有干我们期望干的事情。

1.5gdb调试段错误

什么是段错误?段错误是由于访问非法地址而产生的错误。

  • 访问系统数据区,尤其是往系统保护的内存地址写数据。比如:访问地址为0的地址。

  • 内存越界(数组越界,变量类型不一致等)访问到不属于当前程序的内存区域。

gdb调试段错误,可以直接运行程序,当程序运行崩溃后,gdb会打印运行的信息,比如:收到了SIGSEGV信号,然后可以使用bt命令,打印栈回溯信息,然后根据程序发生错误的代码,修改程序。

2.6.core文件调试

(1)core文件

在程序崩溃时,一般会生成一个文件叫core文件。core文件记录的是程序崩溃时的内存映像,并加入调试信息,core文件生成过程叫做core dump(核心已转储)。系统默认不会生成该文件。

(2)设置生成core文件

  • ulimit -c:查看core-dump状态。

  • ulimit -c xxxx:设置core文件的大小。

  • ulimit -c unlimited:core文件无限制大小。

(3)gdb调试core文件

当设置完ulimit -c xxxx后,再次运行程序发生段错误,此时就会生成一个core文件,使用gdb core调试core文件,使用bt命令打印栈回溯信息。

三、GDB调试程序用法

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

1、启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
2、可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)
3、当程序被停住时,可以检查此时你的程序中所发生的事。
4、动态的改变你程序的执行环境。

从上面看来,GDB和一般的调试工具没有什么两样,基本上也是完成这些功能,不过在细节上,你会发现GDB这个调试工具的强大,大家可能比较习惯了图形化的调试工具,但有时候,命令行的调试工具却有着图形化工具所不能完成的功能。让我们一一看来。

一个调试示例:

源程序:tst.c1 #include <stdio.h>
2
3 int func(int n)
4 {
5 int sum=0,i;
6 for(i=0; i<n; i++)
7 {
8 sum+=i;
9 }
10 return sum;
11 }
12
13
14 main()
15 {
16 int i;
17 long result = 0;
18 for(i=1; i<=100; i++)
19 {
20 result += i;
21 }
22
23 printf("result[1-100] = %d /n", result );
24 printf("result[1-250] = %d /n", func(250) );
25 }

编译生成执行文件:(Linux下)

hchen/test> cc -g tst.c -o tst

使用GDB调试:

hchen/test> gdb tst <---------- 启动GDB
GNU gdb 5.1.1
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-SUSE-linux"...
(gdb) l <-------------------- l命令相当于list,从第一行开始例出原码。
1 #include <stdio.h>
2
3 int func(int n)
4 {
5 int sum=0,i;
6 for(i=0; i<n; i++)
7 {
8 sum+=i;
9 }
10 return sum;
(gdb) <-------------------- 直接回车表示,重复上一次命令
11 }
12
13
14 main()
15 {
16 int i;
17 long result = 0;
18 for(i=1; i<=100; i++)
19 {
20 result += i;
(gdb) break 16 <-------------------- 设置断点,在源程序第16行处。
Breakpoint 1 at 0x8048496: file tst.c, line 16.
(gdb) break func <-------------------- 设置断点,在函数func()入口处。
Breakpoint 2 at 0x8048456: file tst.c, line 5.
(gdb) info break <-------------------- 查看断点信息。
Num Type Disp Enb Address What
1 breakpoint keep y 0x08048496 in main at tst.c:16
2 breakpoint keep y 0x08048456 in func at tst.c:5
(gdb) r <--------------------- 运行程序,run命令简写
Starting program: /home/hchen/test/tstBreakpoint 1, main () at tst.c:17 <---------- 在断点处停住。
17 long result = 0;
(gdb) n <--------------------- 单条语句执行,next命令简写。
18 for(i=1; i<=100; i++)
(gdb) n
20 result += i;
(gdb) n
18 for(i=1; i<=100; i++)
(gdb) n
20 result += i;
(gdb) c <--------------------- 继续运行程序,continue命令简写。
Continuing.
result[1-100] = 5050 <----------程序输出。Breakpoint 2, func (n=250) at tst.c:5
5 int sum=0,i;
(gdb) n
6 for(i=1; i<=n; i++)
(gdb) p i <--------------------- 打印变量i的值,print命令简写。
$1 = 134513808
(gdb) n
8 sum+=i;
(gdb) n
6 for(i=1; i<=n; i++)
(gdb) p sum
$2 = 1
(gdb) n
8 sum+=i;
(gdb) p i
$3 = 2
(gdb) n
6 for(i=1; i<=n; i++)
(gdb) p sum
$4 = 3
(gdb) bt <--------------------- 查看函数堆栈。
#0 func (n=250) at tst.c:5
#1 0x080484e4 in main () at tst.c:24
#2 0x400409ed in __libc_start_main () from /lib/libc.so.6
(gdb) finish <--------------------- 退出函数。
Run till exit from #0 func (n=250) at tst.c:5
0x080484e4 in main () at tst.c:24
24 printf("result[1-250] = %d /n", func(250) );
Value returned is $6 = 31375
(gdb) c <--------------------- 继续运行。
Continuing.
result[1-250] = 31375 <----------程序输出。Program exited with code 027. <--------程序退出,调试结束。
(gdb) q <--------------------- 退出gdb。
hchen/test>

好了,有了以上的感性认识,还是让我们来系统地认识一下gdb吧。

基本gdb命令:

GDB常用命令	格式	含义	简写
list	List [开始,结束]	列出文件的代码清单	l
prit	Print 变量名	打印变量内容	p
break	Break [行号或函数名]	设置断点	b
continue	Continue [开始,结束]	继续运行	c
info	Info 变量名	列出信息	i
next	Next	下一行	n
step	Step	进入函数(步入)	S
display	Display 变量名	显示参数	 
file	File 文件名(可以是绝对路径和相对路径)	加载文件	 
run	Run args	运行程序	r

四、GDB进阶功能

4.1回溯追踪(backtrace)

在程序调试过程中,了解函数调用顺序及各层调用间的上下文关系至关重要。有时候程序出现错误,但我们并不知道错误是在哪个函数调用链路中产生的,这时候回溯追踪功能就派上用场了。GDB 提供了backtrace命令,简写为bt,用于展示当前的调用栈信息。

当程序运行出现异常或者在断点处暂停时,输入bt命令,GDB 会按深度由浅至深列出各个栈帧,每个栈帧包含了函数名、源文件名、行号及参数值等关键信息。例如,我们有一个包含多个函数调用的程序:

#include <stdio.h>void function_c(int num) {int result = num * 2;printf("Function C: result = %d\n", result);
}void function_b(int num) {function_c(num + 1);
}void function_a() {int num = 5;function_b(num);
}int main() {function_a();return 0;
}

在 GDB 中调试这个程序,当程序在function_c函数内暂停时,输入bt命令,输出结果可能如下:

(gdb) bt
#0  function_c (num=6) at test.c:5
#1  0x000000000040056d in function_b (num=5) at test.c:9
#2  0x0000000000400588 in function_a () at test.c:13
#3  0x00000000004005a4 in main () at test.c:17

从输出中可以清晰地看到函数的调用顺序:main调用function_a,function_a调用function_b,function_b调用function_c,并且还能看到每个函数调用时的参数值 。这对于我们快速定位问题发生的位置非常有帮助,比如如果function_c中出现了除零错误,我们就可以通过回溯追踪信息,从调用链路上查找传入function_c的参数是如何计算得出的,进而找到问题的根源。

4.2动态内存检测

内存泄漏、非法访问等内存问题是程序健壮性的隐形杀手,它们可能会导致程序运行一段时间后出现性能下降甚至崩溃。虽然有像 Valgrind 这样专门的内存分析工具,但 GDB 自身也具备一定的内存检测能力,尤其是结合 heap 插件,可以对程序的堆内存使用情况进行初步排查。

首先,我们需要获取并加载 heap 插件,假设插件文件为gdbheap.py,使用以下命令加载插件:

(gdb) source /path/to/gdbheap.py

然后,我们可以将 GDB 附加到正在运行的进程上(假设进程 ID 为<pid>),并使用插件提供的命令来查看堆内存分配情况:

(gdb) attach <pid>
(gdb) monitor heap

执行上述命令后,GDB 会显示堆内存的相关信息,比如内存块的数量、大小、分配状态等。通过观察这些信息,我们可以发现一些潜在的内存问题。例如,如果发现有大量的小内存块被分配且长时间没有释放,可能存在内存泄漏的风险;如果看到内存块的分配和释放顺序异常,可能存在非法内存访问的问题。

下面是一个简单的示例,展示如何使用 GDB 和 heap 插件检测内存问题:

#include <stdio.h>
#include <stdlib.h>int main() {int *ptr1 = (int *)malloc(10 * sizeof(int));int *ptr2 = (int *)malloc(20 * sizeof(int));free(ptr1);// 故意不释放ptr2,制造内存泄漏return 0;
}

在程序运行后,使用 GDB 和 heap 插件进行检测,通过分析插件输出的堆内存信息,我们就有可能发现ptr2所指向的内存没有被释放,从而定位到内存泄漏问题。

4.3条件断点与观察点

条件断点:在一些复杂的程序中,我们可能不希望程序在每个断点处都暂停,而是希望当满足特定条件时才暂停程序执行,这时候就可以使用条件断点。例如,在一个处理数组的程序中,我们怀疑当数组下标i大于数组大小时会出现数组越界问题,我们可以设置如下条件断点:

(gdb) break array_processing_function if i >= array_size

这样,只有当i大于或等于array_size时,程序才会在array_processing_function处暂停,大大提高了调试效率,避免了在无关断点处频繁暂停程序,让我们能够更精准地捕捉到问题出现的时刻 。

观察点:观察点(Watchpoint)用于监控变量值的变化。当观察的变量被修改时,GDB 会自动暂停程序,这对于追踪难以复现的偶发问题尤为有用。比如,在一个多线程程序中,某个全局变量的值被意外修改,但我们不确定是哪个线程在什么情况下修改的,就可以为这个全局变量设置观察点:

(gdb) watch global_variable

当global_variable的值发生改变时,程序会立即暂停,此时我们可以查看当前的线程状态、调用栈等信息,来确定变量是如何被修改的,从而找到问题的根源。此外,还可以设置读观察点(rwatch)和读写观察点(awatch),rwatch在变量被读取时暂停程序,awatch在变量被读取或修改时暂停程序,根据具体的调试需求选择合适的观察点类型 。

4.3远程调试

在实际开发中,我们经常会遇到需要调试部署在远程服务器或嵌入式设备上的程序的情况,GDB 支持通过网络进行远程调试,这极大地简化了跨设备调试的复杂性。

远程调试的基本原理是在远程设备上运行 GDB 的服务器端(gdbserver),并在本地 GDB 客户端连接至服务器端。具体操作步骤如下:

⑴在远程设备上:首先确保远程设备上安装了gdbserver,可以通过gdbserver --version命令检查是否安装。然后启动gdbserver,并指定调试的程序和监听端口,例如:

gdbserver :<port> /path/to/remote_program

其中<port>是未被占用的端口号,可以根据实际情况任意指定,/path/to/remote_program是要调试的程序路径。启动成功后,gdbserver会监听指定端口,等待本地 GDB 客户端连接。

在本地 GDB 客户端:在本地启动 GDB,并加载本地保存的与远程程序相同的可执行文件副本(确保编译时带有调试信息),然后使用target remote命令连接到远程gdbserver:

gdb ./local_program
(gdb) target remote <remote_host>:<port>

<remote_host>是远程设备的 IP 地址或主机名,<port>是在远程设备上启动gdbserver时指定的端口号。连接成功后,就可以像在本地调试程序一样,在本地 GDB 客户端使用各种调试命令,如设置断点、单步执行、查看变量值等,GDB 会通过网络与远程gdbserver通信,实现对远程程序的调试 。

例如,在开发一款嵌入式系统程序时,我们可以在开发板(远程设备)上运行gdbserver,在本地 PC 上使用 GDB 客户端进行调试,通过这种方式,能够在本地环境中方便地调试运行在远程嵌入式设备上的程序,提高开发效率 。

五、实战技巧

5.1利用 TUI 模式提升效率

GDB 的 TUI(Terminal User Interface,终端用户界面)模式提供了一种基于文本交互和图形用户交互之间的折中方法,在调试过程中能显著提升效率。在 TUI 模式中,GDB 将终端屏幕划分为源文本窗口和控制台窗口,让我们可以直观地看到代码的执行情况 。

启动 TUI 模式非常简单,只需在启动 GDB 时加上 -tui 参数即可。例如:

gdb -tui my_program

如果已经在普通 GDB 模式下,还可以通过快捷键 Ctrl + X + A 来切换到 TUI 模式,再次按下该快捷键则可以返回普通模式。

进入 TUI 模式后,我们可以使用一系列快捷键和命令来进行调试操作。比如,使用 n(next)命令单步执行代码时,源文本窗口会实时高亮显示当前执行的代码行,同时控制台窗口会输出执行结果;设置断点时,断点所在的行号前会显示特殊标记,方便我们识别和管理断点。

在调试一个复杂的 C++ 项目时,我需要在多个函数之间来回切换查看代码执行逻辑,TUI 模式让我可以直接在源文本窗口中清晰地看到代码的上下文关系,配合单步执行和断点设置,快速定位到了程序中的逻辑错误。而且,当程序暂停时,按下 Ctrl + X + S 快捷键后,就可以直接使用 GDB 命令,而无需每次都回车确认,进一步提高了调试效率 。

5.2自定义命令与脚本自动化

在日常调试工作中,我们经常会重复执行一些相同的命令序列,比如每次调试时都需要设置相同的断点、查看特定变量的值等。为了提高调试效率,GDB 允许我们将这些常用命令定义成自定义命令或脚本。

自定义命令的定义格式如下:

define command_namestatement1statement2...
end

其中,command_name 是自定义命令的名称,statement 是具体的 GDB 命令。例如,我们可以定义一个名为 my_debug 的自定义命令,用于设置多个断点并启动程序:

define my_debugb mainb function_ab function_br
end

定义好自定义命令后,在 GDB 中直接输入命令名即可执行这些命令。

对于更复杂的操作,我们可以编写 GDB 脚本。GDB 脚本是一个包含一系列 GDB 命令的文本文件,其扩展名为 .gdb 。例如,我们创建一个名为 debug_script.gdb 的脚本文件,内容如下:

b main
b function_c if i > 10
r

在 GDB 中使用 source 命令加载脚本:

(gdb) source debug_script.gdb

这样,脚本中的命令就会依次执行。GDB 还提供了丰富的流程控制命令,如 if...else...end、while...end 等,结合这些命令,我们可以编写功能强大的自动化调试脚本,实现复杂的调试逻辑。在调试一个大型数据库应用程序时,我编写了一个脚本,通过循环遍历数据库连接池中的连接对象,检查每个连接的状态和属性,快速发现了连接泄漏和配置错误的问题,大大节省了调试时间 。

5.3配合 IDE 使用

虽然 GDB 本身是一个强大的命令行调试工具,但它也可以与一些集成开发环境(IDE)配合使用,充分发挥两者的优势。以 Eclipse CDT 为例,它提供了直观的图形化界面,方便我们进行代码编辑、项目管理和调试操作,同时又集成了 GDB 的强大调试功能。

在 Eclipse CDT 中使用 GDB 进行调试,首先需要创建一个 C/C++ 项目,并确保项目的编译设置中包含调试信息(通常在项目属性的 C/C++ Build - Settings 中,选择 Debug 配置,勾选 Generate debug info 选项)。然后,在项目的源代码中设置断点,点击 Eclipse 工具栏上的调试按钮,Eclipse 会自动启动 GDB,并将其与项目关联起来。

在调试过程中,我们可以在 Eclipse 的调试视图中查看变量值、调用栈信息,进行单步执行、继续执行等操作,这些操作都通过 Eclipse 的图形界面完成,但底层实际上是由 GDB 来执行的。这种方式既保留了 GDB 的强大功能,又提供了更加便捷和直观的调试体验,对于新手开发者来说尤其友好,能够快速上手调试工作 。

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

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

相关文章

LangChain4j(2):整合SpringBoot

1 新建Springboot项目 1.1 引入依赖 <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0…

移动端六大语言速记:第2部分 - 控制结构

移动端六大语言速记&#xff1a;第2部分 - 控制结构 本文继续对比Java、Kotlin、Flutter(Dart)、Python、ArkTS和Swift这六种移动端开发语言的控制结构&#xff0c;帮助开发者快速掌握各语言的语法差异。 2. 控制结构 2.1 条件语句 各语言条件语句的语法对比&#xff1a; …

Linux-线程概念与线程控制的常用操作

一.Linux线程概念 1-1.线程是什么 在Linux中&#xff0c;线程是基于Linux原有的进程实现的。本质是轻量级进程(LWP)。在⼀个程序⾥的⼀个执⾏路线就叫做线程&#xff08;thread&#xff09;。更准确的定义是&#xff1a;线程是“⼀个进程内部的控制序列”。 我们之前所学习的进…

dfs记忆化搜索刷题 + 总结

文章目录 记忆化搜索 vs 动态规划斐波那契数题解代码 不同路径题解代码 最长递增子序列题解代码 猜数字大小II题解代码 矩阵中的最长递增路径题解代码 总结 记忆化搜索 vs 动态规划 1. 记忆化搜索&#xff1a;有完全相同的问题/数据保存起来&#xff0c;带有备忘录的递归 2.记忆…

【HTML】验证与调试工具

个人主页&#xff1a;Guiat 归属专栏&#xff1a;HTML CSS JavaScript 文章目录 1. HTML 验证工具概述1.1 验证的重要性1.2 常见 HTML 错误类型 2. W3C 验证服务2.1 W3C Markup Validation Service2.2 使用 W3C 验证器2.3 验证结果解读 3. 浏览器开发者工具3.1 Chrome DevTools…

认识rand, srand, time函数,生成随机数

要完成猜数字游戏&#xff0c;首先要生成随机数&#xff0c;那么该怎么生成随机数&#xff1f;、 1.rand函数 rand函数是库函数&#xff0c;使用的时候要使用头文件stdlib.h c语言中&#xff0c;提供了rand函数来生成随机数&#xff0c;来看一下函数使用&#xff1a; 但是r…

BKA-CNN-GRU、CNN-GRU、GRU、CNN四模型多变量时序预测(Matlab)

BKA-CNN-GRU、CNN-GRU、GRU、CNN四模型多变量时序预测&#xff08;Matlab&#xff09; 目录 BKA-CNN-GRU、CNN-GRU、GRU、CNN四模型多变量时序预测&#xff08;Matlab&#xff09;预测效果基本介绍程序设计参考资料 预测效果 基本介绍 BKA-CNN-GRU、CNN-GRU、GRU、CNN四模型多…

Go语言从零构建SQL数据库引擎(2)

SQL标准与数据库系统实现差异 在上一节中&#xff0c;我们了解了关系型数据库的基础概念。现在&#xff0c;让我们深入探讨SQL语言标准以及不同数据库系统之间的实现差异。 SQL语言的诞生与演进 想象你经营的咖啡店生意蒸蒸日上&#xff0c;需要一个更强大的系统来管理数据。…

智能导诊系统的技术体系组成

智能导诊系统的技术体系由基础支撑技术、核心交互技术、应用场景技术及安全保障技术构成&#xff0c;具体可归纳为以下六个维度&#xff1a; 一、基础支撑技术 1、AI大模型与深度学习 医疗大模型&#xff1a;如腾讯医疗AI、DeepSeek等&#xff0c;通过海量医学文献和病例训…

QML输入控件: TextField(文本框)的样式定制

目录 引言示例简介示例代码与关键点示例1&#xff1a;基础样式定制示例2&#xff1a;添加图标示例3&#xff1a;交互式元素&#xff08;清除按钮&#xff09; 实现要点总结完整工程下载 引言 在Qt Quick应用程序开发中&#xff0c;文本输入是最常见的用户交互方式之一。TextFi…

leetcode hot100 多维动态规划

1️⃣2️⃣ 多维动态规划&#xff08;区间 DP、状态机 DP&#xff09; 62. 不同路径 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图…

3.27学习总结 爬虫+二维数组+Object类常用方法

高精度&#xff1a; 一个很大的整数&#xff0c;以字符串的形式进行接收&#xff0c;并将每一位数存储在数组内&#xff0c;例如100&#xff0c;即存储为[1][0][0]。 p2437蜜蜂路线 每一个的路线数前两个数的路线数相加。 #include <stdio.h> int a[1005][1005]; int …

车载以太网网络测试-26【SOME/IP-通信方式-2】

目录 1 摘要2 Method &#xff08;FF/RR&#xff09;、Event、Filed介绍2.1. SOME/IP Method 接口2.1.1 **Fire & Forget (FF)** - 单向调用2.1.2 **Request/Response (RR)** - 请求/响应模式2.1.3 **车载ECU通信实现示例**:2.1.4 **通信序列示例**2.1.5 实现注意事项 2.2 …

把doi直接插入word中,然后直接生成参考文献

这段代码通过提取、查询、替换DOI&#xff0c;生成参考文献列表来处理Word文档&#xff0c;可按功能模块划分&#xff1a; 导入模块 import re from docx import Document from docx.oxml.ns import qn from habanero import Crossref导入正则表达式模块re用于文本模式匹配&a…

[C++] : C++11 右值引用的理解

&#xff08;一&#xff09;什么是左值和右值&#xff1f; 传统的C语法中就有引用的语法&#xff0c;而C11中新增了的右值引用语法特性&#xff0c;所以从现在开始我们 之前学习的引用就叫做左值引用。无论左值引用还是右值引用&#xff0c;都是给对象取别名。 1.左值 左值是一…

windows服务器切换到linux服务器踩坑点

单节点环境依赖性 单节点问题&#xff0c;影响业务可用性&#xff0c;windows影响后续自动化&#xff0c;健壮性的提升&#xff0c;需要进行linux化 每个服务至少是双节点&#xff0c;防止单点故障&#xff0c;提升系统的可用性&#xff0c;健壮性。linux化后可以进行docker化…

美颜SDK兼容性挑战:如何让美颜滤镜API适配iOS与安卓?

如何让美颜滤镜API同时适配iOS与Android&#xff0c;并确保性能流畅、效果一致&#xff0c;是开发者面临的一大挑战。今天&#xff0c;我将与大家一同深度剖析美颜SDK的跨平台兼容性问题&#xff0c;并分享优化适配方案。 一、美颜SDK兼容性面临的挑战 1.1不同平台的图像处理框…

Vue3 表单

Vue3 表单 随着前端技术的发展,Vue.js 作为一款流行的前端框架,不断更新迭代,以适应更高效、更便捷的开发需求。Vue3 作为 Vue.js 的第三个主要版本,引入了许多新特性和改进,其中包括对表单处理机制的优化。本文将深入探讨 Vue3 表单的使用方法、技巧以及注意事项。 1. …

笔记:代码随想录算法训练营day62:108.冗余连接、109.冗余连接II

学习资料&#xff1a;代码随想录 108. 冗余连接 卡码网题目链接&#xff08;ACM模式&#xff09; 判断是否有环的依据为&#xff0c;利用并查集&#xff0c;isSame函数&#xff0c;判断当下这条边的两个节点入集前是否为同根&#xff0c;如果是的话&#xff0c;该边就是会构…

RK3588,V4l2 读取Gmsl相机, Rga yuv422转换rgb (mmap)

RK3588, 使用V4l2 读取 gmsl 相机,获得yuv422格式图像, 使用 rga 转换 rgb 图像。减少cpu占用率. 内存管理方式采用 mmap… 查看相机信息 v4l2-ctl --all -d /dev/cam0 , 查看自己相机分辨率,输出格式等信息,对应修改后续代码测试… Driver Info:Driver name : rkcif…