操作教程(要敲一遍):https://www.ibm.com/developerworks/cn/linux/sdk/gdb/index.html
连载系列:https://developer.aliyun.com/article/681075
目录
一、Gdb介绍
1. 背景及用处
2. gdb启动
二、常用命令
1. breakpoint/watchpoint
2. 程序执行控制
3. 打印显示
4. info显示信息
5. 线程
6. 分割窗口
7. 其他
三、调试coredump问题
1. coredump介绍
2. gdb调试core文件
GDB是一个由GNU开源组织UNIX/LINUX操作系统下的基于命令行的、功能强大的程序调试工具。
GDB主要帮助完成下面4个方面的功能:
1、启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。(比如给变量赋值)
2、可以让被调试的程序在你所指定的位置的断点处停住。
3、当程序被停住时,可以检测此时你的程序中所发生的事。
4、你可以改变你的程序,将一个BUG产生的影响修正从而测试其它BUG。
根据程序执行时的出错现象假设错误原因,然后在代码中找到适当的位置,构造错误现象触发的环境,进入gdb环境添加断点,执行程序并分析,通过程序执行过程以及变量的值确认错误原因。
开始调试之前,必须用程序中的调试信息编译要调试的程序。这样,gdb 才能够调试所使用的变量、代码行和函数。如果要进行编译,请在 gcc(或 g++)下使用额外的 '-g' 选项来编译程序:
g++ -g hello.cpp -o hello
在 shell 中,可以使用 'gdb' 命令并指定程序名作为参数来运行 gdb,例如 'gdb eg';或者在 gdb 中,可以使用 file 命令来装入要调试的程序,例如 'file eg'。
1.调试可执行文件:
gdb
program也就是你的执行文件,一般在当前目录下。
2.调试core文件(core是程序非法执行后core dump后产生的文件):
$gdb
$gdb program core.11127
3. 调试服务程序:
$gdb
$gdb hello 11127
如果你的程序是一个服务程序,那么你可以指定这个服务程序运行时的进程ID。gdb会自动attach上去,并调试他。program应该在PATH环境变量中搜索得到。
GDB启动时,可以加上一些GDB的启动开关,详细的开关可以用gdb -help查看。我在下面只例举一些比较常用的参数:
-symbols
-s
从指定文件中读取符号表。
-se file
从指定文件中读取符号表信息,并把他用在可执行文件中。
-core
-c
调试时core dump的core文件。
-directory
-d
加入一个源文件的搜索路径。默认搜索路径是环境变量中PATH所定义的路径。
gdb中,输入命令时,可以不用打全命令,只用打命令的前几个字符就可以了,当然,命令的前几个字符应该要标志着一个唯一的命令,在Linux下,你可以敲击两次TAB键来补齐命令的全称,如果有重复的,那么gdb会把其例出来。
break(b):添加断点,可对函数添加断点外,也可对当前函数中的行号添加断点。
delete
enable
disable
info breakpoint:查看断点信息
clear 行号n:清除第n行的断点,不加行号清空所有断点信息
watch:监视某块内存,当内存被改变时触发.一般用来观察某个表达式(变量也是一种表达式)的值是否有变化。相当于,在每次该表达式值变化的地方,都设置一个断点。我们需要使用continue命令前进。用法类似break。
whatis :查询变量或函数
start:开始执行程序,停在main函数第一行语句前等待命令
run(r):执行程序
continue(c):继续执行,直到下一个断点或者程序结束
next(n):下一步(逐过程),如果该语句为函数调用,不会进入函数内部执行(即不会一步步地调试函数内部语句)
next
,执行后面的count
条语句(不进入函数),然后再停住step(s):下一步(逐语句)。如果该语句为函数调用,则进入函数执行其中第一条语句
step
,执行后面的count
条语句,然后再停住
finish(fin):执行到退出当前函数,并打印函数返回时的堆栈地址和返回值及参数值等信息。
until:当你厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体。
until 行号: 运行至某行,不仅仅用来跳出循环。
quit(q)或者ctrl+d:退出gdb调试环境
call 函数(参数):调用程序中可见的函数,并传递“参数”,如:call gdb_test(55)
printf(p) 变量名:打印变量的值
print /
,将指定表达式的值以f
格式输出x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十六进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
c 按字符格式显示变量。
f 按浮点数格式显示变量。
set var变量名=值:修改某个变量的值(p 变量名=值 也可以修改)
set args 参数:指定运行时的参数
show args:显示运行时的参数
display(disp):跟踪查看某个变量,每次停下来都显示它的值
list(l):显示源代码,,接着上次的位置往下列,每次列十行,可以自己接上一个list再回车继续向后列出。list -,向前显示源程序.
info<> 显示各类信息,详情查看“help info”,例如info breakpoint 查看所有断点
thread 线程id号:进入某个线程
b XXXX thread xxx :为某个线程设置断点函数(确认线程id:对进程加断点,当某个线程调度到后使用info threads,线程id前有*的就是当前线程)
detach:可以直接取消当前挂在的pid
attch pid:不用退出直接再挂接其它进程,最好先detach上一个进程
thread apply all bt:打开所有线程的堆栈信息
layout:用于分割窗口,可以一边查看代码,一边测试:
layout src:显示源代码窗口
layout asm:显示反汇编窗口
layout regs:显示源代码/反汇编和CPU寄存器窗口
layout split:显示源代码和反汇编窗口
Ctrl + L:刷新窗口
examine(x)/nfu
shell [commandline]:在不退出调试环境的情况下使用 shell 命令
make
backtrace(bt):查看当前堆栈信息,与info stack
一样。
up/down :改变堆栈显示的深度
disassemble:查看当前函数的反汇编代码。
help
在 gdb 下运行程序可以使俘获错误变得更容易,但在调试器外运行的程序通常会中止而只留下一个 core 文件。gdb 可以装入 core 文件,并让您检查程序中止之前的状态。
Linux下core开关设置:https://www.cnblogs.com/lee-qi/p/12422235.html
Coredump叫做核心转储,它是进程运行时在突然崩溃的那一刻的一个内存快照。操作系统在程序发生异常而异常在进程内部又没有被捕获的情况下,会把进程此刻内存、寄存器状态、运行堆栈等信息转储保存在一个文件里。
该文件也是二进制文件,可以使用gdb、elfdump、objdump或者windows下的windebug、solaris下的mdb进行打开分析里面的具体内容。
1. gdb打开core文件的命令:
gdb程序名(包含路径) core*(core文件名和路径)
gdb打开core文件时,显示没有调试信息,是因为之前编译的时候没有带上-g选项,没有调试信息是正常的,实际上它也不影响调试core文件。因为调试core文件时,符号信息都来自符号表,用不到调试信息。
2. 查看coredump时的堆栈:
bt或者where命令
在带上调试信息的情况下,我们实际上是可以看到core的地方和代码行的匹配位置。没有调试信息的情况下,打开coredump堆栈,并不会直接显示core的代码行。
3. 没有调试信息的情况下找core的代码行
frame addr(帧数):跳到堆栈的第几帧,f 1跳转到core堆栈的第1帧。
disassemble:打开该帧函数的反汇编代码。
shell echo free@plt |c++filt 去掉函数的名词修饰。我们可以推测到这里是在调用free函数。如此,我们就能知道我们coredump的位置,从而进一步能推断出coredump的原因。
4. info frame查看堆栈寄存器信息。
具体可参照(后面有几个例子):https://blog.csdn.net/u014403008/article/details/54174109
除了调试 core 文件或程序之外,gdb 还可以连接到已经运行的进程(它的程序已经过编译,并加入了调试信息),并中断该进程。
启动 gdb 并指定进程标识,在我举的这个例子中是 'gdb eg2 1283'。gdb 会查找一个叫作 "1283" 的 core 文件。如果没有找到,那么只要进程 1283 正在运行(在本例中可能在 sleep() 中),gdb 就会连接并中断该进程。
此时,可以发出所有常用 gdb 命令。可以使用 'backtrace' 来查看当前位置与 main() 的相对关系,以及 main() 的帧号是什么,然后切换到 main() 所在的帧,查看已经在 "for" 循环中运行了多少次:
(gdb) backtrace
#0 0x400a87f1 in __libc_nanosleep () from /lib/libc.so.6
#1 0x400a877d in __sleep (seconds=1) at ../sysdeps/unix/sysv/linux/sleep.c:78
#2 0x80483ef in main (argc=1, argv=0xbffff9c4) at eg2.c:7
(gdb) frame 2
#2 0x80483ef in main (argc=1, argv=0xbffff9c4) at eg2.c:7
7 sleep(1);
(gdb) print i
$1 = 50
还可以首先使用 'file eg2' 装入文件,然后发出 'attach 1283' 命令连接到进程标识 1283 下的 eg2。如果已经完成了对程序的修改,可以 'detach' 命令继续执行程序,或者 'kill' 命令杀死进程。