【C/C++ 调试 GDB指南 】gdb调试基本操作

简介: 【C/C++ 调试 GDB指南 】gdb调试基本操作

gdb 简介

GDB 全称“GNU symbolic debugger”,从名称上不难看出,它诞生于 GNU 计划(同时诞生的还有 GCC、Emacs 等),是 Linux 下常用的程序调试器。发展至今,GDB 已经迭代了诸多个版本,当下的 GDB 支持调试多种编程语言编写的程序,包括 C、C++、Go、Objective-C、OpenCL、Ada 等。实际场景中,GDB 更常用来调试 C 和 C++ 程序。


一般来说,GDB主要帮助我们完成以下四个方面的功能:

1. 启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
2. 在某个指定的地方或条件下暂停程序。
3. 当程序被停住时,可以检查此时你的程序中所发生的事。
4. 在程序执行过程中修改程序中的变量或条件,将一个bug产生的影响修正从而测试其他bug。

要使用GDB调试某个程序,该程序编译时必须加上编译选项 -g,否则该程序是不包含调试信息的; GCC编译器支持 -O 和 -g一起参与编译。GCC编译过程对进行优化的程度可分为5个等级.


重要快捷键:

ctrl+A+X:进入(TextUser Interface),它为GDB调试的文本用户界面,可以方便地显示源代码、汇编和寄存器文本窗口.

GDB调试步骤

  • 启动GDB
  • 直接调试目标程序:gdb ./hello_server
  • 附加进程id:gdb attach pid
  • 调试core文件:gdb filename corename
  • 退出GDB

使用命令:q(quit的缩写)或者 Ctr + d 退出GDB。

如果GDB attach某个进程,退出GDB之前要用命令 detach 解除附加进程。


GDB常用命令

命令名称 命令缩写 命令说明
run r 运行一个待调试的程序
continue c 让暂停的程序继续运行
next n 运行到下一行
step s 单步执行,遇到函数会进入
until u 运行到指定行停下来
finish fi 结束当前调用函数,回到上一层调用函数处
return return 结束当前调用函数并返回指定值,到上一层函数调用处
jump j 将当前程序执行流跳转到指定行或地址
print p 打印变量或寄存器值
backtrace bt 查看当前线程的调用堆栈
frame f 切换到当前调用线程的指定堆栈
thread thread 切换到指定线程
break b 添加断点
tbreak tb 添加临时断点
delete d 删除断点
enable enable 启用某个断点
disable disable 禁用某个断点
watch watch 监视某一个变量或内存地址的值是否发生变化
list l 显示源码
info i 查看断点 / 线程等信息
ptype ptype 查看变量类型
disassemble dis 查看汇编代码
set args set args 设置程序启动命令行参数
show args show args 查看设置的命令行参数

  • run命令

默认情况下,以 gdb ./filename

方式启用GDB调试只是附加了一个调试文件,并没有启动这个程序,需要输入run命令(简写为r)启动这个程序

  • continue命令

当GDB触发断点或者使用 Ctrl + C 命令中断下来后,想让程序继续运行,只要输入 continue(简写为c)命令即可。

  • break命令

break命令(简写为b)用于添加断点,可以使用以下几种方式添加断点:

break FunctionName,在函数的入口处添加一个断点;

break LineNo,在当前文件行号为LineNo处添加断点;

break FileName:LineNo,在FileName文件行号为LineNo处添加一个断点;

break FileName:FunctionName,在FileName文件的FunctionName函数的入口处添加断点;

break -/+offset,在当前程序暂停位置的前/后 offset 行处下断点;

break … if cond,下条件断点;

  • info break、enable、disable和delete命令

命令格式及作用:

info break,也可简写为 i b,作用是显示当前所有断点信息;

disable 断点编号,禁用某个断点,使得断点不会被触发;

enable 断点编号,启用某个被禁用的断点;

delete 断点编号,删除某个断点。

  • backtrace和frame命令

命令格式及作用:

backtrace,也可简写为 bt,用于查看当前调用堆栈。

frame 堆栈编号,也可简写为 f 堆栈编号,用于切换到其他堆栈处。

  • list命令

命令格式及作用:

list,输出上一次list命令显示的代码后面的代码,如果是第一次执行list命令,则会显示当前正在执行代码位置附近的代码;

