cat writebug.cn/history

一个开发者的技术博客。

gdb的基本使用方法

获取进程的coredump (内核转储)

启用内核转储

功能 命令
查看coredump功能是否有效 ulimitulimit -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

  1. 安装systemd-coredump && reboot

  2. sudo coredumpctl list 列出当前corefile 文件

  3. sudo coredumpctl list 'smplayer' | tail -n 5

  4. /var/lib/systemd/coredump/ 这是corefile 默认保存位置

  5. coredumpctl -o core dump PID # 获得转储信息,输出到文件core 中

  6. 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 可以attach到指定进程,和gdb -p 效果类似。

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 切换到指定线程

参考资料&其他学习资料