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 00:00>

Last updated: <2018-02-09 Fri 22:32>

Creator: Emacs 25.3.1 (Org mode 9.1.4)