list -,带一个减号,显示上一次list命令显示的代码前面的代码;

list LineNo,显示当前代码文件第 LineNo 行附近的代码;

list FileName:LineNo,显示 FileName 文件第 LineNo 行附近的代码;

list FunctionName,显示当前文件的 FunctionName 函数附近的代码;

list FileName:FunctionName,显示 FileName 文件的 FunctionName 函数附件的代码;

list from,to,其中from和to是具体的代码位置,显示这之间的代码;

list命令默认只会输出 10 行源代码,也可以使用如下命令修改:

show listsize#查看 list 命令显示的代码行数;
set listsize count#设置 list 命令显示的代码行数为 count;
  • print和ptype命令

命令格式及作用:

print param,用于在调试过程中查看变量的值;

print param=value,用于在调试过程中修改变量的值;

print a+b+c,可以进行一定的表达式计算,这里是计算a、b、c三个变量之和;

print func(),输出func函数执行的结果,常见的用途是打印系统函数执行失败原因:print > strerror(errno);

print *this,在c++对象中,可以输出当前对象的各成员变量的值;

  • whatis和ptype命令

命令格式及功能:

whatis val,用于查看变量类型;

ptype val,作用和 whatis 类似,但功能更强大,可以查看复合数据类型,会打印出该类型的成员变量。

  • thread命令

命令格式及作用:

info thread,查看当前进程的所有线程运行情况;

thread 线程编号,切换到具体编号的线程上去;

  • next、step命令

next 和 step 都是单步执行,但也有差别:

next 是 单步步过(step over),即遇到函数直接跳过,不进入函数内部。

step 是 单步步入(step into),即遇到函数会进入函数内部。

  • return、finish命令

return 和 finish 都是退出函数,但也有差别:

return 命令是立即退出当前函数,剩下的代码不会执行了,return 还可以指定函数的返回值。

finish 命令是会继续执行完该函数剩余代码再正常退出。

  • until命令

该命令使得程序执行到指定位置停下来,命令参数和 break 命令一样。

  • jump命令

命令格式及作用:

jump LineNo,跳转到代码的 LineNo 行的位置;

jump +10,跳转到距离当前代码下10行的位置;

jump *0x12345678,跳转到 0x12345678 地址的代码处,地址前要加星号;

jump 命令有两点需要注意的:

中间跳过的代码是不会执行的;

跳到的位置后如果没有断点,那么GDB会自动继续往后执行;

  • set args 和 show args命令

很多程序启动需要我们传递参数,set args 就是用来设置程序启动参数的,show args 命令用来查询通过 set args 设置的参数,命令格式:

set args args1,设置单个启动参数 args1;

set args “-p” “password”,如果单个参数之间有空格,可以使用引号将参数包裹起来;

set args args1 args2 args3,设置多个启动参数,参数之间用空格隔开;

set args,不带参数,则清除之前设置的参数;

  • tbreak命令

该命令时添加一个临时断点,断点一旦被触发就自动删除,使用方法同 break。

  • watch命令

watch 命令用来监视一个变量或者一段内存,当这个变量或者内存的值发生变化时,GDB就会中断下来。被监视的某个变量或内存地址会产生一个 watch point(观察点)。

命令格式:

watch 整型变量;

watch 指针变量,监视的是指针变量本身;

watch *指针变量,监视的是指针所指的内容;

watch 数组变量或内存区间;

  • call命令

命令格式及作用:

call func(),执行 func() 函数,同 print func()。

  • help命令

通过 help 命令可以查看目标命令的具体用法。


GDB多线程调试

概述

多线程程序的编写更容易产生异常或 Bug(例如线程之间因竞争同一资源发生了死锁、多个线程同时对同一资源进行读和写等等)。GDB调试器不仅仅支持调试单线程程序,还支持调试多线程程序。本质上讲,使用GDB调试多线程程序的过程和调试单线程程序类似,不同之处在于,调试多线程程序需要监控多个线程的执行过程。
用GDB调试多线程程序时,该程序的编译需要添加 -lpthread 参数。

一些命令

1. info thread,查看当前调试程序启动了多少个线程,并打印出各个线程信息;

2. thread 线程编号,将该编号的线程切换为当前线程;

