[Linux]GDB调试技巧
命令行参数
gdb有下面几种运行方式:
|
|
几个值得注意的参数:
|
|
简单的例子:coredump分析
gdb的一大用处是通过coredump文件分析程序哪里发生了错误。首先要打开coredump生成开关:
|
|
coredump文件会默认生成在程序相同目录下。如果没有对应文件,可以查看/etc/sysctl.conf
:
|
|
我们先编写一个简单的c程序main.c
,它试图从非法地址读取数据:
|
|
编译,一定要加上-g
选项:
|
|
运行,果然dump:
|
|
然后使用第一节里的第一种方式启动, 携带coredump文件:
|
|
进去后就能看到dump的地方以及原因。使用bt
查看coredump时栈顶的信息:
|
|
GDB的用法
GDB启动时会读取二进制文件的符号表,然后进入调试命令行。这里可以运行各种命令查看程序的符号表、变量、内存值以及控制程序的运行。
控制命令
- file 加载一个二进制文件,直接进入gdb时可以用这个加载。参数为文件名。
- run (或者r) 从头运行程序一直到断点。没有断点会一直运行结束,或者直到遇到异常。可以r < a.in 重定向输入输出。
- continue (或者c) 运行程序一直到下一个断点。
- next/step (或者n/s) 单步调试,next不会在行内进入函数体,step则会跳入函数体。 参数为跳多行。
- until (或者 u) 直接跳出当前循环。(但是还是会被断点卡住)。参数为跳到指定行。
- finish 直接运行到函数返回
- call 运行某函数,参数为函数和参数,如
call foo(2, "hello")
- 回车键 重复上个指令
断点
- break n (或者b) 在第n行打断点
- b main.c:n 指定文件打断点; b main 指定函数入口处打断点; b main:label 指定函数的标签处打断点
- b n if i == 5 满足条件打断点,对循环尤其有效。
- info b 查看断点号和信息,或者 i b
- delete no (或者d) 删除对应编号的断点; d breakpoints 删除所有断点
- clear lineno 删除对应行的断点
- disable/enable no 屏蔽/使用对应编号的断点
查看代码和变量
- list (或者l) 列出10行源文件。每次从上次结束的地方开始列。
- list lineno 列出某行的前后源码;l main 列出某函数的源码
- print exp (或者p) 打印任意变量、表达式、函数、字符串、数组的值
- display exp 每次单步完打印该表达式
- watch exp 如果该表达式值改变了,打印并停止程序
- whatis 查询变量,函数的类型
- info (或者i) 查询信息 i locals 所有变量的值 i args 所有参数的值 i function 函数信息 i frame 栈信息
i program 程序信息 i threads 线程信息
堆栈
-
bt 查看栈信息; bt n 栈顶n层;bt -n 栈底n层
1 2 3
(gdb) bt #0 foot () at t.c:7 #1 0x0000000000400525 in main () at t.c:15
-
frame (或者f)查看帧信息。上面一个#号(一层)是一帧。
frame n 查看第n帧 ;up n 上面n帧; down n 下面n帧;frame addr 查看某地址的帧 -
info frame n/addr 帧详细信息
ip是下条命令的地址(pc),bp是栈底的地址,sp是栈顶的地址。
64位机的帧信息:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
(gdb) i f 0 // 帧地址 Stack frame at 0x7fffffffe560: // rip:帧PC 帧函数名 saved rip:caller帧PC rip = 0x4004fd in foot (t.c:7); saved rip 0x400525 // caller帧地址 called by frame at 0x7fffffffe570 source language c. // 帧参数地址 Arglist at 0x7fffffffe550, args: // 帧局部变量地址,caller的栈顶地址 Locals at 0x7fffffffe550, Previous frame sp is 0x7fffffffe560 // 帧寄存器列表 Saved registers: rbp at 0x7fffffffe550, rip at 0x7fffffffe558
32位机的帧信息:
1 2 3 4 5 6 7 8 9 10 11 12
(gdb) i f Stack level 0, frame at 0xbffff630: // eip:帧PC 帧函数名 saved eip:caller帧PC eip = 0x80483e4 in main (a.c:8); saved eip = 0xb7e31637 source language c. // 帧参数地址 Arglist at 0xbffff628, args: // 帧局部变量地址,caller的栈顶地址 Locals at 0xbffff628, Previous frame sp is 0xbffff630 // 帧寄存器列表 Saved registers: ebp at 0xbffff628, eip at 0xbffff62c
内存
gdb中使用x
命令来打印内存的值,格式为x/nfu addr
。
含义为以f
格式打印从addr
开始的n
个长度单元为u
的内存值。参数具体含义如下:
a)n:输出单元的个数。
b)f:是输出格式。比如x
是以16进制形式输出,o
是以8进制形式输出,等等。
c)u:标明一个单元的长度。b
是一个bytes
,h
是两个bytes
(halfword),w
是四个bytes
(word),g
是八个bytes
(giant word)。
比如对一个字符串arr查看内存:(这个数组越界踩掉了总共10 char的内容,每个char在32位机上是32bit)
|
|
信号
这部分的详细内容在GDB Online Docs-5.4 Signals中。你可以自由地对程序进行发送信号(signal)、捕获信号(catch)或者处理信号(handle)。
线程
除了上文的查看线程信息之外,还可以查看详细的线程信息,参考GDB Online Docs-4 Threads,或者查看fork的情况,参考GDB Online Docs-5Forks 。