Debugging with GDB
GDB(GNU Debugger)
是Linux
上通用的命令行调试工具,DDD(Data Display Debugger)
则是GDB
的GUI
版本。
可以使用他们在Linux
上实现c/c++
的调试工作
gdb --version
#=> GNU gdb (GDB) Fedora 8.1.1-3.fc28
ddd --version
#=> GNU DDD 3.3.12 (x86_64-redhat-linux-gnu)
GDB基本命令
设置断点
break function
break line_number
break filename:line_number
break filename:function
删除断点
delete breakpoint-list-number
根据断点编号来删除断点delete
删除所有断点clear
删除即将遇到的最近的一个断点clear function
clear line_number
clear filename:line_number
clear filename:function
enable/disable断点
disable breakpoint-list-number
disable
disable所有断点enable breakpoint-list-number
enable
enable所有断点enable once breakpoint-list-number
继续执行
- 单步执行
next
: step overnext n
: step over n timesstep
: step into
continue
继续执行到下一断点或程序结束continue n
继续执行,并忽略即将遇到的n
个断点finish
: step out
跳出当前的栈,即执行完当前所在的函数。如果其中遇到断点会暂停until
执行到下一个行号大于当前行号的语句,通常用来跳出循环
until
还可以后接参数,其参数与break
的参数同等含义
until 17
执行到第17行。until swap
执行到swap
函数的入口
条件断点
break break-args if (condition)
其中的condition
是一个返回Boolean
的表达式,括号是可选的。表达时中可以使用你程序中的函数。cond breakpoint-list-number condition
为已经存在的断点添加条件,使其变成条件断点cond breakpoint-list-number
将条件断点变成普通断点
断点执行命令
可以为每个断点配置一个命令脚本,每当断点被触发时,相应的命令就会被执行
commands breakpoint-list-number
...
your commands
...
end
通过这种方式可以在触发断点时自动输出相关变量的值
示例
(gdb) command 1
Type commands for when breakpoint 1 is hit, one per line.
End with a line saying just "end".
>silent
>printf "fibonacci was passed %d.\n", n
>continue
>end
silent
参数使gdb
在触发断点时不会打印断点本身属性,有利于我们自己的输出内容更连贯。
continue
使其在断点触发后自动继续运行,避免手动
这就相当于在程序中添加了一行输出变量的语句,但是却没有改变代码
宏定义
define macro_name
...
your commands
...
end
示例
(gdb) define print_and_go
Redefine command "print_and_go"? (y or n) y
Type commands for definition of "print_and_go".
End with a line saying just "end".
>printf $arg0, $arg1
>continue
>end
这样上面的断点命令可以写成
(gdb) commands 1
Type commands for when breakpoint 1 is hit, one per line.
End with a line saying just "end".
>silent
>print_and_go "fibonacci() was passed %d\n" n
>end
可以把这段宏定义写入.gdbinit
文件
Watchpoints
watch expression
当监视的表达式的值改变时将自动触发断点。
被监视表达式中的变量必须在当前作用域中,当超出作用域时自动删除watchpoint
。
watchpoint
不支持多线程,它只能监视变量在单个线程中的变化
查看变量值
print variablename
显示变量当前值,可以直接显示结构体,数组等复杂数据结构。print/x var
以十六进制输出变量的值,其它格式参数c
: 单个字符,s
: 字符串,f
: 浮点型display variablename
在每个断点被触发后自动显示变量的值(如果变量在作用域内的话)info display
列出所有display
列表。disable display display_list_number
enable display display_list_number
undisplay display_list_number
删除command
使用command
语法自定义输出变量与格式,示例:>p tmp->val >if (tmp->left != 0) >p tmp->left->val >else >printf "%s\n", "none" >end >if (tmp->right != 0) >p tmp->right->val >else >printf "%s\n", "none" >end >end
call function()
在command
语句中,使用使用call function()
的方式可以调用程序中的函数- 动态数组
对于普通数组int x[10]
,可以使用print x
直接将整个数组全部输出,
当时对于动态分配的数组int* y; y = (int*)malloc(10*sizeof(ing));
, 这种方法却不行,print y
输出的是y
的地址,print *y
输出的时y[0]
的值。
要想输出整个数组,需要使用*pointer@number_of_elements
的形式,即print *y*10
。 同时支持在输出时的类型转化:print (int [10])*y
ptype classname
输出类的基本结构:public
的变量与接口info locals
列出当前调用栈当中的所有局部变量的值
修改变量值
set var = new_value
调用栈(call stack)
backtrace
显示当前调用栈信息,每个栈都有一个序号(从0
开始)frame n
调转到指定栈,(n
为backtrace
列表中的序号)up
,down
向上/下跳转一个栈backtrace n
显示从栈顶开始的n
栈信息backtrace -n
显示从栈底开始的n
栈信息
命令缩写
b == break
n == next
s == step
c == continue
fin == finish
u = until
p == print
dis == disable
disp == display
undisp == undisplay
bt == backtrace
other
-tui
进入TUI
模式(可以显示源码),在普通模式下也可以通过按ctrl+x,ctrl+a
进入TUI
模式- startup files
.gdbinit
文件会在gdb
每次启动时被载入,所以可以在其中放入一些基础设定,避免每次打开时要重新配置断点等信息。
在$HOME
目录和项目目录中都可以有.gdbinit
文件,其中$HOME
目录的.gdbinit
文件会最先载入,然后载入可执行文件,最后载入项目文件中的.gdbinit
文件。也可以使用gdb -command=startup_files executable
指定其他启动文件
core dump
程序在运行中崩溃时通常会生成core dump
文件,生成文件的目录位置与文件名可以通过sysctl kernel.core_pattern
查看
core_pattern
的具体含义可以查看man 5 core
或kernel_doc
而且shell
通常会限制core
文件的产生,对于bash
可以使用ulimit -c
查看core
文件的大小限制,大于设定值的core
文件将不会产生
可以使用ulimit -c n
或ulimit -c unlimited
修改大小限制
有了core
文件后,我们就可以gdb program corefile
来启动调试,输入backtrace
查看崩溃时的调用栈,
从而发现究竟时那条语句触发的崩溃,以及崩溃时个变量的值是多少
而在Fedora
中,默认是不会产生core
文件的,但可以通过coredumpctl
命令来获取
Example 1. List all the core dumps of a program named foo
# coredumpctl list foo
Example 2. Invoke gdb on the last core dump
# coredumpctl gdb
Example 3. Show information about a process that dumped core, matching by its PID 6654
# coredumpctl info 6654
Example 4. Extract the last core dump of /usr/bin/bar to a file named bar.coredump
# coredumpctl -o bar.coredump dump /usr/bin/bar
在Ubuntu
中,其core
文件会被导向apport
服务,同样可以使用coredumpctl
来获取。
但也可以直接关闭apport
服务,service apport stop
,这样程序在崩溃时就会直接生成core
文件
多线程
info threads
列出当前所有线程,当前线程前有一个*
标记thread n
调转到指定线程(n
为info threads
命令列出的编号)break args thread n
在指定线程上设置断点
重定向
通常运行程序的输出与GDB在同一窗口,对于某些程序,特别是ncurse
程序这样很不适合调试。
这时需要将程序输出重定向到别的窗口。
- 先启动一个终端窗口作为运行程序的输出窗口,在该窗口中运行
tty
获取其ID,如/dev/pts/7
- 启动
GDB
,运行tty /dev/pts/7
将程序输出重定向到先前打开的终端窗口
其他工具
strace
打印出所有系统调用,调用参数,返回结果,错误信息ltrace
打印出所有库函数调用,调用参数,返回结果,错误信息lint
等静态分析工具- 内存检测工具, 参见Memory Debuggers
其它语言
GDB
还可以用来调试其它语言,如Java
, Python
, Perl
Chore
print打印完整字符串
使用print
打印字符串时,如果字符串太长,后面部分会被省略,以...
结尾,这是gdb
对打印字符的限制
可以通过show print elements
显示可以完整打印的长度
通过set print elements newNumber
设置新的打印长度
set print elements 0
取消打印长度限制