1、p打印字符串时不省略
默认情况下,gdb调试时,太长的字符串只显示一部分,如果想要完全显示,可以设置:set print element 0
2、查看产生coredump文件的进程
(1)gdb -c corefile 使用gdb调试core文件
(2)info auxv 索引31对应的是core文件的应用程序
调试coredump文件命令:gdb coredump应用程序 coredump文件
info threads 显示所有线程
bt 显示线程堆栈信息
thread thread_num 切换线程
frame num 切换栈
info r 显示当前帧的寄存器信息 (每一帧的寄存器信息都是不相同的)
3、ulimit不生效规避
有时候通过 ulimit -c 0 设置不产生coredump文件不生效,规避方法可以设置core文件名相同,产生路径为一个空间比较大的地方,以免根分区被占满。
(1)echo "0" > /proc/sys/kernel/core_uses_pid 控制生成coredump文件名是一个,保证始终只生成一个coredump文件
(2)echo "/opt/usr/core" > /proc/sys/kernel/core_pattern 控制生成的coredump文件路径
为保证系统重启也能生效,将上面两个命令写到/etc/profile里面。
4、基本常识
4.1 前提
用gdb调试的程序必须在编译时加 -g 参数,生成带调试信息的程序。
4.2 启动调试
gdb programname 直接运行并调试
gdb at pid 附加到已经运行的进程上调试
4.3 设置程序参数
set args
可指定运行时参数。(如:set args 10 20 30 40 50 ) show args
命令可以查看设置好的运行参数。 run (r)
启动程序
不指定运行参数 r
指定运行参数r 10 20 30 40 50
4.4 设置断点
break
设置断点,可以简写为b b 10
设置断点,在源程序第10行 b func
设置断点,在func
函数入口处
C++中可以使用 class::function或function(type,type)
格式来指定函数名。如果有名称空间,可以使用namespace::class::function
或者function(type,type)
格式来指定函数名。 break filename:linenum
在源文件filename
的linenum
行处停住 break filename:function
在源文件filename
的function
函数的入口处停住 break class::function
或function(type,type)
在类class的function函数的入口处停住 break namespace::class::function
在名称空间为namespace的类class的function函数的入口处停住
4.5 查询断点
查询所有断点:info b
4.6 观察点
watch
为表达式(变量)expr设置一个观察点。当表达式值有变化时,马上停住程序。 rwatch
表达式(变量)expr被读时,停住程序。 awatch
表达式(变量)的值被读或被写时,停住程序。 info watchpoints
列出当前所设置了的所有观察点。
4.7 条件断点
设置一个条件断点 b test.c:8 if intValue == 5
condition
与break if
类似,只是condition
只能用在已存在的断点上
修改断点号为bnum
的停止条件为expression
condition bnum expression
清楚断点号为bnum
的停止条件 condition bnum
ignore
忽略停止条件几次
表示忽略断点号为bnum的停止条件count次 Ignore bnum count
4.8 为停止点设定运行命令
commands [bnum]
… command-list …
end
4.9 调试命令
run
运行程序,可简写为rnext
单步跟踪,函数调用当作一条简单语句执行,可简写为nstep
单步跟踪,函数调进入被调用函数体内,可简写为sfinish
退出函数until
在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体,可简写为u。continue
继续运行程序,可简写为cstepi
或si
,nexti
或ni
单步跟踪一条机器指令,一条程序代码有可能由数条机器指令完成,stepi和nexti可以单步执行机器指令。info program
来查看程序的是否在运行,进程号,被暂停的原因。
4.10 自动显示
display expr
display/fmt expr
display/fmt addr
4.11 设置变量值
set xx=yy
4.12 强制函数返回
return
return expression
4.13 强制调用函数
call name
print name
4.14 线程断点
break linespec thread threadno
break linespec thread threadno if …
threadno是线程ID,可以通过info threads查看。当你的程序被GDB停住时,所有的运行线程都会被停住。这方便你你查看运行程序的总体情况。而在你恢复程序运行时,所有的线程也会被恢复运行。那怕是主进程在被单步调试时。
4.15 显示栈帧
bt
bt full
frame n
up
down
4.16 其他
print h@10 显示数组h后面的10个值
whatis xx 识别变量类型
shell + 命令,可以在gdb时执行shell命令
在gdb里面用print errno有时会:
Cannot access memory at address 0x8
这是由于errno本身是一个宏,而gdb有时不能正确的处理errno这个宏。
可以试试如下命令:
p *__errno_location()
使用“info functions
”命令可以列出可执行文件的所有函数名称
执行“set step-mode on”命令,这样gdb就不会跳过没有调试信息的函数
frame n up down
i frame
i registers
crtl+D,模拟EOF使程序结束。
分割窗口:
- layout:用于分割窗口,可以一边查看代码,一边测试:
- layout src:显示源代码窗口
- layout asm:显示反汇编窗口
- layout regs:显示源代码/反汇编和CPU寄存器窗口
- layout split:显示源代码和反汇编窗口
- Ctrl + L:刷新窗口
5 寄存器、栈调试
5.1 问题场景
- 优化的代码在printf或其它glibc函数处core
- 没有检查返回值的函数调用异常导致的异常
- 优化的代码的计算异常的中间过程分析
- 栈溢出导致的core
- 局部变量越界导致栈异常的core
通常调试的代码基本上都是在未开启优化的情况下,各个变量都可以直接查看,因此造成很多人调试时基本上不会看寄存器,但是对于线上的生产环境,可能会因为性能的因素,需要打开代码优化,此时出现异常需要调试时就通常需要查看寄存器了,下列是gdb调试中需要了解的寄存器。
- $rip 指令寄存器,指向当前执行的代码位置
- $rsp 栈指针寄存器,指向当前栈顶
- $rax,$rbx,$rcx,$rdx,$rsi,$rdi,$rbp,$r8,$r9,$r10,$r11,$r12,$r13,$r14,$r15 通用寄存器
5.2 函数入参
一般linux下会优先将参数压到寄存器中,只有当寄存器不够所有的参数时,才会将入参压到栈上,一般入参的压栈顺序为$rdi、$rsi、$rdx、$rcx、$r8、$r9。多的参数会被压到栈上。
i r //查看所有寄存器值
查看某个寄存器值:p/s (char*)$rdi
p/d $rsi
函数返回值由$rax保存返回
显示汇编代码:layout asm
栈帧:
6、辅助命令
nm
nm用来列出目标文件的符号清单。
objdump
ogjdump工具用来显示二进制文件的信息,就是以一种可阅读的格式让你更多地了解二进制文件可能带有的附加信息。
$objdump -d myprogrammer
- -f 显示文件头信息
- -D 反汇编所有section (-d反汇编特定section)
- -h 显示目标文件各个section的头部摘要信息
- -x 显示所有可用的头信息,包括符号表、重定位入口。-x 等价于 -a -f -h -r -t 同时指定。
- -i 显示对于 -b 或者 -m 选项可用的架构和目标格式列表。
- -r 显示文件的重定位入口。如果和-d或者-D一起使用,重定位部分以反汇编后的格式显示出来。
- -R 显示文件的动态重定位入口,仅仅对于动态目标文件有意义,比如某些共享库。
- -S 尽可能反汇编出源代码,尤其当编译的时候指定了-g这种调试参数时,效果比较明显。隐含了-d参数。
- -t 显示文件的符号表入口。类似于nm -s提供的信息
readelf
这个工具和objdump命令提供的功能类似,但是它显示的信息更为具体,并且它不依赖BFD库。
$readelf -all a.out
ELF文件类型
ELF(Executable and Linking Format)是一种对象文件的格式,用于定义不同类型的对象文件(Object files)中都放了什么东西、以及都以什么样的格式去放这些东西。它自最早在 System V 系统上出现后,被 xNIX 世界所广泛接受,作为缺省的二进制文件格式来使用。可以说,ELF是构成众多xNIX系统的基础之一。
ELF文件有三种类型:
-
可重定位的对象文件(Relocatable file)
由汇编器汇编生成的 .o 文件
-
可执行的对象文件(Executable file)
可执行应用程序
-
可被共享的对象文件(Shared object file)
动态库文件,也即 .so 文件
size
size这个工具用来查看程序运行时各个段的实际内存占用:$size a.out
file
$file a.out
查看文件类型,也可以查看core文件是由谁生成的。
strings
一个文件中包含二进制数据和文本数据,如果只需要查看其文本信息,使用这个命令就很方便;过滤掉非字符数据,将文本信息输出:
$strings <objfile>
fuser
显示所有正在使用着指定的file, file system 或者 sockets的进程信息;
$fuser -m -u redis-server
redis-server: 11552rce(weber) 22912rce(weber) 25501rce(weber)
使用了-m和-u选项,用来查找所有正在使用redis-server的所有进程的PID以及该进程的OWNER;
fuser通常被用在诊断系统的”resource busy”问题。如果你希望kill所有正在使用某一指定的file, file system or sockets的进程的时候,你可以使用-k选项:
$fuser –k /path/to/your/filename
xxd
以十六进制方式显示文件,只显示文本信息:
$xxd a.out
od
通常使用od命令查看特殊格式的文件内容。通过指定该命令的不同选项可以以十进制、八进制、十六进制和ASCII码来显示文件。
ldd
用来查看程式运行所需的共享库,常用来解决程式因缺少某个库文件而不能运行的一些问题。ldd -r xxx
/opt/app/todeav1/test$ldd test
libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00000039a7e00000)
libm.so.6 => /lib64/libm.so.6 (0x0000003996400000)
- 第一列:程序需要依赖什么库
- 第二列: 系统提供的与程序需要的库所对应的库
- 第三列:库加载的开始地址