Linux 包含了一个叫gdb 的GNU 调试程序。gdb 是一个用来调试C和C++程序的强力调试器。它使你能在程序运行时观察程序的内部结构和内存的使用情况。以下是 gdb 所提供的一些功能:它使你能监视你程序中变量的值、它使你能设置断点以使程序在指定的代码行上停止执行、它使你能一行行的执行你的代码。在命令行上键入gdb并按回车键就可以运行gdb 了。(Windows需要安装MinGW或者CygWin并且需要配置环境变量才可以使用)
GDB 命令行参数
启动 GDB:
l gdb executable
l gdb -e executable -c core-file
l gdb executable -pid process-id
(使用命令 'ps -auxw' 可以查看进程的 pid)
选项 | 含义 |
–help | 列出命令行参数。 |
–exec=file | 指定可执行文件。 |
–core=core-file | 指明 core 文件。 |
–command=file | 从指定文件中读取 gdb 命令。 |
–directory=directory | 把指定目录加入到源文件搜索路径中。 |
–cd=directory | 以指定目录作为当前路径来运行 gdb 。 |
–nx | 不要执行 .gdbinit 文件中的命令。默认情况下,这个文件中的命令会在所有命令行参数处理完后被执行。 |
–batch | 在非交互模式下运行 gdb 。从文件中读取命令,所以需要 -x 选项。 |
–symbols=file | 从指定文件中读取符号表。 |
-write | 允许对可执行文件和 core 文件进行写操作。 |
–quiet | 不要打印介绍和版权信息。 |
–tty=device | 指定 device 为运行程序的标准输入输出。 |
–pid=process-id | 指定要附属的进程 ID 。 |
GDB命令
GDB 中使用的命令:
命令 | 描述 |
help | 列出 gdb 帮助信息。 |
help topic | 列出相关话题中的 gdb 命令。 |
help command | 列出命令描述信息。 |
apropos search-word | 搜索相关的话题。 |
info args | 列出运行程序的命令行参数。 |
info breakpoints | 列出断点。 |
info break | 列出断点号。 |
info break breakpoint-number | 列出指定断点的信息。 |
info watchpoints | 列出观察点。 |
info registers | 列出使用的寄存器。 |
info threads | 列出当前的线程。 |
info set | 列出可以设置的选项。 |
Break and Watch | |
break funtion | 在指定的函数,或者行号处设置断点。 |
break +offset | 在当前停留的地方前面或后面的几行处设置断点。 |
break file:func | 在指定的file文件中的func处设置断点。 |
break file:nth | 在指定的file文件中的第nth行设置断点。 |
break *address | 在指定的地址处设置断点。一般在没有源代码时使用。 |
break line-number ifcondition | 如果条件满足,在指定位置设置断点。 |
break line threadthread-number | 在指定的线程中中断。使用info threads可以显示线程号。 |
tbreak | 设置临时的断点。中断一次后断点会被删除。 |
watch condition | 当条件满足时设置观察点。 |
clear | 清除函数func处的断点。 |
delete | 删除所有的断点或观察点。 |
delete breakpoint-number | 删除指定的断点,观察点。 |
disable breakpoint-number-or-range | 不删除断点,仅仅把它设置为无效,或有效。 |
enable oncebreakpoint-number | 设置指定断点有效,当到达断点时置为无效。 |
enable del breakpoint-number | 设置指定断点有效,当到达断点时删除它。 |
finish | 继续执行到函数结束。 |
Line Execution | |
step | 进入下一行代码的执行,会进入函数内部。 |
next | 执行下一行代码。但不会进入函数内部。 |
until until line-number ifcondition | 继续运行直到到达指定行号,或者函数,地址等。 |
return | 弹出选中的栈帧(stack frame)。如果后面指定参数,则返回表达式的值。 |
stepi | 执行下一条汇编/CPU指令。 |
info signals | 当收到信号时执行下列动作:nostop(不要停止程序),stop(停止程序执行),print(显示信号),noprint(不显示),pass/noignore(允许程序处理信号),nopass/ignore(不让程序接受信号) |
where | 显示当前的行号和所处的函数。 |
Program Stack | |
backtrace | 显示当前堆栈的追踪,当前所在的函数。 |
backtrace full | 打印所有局部变量的值。 |
frame number | 选择指定的栈帧。 |
up number | 向上或向下移动指定个数的栈帧。 |
info frame addr | 描述选中的栈帧。 |
info args | 显示选中栈帧的参数,局部变量,异常处理函数。all-reg也会列出浮点寄存器。 |
Source Code | |
list | 列出相应的源代码。 |
set listsize count | 设置list命令打印源代码时的行数。 |
directory directory-name | 在源代码路径前添加指定的目录。 |
directory | 当后面没有参数时,清除源代码目录。 |
Examine Variables | |
print variable | 打印指定变量的值。 |
p *array-var@length | 打印arrary-var中的前length项。 |
p/x var | 以十六进制打印整数变量var。 |
p/d var | 把变量var当作有符号整数打印。 |
p/u var | 把变量var作为无符号整数打印。 |
p/o var | 把变量var作为八进制数打印。 |
p/t var | 以整数二进制的形式打印var变量的值。 |
p/c variable | 当字符打印。 |
p/f variable | 以浮点数格式打印变量var。 |
p/a variable | 打印十六进制形式的地址。 |
x/w address | 打印指定的地址,以四字节一组的方式。 |
call expression | 类似于print,但不打印 void 。 |
disassem addr | 对指定地址中的指令进行反汇编。 |
Controlling GDB | |
set gdb-option value | 设置 GDB 的选项。 |
set print array on | 以可读形式打印数组。默认是 off 。 |
set print array-indexes on | 打印数组元素的下标。默认是 off 。 |
set print pretty on | 格式化打印 C 结构体的输出。 |
set print union on | 打印 C 中的联合体。默认是 on 。 |
set print demangle on | 控制 C++ 中名字的打印。默认是 on 。 |
Working Files | |
info files | 列出当前的文件,共享库。 |
file file | 把file当作调试的程序。如果没指定参数,丢弃。 |
core file | 把file当作 core 文件。如果没指定参数,则丢弃。 |
exec file | 把file当作执行程序。如果没指定参数,则丢弃。 |
symbol file | 从file中读取符号表。如果没指定参数,则丢弃。 |
load file | 动态链入file文件,并读取它的符号表。 |
path directory | 把目录directory加入到搜索可执行文件和符号文件的路径中。 |
Start and Stop | |
run | 从头开始执行程序,也允许进行重定向。 |
continue | 继续执行直到下一个断点或观察点。 |
continue number | 继续执行,但会忽略当前的断点number次。当断点在循环中时非常有用。 |
kill | 停止程序执行。 |
quit | 退出 GDB 调试器。 |
GDB 操作提示
l 不要使用编译器的优化选项,比如: "-O","-O2"。因为编译器会为了优化而改变程序流程,那样不利于调试。
l 在 GDB 中执行 shell 命令可以使用:shell command
l GDB 命令可以使用 TAB 键来补全。按两次 TAB 键可以看到所有可能的匹配。
l GDB 命令缩写:例如 info bre 中的 bre 相当于 breakpoints。
gdb -tui 代码窗口相关命令(CTRL + X + a):
info win 显示窗口的大小
layout next 切换到下一个布局模式
layout prev 切换到上一个布局模式
layout src 只显示源代码
layout asm 只显示汇编代码
layout split 显示源代码和汇编代码
layout regs 增加寄存器内容显示
focus cmd/src/asm/regs/next/prev 切换当前窗口
refresh 刷新所有窗口
tui reg next 显示下一组寄存器
tui reg system 显示系统寄存器
update 更新源代码窗口和当前执行点
winheight name +/- line 调整name窗口的高度
tabset nchar 设置tab为nchar个字符
gdb在执行中,会自动跟踪代码
GDB 在 Emacs 中的操作:
emacs 按键 | 动作 |
M-x gdb | 切换到 gdb 模式。 |
C-h m | 显示 gdb 模式介绍信息。 |
M-s | 等同于gdb 中的 step 命令。 |
M-n | 等同于gdb 中的 next 命令。 |
M-i | 等同于gdb 中的 stepi 命令。 |
C-c C-f | 等同于gdb 中的 finish 命令。 |
M-c | 等同于gdb 中的 continue 命令。 |
M-u | 等同于gdb 中的 up 命令。 |
M-d | 等同于gdb 中的 down 命令。 |
最近使用GDB调试程序的一点经验之谈:
1、程序运行中生成一个Core文件便于保护现场,调试程序,gcore可以满足这种需求:
gcore:对正在运行的进程生成一个内存转储文件,方便保护现场。例如:
– gcore -s -c test.core pid
– 用-s选项使进程在转储过程中处于停止状态,可以保证转储文件的一致性。若不用-c选项指定转储文件名,则会使用默认的core.pid形式的文件名。
2、我的linux虚拟机中调试程序时,发现直接run程序没问题,但gdb调试的时候就提示找不到某个动态库
原因如下:
1)我的虚拟机里同一个用户下由于要编译测试多个不同版本的代码,而每个版本都有不同的运行环境(如:PATH、LD_LIBRARY_PATH等环境变量设置)
2)我目前使用csh,为了快速切换不同的编译环境,我把运行环境所需的环境变量写到不同的.cshrc脚本中,需要切换时运行source .cshrc_XXX
3) GDB调试程序时会启一个新的shell,而且新shell启动时使用默认的.cshrc脚本,从而导致上述的问题,从本质上来说GDB使用的shell的环境非我们要调试程序所需的shell环境设置
解决方法:
1)最简单:将所有动态库的查找位置都设置到默认.cshrc中的LD_LIBRARY_PATH的环境变量中
2)其他方法有待探索
下面转载一篇不错的gdb总结
gdb高级用法
• fin(ish):持续运行至当前堆栈帧结束,并显示返回值。
• adv(ance) [LOC]:持续运行至指定的位置。例如:
– adv func
– adv *0x40107b
• att(ach) pid:将gdb附加到指定的进程上并中断在该进程当前执行位置处。
• det(ach):使gdb不再附加在当前进程上并恢复被调试进程的运行。
• info addr <symbol>:查找指定符号对应的地址。
• info sym <addr>:查找指定地址对应的符号。
• 当符号自动补全的范围太大时可以用单引号开头强制前缀匹配,这样来补全C++类成员很方便。
• bt [full]:查看所有堆栈帧,若加上full选项则还会显示每个堆栈帧内的局部变量值。
• f(rame) [FRAMENO]:选择或显示当前堆栈帧。
• up/down:将向上/向下一层的堆栈帧作为当前选择的帧。
常用辅助调试工具
• gcore:对正在运行的进程生成一个内存转储文件,方便保护现场。例如:
– gcore -s -c test.core pid
– 用-s选项使进程在转储过程中处于停止状态,可以保证转储文件的一致性。若不用-c选项指定转储文件名,则会使用默认的core.pid形式的文件名。
• pstack:查看转储文件/正在运行的进程中的完整堆栈帧。例如:
– pstack -t -O pid/corefile
– 用-t选项可以显示提取堆栈帧时进程被pstack暂停的时间;用-O选项可以显示堆栈帧中每个函数所处目标文件的完整路径。
– 默认情况下,为了保证结果的一致性,pstack在提取堆栈帧时会暂停对应进程的活动,但在进程逻辑对时间很敏感时这样可能导致逻辑混乱引起进程崩溃;此时可以使用-n选项强制pstack不暂停要查看的进程,不过相应地取得的堆栈帧数据就有可能出现问题。
• truss:仅用于FreeBSD,跟踪命令行或已运行进程的系统调用序列和信号。例如:
– 跟踪命令行:truss /bin/echo hello
– 跟踪已运行的进程:truss -p 1377
• strace:可用于FreeBSD/Linux,功能同truss相近,输出结果详细一些,包含了系统调用的具体参数。一般用法同truss。
• ltrace:可用于FreeBSD/Linux,跟踪命令行或已运行进程的运行库调用序列。一般用法同truss。
• ktrace/kdump:它们是FreeBSD提供的内核级进程跟踪工具,会监控进程的系统调用、文件系统节点转换、信号处理等各种内核级操作。用法:
– 跟踪命令行并记录在ktrace.out中:ktrace command
– 跟踪活动进程并记录在ktrace.out中:ktrace -p pid
– 关闭所有跟踪活动:ktrace -C
– 显示ktrace.out中的跟踪结果:kdump
• valgrind:x86硬件虚拟机,其中的memcheck模块可用来检测程序是否存在内存泄漏:
– valgrind -v --num-callers=20 --leak-check=yes --leak-resolution=high--show-reachable=yes command
– 结果中的LEAK SUMMARY总结了内存泄漏情况,其中需要注意的是definitely lost和possibly lost两部分。
调试建议
• 产品程序在编译时留下尽可能多的调试符号信息(用-g3选项编译),这些信息不会减慢运行速度,但在出现问题时对调试工作很有帮助。
• 调试用程序不使用优化,并关闭STL的存储池分配方式,这样可以避免valgrind错误地报告内存泄漏,选项为-O0 -g3 –DGLIBCXX_FORCE_NEW