gdb调试学习与实践笔记 -- 常用命令解析1

目录

Redis 源码解压及编译

使用 GDB 附加 redis-server 这个程序

run 命令(简写为 r)启动这个程序

ontinue 命令 让程序继续运行

break 命令(简写为 b)添加断点

backtrace 命令(简写为 bt)查看当前调用堆栈

info break 命令(简写为 info b)查看加了哪些断点及enable、disable 和 delete 

list 命令(简写为 l)查看当前断点处的代码

print 命令(简写为 p)在调试过程中查看变量的值、修改当前内存中的变量值

ptype (print type)就是输出一个变量的类型

Redis 源码解压及编译

  • 进入生成的目录使用 makefile 命令进行编译

  • 注意
    • 设置编译器的选项时使用的是 CFLAGS 选项;如果项目使用的语言是 C++,那么使用的编译器一般是 g++,相对应的编译器选项是 CXXFLAGS。
    • 此处makefile 使用了 -j 选项,其值是 4,表示开启 4 个进程同时编译,加快编译速度。
  •  src 目录下生成多个可执行程序,其中 redis-server 和 redis-cli 是需要调试的程序。

使用 GDB 附加 redis-server 这个程序

run 命令(简写为 r)启动这个程序

  • 这是 redis-server 启动界面,假设程序已经启动,再次输入 run 命令则是重启程序。
  • 在 GDB 界面按 Ctrl + C 快捷键让 GDB 中断下来,再次输入 r 命令,GDB 会询问我们是否重启程序,输入 yes 确认重启。

ontinue 命令 让程序继续运行

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

break 命令(简写为 b)添加断点

break functionname在函数名为 functionname 的入口处添加一个断点
break LineNo在当前文件行号为 LineNo 处添加一个断点
break filename:LineNo在 filename 文件行号为 LineNo 处添加一个断点
  • 对于一般的 Linux 程序来说,main() 函数是程序入口函数,redis-server 也不例外,知道了函数的名字,就可以直接在 main() 函数处添加一个断点:

  • 添加好了以后,使用 run 命令重启程序,就可以触发这个断点了,GDB 会停在断点处

  • redis-server 默认端口号是 6379 ,这个端口号肯定是通过操作系统的 bind() 函数创建的,通过文件搜索,找到调用这个函数的文件,其位于 anet.c 455 行。

  • 使用 break 命令在这个地方加一个断点:
  • 由于程序绑定端口号是 redis-server 启动时初始化的,为了能触发这个断点,再次使用 run 命令重启下这个程序,GDB 第一次会触发 main() 函数处的断点,输入 continue 命令继续运行,接着触发 anet.c:455处的断点

  • 现在断点停在第 455 行,所以当前文件就是 anet.c,可以直接使用“break 行号”添加断点。
    • 例如,可以在第 458 行、464 行、466 行分别加一个断点,看看这个函数执行完毕后走哪个 return 语句退出,则可以执行:

  • 添加好这三个断点以后,使用 continue 命令继续运行程序,发现程序运行到第 466 行中断下来

  • 说明 redis-server 绑定端口号并设置侦听(listen)成功,再打开一个 SSH 窗口,验证一下,发现 6379 端口确实已经处于侦听状态了:

backtrace 命令(简写为 bt)查看当前调用堆栈

  • 接上,redis-server 现在中断在 anet.c:466 行,可以通过 backtrace 命令来查看当前的调用堆栈:

  • 一共有 6 层堆栈,最顶层是 main() 函数,最底层是断点所在的 anetListen() 函数,堆栈编号分别是 #0 ~ #5
  • 如果想切换到其他堆栈处,可以使用 frame 命令(简写为 f),该命令的使用方法是“frame 堆栈编号(编号不加 #)”。在这里依次切换至堆栈顶部,然后再切换回 #0 练习一下:

  • 通过查看上面的各个堆栈,可以得出这里的调用层级关系,即:
    • main() 函数在第 5162 行调用了 initServer() 函数
    • initServer() 在第 2795 行调用了 listenToPort() 函数
    • listenToPort() 在第 2651 行调用了 anetTcp6Server() 函数
    • anetTcp6Server() 在第 524 行调用了 _anetTcpServer() 函数
    • _anetTcpServer() 函数在第 501 行调用了 anetListen() 函数
    • 当前断点正好位于 anetListen() 函数中

info break 命令(简写为 info b)查看加了哪些断点及enable、disable 和 delete 

  • 通过上面的内容片段可以知道,目前一共增加了 6 个断点,其他信息比如每个断点的位置(所在的文件和行号)、内存地址、断点启用和禁用状态信息也一目了然。
  • 如果我们想禁用某个断点,使用“disable 断点编号”就可以禁用这个断点了,被禁用的断点不会再被触发;
  • 被禁用的断点也可以使用“enable 断点编号”重新启用。

  • 使用 disable 1 以后,第一个断点的 Enb 一栏的值由 y 变成 n,重启程序也不会再次触发

  • 如果 disable 命令和 enable 命令不加断点编号,则分别表示禁用和启用所有断点

  • 使用“delete 编号可以删除某个断点,如 delete 2 3 则表示要删除的断点 2 和断点 3:

  • 如果输入 delete 不加命令号,则表示删除所有断点。

list 命令(简写为 l)查看当前断点处的代码

  • 使用 frame 命令切换到刚才的堆栈 #3 处,然后输入 list 命令看下效果:

  • 断点停在第 2795 行,输入 list 命令以后,会显示第 2795 行前后的 10 行代码,再次输入 list 命令试一下:

  • 代码继续往后显示 10 行,第一次输入 list 命令会显示断点处前后的代码,继续输入 list 指令会以递增行号的形式继续显示剩下的代码行,一直到文件结束为止。
  • list 指令还可以往前和往后显示代码,命令分别是“list + (加号)”和list - (减号)”:

  • list 默认显示多少行可以通过修改相关的 GDB 配置。
  • list 不仅可以显示当前断点处的代码,也可以显示其他文件某一行的代码,更多的用法可以在 GDB 中输入 help list 查看(也可以通过):

  • 可以使用 list FILE:LINENUM 来显示某个文件的某一行处的代码。

print 命令(简写为 p)在调试过程中查看变量的值、修改当前内存中的变量值

  • 使用 print 命令分别打印出 server.port 、server.ipfd 、server.ipfd_count 的值
    • 其中 server.ipfd 显示 “{0 \}”,这是 GDB 显示字符串或字符数据特有的方式
    • 当一个字符串变量或者字符数组或者连续的内存值重复若干次,GDB 就会以这种模式来显示以节约空间。
  • print 命令不仅可以显示变量值,也可以显示进行一定运算的表达式计算结果值,甚至可以显示一些函数的执行结果值
    • 如输入 p &server.port 来输出 server.port 的地址值
    • 如果在 C++ 对象中,可以通过 p this 来显示当前对象的地址,也可以通过 p *this 来列出当前对象的各个成员变量值
    • 如果有三个变量可以相加( 假设变量名分别叫 a、b、c ),可以使用 p a + b + c 来打印这三个变量的结果值。

  • 若 func() 是一个可以执行的函数,p func() 命令可以输出该变量的执行结果
    • 如,某个时刻,某个系统函数执行失败了,通过系统变量 errno 得到一个错误码,则可以使用 p strerror(errno) 将这个错误码对应的文字信息打印出来
  • print 命令不仅可以输出表达式结果,同时也可以修改变量的值
    • 将上文中的端口号从 6379 改成 6666 试试:

  • 一个变量值修改后能否起作用要看这个变量的具体位置和作用
    • 对于表达式 int a = b / c ; 如果将 c 修改成 0 ,那么程序就会产生除零异常。
  • 利用 print 命令,不仅可以查看程序运行过程中的各个变量的状态值,也可以通过临时修改变量的值来控制程序的行为。

ptype (print type)就是输出一个变量的类型

  • 输出 Redis 堆栈 #4 的变量 server 和变量 server.port 的类型:

  • 对于一个复合数据类型的变量,ptype 不仅列出了这个变量的类型( 这里是一个名叫 redisServer 的结构体),而且详细地列出了每个成员变量的字段名。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值