3. thread apply 线程编号1 线程编号2 … command,将GDB命令作用指定对应编号的线程,可以指定多个线程,若要指定所有线程,用 all 替换线程编号;

4. break location thread 线程编号,在 location 位置设置普通断点,该断点只作用在特定编号的线程上;

设置线程锁

使用GDB调试多线程程序时,默认的调试模式是:一个线程暂停运行,其他线程也随即暂停;一个线程启动运行,其他线程也随即启动。但在一些场景中,我们希望只让特定线程运行,其他线程都维持在暂停状态,即要防止线程切换,要达到这种效果,需要借助 set scheduler-locking 命令。

命令格式及作用:

set scheduler-locking on,锁定线程,只有当前或指定线程可以运行;
set scheduler-locking off,不锁定线程,会有线程切换;
set scheduler-locking step,当单步执行某一线程时,其他线程不会执行,同时保证在调试过程中当前线程不会发生改变。但如果在该模式下执行 continue、until、finish 命令,则其他线程也会执行;
show scheduler-locking,查看线程锁定状态;

结语

在我们的编程学习之旅中,理解是我们迈向更高层次的重要一步。然而,掌握新技能、新理念,始终需要时间和坚持。从心理学的角度看,学习往往伴随着不断的试错和调整,这就像是我们的大脑在逐渐优化其解决问题的“算法”。

这就是为什么当我们遇到错误,我们应该将其视为学习和进步的机会,而不仅仅是困扰。通过理解和解决这些问题,我们不仅可以修复当前的代码,更可以提升我们的编程能力,防止在未来的项目中犯相同的错误。

我鼓励大家积极参与进来,不断提升自己的编程技术。无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力。

相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
目录
相关文章
|
1月前
|
存储 NoSQL 安全
【C++调试】深入探索C++调试:从DWARF到堆栈解析
【C++调试】深入探索C++调试:从DWARF到堆栈解析
62 1
|
1月前
|
Linux 编译器 程序员
【Linux 调试秘籍】深入探索 C++:运行时获取堆栈信息和源代码行数的终极指南
【Linux 调试秘籍】深入探索 C++:运行时获取堆栈信息和源代码行数的终极指南
69 0
|
2天前
|
NoSQL Ubuntu 测试技术
【GDB自定义指令】core analyzer结合gdb的调试及自定义gdb指令详情
【GDB自定义指令】core analyzer结合gdb的调试及自定义gdb指令详情
7 1
|
1月前
|
NoSQL C++ 开发者
【C/C++ 调试 GDB指南 】GDB中的‘info’命令:一次全面的探索
【C/C++ 调试 GDB指南 】GDB中的‘info’命令:一次全面的探索
40 0
|
1月前
|
缓存 Linux iOS开发
【C/C++ 集成内存调试、内存泄漏检测和性能分析的工具 Valgrind 】Linux 下 Valgrind 工具的全面使用指南
【C/C++ 集成内存调试、内存泄漏检测和性能分析的工具 Valgrind 】Linux 下 Valgrind 工具的全面使用指南
66 1
|
1月前
|
算法 Unix Linux
【C/C++ 疑难解决】深入解析C++链接错误:实用的调试技巧和方法
【C/C++ 疑难解决】深入解析C++链接错误:实用的调试技巧和方法
14 1
|
1月前
|
SQL 存储 关系型数据库
【C/C++ 应用开发 数据库】入门 Qt数据库编程:从基本操作到高级技巧
【C/C++ 应用开发 数据库】入门 Qt数据库编程:从基本操作到高级技巧
77 0
|
1月前
|
并行计算 安全 编译器
【C/C++ 编译相关 gcc】一次搞懂GCC编译选项:优化代码、调试程序必备!
【C/C++ 编译相关 gcc】一次搞懂GCC编译选项:优化代码、调试程序必备!
39 0
|
1月前
|
NoSQL 算法 Shell
【C/C++ 调试 GDB指南 】详解 gdb 断点的设置方式
【C/C++ 调试 GDB指南 】详解 gdb 断点的设置方式
19 2
|
5天前
|
存储 编译器 C语言
c++的学习之路:5、类和对象(1)
c++的学习之路:5、类和对象(1)
21 0