当前位置: 首页 > news >正文

c/c++开发调试工具之gdb

调试

调试就是让代码一步一步的慢执行,跟踪程序的运行过程。比如,可以让程序停在某个地方,查看当前所有变量的值,或者内存中的数据;也可以让程序一次只执行一条或者几条语句,看看程序到底执行了哪些代码。帮助我们发现代码中的错误,改进代码。

gdb的功能

灵活控制程序的启动方式

可以给程序设置启动参数,也可以设置环境变量,让程序在指定的条件下开始运行

支持断点设置与程序执行控制

可以在指定的行、函数设置断点,使程序在特定位置暂停

支持单步执行、逐过程执行、继续运行等操作便于逐步排查程序逻辑

实时查看程序运行的状态

程序暂停时可以查看当前执行位置的上下文,可查看局部变量、全局变量、寄存器等的当 前值,可查看函数调用栈、线程信息等,帮助定位问题来源

动态修改程序的执行过程

可在程序暂停时修改变量的值,观察不同的值对程序的影响,也可改变程序的执行路径,      比如跳到其他代码行继续执行

总结:gdb 能让程序按你设定的方式启动,比如加参数或设环境变量;还能在你关心的地方通过加断点的方式停下来方便检查;程序停住后,你可以查看变量值、调用关系等运行状态;不仅能查看,还能改变变量值、改变执行流程,帮你快速定位并尝试修复问题。

注意:

使用gdb调试需要一个可执行文件,这个可执行文件可以由gcc/g++直接生成,但是gcc/g++默认生成的是release(发布)版本的可执行文件,但是gdb需要的是debug(调试)版本的可执行文件,所以在使用gcc/g++生成可执行文件时要加上选项-g,此时生成的就是debug版本的可执行文件。

g++ -g -o test_threads test_threads.cpp

常用的gdb指令

基本运行控制

命令含义
r(run)启动程序。若有断点则停在第一个断点处,否则直接运行完
c(continue)从当前停住的位置继续运行,直到下一个断点或程序结束
n(next)单步调试,跳过函数调用,即执行该行但不进入函数内部
s(step)单步调试,会进入函数体内部
finish快速执行完当前函数,停在调用者处下一行
until 行号执行到指定行号再停住,适合跳出循环等

示例

1: int add(int x, int y) { return x + y; }
2: int main() {
3:     int a = 1;
4:     int b = 2;
5:     int c = add(a, b);
6:     return 0;
}

①理解 next vs step

停在第5行时执行命令:

  • n:直接跳到第6行,add 被调用但不进入

  • s:进入 add() 函数,从 return x + y 开始调试

②理解finish:

假设你用 step 进入了 add(a, b) 函数

(gdb) step

你现在在:

