前言
又一个新系列(坑)开始了!这个系列我会发布一些自己学习Linux开发过程中写下的笔记。
第一篇笔记便是如何愉快(?)的在Linux环境下进行Debug工作(以C语言举例)。
使用的主要工具为 systemd-coredump、GDB。
Core dump 介绍
Core Dump,中文译名为 内核转储 或者 核心转储。
Core Dump 是程序崩溃时,内核将进程的内存镜像、寄存器状态、文件描述符等信息写入磁盘的文件。
简单地说,其相当于程序崩溃瞬间的“快照”,可用于事后调试(如通过 gdb 分析崩溃原因)。
Core dump 应仅与受信任的方共享,因为它们可能包含敏感数据(如密码或加密密钥)。
Core dump 通常不被普通用户使用,但开发人员可以将其作为程序崩溃时状态的事后快照,特别是当故障难以可靠复现时。
可以通过 systemd-coredump 等工具进行管理。本文基于 systemd-coredump 演示。
如果你已经有了心仪的工具,可以直接跳过下一小结。
systemd-coredump 介绍
systemd-coredump 是 systemd 的一个组件,用于管理 Core dump 文件。
可以通过如下的方式查看当前设备的核心转储处理方式,
~$ cat /proc/sys/kernel/core_pattern
|/usr/lib/systemd/systemd-coredump %P %u %g %s %t 9223372036854775808 %h %d
如果在你的电脑上没有显示管道传输到 systemd-coredump 中,说明你用的不是 systemd-coredump。
一般来说,会出现在一些比较有想法的Linux版本中,比如Ubuntu24.04,其结果可能会如下:
~$ cat /proc/sys/kernel/core_pattern
|/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g -F%F -- %E
如果你像我一样更喜欢用 systemd-coredump,而非 apport ,你可以执行下面的命令。
# 彻底关闭 apport
# 1. 删除 apport 可能产生的崩溃报告
~$ rm -rf /var/crash/*
# 2. 编辑如下的文件,将 enabled 的值修改为 0
~$ vim /etc/default/apport
enabled=0
# 3. 关闭 apport 的启动
~$ systemctl stop apport
~$ systemctl disable apport
# 安装 systemd-coredump (如果你的设备上没有)
~$ sudo apt install systemd-coredump
# 此时执行一开始的命令,即可看到变化(如果没有请自行修改)
~$ cat /proc/sys/kernel/core_pattern
|/usr/lib/systemd/systemd-coredump %P %u %g %s %t 9223372036854775808 %h %d
在 systemd 系统中,systemd-coredump 的行为由 /etc/systemd/coredump.conf 或 /etc/systemd/coredump.conf.d/*.conf 控制。
根据手册,建议在后者,也就是 /etc/systemd/coredump.conf.d/*.conf 中进行配置,如下:
~$ cat /etc/systemd/coredump.conf.d/custom.conf
[Coredump]
# 保存到磁盘
Storage=external
# 启用xz压缩以节省空间
Compress=yes
# 核心文件处理最大限制
ProcessSizeMax=2G
# 核心文件的最大存储限制与日志的最大存储限制
ExternalSizeMax=2G
JournalSizeMax=767M
# 核心存储目录最多可以使用多少磁盘空间
# 分区上至少保存多少空闲空间
MaxUse=5G
KeepFree=1G
具体的配置可以查看手册 coredump.conf(5) 。
创建完成后可以执行重启守护程序与单元的命令,如下:
~$ sudo systemctl daemon-reload
~$ sudo systemctl restart systemd-coredump.socket
值得注意的是一些文章可能会说还要执行 ulimit -c 命令查看是否有进程级限制,
但是据我查询手册 core(5),发现并非如此。手册的原文如下:
The RLIMIT_CORE (core file size) or RLIMIT_FSIZE (file size) resource limits for the process are set to zero; see getrlimit(2) and the documentation of the shell's ulimit command (limit in csh(1)). However, RLIMIT_CORE will be ignored if the system is configured to pipe core dumps to a program.
也就是我们将 coredump 管道传输到 systemd-coredump 等程序,Linux 内核本身会忽略 ulimit 设置。
实际上传统的配置方法才会受此影响,具体可以查阅我给出的参考文章
如果你不想让 systemd-coredump 处理 Core dump 文件,那么应该如下设置配置文件:
~$ cat /etc/systemd/coredump.conf.d/custom.conf
[Coredump]
Storage=none
ProcessSizeMax=0
如果你配置好了,那么设备上的 Core dump 文件默认会保存于 /var/lib/systemd/coredump/ 目录下。
不过一般通过 systemd-coredump 的管理工具 coredumpctl 查询,如下:
coredumpctl list
TIME PID UID GID SIG COREFILE EXE SIZE
Sun 2026-01-11 17:51:40 CST 359684 1000 1000 SIGSEGV present /home/bigshot/project/c/error/test.exe 19.4K
Sun 2026-01-11 18:06:00 CST 370138 1000 1000 SIGSEGV present /home/bigshot/project/c/error/test.exe 19.3K
Sun 2026-01-11 18:25:08 CST 378385 1000 1000 SIGSEGV present /home/bigshot/project/c/error/test.exe 19.3K
具体的用法可以通过命令coredumpctl help或者手册 coredumpctl(1) 查询。
GDB 介绍
GDB (GNU Debugger) 应该是 Linux 系统下最常用的程序调试工具了,程序员们都或多或少用过它或者基于它的工具进行Debug。
如果想要使用GDB进行调试,那么编译C代码时必须使用 debug 模式(或者尽可能保留Debug信息),比如使用 gcc 的 -g 选项。
使用 gdb <可执行文件> 即可开启对指定程序的Debug,
GDB初始化文件
和shell类似,GDB也有初始化文件,不同的是GDB的初始化文件使用GDB命令。
其会按照以下的顺序读取初始化文件,并依次执行:
- $HOME/.gdbinit
- 运行命令行选项
- ./.gdbinit
- 通过-x选项给出的命令文件
此外通过 source 命令也能引入文件。
常用的GDB命令
具体如下(部分不常用的命令或格式省略):
举例演示的补充说明跳转到下一小节。
| 命令 | 简写 | 作用 | 跳转目录 |
|---|---|---|---|
| info | inf i | 查询有关信息 | info inf i 信息 |
| sharedlibrary | share | 手动添加共享库(动态库) | sharedlibrary share 手动添加共享库(动态库) |
| break | brea bre br b | 设置断点 | break brea bre br b 断点 |
| hbreak tbreak thbreak | 设置其它断点 | hbreak tbreak thbreak 其它断点 | |
| clear | cl | 清除断点 | clear cl 清除断点 |
| enable | en | 启用断点 | enable en / disable disa dis 启用/禁用断点 |
| disable | disa dis | 禁用断点 | enable disable 启用/禁用断点 |
| conditon | 断点条件的添加(替换)与删除 | conditon 断点条件的添加(替换)与删除 | |
| run | r | (重头开始)运行 | run r 运行 |
| start | (重头开始)运行到main函数 | start 开始 | |
| backtrace | bt where | 显示栈帧 | backtrace bt where 显示栈帧 |
| p inspect | 显示变量 | print p 显示变量 | |
| x | 显示内容中的内容 | x 显示内存 | |
| disassemble | disas | 反汇编 | disassemble disas 反汇编 |
| next | n | 单步(不进入函数内部) | next n 单步(不进入函数内部) |
| step | s | 单步(进入函数内部) | step s 单步(进入函数内部) |
| ignore | 忽略断点、监视点、捕获点 | ignore 忽略断点、监视点、捕获点 | |
| continue | c fg | 继续运行 | continue c fg 继续运行 |
| finish | fin | 执行完当前函数 | finish fin 执行完当前函数 |
| until | u | 运行到某一位置 | until u 运行到某一位置 |
| watch | 添加监视点(改变) | watch awatch rwatch 监视点 | |
| awatch | 添加监视点(被访问、改变) | watch awatch rwatch 监视点 | |
| rwatch | 添加监视点(被访问) | watch awatch rwatch 监视点 | |
| delete | d del | 删除断点和监视点 | delete d del 删除断点和监视点 |
| set | 设置值 | set 设置值 | |
| generate-core-file | gcore | 生成Core dump文件 | generate-core-file gcore 生成Core dump文件 |
| attach | 调试(附加到)已启动进程 | attach detach 调试(附加到)和脱离已启动进程 | |
| detach | 脱离已启动进程 | attach detach 调试(附加到)和脱离已启动进程 | |
| commands | 断点命令 | commands 断点命令 | |
| list | l | 显示函数或行 | list l 显示函数或行 |
| help | h | 帮助文档 | help h 帮助文档 |
| show | 显示数据 | show 显示数据 | |
| define | 命令定义 | define 命令定义 / document 命令说明 | |
| document | 命令说明 | define 命令定义 / document 命令说明 | |
| source | 引入初始化文件 | source 引入初始化文件 |
举例演示的补充说明
演示程序如下:
~$ cat main.c
#include <stdio.h>
void deepdark()
{
printf("deepdark\n");
}
void unhappy()
{
printf("unhappy\n");
deepdark();
}
void happy()
{
int i = 5;
printf("happyi %d\n", i);
unhappy();
}
int main()
{
printf("this is a gdb test program\n");
happy();
return 0;
}
~$ gcc main.c -o gdb_test.exe -g
~$ ls gdb_test.exe main.c
gdb_test.exe main.c
info inf i 信息
| 格式 | 同义 | 作用 |
|---|---|---|
| info break | info breakpoints, info b | 查看当前设置的所有断点 |
| info stack | info s | 同 backtrace |
| info registers | info reg | 显示寄存器 |
| info proc | 显示进程信息 | |
| info sharedlibrary | info share, info shared | 显示已加载的共享库(动态库)的状态 |
| info set | 同 show |
sharedlibrary share 手动添加共享库(动态库)
格式:sharedlibrary 共享库(动态库)路径
手动添加共享库(动态库)到当前进程。
break brea bre br b 断点
断点的设置:
| 格式 | 作用 |
|---|---|
| break 函数名 | 在当前文件指定函数处打断点 |
| break 行号 | 在当前文件指定行打断点 |
| break 文件名:行号 | 在指定文件的指定行处打断点 |
| break 文件名:函数名 | 在指定文件的指定函数处处打断点 |
| break +偏移量 | 在当前暂停位置往后几行处打断点 |
| break -偏移量 | 在当前暂停位置往前几行处处打断点 |
| break *地址 | 在指定地址处打断点 |
断点可以附加条件:
格式:break 断点 if 条件
举例,如下:
gdb ./gdb_test.exe
......
(gdb) b 17 if i==5
Breakpoint 1 at 0x11ba: file main.c, line 17.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000000011ba in happy at main.c:17
stop only if i==5
(gdb) run
Starting program: /home/yuangod/project/c/gdb_test/gdb_test.exe
Breakpoint 1, happy () at main.c:17
17 printf("happyi %d\n", i);
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551ba in happy at main.c:17
stop only if i==5
breakpoint already hit 1 time
hbreak tbreak thbreak 其它断点
硬件断点(hbreak),适用于ROM 空间等无法修改的内存区域中的程序。在有些架构中无法使用。
临时断点(tbreak)和临时硬件断点(thbreak),与断点(硬件断点)相同,都会在运行到该处时暂停,
不同之处就是临时断点(临时硬件断点)会在此时被删除,所以在只需要停止一次时用起来很方便。
三者的语法与break一样
clear cl 清除断点
break 的反命令,
格式与break类似,不过 break 会创建断点,而 clear 会删除断点。
enable en / disable disa dis 启用/禁用断点
对于 enable:
| 格式 | 作用 |
|---|---|
| enable | 启用所有断点 |
| enable 断点编号 | 启用指定的断点 |
对于 disable:
| 格式 | 作用 |
|---|---|
| disable | 禁用所有断点 |
| disable 断点编号 | 禁用指定的断点 |
conditon 断点条件的添加(替换)与删除
| 格式 | 作用 |
|---|---|
| condition 断点编号 | 删除断点的条件 |
| condition 断点编号 条件 | 添加(替换)断点的条件(如果没有条件就添加,有条件就替换) |
举例,如下:
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551ba in happy at main.c:17
stop only if i==5
breakpoint already hit 1 time
(gdb) condition 1
Breakpoint 1 now unconditional.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551ba in happy at main.c:17
breakpoint already hit 1 time
(gdb) condition 1 i==5
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551ba in happy at main.c:17
stop only if i==5
breakpoint already hit 1 time
(gdb) condition 1 i!=6
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551ba in happy at main.c:17
stop only if i!=6
breakpoint already hit 1 time
(gdb) condition 1 i!=6 && i==5
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551ba in happy at main.c:17
stop only if i!=6 && i==5
breakpoint already hit 1 time
run r 运行
格式:run [参数...]
(重头开始)运行,直到结束或者遇到断点。
添加的参数会传递给程序(而非run本身有参数)。
start 开始
格式:start [参数...]
与 run 类似,但是运行到 main 函数停止,
等价于在 main 函数处打上断点( break main ),然后执行 run。
backtrace bt where 显示栈帧
| 格式 | 作用 |
|---|---|
| backtrace | 显示所有栈帧 |
| backtrace full | 显示所有栈帧(及其局部变量) |
| backtrace N | 显示开头(栈顶) N 个栈帧 |
| backtrace full N | 显示开头(栈顶) N 个栈帧(及其局部变量) |
| backtrace -N | 显示最后(栈底) N 个栈帧 |
| backtrace full -N | 显示最后(栈底) N 个栈帧(及其局部变量) |
print p 显示变量
格式①:print 变量
| 举例 | 作用 |
|---|---|
| print argv | 查看参数字符串组的地址 |
| print *argv | 查看参数字符串组的首元素(程序名) |
| print argv[0] | 查看参数字符串组的首元素(程序名) |
| print argv[1] | 查看参数字符串组的第二个元素(第一个参数) |
格式②: print/格式 $寄存器
| 格式 | 作用 |
|---|---|
| x | 显示为十六进制数 |
| d | 显示为十进制数 |
| u | 显示为无符号十进制数 |
| o | 显示八进制数 |
| t | 显示为二进制数,t的由来是two |
| a | 地址 |
| c | 显示为字符(ASCII) |
| f | 浮点小数 |
| s | 显示为字符串 |
| i | 显示为机器语言(仅在显示内存的x命令中可用) |
如下举例:
(gdb) p $1
$4 = 5
(gdb) p/t $1
$5 = 101
x 显示内存
x 源自 eXamining。
格式:x/NFU 地址
N:重复的次数
F:同print p 显示变量中提到的格式。
U:表示单位( b、h、w、g , 分别为 字节、半字(2字节)、字(四字接)、双字(8字节) )
如下举例:
# 读取pc当前指向的汇编指令
(gdb) x/i $pc
=> 0x555555555171 <deepdark+8>: lea 0xe8c(%rip),%rax # 0x555555556004
disassemble disas 反汇编
| 格式 | 作用 |
|---|---|
| disassemble | 反汇编当前整个函数 |
| disassemble 程序计数器 | 从pc开始,反汇编其所在函数的末尾 |
| disassemble 开始地址,结束地址 | 反汇编从开始地址到结束地址之前的部分(注意逗号要加上) |
next n 单步(不进入函数内部)
| 格式 | 作用 |
|---|---|
| next | (源代码级别)单步(不进入函数内部)执行 1 行 |
| next N | (源代码级别)单步(不进入函数内部)执行 n 行 |
| nexti | (机械指令、汇编级别)单步(不进入函数内部)执行 1 行 |
| nexti N | (机械指令、汇编级别)单步(不进入函数内部)执行 n 行 |
step s 单步(进入函数内部)
| 格式 | 作用 |
|---|---|
| step | (源代码级别)单步(进入函数内部)执行 1 行 |
| step N | (源代码级别)单步(进入函数内部)执行 n 行 |
| stepi | (机械指令、汇编级别)单步(进入函数内部)执行 1 行 |
| stepi N | (机械指令、汇编级别)单步(进入函数内部)执行 n 行 |
ignore 忽略断点、监视点、捕获点
格式:ignore 断点编号 N
忽略指定编号的忽略断点、监视点、捕获点接下来会忽略 N 次命中。
效果类似与 continue N ,但有所不同。
continue c fg 继续运行
| 格式 | 作用 |
|---|---|
| continue | 继续运行,直到结束或者遇到断点 |
| continue N | 同 continue,不过当前停止处的断点接下来会忽略 N-1 次命中(即使使用run等命令重启程序也会生效) |
continue N效果类似 ignore,但由所不同。- 如果没有停在断点处,并执行格式
continue N,那么会自动退化为 continue。 continue 1效果上等同于continue。
举例,如下:
......
Breakpoint 2, happy () at main.c:16
16 int i = 5;
(gdb) c 3
Will ignore next 2 crossings of breakpoint 2. Continuing.
happyi 5
Breakpoint 3, unhappy () at main.c:10
10 printf("unhappy\n");
(gdb) info b
Num Type Disp Enb Address What
2 breakpoint keep y 0x00005555555551b3 in happy at main.c:16
breakpoint already hit 1 time
ignore next 2 hits
3 breakpoint keep y 0x000055555555518b in unhappy at main.c:10
breakpoint already hit 1 time
......
finish fin 执行完当前函数
格式 finish
执行后会直接运行到当前函数结束(退出当前函数)。
until u 运行到某一位置
| 格式 | 作用 |
|---|---|
| until | 运行到源代码中下一行(比当前行号大的行)。如果位于循环体的最后一行,会执行完循环体,然后停在下一行,如果在函数体的最后一行,那么会退出函数。 |
| until 位置 | 运行,直到指定的位置(可以是行号、函数、地址等等) |
- 会触发断点
- 格式
until 位置,位置必须在当前栈帧内,如果不在,大概率会运行到下个断点或者程序结束处。
watch awatch rwatch 监视点
大型软件或大量使用指针的程序中,很难弄清变量在什么地方被改变。
要想找到变量在何处被改变,可以使用 watch 相关的命令(监视点,watchpoint)
| 格式 | 作用 |
|---|---|
| watch <表达式> | 监视<表达式>改变(写入) |
| awatch <表达式> | 监视<表达式>被访问(读取)、改变(写入) |
| rwatch <表达式> | 监视<表达式>被访问(读取) |
<表达式>一般为常量或者变量等等。
使用监视点可能会降低运行速度。
举例,如下:
(gdb) rwatch i
Hardware read watchpoint 6: i
(gdb) c
Continuing.
Hardware read watchpoint 6: i
Value = 5
0x00005555555551bd in happy () at main.c:17
17 printf("happyi %d\n", i);
(gdb) info break
Num Type Disp Enb Address What
2 breakpoint keep y 0x0000555555555171 in deepdark at main.c:5
4 breakpoint keep y 0x00005555555551b3 in happy at main.c:16
breakpoint already hit 1 time
6 read watchpoint keep y i
breakpoint already hit 1 time
delete d del 删除断点和监视点
格式:delete <编号>
删除<编号>所指示的断点或监视点。
set 设置值
| 格式 | 作用 |
|---|---|
| set variable <变量>=<表达式> | 改变变量的值 |
| set $<便利变量名>=<表达式> | 设置 gdb 内的"便利变量"(类似于宏) |
| set history [功能] | 开启命令历史的功能 |
格式 set variable <变量>=<表达式> 举例,如下:
(gdb) p i
$14 = 5
(gdb) set variable i = 2026
(gdb) p i
$15 = 2026
格式 set $<便利变量名>=<表达式> 举例,如下:
(gdb) set $my_var=10
(gdb) p $my_var
$1 = 10
(gdb) set $log10 = (double (*)(double)) log10
(gdb) p $log10(10)
$4 = 1
(gdb) p $log10($my_var)
$5 = 1
generate-core-file gcore 生成Core dump文件
用于在调试的过程中生成 Core dump 文件,制作进程的"快照"。
如下:
(gdb) generate-core-file
warning: Memory read failed for corefile section, 4096 bytes at 0xffffffffff600000.
Saved corefile core.638095
(gdb) q
A debugging session is active.
Inferior 1 [process 638095] will be killed.
Quit anyway? (y or n) y
~$ gdb -c core.638095 ./gdb_test.exe
......
值得注意的是 gcore 也是一个GNU工具( 详情见 gcore(1) ),可以直接在 shell 中使用,如下:
# gcore [-a] [-o filename] pid[s]
~$ ps aux | grep vim
bigshot 633690 0.3 0.0 28244 13948 pts/1 Sl+ 18:30 0:00 vim hello
bigshot 633706 0.0 0.0 9304 2460 pts/2 S+ 18:30 0:00 grep --color=auto vim
~$ gcore -o core_vim 633690
[New LWP 633691]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
0x000072ecb0126d07 in select () from /lib/x86_64-linux-gnu/libc.so.6
warning: Memory read failed for corefile section, 4096 bytes at 0xffffffffff600000.
Saved corefile core_vim.633690
~$ ls core_vim.633690
core_vim.633690
# 之后边可以执行下面的命令复原保存时的 vim 进程环境
~$ gdb -c core_vim.633690 vim
......
attach detach 调试(附加到)和脱离已启动进程
要调试守护进程(daemon process)等已经启动的进程,或是调试陷入死循环而无法返回控制台的进程时,可以使用 attach 命令。
格式:attach <pid>
而脱离直接输入 detach 即可。
attach 之后就能使用普通的 gdb 命令,因此可以通过 print 命令显示变量,也可以设置断点。
此外,恢复程序运行一般可以使用 continue命令(简写为c)。
确认了行为之后,需要在 gdb 和 进程 分离时使用 detach 命令。这样调试中的进程就被从 gdb 的控制下释放出来。进程在被 detach 后会继续运行。
举例,如下:
~$ sleep 10000 &
[1] 647246
~$ gdb
......
(gdb) attach 647246
......
# 这里通过 attach 和 bt 直接看到 sleep 程序的栈帧
(gdb) bt
#0 0x000078618caeca7a in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0,
req=0x7ffcb34c71e0, rem=0x7ffcb34c71d0) at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
#1 0x000078618caf9a27 in __GI___nanosleep (req=<optimized out>, rem=<optimized out>)
at ../sysdeps/unix/sysv/linux/nanosleep.c:25
#2 0x000059a509e9da7f in ?? ()
#3 0x000078618ca2a1ca in __libc_start_call_main (main=main@entry=0x59a509e9d5d0, argc=argc@entry=2,
argv=argv@entry=0x7ffcb34c7358) at ../sysdeps/nptl/libc_start_call_main.h:58
#4 0x000078618ca2a28b in __libc_start_main_impl (main=0x59a509e9d5d0, argc=2, argv=0x7ffcb34c7358,
init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffcb34c7348)
--Type <RET> for more, q to quit, c to continue without paging--
at ../csu/libc-start.c:360
#5 0x000059a509e9dba5 in ?? ()
# 查看进程信息
(gdb) info proc
process 647246
cmdline = 'sleep 10000'
cwd = '/home/yuangod/project/c/gdb_test'
exe = '/usr/bin/sleep'
# 脱离
(gdb) detach
Detaching from program: /usr/bin/sleep, process 647246
[Inferior 1 (process 647246) detached]
(gdb) info proc
No current process: you must name one.
commands 断点命令
格式:
commands 断点编号
命令
...
end
举例,如下:
......
Temporary breakpoint 1, main () at main.c:23
23 printf("this is a gbd test program\n");
(gdb) b 17
Breakpoint 2 at 0x5555555551ba: file main.c, line 17.
(gdb) commands 2
Type commands for breakpoint(s) 2, one per line.
End with a line saying just "end".
>silent
>p i
>end
(gdb) c
Continuing.
this is a gbd test program
$1 = 5
# silent命令的作用是不显示断点处暂停的消息。
......
list l 显示函数或行
| 格式 | 作用 |
|---|---|
| list | 列出上次列出内容之后的十行代码,或者围绕上次列出位置(取决于上下文)。 |
| list + | 列出上一次列出的十行代码之后的十行代码。 |
| list - | 列出上一次列出的十行代码之前的十行代码。 |
| list . | 列出当前执行点(即程序暂停的位置)周围十行的代码。 |
| list 行号 | 列出该行周围十行的代码。 |
| list 开始行号,结束行号 | 列出从开始行号到结束行号范围内的代码。 |
- 位置还可以为地址等,因为过多,这里省略。
- 特别地,
list *$pc可以显示当前行周围的代码(与list .不同,这显示当前的地址与行号)。
help h 帮助文档
格式:help 命令 举例,如下:
......
(gdb) help help
help, h
Print list of commands.
......
show 显示数据
| 格式 | 作用 |
|---|---|
| show value | print 命令显示历史的最新十个记录 |
| show history [功能] | 显示命令历史功能的开关与说明,与 set history [功能] 配合使用 |
define 命令定义 / document 命令说明
define 可以用于自定义命令(类似函数),格式如下:
define 命令名
命令
...
end
document 可以用于给自定义命令添加说明,格式如下:
document 命令名
说明
end
举例,如下:
(gdb) define li
Type commands for definition of "li".
End with a line saying just "end".
>x/10i $pc
>end
(gdb) document li
Type documentation for "li".
End with a line saying just "end".
>list machine instruction
>end
(gdb) li
=> 0x5555555551e8 <main+8>: lea 0xe31(%rip),%rax # 0x555555556020
0x5555555551ef <main+15>: mov %rax,%rdi
0x5555555551f2 <main+18>: call 0x555555555060 <puts@plt>
0x5555555551f7 <main+23>: mov $0x0,%eax
0x5555555551fc <main+28>: call 0x5555555551a7 <happy>
0x555555555201 <main+33>: mov $0x0,%eax
0x555555555206 <main+38>: pop %rbp
0x555555555207 <main+39>: ret
0x555555555208 <_fini>: endbr64
0x55555555520c <_fini+4>: sub $0x8,%rsp
(gdb) help li
list machine instruction
source 引入初始化文件
格式:source 文件名
除了初始化文件,这种方式也能引入命令进行设置。
远程调试
有时候我们需要在一台设备中远程调试另一台设备,比如在x86_64架构的PC上调试在ARM架构的Linux开发板上运行的程序(下文以这个举例)。
为了完成远程调试,我们需要对应架构的 gdb 工具与 gdbserver 工具。一般来说,交叉编译工具链会提供对应架构的gdb与gdbserver。
获取对应架构的工具后,我们需要将 gdbserver 与用交叉编译工具链编译的对应架构可执行文件放到开发板上,而主机上需要对应的源码,可执行文件,与对应架构的gdb工具。
假设可执行文件的名字叫 gdbtest ,而对应的源码叫 gdbtest.c ,我们需要在开发板上如下操作:
~$ gdbserver <主机IP地址>:<任意端口> gdbtest
然后在主机下进行如下操作:
# 这里 arm-none-linux-gnueabihf-gdb 为交叉编译工具链
# 注意 gdbtest 要在本地备份一个供 gdb 读取
~$ arm-none-linux-gnueabihf-gdb gdbtest
......
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from gdbtest...
# 这里要输入值令进行连接
(gdb) target remote <开发板IP地址>:<与上文对应的端口>
Remote debugging using 192.168.1.50:2001
Reading symbols from /usr/local/arm/arm-gnu-toolchain-15.2.rel1-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/libc/lib/ld-linux-armhf.so.3...
0x76feff30 in _start ()
from /usr/local/arm/arm-gnu-toolchain-15.2.rel1-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/libc/lib/ld-linux-armhf.so.3
(gdb) start
The "remote" target does not support "run". Try "help target" or "continue".
(gdb) b main
Breakpoint 1 at 0x10422: file gdbtest.c, line 6.
(gdb) c
Continuing.
Breakpoint 1, main (argc=1, argv=0x7efffd74) at gdbtest.c:6
6 unsigned int times = 0;
# 下文正常运行 gdb 相关值令即可
参考文章
- Linux 核心转储(Core Dump)位置详解:配置、实践与故障排除
- Arch Linux 中文维基
- Linux 配置 coredump
- Debug Hacks中文版