在Debian系统上,程序调试是开发和维护过程中不可或缺的一环。本篇教程主要介绍基本的 gdb 使用命令、调试 Debian 软件包、获得栈帧、高级 gdb 命令、检查库依赖性、动态调用跟踪工具、调试与 X 相关的错误、内存泄漏检测工具和反汇编二进制程序。
调试软件包列表:
软件包 | 流行度 | 大小 | 文档 |
---|---|---|---|
gdb |
V:14, I:96 | 11637 | 由 gdb-doc 包提供的“info gdb ” |
ddd |
V:0, I:7 | 4105 | 由 ddd-doc 包提供的“info ddd ” |
一、gdb命令
Debian 上原始的调试器是 gdb(1), 它能让在程序执行的时候检查程序。
通过如下所示的命令来安装 gdb 及其相关程序。
# apt-get install gdb gdb-doc build-essential devscripts
下面例子用 gdb(1) 在”程序”带有 “-g” 选项编译的时候来产生调试信息。
$ gdb program (gdb) b 1 # set break point at line 1 (gdb) run args # run program with args (gdb) next # next line ... (gdb) step # step forward ... (gdb) p parm # print parm ... (gdb) p parm=12 # set value to 12 ... (gdb) quit
二、调试Debian软件包
在Debian系统中,为了节省空间并提高安全性,默认情况下安装的二进制程序会经过strip处理,这意味着大部分调试符号(debugging symbols)在常规的软件包中是被移除的。若要使用gdb(1)对Debian软件包进行调试,需要安装相应的*-dbgsym软件包。例如,要调试coreutils程序,就需要安装coreutils-dbgsym软件包。这些源代码软件包与普通的二进制软件包是同时生成的,并且会自动创建*-dbgsym软件包。这些专门的调试软件包被单独存放在debian-debug存档库中。
如果一个需要被调试的软件包没有提供其 *-dbgsym 软件包,需要按如下所示的从源代码中重构并且安装它:
$ mkdir /path/new ; cd /path/new $ sudo apt-get update $ sudo apt-get dist-upgrade $ sudo apt-get install fakeroot devscripts build-essential $ apt-get source package_name $ cd package_name* $ sudo apt-get build-dep ./
按需修改bug即可。
软件包调试版本跟它的官方 Debian 版本不冲突,例如当重新编译已存在的软件包版本产生的 “+debug1” 后缀,如下所示是编译未发行的软件包版本产生的 “~pre1” 后缀。
$ dch -i
如下所示编译并安装带有调试符号的软件包:
$ export DEB_BUILD_OPTIONS="nostrip noopt" $ debuild $ cd .. $ sudo debi package_name*.changes
需要检查软件包的构建脚本并确保编译二进制的时候使用了 “CFLAGS=-g -Wall” 选项。
三、获得栈帧
当碰到程序崩溃的时候,报告 bug 时附上栈帧信息是个不错的注意。
使用如下方案之一,可以通过 gdb(1) 取得栈帧信息:
1、在 GDB 中崩溃的方案:
- 从 GDB 运行程序;
- 崩溃程序;
- 在 GDB 提示符输入 “bt”。
2、奔溃的方案:
- 更新“/etc/security/limits.conf”文件,包括下面内容:
* soft core unlimited
- shell 提示符下输入 “ulimit -c unlimited”;
- 从这个 shell 提示符运行程序;
- 崩溃的程序产生一个 core dump 文件;
- 加载 core dump 文件到 GDB,用 “gdb gdb ./program_binary core” ;
- 在 GDB 提示符输入 “bt”。
注意:如果看到堆栈顶部有一行或者多行有 “malloc()” 或 “g_malloc()”.当这个出现的时候,堆栈不是非常有用的。找到一些有用信息的一个简单方法是设置环境变量 “$MALLOC_CHECK_” 的值为 2 (malloc(3)).可以通过下面的方式在运行 gdb 时设置。
$ MALLOC_CHECK_=2 gdb hello
四、高级gdb命令
高级 gdb 命令列表:
命令 | 命令用途的描述 |
---|---|
(gdb) thread apply all bt |
得到多线程程序的所有线程栈帧 |
(gdb) bt full |
查看函数调用栈中的参数信息 |
(gdb) thread apply all bt full |
和前面的选项一起得到堆栈和参数 |
(gdb) thread apply all bt full 10 |
得到前10个调用的栈帧和参数信息,以此来去除不相关的输出 |
(gdb) set logging on |
把 gdb 的日志输出到文件 (默认的是 “gdb.txt “) |
五、检查库依赖性
按如下所示使用 ldd(1) 来找出程序的库依赖性:
$ ldd /usr/bin/ls librt.so.1 => /lib/librt.so.1 (0x4001e000) libc.so.6 => /lib/libc.so.6 (0x40030000) libpthread.so.0 => /lib/libpthread.so.0 (0x40153000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
因为 ls(1) 运行在 `chroot`ed 环境,以上的库在 `chroot`ed 环境也必须是可用的。
六、调试X相关错误
如果一个 GNOME 程序 preview1 收到了一个 X 错误,您应当看见一条下面这样的信息。
The program 'preview1' received an X Window System error.
如果就是这种情况,可以尝试在运行程序的时候加上 “–sync” 选项,并且在 “gdk_x_error” 函数处设置中断来获得栈帧信息。
七、内存泄漏检测工具
Debian 上有一些可用的内存泄漏检测工具。
内存泄漏检测工具的列表:
软件包 | 流行度 | 大小 | 说明 |
libc6-dev | V:259, I:569 | 12051 | mtrace(1):调试 glibc 中的 malloc |
valgrind | V:5, I:36 | 78191 | 内存调试器和分析器 |
electric-fence | V:0, I:3 | 73 | malloc(e) 调试器 |
libdmalloc5 | V:0, I:2 | 390 | 内存分配库调试 |
duma | V:0, I:0 | 296 | 在 C 和 C++ 程序中检测缓存溢出和缓存欠载( buffer under-runs )的库 |
leaktracer | V:0, I:1 | 56 | C++ 程序内存泄露跟踪器 |
八、反汇编二进制程序
可以使用下面的方式通过 objdump(1) 反编译二进制代码。
$ objdump -m i386 -b binary -D /usr/lib/grub/x86_64-pc/stage1
注意:gdb(1) 可以用来交互式反汇编代码。