获取进程的coredump (内核转储)
启用内核转储
| 功能 | 命令 |
|---|---|
| 查看coredump功能是否有效 | ulimit和ulimit -c |
| 开启内核转储 | ulimit -c unlimited |
| 设置coredump大小上限 | ulimit -c 大小(Byte) |
| gdb调试core文件 | gdb -c core.xxxx ./a. |
| 检查是否是core文件 | file xxx输出core file |
在专用目录中生成coredump
coredump保存位置的完整路径可以通过sysctl变量kernel.core_pattern设置。
-
临时修改使用
echo /var/core/%t-%e-%p-%c.core > /proc/sys/kernel/core_pattern -
或者
/etc/sysctl.conf中设置kernel.core_pattern=/var/core/%t-%e-%p-%c.core。然后sysctl -p让配置生效。
kernel.core_uses_pid=1 作用是在生成的core文件名字结尾添加.PID。
kernel.core_uses_pid=0则不添加。
kernel.core_pattern中可以设置的格式符
| 格式符 | 说明 |
|---|---|
| %% | %字符本身 |
| %p | 进程PID |
| %u | 进程的真实用户ID |
| %g | 进程的真实组ID |
| %s | 引发coredump的信号 |
| %t | 转储时间戳 |
| %h | 主机名 |
| %e | 可执行文件名 |
| %c | 转储文件大小上限 |
使用用户程序辅助自动压缩coredump文件
kernel.core_pattern中可以加入管道符,管道符后面写程序名。
例如:
-
echo "|/usr/local/sbin/core_helper %t %e %p %c" > /proc/sys/kernel/core_pattern -
或者 sudo sysctl -w 'kernel.core_pattern=%t-%e-%p-%c.core'
-
或者 /etc/sysctl.conf
中设置kernel.core_pattern=|/usr/local/sbin/core_helper %t %e %p %c。然后sysctl -p让配置生效。
core_heaper 文件内容很简单:
cat core_heaper
#!/bin/sh
exec gzip - > /var/core/$1-$2-$3-$4.core
启用整个系统的coredump功能
-
在 service 文件
[Service]中添加LimitCORE=infinity, 不制service服务core文件的大小 -
使SUID 程序也转储
sysctl -w 'fs.suid_dumpable=1
systemd用户的coredumpctl
-
安装systemd-coredump && reboot
-
sudo coredumpctl list 列出当前corefile 文件
-
sudo coredumpctl list 'smplayer' | tail -n 5
-
/var/lib/systemd/coredump/ 这是corefile 默认保存位置
-
coredumpctl -o core dump PID # 获得转储信息,输出到文件core 中
-
man systemd-coredump; man core 查看手册
利用内核转储掩码排除共享内存
有些程序会使用多个进程和几个G的共享内存,coredmp时间过长。
在使用各个共享内存的进程中,共享内存的内容是相同的,没必要所有进程都转储共享内存。这种应用程序应当设置成只在某个进程中转储共享内存,其他进程无须转储。
设置方法:echo 1 > /proc/<PID>/coredump_filter (16进制)
比特掩码对应的内存类型:
- (bit 0) anonymous private memory(匿名私有内存段)
- (bit 1) anonymous shared memory(匿名共享内存段)
- (bit 2) file-backed private memory(file-backed 私有内存段)
- (bit 3) file-backed shared memory(file-bakced 共享内存段)
- (bit 4) ELF header pages in file-backed private memory areas (ELF 文件映射, it is effective only if the bit 2 is cleared)
- (bit 5) hugetlb private memory(大页面私有内存)
- (bit 6) hugetlb shared memory(大页面共享内存)
默认的coredump_filter 的值一般是0x33
要跳过所有的共享内存,可以设置为 echo 1 > /proc//coredump_filter
调试器的基本使用方法
调试前的准备:修改编译参数
- 通过gcc的-g选项生成调试信息。
gcc -Wall -O2 -g source.c
-
-ggdb 选项允许使用只有GDB才能使用的额外调试信息,包括gdb扩展
-
-glevel | -ggdblevel `level取值范围 0 1 2 3。-g0不生成调试信息,否定-g。-g1生成调试信息最少,没有局部变量。-g2生成默认级别的调试信息。-g3包括额外的调试信息,支持宏定义。
-
如果使用Makefile构建,一般要求给CFLAGS中指定-g选项。
CFLAGS = -Wall -O2 -g
- 如果用configure脚本生成Makefile,可以这样用。
./configure CFLAGS="-Wall -O2 -g"
构建方法通常会写在INTALL, README等文件中。
-Wall -Werror 选项可以在警告发生时,当成错误来处理
-Wl,-Map=output.map #-Wl,的意思是把后面的参数传递给连接器,传递给连接器的参数列表需要紧跟-Wl,用逗号分隔,不能有带空格。-Wl,-Map=output.map的作用是生成map文件。map文件包含函数地址入口等信息,如果gcc编译时候没有带调试信息,可以用map来定位调用栈中的函数,也可以通过函数地址入口信息通过gdb或者代码调用函数。
如何知道程序带调试信息
readelf -S ./a.out | grep \.debug 如果带.debug段说明有调试信息。
启动调试
-
gdb 可执行文件名
-
gdb --args ./a.out 可执行文件参数列表
-
gdb -p PID
-
gdb -c corefile 可执行文件
设置断点
程序运行后,到达断点会自动暂停运行。
断点命令break,简写b。
| break命令格式 | 例子 |
|---|---|
| break (在下一行代码上设置断点) | break |
| break 函数名 | break sendto |
| break 行号 | break 516 |
| break 文件名:行号 | break main.c:120 |
| break +相对暂停位置的偏移量(行数) | break +3 |
| break -相对暂停位置的偏移量(行数) | break -5 |
| break *地址 | break *0x08116fd3 |
设置好的断点可以通过 info break [n]查看。
使用delete [breakpoints] [n] 命令删除段店,breakpoints可有可无,n为断点编号,如果省略n则删除所有断点。
运行
| 命令 | 说明 |
|---|---|
| run | 开始运行 |
| start | 开始运行,并在main上暂停,相当于自动给main设置断点然后开始运行 |
| set args [Arguments] | 设置进程启动参数 |
| show args | 查看进程参数列表 |
显示代码
list 命令向下查看10行代码
list - 向上查看10行代码
list LINENUM 查看行号的代码
list FUNCTION 查看指定函数代码
list FILE:LINENUM
list FILE:FUNCTION
list *ADDRESS 按地址查看代码
显示调用栈
| 命令 | 说明 |
|---|---|
| backtrace | 显示调用栈栈帧 |
| bt | breaktrace的简写(别名) |
| backtrace N | 只显示开头N个栈帧 |
| bt N | 只显示开头N个栈帧 |
| backtrack -N | 只显示最后N个栈帧 |
| bt -N | 只显示最后N个栈帧 |
| backtrace full | 不仅显示backtrace,还显示局部变量 |
| bt full | 不仅显示backtrace,还显示局部变量 |
| backtrace full N | 不仅显示backtrace,还显示局部变量,N的含义同上 |
| bt full N | 不仅显示backtrace,还显示局部变量,N的含义同上 |
| backtrace full N | 不仅显示backtrace,还显示局部变量,N的含义同上 |
| bt full -N | 不仅显示backtrace,还显示局部变量,N的含义同上 |
| info stack | backtrace的别名 |
| where | backtrace的别名 |
显示局部变量和切换调用栈
info locals 显示局部变量
info args 查看函数参数
info frame查看调用栈帧相关信息
up [N] 查看上一层函数调用栈,N为层数,默认是1
down [N] 查看下一层函数调用栈,N为层数,默认是1
frame <栈帧N> N为bt命令显示的编号,查看第N个调用栈
frame 切换到当前线程正在执行的调用栈
显示变量
print命令可以显示变量,简写p。
格式: print 变量。
| 例子 | 显示 |
|---|---|
| p argv | (char**)0xbf9cd714 |
| p *argv | "./a.out" |
| p argv[0] | "./a.out" |
| p argv[1] | "hello" |
自动显示变量的值
display 命令用于监视变量或者内存的值,每次 gdb 中断,都会自动输出这些被监视变量或内存的值。
-
display命令的使用格式是
display 变量名/内存地址/寄存器名。 -
info display# 查看display设置的自动显示的信息。 -
undisplay <编号> # 取消自动显示变量(info display时显示的编号)
-
delete display range… # range为编号,或者空格分割的编号,或者 编号-编号,用来删除一个范围
查看寄存器
info registers可以查看寄存器,简写为info reg。
在寄存器名称前加上$, 可以用p命令查看寄存器。
p $rax
程序指针可以写为 $pc, 也可以写为 $eip。
p $pc 能够常看当前执行的程序地址。
print 命令支持选择显示的格式。print/c $rax p/u $rdx
print命令可以使用的显示格式
| 格式 | 说明 |
|---|---|
| x | 显示为16进制数字 |
| d | 显示为十进制数字 |
| u | 显示为无符号十进制数 |
| o | 显示为8进制数字 |
| t | 显示为2进制数字,t的由来是two |
| a | 显示地址 |
| c | 显示为字符(ASCII) |
| f | 显示为浮点小数 |
| s | 显示为字符串 |
| i | 显示为汇编语言。 |
查看内存
x命令可以显示内存中的内容。x这个名字的由来是eXamining.
格式: x/格式 地址 x /格式 寄存器
一般使用x命令时,格式为 x/NFU ADDR。addr为希望显示的地址。N为重复次数,F为上一节中的显示格式(x,d,u,o,t,a,c,f,s,i)。U下是面的单位。
| U代表的单位 | 说明 |
|---|---|
| b | 字节 |
| h | 半字(2字节) |
| w | 字(4字节,默认值) |
| g | 双字(8字节) |
显示$pc所指地址开头的10条指令: x /10i $pc
反汇编指令 disassemble
反汇编指令 disassemble,简写为disas。
| 格式 | 说明 | 例子 |
|---|---|---|
| disas | 反汇编当前整个函数 | disas |
| disas 程序计数器 | 反汇编程序计数器所在的函数整个函数 | disas $pc |
| disas 开始地址 结束地址 | 反汇编从开始地址到结束地址之间的部分 | disas $pc $pc+50 |
单步执行
单步执行的意思是根据代码一行一行的执行。
单步执行命令为next,简写n.
进入函数内部的单步命令为step,简写为p.
| 命令 | 简写 | 含义 |
|---|---|---|
| next | n | 执行下一行。执行完毕后显示再下一行的源码。不会进入下一行中的函数内部。 |
| step | s | 执行下一行,但会进入下一行中函数内部。 |
| nexti | - | 逐条执行汇编指令 ,不会进入函数内部。 |
| stepi | - | 逐条执行汇编指令 ,会进入函数内部。 |
继续运行
continue命令(简写c)可以继续运行程序。程序会在遇到断点后再次暂停运行。如果没有断点,就会一直运行到结束。
| 命令 | 说明 |
|---|---|
| continue | 继续运行 |
| continue 次数 | 指定次数忽略遇到的断点。例如continue 5,遇到5次断点不暂停运行。 |
监视点
watch命令可以找到变量或内存在何时被改变或访问。
| 命令 | 说明 |
|---|---|
| awatch <表达式> | 表达式被访问,改变时暂停运行。即watch和rwatch。 |
| watch <表达式> | 表达式发生变化是暂停运行。 |
| rwatch <表达式> | 表达式被访问是暂停运行。 |
表达式的的意思是常量或者变量。例如watch name watch argv[0]
删除断点和监视点
| 命令 | 说明 |
|---|---|
| info b | 显示断点和监视点 |
| delete <编号> | 删除指定的断点或监视点 |
| delete | 删除所有断点和监视点 |
其他断点
-
硬件断点
hbreak,适用于ROM空间等无法修改的内存区域中的程序,在有些架构中无法使用。 -
临时断点
tbreak和临时硬件断点thbreak,与断点(硬件断点)基本相同,不同之处是临时断点会在到达断点程序暂停时被删除,所以只需要暂停一次时候使用起来很方便。 -
遗憾的是没有临时监视点。
改变变量的值
set variable <变量>=<表达式>
set variable num=0 将num的值设置为0
生成coredump文件
gdb中 generate-core-file命令在当前目录下生成coredump文件,可以写为gcore。
linux shell中 gcore [-a] pid1 [pid2...pidN] 命令可以根据进程PID生成coredump文件。
例如gcore $(pidof emacs)
attach到进程
attachh
detach 命令可以脱离调试的进程。
attach后,进程暂停运行,此时可以通过bt命令查看backtrace, 通过continue命令继续运行程序。
进程信息可以用info proc命令查看。
条件断点
有一种断点仅在特定条件下中断。
格式:break 断点 if 条件
这条命令将测试给定的条件,如果为真则暂停运行。
例break iseq_compile if node==0
condition 断点编号 #这条命令可以删除指定断点的出发条件。
condition 断点编号 条件 # 这条命令给指定的断点增加出发条件。
反复执行
| 命令 | 说明 |
|---|---|
| ignore <断点编号> <次数> | 在指定的断点,监视点,或捕获点忽略指定的次数 |
| continue <次数> | 忽略指定次数,到达断点,监视点,捕获点不暂停运行 |
| step 次数 | 单步运行次数(进入函数) |
| stepi 次数 | 单步运行汇编代码,进入函数 |
| next 次数 | 单步运行次数(不进入函数) |
| nexti 次数 | 单步运行汇编代码,不进入函数 |
| finish | 执行完当前的函数后暂停 |
| until | 执行完当前的代码块,或者执行完循环后暂停,常用于跳出循环 |
| return 表达式 | 结束当前函数调用并返回指定的值,回到上一层函数调用 |
| jump LINE | 跳转到指定的行执行 |
| jump *ADDR | 跳转到指定的地址执行 |
删除断点和禁用断点
| 命令 | 说明 |
|---|---|
| clear 函数名 | 删除函数中的断点 |
| clear 行号 | 删除源码行号上的断点 |
| clear 文件名:函数名 | 删除指定文件函数中的断点 |
| clear 文件名:行号 | 删除源码行号上的断点 |
| clear *程序地址 | 删除位于地址的断点 |
| delete [breakpoints] 断点编号 | 删除指定编号的断点, breakpoints关键字可以省略 |
| delete [breakpoints] | 删除所有断点 |
| disable [breakpoints] | 临时禁用所有断点,后面还能再启用 |
| disable [breakpoints] 断点编号 | 临时禁用指定的断点,后面还能再启用 |
| disable diaplay 显示编号 | 禁用display命令定义的自动显示 |
| disable mem 内存区域 | 禁用mem命令定义的内存区域 |
| enable [breakpoints] | 启用被禁用的所有断点。 |
| enable [breakpoints] 断点编号 | 启用指定断点 |
| enable [breakpoints] once 断点编号 | 启用一次断点,随后禁用断点。 |
| enable [breakpoints] delete 断点编号 | 启用一次断点,随后删除断点。 |
| enable diaplay 显示编号 | 启用display命令定义的自动显示 |
| enable mem 内存区域 | 启用mem命令定义的内存区域 |
显示变量类型和定义变量的文件
whatis 表达式 # 显示变量类型声明
ptype 表达式 # 显示变量类型定义
调试多线程
info threads 查看所有线程列表
thread N 切换到指定线程
参考资料&其他学习资料
-
《DEBUG HACKS中文版》
-
https://visualgdb.com/gdbreference
-
man gdb