GDB

Table of Contents

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. 常用基本命令

Table 1: GDB 常用基本命令
命令 描述
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 就可以生效。

Table 2: GDB 中查看和设置环境变量
GDB 命令 说明
show env [varname] 查看所有或某个环境变量的值
set env varname [=value] 设置环境变量
unset env varname 取消对环境变量的设置
2.3.3.1. 查看和设置 PATH

PATH 环境变量的查看和设置除了可用前面介绍的方法外,还有其它方法。

Table 3: GDB 中查看和设置 PATH 变量
GDB 命令 说明
show paths 查看 PATH 变量的值,和 show env PATH 作用一样
path dir 把 dir 添加到 PATH 中

2.3.4. 设置 inferior 的工作目录

当用 run 命令执行程序时,程序的工作目录继承自 GDB 的工作目录。

Table 4: 查看和设置 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. 断点

Table 5: gdb 断点相关命令
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.

Table 6: gdb 中 watchpoint 相关命令
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

Table 7: gdb 中 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.

Table 8: gdb 中 call stack 相关命令
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

Table 9: gdb 中查看变量
基本的 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
Table 10: print 命令中/fmt 的候选项
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

Table 11: gdb 中 x 命令的选项
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 设置过的变量。

Table 12: gdb 中 Automatic 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.5. Registers

Table 13: gdb 中查看 register
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).
Table 14: gdb 中 4 个“标准”寄存器
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 中,能方便地修改程序执行过程中变量的值,修改代码执行流程(如直接跳到某个位置执行,以指定值作为返回值立刻从函数返回等等)。

Table 15: gdb 中 Altering Execution 相关命令
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

Table 16: gdb 查看符号表的常用命令
查看符号表的常用命令 描述
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
Table 17: gdb 自动创建的 Convenience Variables
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.

Table 18: gdb 中的 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 提示了在远程机器和本地机器传输文件的功能。
远程调试时,在本地机器和远程机器中都需要被调试的程序文件,有了这些命令传输文件就很方便了。

Table 19: 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.

Table 20: 和 fork 相关的 gdb 命令
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. 多线程调试

Table 21: 和多线程调试相关的 gdb 命令
多线程调试相关 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>

参考:
http://stackoverflow.com/questions/9495113/how-to-get-the-handlers-name-address-for-some-signals-e-g-sigint-in-postgres

3.11. 记录屏幕输出到文件 (set logging)

GDB 有记录输出到文件的功能。注意:记录功能只能记录 GDB 的输出信息,被调试程序(inferior)的输出信息仍然会输出到终端。

Table 22: gdb 日志相关命令
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.

参考:http://lldb.llvm.org

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

Author: cig01

Created: <2011-11-12 Sat>

Last updated: <2018-02-09 Fri>

Creator: Emacs 27.1 (Org mode 9.4)