GDB:the GNU Project Debugger。
简单来说,GDB 就是 Linux(或者说 *nix ?)环境下最通用的调试工具,它是非图形化的,人机通过“键入”各种指令直接交互,省去了鼠标点来点去之繁琐,并且在一定程度上据说也可以实现自动化调试。
按官方的说法,GDB 可以让你在另一个程序运行的时候看到程序内部发生的变化,或者看到程序崩溃时的当前操作。更具体地,GDB 主要可以做 4 件事:
- 启动程序,并指定任意运行条件
- 在给定条件下停止程序执行
- 在程序停止时,检查已经发生了什么
- 改变程序,“越过”一个 bug 去寻找更多 bug
不过知道这些其实没什么用处,GDB 归根结底是拿来用的,将之应用于实际才是硬道理。作为一个已经学习计算机科学半个多时代的“人才”,鄙人接触 Linux 也已经有几个年头,对 GDB 的大名岂知是“早有耳闻”,简直是“如雷贯耳”并且“久仰久仰”。可惜此前从未用过,每次别人说“你用 GDB 跟踪一下”的时候,鄙人总是搪塞过去,转而默默地用“printf”。所幸,最近终于大脑开窍,主动了解、学习起了 GDB,不得不说其简洁易上手,好用得紧。而这篇,就算是我对 GDB 基本用法的一个总结。
加了调试选项才能正确地显示函数、变量和代码等信息,选项很简单,就一个 -g。
在最一般的情况下,比如调试 a.out,只需要“gdb a.out”即可。其他用法还包括指定 core dump 后的 dump 文件和调试服务程序时绑定 Pid。
退出的命令是 quit,快捷键 Ctrl+d 。
在交互中,直接回车代表输入和上一条一样的命令,这在单步调试时比较有用。另外支持用 TAB 键自动补全。
查看代码用 list,简写为 l。
(gdb) l (gdb) l <linenum> #默认输出行号附近 10 行代码
设置断点用 break,简写为 b。基本用法有:
(gdb) b <linenum> # 在某行设置断点 (gdb) b <funcname> # 在某一函数处设置断点 (gdb) info b # 查看当前断点
设置断点后,还可以维护这些断点,比如删除、disable 以及激活等。其实维护的可能不仅仅是代码中的断点,还可能有观察点等,统称为停止点。
(gdb) delete # 删除所有断点 (gdb) delete <pointnum> # 删除某一断点 (gdb) disable # disable 所有断点,简写 dis (gdb) dis <pointnum> # disable 某一断点 (gdb) enable # enable 所有断点,简写 en (gdb) en <pointnum> # enable 某一断点
(gdb) r # 即 run,从开始运行 (gdb) c # continue,继续运行到某一停止点 (gdb) n # next,单步调试,不会进入函数 (gdb) s # step,单步调试,会进入函数内部
(gdb) p i # print,打印变量或静态数组 i (gdb) p *array@len # 打印动态数组 array,指定长度 len (gdb) bt # backtrace,查看栈信息,即函数调用信息
(gdb) u # until,执行到当前循环退出 (gdb) finish # 执行到当前函数退出
可以用 display 命令设置自动显示的变量,当程序停止或单步跟踪时变量会自动打印。具体用法有:
(gdb) display <expr> (gdb) display/<fmt> <expr> (gdb) display/<fmt> <addr> # addr 代表内存地址 (gdb) undisplay <dnums...> # 删除自动显示 (gdb) delete display <dnums...> # 删除自动显示 (gdb) disable display <dnums...> # 使某个自动显示失效 (gdb) enable display <dnums...> # 激活被失效的自动显示 (gdb) info display # 查看 display 信息
就像 printf 中的格式控制一样,gdb 可以按照不同格式打印值,具体有:
x 按十六进制格式显示变量。 d 按十进制格式显示变量。 u 按十六进制格式显示无符号整型。 o 按八进制格式显示变量。 t 按二进制格式显示变量。 a 按十六进制格式显示变量。 c 按字符格式显示变量。 f 按浮点数格式显示变量。
比如:
(gdb) p i $2 = 101 (gdb) p/a i $3 = 0x65 (gdb) p/x i $4 = 0x65 (gdb) p/t i $5 = 1100101 (gdb) p/c i $6 = 101 'e' (gdb) p/f i $7 = 1.41531145e-43
使用 examine 命令查看内存,简写为 x,其提供 3 个可选选项:n、f、u。
x/<n/f/u> <addr> n 显示几个单位的地址内容 f 显示的格式 u 从当前地址请求的字节数,默认 4B。 b - 单字节,h - 双字节, w - 4字节, g - 8字节
例如,x /3uh 0x8170 表示,从地址 0x8170 处读取内存,以双字节为单位,读取 3 个单位并以 16 进制显示。
太难了,臣妾学不会啊……