07-gdb
调试
gcc -Wall -pedantic -ansi
-Wall
参数表示输出所有警告, -pedantic -ansi
表示严格遵循语法规则。
取样法调试程序
宏定义
当定义宏DEBUG时才输出调试信息,如编译器标志 -DDEBUG
#ifdef DEBUE
printf("variable x has value = %d\n", x);
#endif
还可以通过数字调试宏,如定义-DDEBUG=5
将启用BASIC_DEBUG
和SUPER_DEBUG
#define BASIC_DEBUG 1
#define EXTRA_DEBUG 2
#define SUPER_DEBUG 4
#if (DEBUG & EXTRA_DEBUG)
printf...
#endif
通过全局变量
可以在程序中添加一个全局变量,这样直接在启动的命令行上通过-d
选项切换是否启用调试模式。
if (debug) {
write_debug();
// ...
}
gdb
编译程序时使用 -g
选项使程序生成调试信息提高调试灵活性,每一个需要调试的源文件都需要添加。调试信息加入使得程序长度成倍增加,但运行内存不变。可以使用strip <file>
命令将可执行文件中调试信息删除。
> gdb <filename>
help
run 运行程序,将命令后的参数作为程序的参数传递给程序,出错时会输出错误提示
backtrace / bt / where
查看堆栈,程序是如何到当前位置的
frame <堆栈编号> 切换到其他堆栈处
print 打印变量,可以使用几乎所有符合C语法的表达式打印变量、数组元素、指针值;
并按打印顺序放入伪变量 $1 $2,最后一个为$ 倒数第二为$$
arr[0]@<number> 打印指定数目的数组元素
list 打印围绕当前位置前后的一段源码,也可以指定一个行号或函数名
whatis val 查看变量类型
ptype val 与whatis类似,但功能更强大,可查看复合数据类型,可打印成员变量
break 21 在第21行设置断点
tbreak 临时断点,出发后删除
next 单步步过
step 单步步进
cont/c 继续
return 立即退出函数,可指定返回值
finish 执行完剩余函数再正常退出
jump 跳过一段代码不执行,后续如果没有断点继续执行
jump <line_no> 跳转到指定行
jump +10 跳到当前行的下10行位置
jump *0x12345678 跳转到 0x12345678 地址的代码处
watch 监视一个变量或者一段内存,当这个变量或者内存的值发生变化时,GDB就会中断下来。被监视的某个变量或内存地址会产生一个 watch point(观察点)。
watch 整型变量
watch 指针变量,监视的是指针变量本身
watch *指针变量,监视的是指针所指的内容
watch 数组变量或内存区间
call func() 执行func()函数,同print func()
display arr[0]@5每次到断点时自动显示数组中5个元素
commands 设置每次到断点时自动执行的命令,无需暂停
> print x
> end
info break 查看设置的断点
info display 查看设置的display命令
disable break 1 禁用断点1
enable break 1 启用断点1
delete break 1 删除断点1
disassemble 查看某段代码的汇编指令
<方向键> 回卷上一条命令
<空命令> 直接按下回车键执行最近执行的命令,使用step next单步执行时及其有用
set args "arg1" "arg2" 设置程序启动参数,后续为空则清楚之前设置的参数
show args 查看之前设置的参数
set variable n=n+1 设置变量的值,通过断点的设置和相应操作,可以尝试修改程序而无需改变程序的源代码并重新编译。
终端图形化
能调试的同时显示代码,在gdb内部使用 (gdb) tui enable
,使用 help tui
查看详细帮助。
调试 core 文件
core文件用于在程序出错时记录内核信息。默认在出现错误时不会出现core文件,使用 ulimit -a
可以查看系统对于资源的限制。使用 ulimit -c unlimited
让系统不限制core文件的大小。在程序出错时就可以得到core文件。
使用 gdb execute core.1372
进行调试。
调试正在运行的程序
gdb execute -p pid
即可调试正在运行的程序,调试时程序会暂停。
调试多进程
set follow-fork-mod paren 默认,调试父进程
set follow-fork-mode child 调试子进程
set detach-on-fork [on|off] 默认on,调试当前进程时其他进程继续运行。为off时其他进程被挂起
info interiors 查看调试的进程
inferior <进程id> 切换当前调试的进程
调试多线程
info threads 查看当前进程所有线程运行情况
thread <线程id> 切换到指定线程
set scheduler-locking on/off 只运行当前线程或运行全部线程
thread apply [id1 id2 ...]/[all] [cmd]
将命令cmd指定对应编号线程,可用all指定所有线程。
break <location> thread <thread_no>
在 location 位置设置普通断点,该断点只作用在特定编号的线程上
默认使用 all-stop mode,借用 set scheduler-locking
让特定线程运行,其他线程暂停
- set scheduler-locking on,锁定线程,只有当前或指定线程可以运行;
- set scheduler-locking off,不锁定线程,会有线程切换;
- set scheduler-locking step,当单步执行某一线程时,其他线程不会执行,同时保证在调试过程中当前线程不会发生改变。但如果在该模式下执行 continue、until、finish 命令,则其他线程也会执行;
- show scheduler-locking,查看线程锁定状态;
gdb 模式状态
-
all-stop mode,全停模式,当程序由于任何原因在GDB下停止时,不止当前的线程停止,所有的执行线程都停止。这样允许你检查程序的整体状态,包括线程切换,不用担心当下会有什么改变。
-
non-stop mode,不停模式,调试器(如VS2008和老版本的GDB)往往只支持 all-stop 模式,但在某些场景中,我们可能需要调试个别的线程,并且不想在调试过程中影响其他线程的运行,这样可以把GDB的调式模式由 all-stop 改成 non-stop,7.0 版本的GDB引入了 non-stop 模式。在 non-stop 模式下 continue、next、step 命令只针对当前线程。
-
record mode,记录模式;
-
replay mode,回放模式;
-
scheduler-locking ,调度锁;
-
schedule-multiple,多进程调度;
常用命令
命令名称 | 命令缩写 | 命令说明 |
---|---|---|
run | r | 运行一个待调试的程序 |
continue | c | 让暂停的程序继续运行 |
next | n | 运行到下一行 |
step | s | 单步执行,遇到函数会进入 |
until | u | 运行到指定行停下来 |
finish | fi | 结束当前调用函数,回到上一层调用函数处 |
return | return | 结束当前调用函数并返回指定值,到上一层函数调用处 |
jump | j | 将当前程序执行流跳转到指定行或地址 |
p | 打印变量或寄存器值 | |
backtrace | bt | 查看当前线程的调用堆栈 |
frame | f | 切换到当前调用线程的指定堆栈 |
thread | thread | 切换到指定线程 |
break | b | 添加断点 |
tbreak | tb | 添加临时断点 |
delete | d | 删除断点 |
enable | enable | 启用某个断点 |
disable | disable | 禁用某个断点 |
watch | watch | 监视某一个变量或内存地址的值是否发生变化 |
list | l | 显示源码 |
info | i | 查看断点 / 线程等信息 |
ptype | ptype | 查看变量类型 |
disassemble | dis | 查看汇编代码 |
set args | set args | 设置程序启动命令行参数 |
show args | show args | 查看设置的命令行参数 |
prof gprof
在程序中链接特殊的函数库,编译时加上 -p 标志(针对prof)或 -pg 标志(针对gprof)。程序生成mon.out或gmon.out文件,使用prof/gprof读取v并生成报告。
断言
#include <assert.h>
void assert(int expression);
assert 宏对表达式进行求值,若结果非零则向标准错误写一些错误信息,然后调用abort结束程序。使用 -DNDEBUG
定义NDEBUG宏关闭断言。