GDB
Table of Contents
- 1. GDB 简介
- 2. GDB 基本用法
- 3. GDB 高级用法
- 3.1. Examining the Symbol Table
- 3.2. Convenience Variables
- 3.3. Convenience Functions
- 3.4. User-defined Commands (define)
- 3.5. Remote Debugging
- 3.6. Tracepoints
- 3.7. 调试 fork
- 3.8. 多线程调试
- 3.9. Signals
- 3.10. 保存和恢复 snapshot (checkpoint/restart)
- 3.11. 记录屏幕输出到文件 (set logging)
- 3.12. text user interface(TUI)
- 4. GDB tips
- 5. LLDB
- 6. DBX
1. GDB 简介
The GNU Debugger, usually called just GDB and named gdb as an executable file, is the standard debugger for the GNU operating system. However, its use is not strictly limited to the GNU operating system; it is a portable debugger that runs on many Unix-like systems and works for many programming languages, including Ada, C, C++, Objective-C, Free Pascal, Fortran, Java and partially others.
Official GDB website: http://www.gnu.org/software/gdb/
Debugging with gdb (online manual): https://sourceware.org/gdb/current/onlinedocs/gdb/
GDB Wiki: https://sourceware.org/gdb/wiki/HomePage
GDB Quick Reference : http://www.cs.berkeley.edu/~mavam/teaching/cs161-sp11/gdb-refcard.pdf
Using The GNU GDB Debugger, by Peter Jay Salzman: http://rsquared.sdf.org/gdb/
GDB commands by function - simple guide: http://web.cecs.pdx.edu/~jrb/cs201/lectures/handouts/gdbcomm.txt
1.1. Debugger 工作原理
gdb 在 POSIX 系统中的实现主要依赖 ptrace
系统调用。
#include <sys/ptrace.h> long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
The ptrace
system call provides a means by which a parent process may observe and control the execution of another process, and examine and change its core image and registers. It is primarily used to implement breakpoint debugging and system call tracing.
The parent can initiate a trace by calling fork(2) and having the resulting child do a PTRACE_TRACEME
, followed (typically) by an exec(3). Alternatively, the parent may commence trace of an existing process using PTRACE_ATTACH
.
ptrace 实例可参考:
http://www.linuxjournal.com/article/6100
调试器的简单实现可参考:
http://eli.thegreenplace.net/2011/01/23/how-debuggers-work-part-1/
http://godorz.info/2011/02/how-debuggers-work-part-1/
http://www.cnblogs.com/quixotic/archive/2012/01/16/2323541.html
MS Windows 调试器原理:
How Windows Debuggers Work
Writing a basic Windows debugger
2. GDB 基本用法
2.1. 常用基本命令
命令 | 描述 |
---|---|
run (r) | 开始运行程序 |
start | 开始运行程序,停在 main 函数处 |
continue (c) | 继续运行程序,直到下一个断点 |
next (n) | 执行下一行代码。如果是函数调用,也当作一行代码 |
step (s) | 跟入函数 |
finish (fin) | 运行直到当前函数返回,并打印出函数返回值 |
list (l) | 列出 10 行源码,其后可以跟上“[文件名:]行号”或“[文件名:]函数名”等 |
break (b) | 设置断点 |
info break | 查看断点 |
info locals | 查看当前栈帧中的所有局部变量 |
delete [N] | 删除断点。N是断点号,可以通过 info break 查看断点号 |
until LOCATION | 运行到 LOCATION 处(如某行) |
backtrace (bt) | 查看当前函数调用栈 |
where | 查看当前函数调用栈,和命令 bt 相同 |
print (p) | 打印或修改变量值。如 p var1=1,变量 var1 的值被修改为 1 |
help (h) | 显示帮助 |
quit (q) | 退出 |
2.2. 编译程序时加入调试信息
用 gcc 的 -g[level]
选项可以在编译程序时加入调试符号信息。
gcc -g -o prog prog.c
level 可以是 0 到 3,默认为 2。指定为 0 时禁止生成调试信息;为生成更多的调试信息(如 C 中宏名可见),可以指定为 3。
gcc -g3 -o prog prog.c
用 gcc 的 -ggdb[level]
选项可以生成 GNU 的扩展调试信息,如果是用 GDB 调试程序,则建议用 -ggdb3
进行编译。
gcc -ggdb3 -o prog prog.c
https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html#Debugging-Options
2.2.1. 禁止优化
GDB 可以调试优化过的代码,但这会增加调试的难度,可以指定 gcc 的 -O0
选项来禁止优化。
2.3. 在 GDB 中运行程序
方法一:启动时指定 prog,再 run
$ gdb prog (gdb) run
方法二:启动时不指定,启动后用 file 命令指定 prog,再 run
$ gdb (gdb) file /path/to/prog (gdb) run
说明:术语 inferior 在 GDB 中是指被调试程序。
2.3.1. attach 到正在运行的进程
方法一:
$ gdb -p pid
方法二:
$ gdb -q (gdb) attach pid
方法三:
$ gdb /path/to/program pid
注意:如果目录下恰好有个文件名叫 pid,则 GDB 会首先认为它是一个 core 文件,而不会认为它是进程号。
调试完成后,可以运行 detach
命令结果对进程的调试。
2.3.2. 设置 inferior 的运行参数
方法一: set args
命令可指定运行时参数, show args
命令可以查看设置好的运行参数。
例如程序 prog 的参数为 arg1 arg2
$ gdb -q prog (gdb) set args arg1 arg2 (gdb) run
方法二:在运行 run 命令时直接指定参数,如 run arg1 arg2
$ gdb -q prog (gdb) run arg1 arg2
方法三:启动时直接指定程序的参数(用--args 终止 gdb 对参数进行分析)
$ gdb prog arg1 arg2 $ gdb --args prog arg1 arg2 (gdb) run
其中 arg1 arg2 是 prog 的参数,但如果它们同时也是 gdb 的参数,则必须用--args 终止 gdb 对参数进行分析。
2.3.2.1. 设置 inferior 的 stdin 和 stdout
程序 stdin 和 stdout 的重定向可以直接在运行 run 命令时指定。
$ gdb -q prog (gdb) run <file1 >file2
2.3.3. 设置 inferior 的环境变量
GDB 会继承 shell 中的环境变量,在 GDB 中可以直接修改环境变量,不用重启 GDB 就可以生效。
GDB 命令 | 说明 |
---|---|
show env [varname] | 查看所有或某个环境变量的值 |
set env varname [=value] | 设置环境变量 |
unset env varname | 取消对环境变量的设置 |
2.3.3.1. 查看和设置 PATH
PATH 环境变量的查看和设置除了可用前面介绍的方法外,还有其它方法。
GDB 命令 | 说明 |
---|---|
show paths | 查看 PATH 变量的值,和 show env PATH 作用一样 |
path dir | 把 dir 添加到 PATH 中 |
2.3.4. 设置 inferior 的工作目录
当用 run 命令执行程序时,程序的工作目录继承自 GDB 的工作目录。
GDB 命令 | 说明 |
---|---|
pwd | 打印 GDB 的工作目录 |
cd [directory] | 设置 GDB 的工作目录,如果不指定 directory,默认设置为用户 HOME 目录 |
2.3.5. kill inferior
The kill
command is useful if you wish to recompile and relink your program, since on many systems it is impossible to modify an executable file while it is running in a process. In this case, when you next type run
, gdb notices that the file has changed, and reads the symbol table again (while trying to preserve your current breakpoint settings).
http://www.sourceware.org/gdb/current/onlinedocs/gdb/Kill-Process.html
2.3.6. 分析 core dump 文件
调试程序时,可以用下面命令加载 core dump 文件:
$ gdb /path/to/executable /path/to/corefile $ gdb /path/to/executable -c /path/to/corefile # 和上面命令相同
在 gdb 中用 bt full
可查看程序 crash 的位置。
(gdb) bt full
2.4. Breakpoints, Watchpoints, and Catchpoints
A breakpoint stops your program whenever a particular point in the program is reached.
A watchpoint stops your program whenever the value of a variable or expression changes.
A catchpoint stops your program whenever a particular event occurs.
2.4.1. 断点
break 命令 | 说明 |
---|---|
break | sets a breakpoint at the next instruction to be executed in the selected stack frame. |
break linenum | 在行 linenum 处设置断点 |
break +/-offset | 在和当前行+/-offset 处设置断点 |
break filename:linenum | 在文件 filename 中行 linenum 处设置断点 |
break function | 在函数 function 处设置断点 |
break function:label | 在函数 function 中的 label 处设置断点 |
break filename:function | 在文件 filename 中函数 function 处设置断点 |
break *address | 在地址 address 处设置断点(在没有调试符号信息或没有源码时很有用) |
break ... if cond | 设置条件断点 |
tbreak ... | 设置临时断点(断下一次后,自动删除断点),参数和 break 一样。 |
rbreak regex | 在匹配上正则表达式 regex 的函数处设置断点(正则语法和 grep 命令一样,如 rbreak . 对所有函数设置断点) |
rbreak filename:regex | 在文件 finename 中匹配上正则表达式 regex 的函数处设置断点 |
break ... thread threadno | 对指定线程设置断点。threadno 为线程标识(可通过"info threads"查看)。thread threadno 位于 if cond 前或后都行。如:(gdb) break frik.c:13 thread 28 if bartab > lim |
info break | 查看断点 |
disable [breakpoints] | disable 断点 |
enable [breakpoints] | enable 断点 |
clear location | 删除断点 |
delete [breakpoints] | 删除断点。要提供断点编号,若省略编号则删除所有断点 |
save breakpoints [filename] | 把断点设置保存到文件 filename 中,用 source filename 命令可以重新从文件中加载断点设置 |
2.4.1.1. 设置断点举例:条件断点
假设源码 1.c 的内容如下:
1: #include <stdio.h> 2: 3: void show(int num) 4: { 5: int i; 6: for (i = 0; i<num; ++i) { 7: printf("i is %d.\n", i); 8: } 9: } 10: 11: int main() 12: { 13: int x = 10; 14: show(x); 15: return 0; 16: }
在 for 循环里(第 7 行处)设置条件断点, break 7 if i==5
:
$ gcc -g -o 1 1.c $ gdb -q ./1 Reading symbols from ./1...done. (gdb) break 7 if i==5 Breakpoint 1 at 0x40051a: file 1.c, line 7. (gdb) r Starting program: /home/cig01/test/1 i is 0. i is 1. i is 2. i is 3. i is 4. Breakpoint 1, show (x=10) at 1.c:7 7 printf("i is %d.\n", i);
2.4.1.2. 删除断点 (clear, delete)
If you want to remove the breakpoint by its location, use clear
.
If you want to remove the breakpoint by its identifier, use delete
.
(gdb) clear main Deleted breakpoint 1 (gdb) clear file1.c:20 Deleted breakpoint 2 (gdb) delete 3 Deleted breakpoint 3
2.4.2. Watchpoint
Watchpoints are similar to breakpoints. However, watchpoints are not set for functions or lines of code. Watchpoints are set on variables. When those variables are read or written, the watchpoint is triggered and program execution stops.
watchpoint 相关命令 | 描述 |
---|---|
Set a write watchpoint | watch [-l] expr [thread threadnum] [mask maskvalue] |
Set a read watchpoint | rwatch [-l] expr [thread threadnum] [mask maskvalue] |
Set a read/write watchpoint | awatch [-l] expr [thread threadnum] [mask maskvalue] |
Prints a list of watchpoints | info watchpoints [n...] |
参考:https://sourceware.org/gdb/current/onlinedocs/gdb/Set-Watchpoints.html#Set-Watchpoints
watchpoint 使用实例:http://www.unknownroad.com/rtfm/gdbtut/gdbwatch.html
2.4.3. Catchpoints
You can use catchpoints to cause the debugger to stop for certain kinds of program events, such as C++ exceptions or the loading of a shared library.
设置 catchpoint 的命令为: catch event
,其中 event 可以为:
- throw [regexp] : C++抛出异常时中断
- rethrow [regexp] : C++重新抛出异常时中断
- catch [regexp] : C++捕捉到异常时中断
- exec : 调用系统调用 exec 时中断
- syscall [name | number] : 调用系统调用时中断
- fork : 调用系统调用 fork 时中断
- vfork : 调用系统调用 vfork 时中断
- load [regexp] : 载入共享库时中断
- unload [regexp] : 卸载共享库时中断
- signal [signal... | 'all'] : 捕捉到信号时中断
设置临时(中断一次后自动删除)的 catchpoint 可以执行命令: tcatch event
Reference:
https://sourceware.org/gdb/current/onlinedocs/gdb/Set-Catchpoints.html#Set-Catchpoints
2.5. Breakpoint Command Lists
You can give any breakpoint (or watchpoint or catchpoint) a series of commands to execute when your program stops due to that breakpoint. For example, you might want to print the values of certain expressions, or enable other breakpoints.
commands [range...] ... command-list ... end
Specify a list of commands for the given breakpoints. With no argument, commands refers to the last breakpoint, watchpoint, or catchpoint.
You can use breakpoint commands to start your program up again. Simply use the continue
command, or step
, or any other command that resumes execution.
If the first command you specify in a command list is silent, the usual message about stopping at a breakpoint is not printed. This may be desirable for breakpoints that are to print a specific message and then continue. If none of the remaining commands print anything, you see no sign that the breakpoint was reached. silent is meaningful only at the beginning of a breakpoint command list.
参考:
https://sourceware.org/gdb/current/onlinedocs/gdb/Break-Commands.html#Break-Commands
2.5.1. Breakpoint Command 实例
For example, here is how you could use breakpoint commands to print the value of x at entry to foo whenever x is positive.
break foo if x>0 commands silent printf "x is %d\n",x continue end
2.5.2. 删除 Breakpoint Command
To remove all commands from a breakpoint, type commands and follow it immediately with end; that is, give no commands.
2.6. Dynamic Printf (dprintf)
The dynamic printf command dprintf
combines a breakpoint with formatted printing of your program’s data to give you the effect of inserting printf calls into your program on-the-fly, without having to recompile it.
注:有了 dprintf 命令,再也不用在源码中插入 printf 语句来辅助调试了。
参考:https://sourceware.org/gdb/current/onlinedocs/gdb/Dynamic-Printf.html#Dynamic-Printf
Dynamic Printf 相关命令 | 描述 |
---|---|
dprintf location,template,expression[,expression...] | Whenever execution reaches location, print the values of one or more expressions under the control of the string template. |
set dprintf-style style | style 可以是 gdb/call/agent。gdb (Handle the output using the gdb printf command), call (Handle the output by calling a function in your program). |
set dprintf-function function | Set the function to call if the dprintf style is call. |
set dprintf-channel channel | If set to a non-empty value, gdb will evaluate it as an expression and pass the result as a first argument to the dprintf-function |
2.6.1. dprintf 实例
As an example, if you wanted dprintf output to go to a logfile that is a standard I/O stream assigned to the variable mylog, you could do the following:
(gdb) set dprintf-style call (gdb) set dprintf-function fprintf (gdb) set dprintf-channel mylog (gdb) dprintf 25,"at line 25, glob=%d\n",glob Dprintf 1 at 0x123456: file main.c, line 25. (gdb) info break 1 dprintf keep y 0x00123456 in main at main.c:25 call (void) fprintf (mylog,"at line 25, glob=%d\n",glob) continue (gdb)
Note that the info break
displays the dynamic printf commands as normal breakpoint commands; you can thus easily see the effect of the variable settings.
2.7. Examining the Stack
When your program is started, the stack has only one frame, that of the function main. This is called the initial frame or the outermost frame. Each time a function is called, a new frame is made. Each time a function returns, the frame for that function invocation is eliminated.
call stack 相关命令 | 描述 |
---|---|
bt | backtrace 的简写。Print a backtrace of the entire stack. |
info stack | 同上 |
bt n | Print only the innermost n frames. |
bt -n | Print only the outermost n frames. |
bt full |
Print the values of the local variables also. |
bt no-filters | Do not run Python frame filters on this backtrace. |
f n | frame n 的简写。Move from one stack frame to another. |
select-frame n | 同上,但不会有输出。This is the silent version of command frame. |
up n | Move n frames up the stack; n defaults to 1. |
down n | Move n frames down the stack; n defaults to 1. |
f | frame 的简写。Prints a brief description of the currently selected stack frame. |
info f | info frame 的简写。Prints a verbose description of the selected stack frame. |
info args | Print the arguments of the selected frame. (显示正在运行的函数参数) |
info locals |
Print the local variables of the selected frame. |
参考:https://sourceware.org/gdb/current/onlinedocs/gdb/Backtrace.html#Backtrace
2.7.1. 切换 frame 实例
假设有程序 1.c 如下所示:
#include <stdio.h> int fun1(int i) { return i + 1; } int main() { int n = 1; return fun1(n); }
下面演示了用命令 f 切换栈帧。
$ gdb -q ./1 Reading symbols from ./1...done. (gdb) b fun1 Breakpoint 1 at 0x4004bd: file 1.c, line 5. (gdb) r Starting program: /home/cig01/test/1 Breakpoint 1, fun1 (i=1) at 1.c:5 5 return i + 1; (gdb) p i # 能打印fun1中局部变量i的值 $1 = 1 (gdb) p n # main中的局部变量n不在当前栈帧中,不能显示 No symbol "n" in current context. (gdb) bt #0 fun1 (i=1) at 1.c:5 #1 0x00000000004004de in main () at 1.c:11 (gdb) f 1 # 切换到栈帧#1,即main所在栈帧 #1 0x00000000004004de in main () at 1.c:11 11 return fun1(n); (gdb) p n # 现在可以打印main中的局部变量n $2 = 1
说明:不管当前在哪个 frame 中,用命令 bt full
能方便显示每个栈帧中的局部变量。
2.8. Examining Data
参考:https://sourceware.org/gdb/current/onlinedocs/gdb/Data.html#Data
基本的 Examining Data 命令 | 描述 |
---|---|
p | print 的简写。打印最近一次打印过的表达式。 |
p expr | 自动选择合适格式打印表达式。 |
p /fmt expr | 按指定的格式打印表达式。 gdb 中命令名字不能包括斜线,所以 p 和/fmt 中的空格可以省略。 |
explore arg |
打印表达式或类型的相关信息,比用 print 打印的信息更丰富。explore 命令仅当 gdb 编译时指定了--with-python 才可用。 |
explore value expr | 打印表达式相关信息。 |
explore type typename | 打印类型相关信息。如 explore type int |
print 命令中/fmt 的候选项 | 描述 |
---|---|
x | Print in hexadecimal. 如 10 进制转 16 进制:p/x 18 => $1 = 0x12 |
d | Print as integer in signed decimal. |
u | Print as integer in unsigned decimal. |
o | Print as integer in octal. |
t |
Print as integer in binary. |
a | Print as an address. |
c | Print both the numerical value and its character representation. |
f | Regard the bits of the value as a floating point number. |
s | Regard as a string, if possible. |
z | 和格式 x 类似,打印 16 进制值。但格式 z 会显示 leading zeros。注:formatter z 是在 GDB 7.8 中新增的。如 p/x 1 会显示$1 = 0x1,而 p/z 1 会显示多余的零$2 = 0x00000001。 |
r | Print using the 'raw' formatting. The 'r' format bypasses any Python pretty-printer which might exist. |
2.8.1. 查看连续内存 (operator '@')
GDB 的"@"操作符可查看连续内存,"@"的左边是内存的地址中的第一个想要查看的值,"@"的右边则是想查看内存的长度。
例如,对于如下代码: int arr[] = {1,2,3,4,5,6};
可以通过命令 p *arr@3
查看 arr 前三个元素, p *(arr+2)@3
查看从第 3 个元素开始的 3 个元素:
(gdb) p *arr@3 $2 = {1, 2, 3} (gdb) p *(arr+2)@3 $3 = {3, 4, 5}
参考:https://sourceware.org/gdb/current/onlinedocs/gdb/Arrays.html#Arrays
2.8.2. Examining Memory (x)
要查看内存地址的内容,可以使用命令: x/nfu addr
x 命令的选项 | 描述 |
---|---|
n | 指定显示内存的长度, 即从当前地址向后显示几个地址的内容,默认为 1。 |
f | 指定显示的格式。The display format is one of the formats used by print ('x', 'd', 'u', 'o', 't', 'a', 'c', 'f', 's'), and in addition 'i' (for machine instructions). |
u | 指定长度单位。可选值为:b(bytes,字节),h(halfwords,两字节),w(words,四字节),g(giant words,八字节)。如果显示的格式为 i(机器指令),则长度单位无意义,常省略。 |
说明:
nfu 都有默认值,均可以省略。
f 的初始默认值为 x(以 16 进制显示),每次运行 x 或 print 命令时都会修改默认值,下次省略 f 时将使用新的默认值。
u 的初始默认值为 w(四字节),每次运行 x 命令时都会修改默认值,下次省略 u 时将使用新的默认值。
2.8.2.1. x 命令实例:查看函数 main 开始的 10 条指令
(gdb) x/10i main 0x4004c5 <main>: push %rbp 0x4004c6 <main+1>: mov %rsp,%rbp 0x4004c9 <main+4>: sub $0x10,%rsp => 0x4004cd <main+8>: movl $0x1,-0x4(%rbp) 0x4004d4 <main+15>: mov -0x4(%rbp),%eax 0x4004d7 <main+18>: mov %eax,%edi 0x4004d9 <main+20>: callq 0x4004b6 <fun1> 0x4004de <main+25>: leaveq 0x4004df <main+26>: retq 0x4004e0 <__libc_csu_init>: push %r15
2.8.2.2. x 命令实例:以 16 进制查看 buf1 开始的 12 个字节
(gdb) x/12xb buf1 0x7ffff6d6c178: 0xf0 0xd8 0xff 0xff 0x18 0x00 0x00 0x00 0x7ffff6d6c180: 0x00 0x00 0x02 0x00
2.8.3. Automatic Display (display)
If you find that you want to print the value of an expression frequently (to see how it changes), you might want to add it to the automatic display list so that gdb prints its value each time your program stops.
每次程序在 gdb 中停下来时,都会显示用 display 设置过的变量。
Automatic Display 命令 | 描述 |
---|---|
display /fmt expr | 对 expr 设置 display,每次程序停下来时,都会显示其值 |
display | 显示 automatic display list 中各个表达式的值 |
info display | 查看当前对哪些变量设置了 display |
undisplay dnums | 取消对某个表达式进行 display 设置,dnums 可从 info display 命令的输出中得到,dnums 可以是一个范围如:2-5 |
delete display dnums | 同上 |
disable display dnums | 禁止某个表达式的 display 设置 |
enable display dnums | 启用某个表达式的 display 设置 |
2.8.4. Search Memory (find)
2.8.5. Registers
gdb 中查看 register | 描述 |
---|---|
info registers | Print the names and values of all registers except floating-point and vector registers (in the selected stack frame). |
info all-registers | Print the names and values of all registers, including floating-point and vector registers (in the selected stack frame). |
gdb 中“标准”寄存器 | 描述 |
---|---|
$pc | Program counter |
$sp | Stack pointer |
$fp | Pointer to the current stack frame |
$ps | Processor status |
参考:https://sourceware.org/gdb/onlinedocs/gdb/Registers.html#Registers
2.9. Altering Execution
在 gdb 中,能方便地修改程序执行过程中变量的值,修改代码执行流程(如直接跳到某个位置执行,以指定值作为返回值立刻从函数返回等等)。
Altering Execution command | 描述 |
---|---|
print x=4 | 设置变量 x 的值为 4,并显示赋值时的提示信息。 |
set var x=4 | 同上,但不显示赋值时的提示信息。它是 set variable x=4 的简写。 |
set x=4 | 同上。它是 set variable x=4 的简写,更加地方便。但有时不能用,如 width 是 gdb 内置变量,程序中也有变量 width 时,无法用 set width=4 来修改程序中的变量 width 值。 |
j location | jump location 的简写。Continuing at a Different Address. |
return [expression] |
You can cancel execution of a function call with the return command. If you give an expression argument, its value is used as the function's return value. |
print fun1(arg1) | 直接调用函数 fun1(arg1),并打印返回值 |
call fun1(arg1) |
直接调用函数 fun1(arg1),当返回值不为 void 时打印返回值 |
参考:https://sourceware.org/gdb/current/onlinedocs/gdb/Altering.html#Altering
2.10. GDB 命令行参数
-q (--quiet, --silent)不显示启动时的版权信息。
-n (--nx)不加载任何目录中的.gdbinit 配置文件。
-c 检查 core dump 文件,如 gdb -c core.23498。
-ex (--eval-command=COMMAND) 执行单个 GDB 命令。
-iex (--init-eval-command=COMMAND) 在加载 inferior 之前 GDB 命令。
gdb --help
可以得到 gdb 所有命令行参数的描述。
3. GDB 高级用法
3.1. Examining the Symbol Table
查看符号表的常用命令 | 描述 |
---|---|
whatis [/flags] [arg] | Print the data type of arg. With no argument, print the data type of the last value in the value history. Available flages are: r,m,M,t,T |
ptype [/flags] [arg] | Same as command whatis, but prints a detailed description. |
info types [regexp] | Print a brief description of all types whose names match the regular expression regexp (or all types in your program, if you supply no argument). |
info source | Show information about the current source file. |
info sources | Print the names of all source files in your program. |
info functions [regexp] | Print the names and data types of all defined functions whose names contain a match for regular expression regexp. |
info variables [regexp] | Print the names and data types of all variables (except for local variables) whose names contain a match for regular expression regexp. |
info source 命令的输出类似于:
(gdb) info source Current source file is 1.c Compilation directory is /home/cig01/test Located in /home/cig01/test/1.c Contains 26 lines. Source language is c. Compiled with DWARF 2 debugging format. Does not include preprocessor macro info.
参考:https://sourceware.org/gdb/current/onlinedocs/gdb/Symbols.html#Symbols
3.2. Convenience Variables
gdb provides convenience variables that you can use within gdb to hold on to a value and refer to it later.
Convenience variables are prefixed with '$'. Any name preceded by '$' can be used for a convenience variable, unless it is one of the predefined machine-specific register names.
用命令 show convenience
或其简写形式 show conv
可以显示所有 convenience variables 和 convenience functions。
在 gdb 中可以自由定义 convenience variable,它们以$开头。如:
(gdb) set var $i=12 # 或 print $i=12 (gdb) p $i $2 = 12
gdb 自动创建的 Convenience Variables | 描述 |
---|---|
$_ | The variable $_ is automatically set by the x command to the last address examined. |
$__ | The variable $__ is automatically set by the x command to the value found in the last address examined. |
$_exitcode | When the program being debugged terminates normally, gdb automatically sets this variable to the exit code of the program, and resets $_exitsignal to void. |
$_exitsignal | When the program being debugged dies due to an uncaught signal, gdb automatically sets this variable to that signal's number, and resets $_exitcode to void. |
$_exception | The variable $_exception is set to the exception object being thrown at an exception-related catchpoint. |
$_probe_arg0 ... $_probe_arg11, $_probe_argc | Arguments to a static probe. |
$_sdata | The variable $_sdata contains extra collected static tracepoint data. |
$_siginfo | The variable $_siginfo contains extra signal information. It will be empty before you execute the run command. |
参考:https://sourceware.org/gdb/current/onlinedocs/gdb/Convenience-Vars.html#Convenience-Vars
3.3. Convenience Functions
GDB also supplies some convenience functions. These have a syntax similar to convenience variables.
用命令 help function
可以显示所有的 Convenience Functions.
gdb 中的 Convenience Functions | 描述 |
---|---|
$_isvoid | Return one if the expression expr is void. Otherwise it returns zero. |
$_memeq(buf1, buf2, length) | Returns one if the length bytes at the addresses given by buf1 and buf2 are equal. Otherwise it returns zero. |
$_regex(str, regex) | Returns one if the string str matches the regular expression regex. Otherwise it returns zero. The syntax of the regular expression is that specified by Python's regular expression support. |
$_streq(str1, str2) | Returns one if the strings str1 and str2 are equal. Otherwise it returns zero. |
$_strlen(str) | Returns the length of string str. |
参考:https://sourceware.org/gdb/current/onlinedocs/gdb/Convenience-Funs.html#Convenience-Funs
3.4. User-defined Commands (define)
A user-defined command is a sequence of gdb commands to which you assign a new name as a command. This is done with the define
command. User commands may accept up to 10 arguments separated by whitespace. Arguments are accessed within the user command via $arg0...$arg9. $argc may be used to find out how many arguments have been passed.
Command document commandname
can document a user-defined command, so that it can be accessed by help.
A trivial example:
define adder print $arg0 + $arg1 + $arg2 end document adder Perform addition operation. end
参考:https://sourceware.org/gdb/current/onlinedocs/gdb/Define.html#Define
3.5. Remote Debugging
第 1 步:在远程机器上用 gdbserver 启动程序。如:
$ gdbserver :9919 prog Process prog created; pid = 24845 Listening on port 9919
或直接 attach 到已经运行的进程:
$ gdbserver :9919 --attach <pid>
第 2 步:在本地机器中用 gdb 启动程序,并设置 target。如:
$ gdb -q prog (gdb) target remote 192.168.1.10:9919 Remote debugging using 192.168.1.10:9919
接下来,就可以在本地机器中调用远程机器上运行的程序了。
参考:
http://www.thegeekstuff.com/2014/04/gdbserver-example/
https://sourceware.org/gdb/onlinedocs/gdb/Remote-Debugging.html#Remote-Debugging
3.5.1. File Transfer (remote get/put)
gdb 提示了在远程机器和本地机器传输文件的功能。
远程调试时,在本地机器和远程机器中都需要被调试的程序文件,有了这些命令传输文件就很方便了。
gdb 传输文件 | 描述 |
---|---|
remote get targetfile hostfile | Copy file targetfile from the target system to hostfile on the host system. |
remote put hostfile targetfile | Copy file hostfile from the host system (the machine running gdb) to targetfile on the target system. |
remote delete targetfile | Delete targetfile from the target system. |
3.6. Tracepoints
In some applications, it is not feasible for the debugger to interrupt the program's execution long enough for the developer to learn anything helpful about its behavior. If the program's correctness depends on its real-time behavior, delays introduced by a debugger might cause the program to change its behavior drastically, or perhaps fail, even when the code itself is correct. It is useful to be able to observe the program's behavior without interrupting it.
Using gdb's trace and collect commands, you can specify locations in the program, called tracepoints, and arbitrary expressions to evaluate when those tracepoints are reached. Later, using the tfind command, you can examine the values those expressions had when the program hit the tracepoints.
当前,tracepoints 只在远程调试模式下支持(在 gdbserver 7.2 之后才被支持)。
参考:https://sourceware.org/gdb/current/onlinedocs/gdb/Tracepoints.html#Tracepoints
3.7. 调试 fork
On most systems, gdb has no special support for debugging programs which create additional processes using the fork
function. By default, when a program forks, gdb will continue to debug the parent process and the child process will run unimpeded. If you have set a breakpoint in any code which the child then executes, the child will get a SIGTRAP signal which (unless it catches the signal) will cause it to terminate.
fork 相关 gdb 命令 | 描述 |
---|---|
set follow-fork-mode parent | 调试父进程,子进程正常执行。这是默认行为。 |
set follow-fork-mode child | 调试子进程,父进程正常执行。 |
show follow-fork-mode | 查看当前 follow-fork-mode 的设置 |
set detach-on-fork on | 只控制一个进程。控制哪个进程由 follow-fork-mode 决定。这是默认行为。 |
set detach-on-fork off | 控制父子两个进程。由 follow-fork-mode 指定的进程被调试,另一个进程被暂停。 |
show detach-on-fork | 查看当前 detach-on-fork 的设置 |
参考:
https://sourceware.org/gdb/current/onlinedocs/gdb/Forks.html
3.8. 多线程调试
多线程调试相关 gdb 命令 | 描述 |
---|---|
info threads | 查询所有线程 |
thread threadno | 切换到 threadno 指定的线程 |
set scheduler-locking on | 仅当前被调试线程会执行 |
set scheduler-locking off | 不锁定任何线程,即所有线程都运行 |
set scheduler-locking step | 单步调试时,其它线程不执行(不用担心正在调试的某个变量被其他线程修改),这是默认行为 |
set scheduler-locking replay | 在 record 模式中和 off 一样,在 replay 模式中和 on 一样 |
show scheduler-locking | 查看当前 scheduler-locking 的设置 |
thread apply td1 td2 command | 让线程 td1,td2 执行 gdb 命令 command |
thread apply all command | 让所有被调试线程执行 gdb 命令 command |
参考:
https://sourceware.org/gdb/current/onlinedocs/gdb/Threads.html#Threads
https://sourceware.org/gdb/current/onlinedocs/gdb/Thread-Stops.html#Thread-Stops
3.9. Signals
用 info signals [signal]
或者 info handle [signal]
可查看 gdb 中所有信号或指定信号的设置。
用 handle signal [nostop|stop print|noprint pass|nopass]
可修改指定信号的设置(pass/nopass 可换为它们的别名 noignore/ignore)。
gdb 信号设置实例:
(gdb) info signals SIGINT Signal Stop Print Pass to program Description SIGINT Yes Yes No Interrupt (gdb) handle SIGINT pass (gdb) info signals SIGINT Signal Stop Print Pass to program Description SIGINT Yes Yes Yes Interrupt
The default is set to nostop, noprint, pass for non-erroneous signals such as SIGALRM, SIGWINCH and SIGCHLD, and to stop, print, pass for the erroneous signals.
参考:https://sourceware.org/gdb/current/onlinedocs/gdb/Signals.html#Signals
3.9.1. 获取 signal handler 的地址
获取当前 signal handler 的地址很简单:
struct sigaction oldact; sigaction(SIGINT, NULL, &oldact); printf("SIGINT handler address: 0x%lx\n", oldact.sa_sigaction);
但这需要修改程序源码。能否直接在 gdb 中获取呢?
如果正在被调试的进程其注册信号的代码已经执行过,则可以通过下面的方法在 gdb 中得到某个信号其处理函数的地址。
(gdb) call malloc(sizeof(struct sigaction)) (gdb) call __sigaction(SIGINT, NULL, $1) (gdb) print ((struct sigaction *)$1)->sa_handler (gdb) info symbol <address from previous step>
3.10. 保存和恢复 snapshot (checkpoint/restart)
3.11. 记录屏幕输出到文件 (set logging)
GDB 有记录输出到文件的功能。注意:记录功能只能记录 GDB 的输出信息,被调试程序(inferior)的输出信息仍然会输出到终端。
gdb 日志相关命令 | 描述 |
---|---|
set logging on | 打开记录功能。 |
set logging off | 关闭记录功能。 |
set logging file filename.txt | 设置记录文件为 filename.txt,默认的记录文件名为 gdb.txt。 |
set logging overwrite [on | off] | 设置 GDB 是否以覆盖的方式写记录信息到文件中。默认为 off,不会覆盖文件。 |
set logging redirect [on | off] | 设置输出信息除写入到文件外,是否显示到屏幕。默认为 off,会同时记录到文件和显示到屏幕。 |
show logging | 显示记录功能中每个选项的设置。 |
注意:在设置 set logging overwrite 或 set logging redirect 时如果记录功能已经打开,需要关闭记录功能再重新打开才能更改生效。
3.12. text user interface(TUI)
用 gdb -tui
启动 gdb 会进入到 TUI。
如果启动时没有指定-tui,启动后可用下面方法进入 TUI:
(gdb) layout split # 或者用下面快捷键 Ctrl x 2
http://stackoverflow.com/questions/9970636/view-both-assembly-and-c-code
http://stackoverflow.com/questions/1902901/show-current-instruction-in-gdb
4. GDB tips
4.1. 查看当前被调试程序的状态
在 gdb 中执行命令 info program
可以查看当前被调试程序的状态,如有没有在运行中,进程 id 是多少等。
如:
(gdb) info program Using the running image of child process 2376. Program stopped at 0x4004ba. It stopped at a breakpoint that has since been deleted.
4.2. trace function call
如果跟踪函数调用?在 Breakpoint Command Lists 中使用命令 backtrace 1
(只显示最内层的 1 个栈帧)即可。
break function-name commands silent backtrace 1 continue end
上面方法能打印函数的参数,却无法打印函数的返回值(加入 finish 命令,在嵌套的函数调用时无法正常打印函数返回值)。
参考:http://dustymabe.com/2012/10/14/trace-function-calls-using-gdb/
4.3. 执行 shell 命令 (shell)
如何在 gdb 中执行 shell 命令?
(gdb) shell ls
或者直接启动 shell,再输入想要执行的命令:
(gdb) shell bash $ execute_your_shell_commands bash $ exit (gdb) # you_have_returned_back_to_gdb_prompt
4.4. 检测程序中是否包含调试信息
如何检测程序中是否包含调试信息?
方法 1:
用 gdb 加载程序,在(gdb)提示符的上一行如果出现了 no debugging symbols found ,则说明程序中不包含调试信息。
$ gdb -q ./test Reading symbols from /home/test/test...(no debugging symbols found)...done. (gdb)
方法 2:
用命令 objdump -t prog |grep debug
,如果什么都没有输出,则说明不包含调试信息。
若包含调试信息,其输出类似于:
$ objdump -t ./test |grep debug 0000000000000000 l d .debug_aranges 0000000000000000 .debug_aranges 0000000000000000 l d .debug_pubnames 0000000000000000 .debug_pubnames 0000000000000000 l d .debug_info 0000000000000000 .debug_info 0000000000000000 l d .debug_abbrev 0000000000000000 .debug_abbrev 0000000000000000 l d .debug_line 0000000000000000 .debug_line 0000000000000000 l d .debug_frame 0000000000000000 .debug_frame 0000000000000000 l d .debug_loc 0000000000000000 .debug_loc
4.5. 调试没有符号信息的程序
当程序没有 debug 符号时,如何调试它呢?
这时,只能调试汇编代码了。具体操作步骤可参考:
GDB - Debugging stripped binaries: http://felix.abecassis.me/2012/08/gdb-debugging-stripped-binaries/
How to handle stripped binaries with GDB?: http://reverseengineering.stackexchange.com/questions/1935/how-to-handle-stripped-binaries-with-gdb-no-source-no-symbols-and-gdb-only-sho
4.6. 用 GDB 实现进制转换
用 GDB 可以方便实现进制转换,基本格式为 p /FMT num
。如把 31558 转换为 16 进制:
(gdb) p /x 31558 $11 = 0x7b46
其中,/FMT 可以为:
/o(octal), /x(hex), /d(decimal), /t(binary)
5. LLDB
LLDB is a next generation, high-performance debugger. It is built as a set of reusable components which highly leverage existing libraries in the larger LLVM Project, such as the Clang expression parser and LLVM disassembler.
LLDB is the default debugger in Xcode on Mac OS X and supports debugging C, Objective-C and C++ on the desktop and iOS devices and simulator.
5.1. GDB to LLDB command map
6. DBX
DBX is a source-level debugger found primarily on Solaris, AIX, IRIX, Tru64 UNIX, Linux and BSD operating systems. It provides symbolic debugging for programs written in C, C++, Pascal, FORTRAN and Java.
DBX is also available on IBM z/OS systems, in the UNIX System Services component.
参考:
Debugging a Program With dbx: http://docs.oracle.com/cd/E19205-01/819-5257/
6.1. GDB to DBX command map
参考:
gdb vs. dbx: commands mapping and feature comparison: https://blogs.oracle.com/dbx/entry/gdb_vs_dbx_commands_mapping