嵌入式linux GDB使用教程
当然可以,以下是结合你所有需求(断点设置、程序启动、继续执行、寄存器查看、栈回溯、调试重启等)的:
✅ RK3588S 上 GDB 调试完整实战指南
适用于你当前的 dtof
程序调试。包含了从启动调试到分析崩溃、查看变量,再到重新运行的全流程。
🚀 一、准备工作
确保你编译时带上调试信息:
gcc -g -O0 -o dtof main.c ...
或确认 dtof
是带符号的可执行文件。
🧭 二、典型调试流程(推荐套路)
(gdb) file ./dtof # 加载可执行文件
(gdb) break main # 在 main 函数打断点
(gdb) break foo.c:42 # (可选) 在特定源码行打断点
(gdb) run # 开始运行程序,遇断点停下(gdb) print my_var # 打印变量
(gdb) info locals # 查看当前函数的局部变量
(gdb) info registers # 查看所有寄存器(x0~x30, sp, pc 等)
(gdb) bt # 查看调用栈 backtrace
(gdb) frame 1 # 切换到上一帧(查看调用路径)
(gdb) info locals # 查看该帧下局部变量(gdb) break main.c:88 # 再添加一个断点
(gdb) continue # 从当前停的位置继续执行
(gdb) next # 执行下一行,不进入函数
(gdb) step # 执行下一行,遇函数会进入
(gdb) finish # 跳出当前函数(gdb) info breakpoints # 查看断点列表
(gdb) disable 2 # 禁用编号为2的断点
(gdb) delete 2 # 删除编号为2的断点(gdb) kill # 杀掉当前运行实例
(gdb) run # 从头重新执行
(gdb) quit # 退出 GDB
🧱 三、命令说明对照表
操作类别 | 命令 | 说明 |
---|---|---|
打断点 | break main / break file:line | 在函数或源码行打断点 |
启动执行 | run | 从头运行程序 |
继续执行 | continue 或 c | 从当前位置继续 |
单步执行 | next / step / finish | 分别用于逐行、进入函数、跳出函数 |
查看变量 | print x / info locals | 打印变量或所有局部变量 |
查看寄存器 | info registers / print $x0 | 查看通用寄存器、栈指针、PC、LR |
栈回溯 | backtrace 或 bt | 调用路径 |
切换调用帧 | frame n | 切换到指定调用帧 |
管理断点 | info breakpoints / delete | 查看、删除断点 |
重启程序 | kill + run | 杀掉当前实例再重启 |
退出调试 | quit | 退出 GDB |
🧠 四、调试技巧提示
场景 | 推荐命令 |
---|---|
崩溃了(如段错误) | bt → frame 0 → info locals 查看崩溃现场 |
想从头再跑一次 | kill → run |
程序停住了,想跳过当前函数 | finish |
想一次性跑到下一个断点 | continue |
想看哪个函数调用了我 | backtrace (栈回溯) |
🔁 示例:实战流程一览
(gdb) file ./dtof
(gdb) break main
(gdb) break sensor.c:128
(gdb) run
# 停在 main
(gdb) next
(gdb) print sensor_status
(gdb) continue
# 停在 sensor.c:128
(gdb) info registers
(gdb) bt
(gdb) frame 1
(gdb) info locals
(gdb) kill
(gdb) run arg1 arg2
❓你可能还关心的问题:
-
💡为什么要
kill
后再run
?因为
run
会杀掉当前进程,除非已经手动 kill 掉,否则 GDB 会提示你是否要重新运行。 -
⚠️使用
run
会清空所有程序运行时状态,只保留断点。如果你想继续运行,就要用continue
。
你说得对,我们来把**“栈回溯(Backtrace)”的内容补全并与整个调试流程融合起来,特别强调它在调试程序崩溃(如 Segmentation fault)**、分析函数调用链上的作用。
🧠 GDB 栈回溯(Backtrace)完整讲解(结合调试全流程)
栈回溯 = 调用栈追踪,告诉你当前函数是被谁调用的,一直追溯到
main()
。
非常适合用来分析程序崩溃的位置和原因,比如段错误、空指针、非法访问等问题。
🔍 常用命令
✅ backtrace
或 bt
— 显示函数调用栈
(gdb) backtrace
示例输出:
#0 crash_here (ptr=0x0) at main.c:42
#1 0x000000000040115a in do_work () at worker.c:88
#2 0x0000000000401204 in main () at main.c:112
含义:
帧编号 | 函数名 | 参数 | 来源文件与行号 |
---|---|---|---|
#0 | crash_here() | ptr=0x0 | 崩溃发生在 main.c:42 |
#1 | do_work() | — | 从 worker.c:88 调用 |
#2 | main() | — | 从 main.c:112 开始 |
✅ frame n
— 查看某一帧详细信息
(gdb) frame 1
这会切换到帧 #1
,然后你可以查看局部变量或源码位置。
(gdb) info locals
(gdb) list
✅ 栈回溯扩展用法
-
只看前 N 层:
(gdb) bt 5
-
查看指定线程的回溯(多线程调试用):
(gdb) thread apply all bt
💡 在调试流程中何时使用?
🚨 程序崩溃时(Segmentation fault)
当程序因为空指针等非法访问崩溃时,会自动停在异常点:
Program received signal SIGSEGV, Segmentation fault.
0x0000000000401042 in crash_here (ptr=0x0) at main.c:42
此时你可以:
(gdb) bt # 查看崩溃是怎么一步步调用来的
(gdb) frame 0 # 进入崩溃函数
(gdb) info locals # 看看哪个变量为空
🧪 实战流程举例
(gdb) run
# 程序崩溃了:
# Program received signal SIGSEGV, Segmentation fault.(gdb) bt
#0 crash_here (ptr=0x0) at main.c:42
#1 do_work () at worker.c:88
#2 main () at main.c:112(gdb) frame 0
(gdb) info locals
(gdb) print ptr
$1 = 0x0(gdb) frame 1
(gdb) info args
这一步一步定位出,main()
→ do_work()
→ crash_here()
,并且因为 ptr=0x0
,所以 *ptr = 1
导致了段错误。
✅ 最佳实践建议
场景 | 命令 |
---|---|
程序崩溃 | bt → frame 0 → info locals |
调试函数调用顺序 | bt |
查看 main 入口 | frame N (N 最大值) |
🧩 集成进完整调试流程
在前面的调试流程中加入:
(gdb) run
# 程序崩溃后自动停止(gdb) bt # 栈回溯
(gdb) frame 0 # 查看当前函数
(gdb) info locals # 当前函数的局部变量
(gdb) frame 1 # 上一层函数
(gdb) info args # 传入参数
(gdb) continue # 若不崩溃则继续运行
是否要我帮你把这整套流程做成 .gdbinit
或 .gdb_cmds
文件,你只要运行 GDB 就自动完成断点设置 + 栈回溯?也可以给你写个调试助手脚本。