1: int add(int x, int y) {

此时你想直接执行完 add 函数,并跳回 main() 的第 5 行之后继续调试,不想在 add 内一步步执行。

使用:

(gdb) finish

GDB 会:

  • 继续执行当前函数直到 return

  • 返回上一层调用点

  • 自动打印返回值,例如:

Run till exit from #0  add (x=1, y=2) at test.cpp:2
0x0000000000401136 in main () at test.cpp:6
Value returned is $1 = 3

③理解until:

目标:

从第 3 行开始执行,跳过第 4、5 行,直接停在第 6 行(return 0;

(gdb) b 3       # 在第3行设置断点
(gdb) r         # 运行程序,停在第3行

执行:

(gdb) until 6

gdb会连续执行第 3、4、5 行,不会进入 add(a, b) 函数内部,然后停在:

6:     return 0;

查看和操作变量

命令含义
p 变量名打印变量值(print)
set var 变量名=值修改变量值
display 变量名每次中断都自动显示变量值(追踪变量)
undisplay 编号取消某个 display 的变量
info locals查看当前函数内所有局部变量
info args查看当前函数的参数
watch 变量名设置“观察点”,变量值改变时自动中断程序

示例

1: int add(int x, int y) {
2:     int result = x + y;
3:     return result;
4: }5: int main() {
6:     int a = 10;
7:     int b = 20;
8:     int c = add(a, b);
9:     return 0;
}
(gdb) b add         # 在 add 函数开头设置断点
(gdb) r             # 启动程序并在 add() 进入时暂停
(gdb) info args     # 查看传入的参数值

输出为:

x = 10
y = 20

add() 函数中,继续查看局部变量 result

(gdb) n              # 执行一行,让 result 被计算出来
(gdb) info locals    # 查看所有局部变量

输出为:

result = 30

main() 中设置对变量 a 的观察,当它值变化时中断程序

(gdb) b 6            # 在第6行设置断点(a赋值前)
(gdb) r              # 启动程序并暂停在第6行
(gdb) watch a        # 设置观察点,监视变量a

输出:

Hardware watchpoint 1: a

然后执行:

(gdb) c              # 继续运行程序

GDB 会在 a 的值第一次变化时自动中断,并提示:

Hardware watchpoint 1: aOld value = <uninitialized>
New value = 10

断点管理

命令含义
b 行号在当前文件的某一行设置断点
b 文件名:行号在指定文件中设置断点
b 文件名:函数名在某函数第一行设置断点
info b查看所有断点信息
d 编号删除某个断点(使用断点编号)
ddelete breakpoints删除所有断点
disable 编号 / enable 编号禁用 / 启用某个断点
disable / enable禁用 / 启用所有断点

 

调用栈相关(定位函数调用)

命令含义
bt(backtrace)打印函数调用栈,查看函数调用路径
frame 编号切换栈帧,进入某一层函数栈中
up / down在函数栈中向上/向下切换调用上下文

示例:多层函数调用

1: void foo(int x) {
2:     int y = x + 1;
3: }4: void bar(int a) {
5:     int b = a * 2;
6:     foo(b);
7: }8: int main() {
9:     int val = 10;
10:    bar(val);
11:    return 0;
}

调试目标:观察函数调用栈,切换上下文,查看各层变量状态

首先,我们在 foo() 函数的 int y = x + 1; 行设置断点,这样当程序执行到 foo 函数时会停止。

(gdb) b 2         # 在 foo 函数的第2行设置断点
(gdb) r           # 运行程序

程序停在 foo() 的第2行时,GDB 会提示你当前停在哪个位置。

查看当前调用栈:使用 bt 命令

(gdb) bt

输出如下:

#0  foo (x=20) at test.cpp:2
#1  bar (a=10) at test.cpp:6
#2  main () at test.cpp:10

可以看到函数调用的顺序是:

main → bar → foo

如果我们想查看 bar() 函数中的状态,可以使用 frame 命令切换到 bar 的栈帧。

(gdb) frame 1      # 切换到 bar 的栈帧

然后,我们可以查看 bar() 函数中的变量 ab

(gdb) info locals  # 查看 bar 函数中的局部变量

输出是:

b = 20
a = 10

如果我们想查看更上层的栈帧,即 main() 函数中的变量,可以使用 up 命令:

(gdb) up           # 切换到上一层,进入 main 函数

然后,我们查看 main() 函数中的变量 val

(gdb) info locals  # 查看 main 函数中的局部变量

输出是:

val = 10

如果我们想回到 foo() 函数的栈帧,可以使用 down 命令:(updown 命令可以用来在栈帧之间循环访问。当你到达栈帧的最顶层或者最底层时,继续执行 updown 会让你跳转到相反的方向。)

(gdb) down         # 切换回 foo 的栈帧

多线程调试

命令含义
info threads查看当前所有线程
thread 编号切换到某个线程

 查看内存和字符串内容

命令格式含义
x/NFU 地址查看内存内容
x/s 地址查看以地址为起点的 C 风格字符串(遇 \0 停)
  • N:查看的单元个数,如 4

  • F:格式,如 x(十六进制)、d(十进制)、c(字符)、s(字符串)

  • U:单位大小,b(字节)、h(半字)、w(字)、g(巨字)

示例:

(gdb) x/4xb ptr     # 查看 ptr 开头的4个字节,按16进制显示
(gdb) x/10dc ptr    # 以10进制+字符形式查看10个字节
(gdb) x/g ptr       # 查看一个8字节的 long long 类型数据
(gdb) x/s str       # 查看 C 字符串内容

手动调用函数

命令含义
call 函数名(参数)在 GDB 中手动调用一个函数进行测试或打印返回值
(gdb) call strlen("hello")

 注意:

  • GDB 会自动记住上次输入的命令,按【回车】可重复执行上条命令。

  • 使用 TAB 可自动补全命令、变量、文件名等。

  • GDB 中按 Ctrl + C 可中断当前程序运行。

相关文章:

  • AI 驱动的智能交通系统:从拥堵到流畅的未来出行
  • Allegro23.1新功能之如何使用文件预览功能操作指导
  • FormCalc 支持的编程语言和软件
  • 流水线相关计算【计算机组成与体系结构】
  • 数字智慧方案5873丨智慧交通设计方案(57页PPT)(文末有下载方式)
  • Linux_sudo命令的使用与机制
  • 力扣刷题 -- 206.反转链表
  • 第Y3周:yolov5s.yaml文件解读
  • TCP三次握手和四次挥手(简要版)
  • 多模态大模型轻量化探索-视觉大模型SAM(Segment Anything Model)
  • Linux 下使用tcpdump进行网络分析原
  • SpringTask
  • 自动化测试项目2 --- 比特纵横 [软件测试实战 Java 篇]
  • Baklib知识中台驱动企业服务智能升级
  • 深入理解C++构造函数:从入门到实践
  • win10系统安卓开发环境搭建
  • Spring 分批处理 + 冷热数据分离:历史订单高效迁移与数据清理实战
  • 数字智慧方案6206丨智慧园区大数据整体解决方案(45页PPT)(文末有下载方式)
  • 数字智慧方案5846丨智慧广场整体解决方案(91页PPT)(文末有下载方式)
  • JavaScript基础-逻辑运算符
  • 浙江“胖都来”开业多位明星祝贺,“胖东来”称已取证投诉,律师:碰瓷侵权
  • 澳大利亚大选今日投票:聚焦生活成本与“特朗普问题”
  • 网红“丢那猩”丢石块闯祸,起哄游客难逃责任
  • 5月1日,全社会跨区域人员流动量完成33271.4万人次
  • 刘国中:毫不松懈巩固拓展脱贫攻坚成果,全力以赴抓好农业防灾减灾工作
  • 2024“好评中国”网络评论大赛结果揭晓