Coredump文件分析宕机问题完整指南
一、环境准备
1.1 目标板启用核心转储
bash
复制代码
# 启用核心转储(无大小限制)
ulimit -c unlimited
# 设置核心转储文件格式
# %e - 可执行文件名
# %p - 进程ID
# %t - 时间戳
# %s - 信号编号
echo "/var/core/core.%e.%p.%s.%t" > /proc/sys/kernel/core_pattern
# 创建转储目录
mkdir -p /var/core
chmod 777 /var/core
# 验证配置
cat /proc/sys/kernel/core_pattern # 查看转储格式
ulimit -a | grep core # 确认core大小限制
1.2 编译选项
makefile
复制代码
# 调试版本编译选项
CFLAGS += -g # 生成调试符号
CFLAGS += -O0 # 禁用优化(便于调试)
CFLAGS += -fno-omit-frame-pointer # 保留帧指针(保证调用栈完整)
# 发布版本(保留调试符号)
CFLAGS += -g -O2 -fno-omit-frame-pointer
# 分离调试符号
objcopy --only-keep-debug myapp myapp.debug # 提取调试符号
strip --strip-debug --strip-unneeded myapp # 剥离符号
objcopy --add-gnu-debuglink=myapp.debug myapp # 关联调试文件
二、启动GDB分析Coredump
2.1 基本启动方式
bash
复制代码
# 本地分析
gdb ./myapp core.1234
# 交叉调试分析
arm-linux-gnueabihf-gdb ./myapp ./myapp.debug core.myapp.1234.11.1699999999
# 分步加载方式
arm-linux-gnueabihf-gdb
2.2 GDB内部加载命令
gdb
复制代码
# 加载可执行文件
file ./myapp
# 加载调试符号文件
add-symbol-file ./myapp.debug
# 加载核心转储文件
core-file core.myapp.1234.11.1699999999
# 设置共享库搜索路径(交叉调试必需)
set solib-search-path ./lib:/opt/sdk/sysroot/lib
# 设置系统根目录(用于查找库文件)
set sysroot /opt/sdk/sysroot
三、崩溃信息分析
3.1 查看基本信息
gdb
复制代码
# 查看当前信号信息(了解崩溃原因)
info signals
# 查看目标信息(确认core文件加载状态)
info target
# 查看程序状态
info program
# 查看当前线程
info threads
3.2 查看寄存器状态
gdb
复制代码
# 查看通用寄存器
info registers
# 查看所有寄存器(包括浮点、向量寄存器)
info all-registers
# 查看特定寄存器
info registers rax rbx rcx rdx # x86架构
info registers r0 r1 r2 r3 # ARM架构
# 打印程序计数器(崩溃位置)
print $pc
print/x $pc # 十六进制格式
# 打印栈指针
print $sp
print $fp # 帧指针
# 查看指令指针处的指令
x/i $pc
四、调用栈分析
4.1 基本调用栈命令
gdb
复制代码
# 显示调用栈
bt
# 显示完整调用栈(包含局部变量)
bt full
# 显示指定层数的调用栈
bt 5 # 只显示5层
bt full 5 # 5层并包含局部变量
# 显示所有线程的调用栈
thread apply all bt
thread apply all bt full
4.2 栈帧操作
gdb
复制代码
# 查看当前栈帧信息
info frame
# 查看当前栈帧的参数
info args
# 查看当前栈帧的局部变量
info locals
# 切换栈帧(向上移动,调用者方向)
up
up 2 # 向上移动2层
# 切换栈帧(向下移动,被调用者方向)
down
down 2 # 向下移动2层
# 切换到指定栈帧
frame 0 # 切换到第0帧(崩溃位置)
frame 3 # 切换到第3帧
# 查看所有栈帧概要
info stack
4.3 查看栈帧源码
gdb
复制代码
# 显示当前位置的源代码
list
# 显示指定行数的源代码
list 50 # 显示第50行附近
list main.c:100 # 显示main.c第100行附近
# 显示更多源代码
list + # 向后显示
list - # 向前显示
# 显示指定范围
list 1,50 # 显示第1到50行
# 查看当前位置的反汇编
disassemble
# 查看指定范围的反汇编
disassemble main
disassemble 0x400500,0x400600
五、变量与内存分析
5.1 打印变量
gdb
复制代码
# 打印变量值
print var
# 打印指针指向的值
print *ptr
# 打印数组(前10个元素)
print arr[0]@10
# 指定格式打印
print/x var # 十六进制
print/d var # 十进制
print/t var # 二进制
print/c var # 字符
print/s str # 字符串
# 打印结构体
print *struct_ptr
print struct_var.member
# 打印字符串(指定长度)
print str[0]@50
# 查看变量类型
whatis var # 简单类型
ptype struct_name # 详细类型定义
5.2 查看内存
gdb
复制代码
# x命令格式: x/NFU address
# N - 显示单元数量
# F - 显示格式(x十六进制, d十进制, t二进制, c字符, s字符串, i指令)
# U - 单元大小(b字节, h半字, w字, g双字)
# 查看内存(十六进制)
x/20x 0x400000 # 20个十六进制字
x/20xw 0x400000 # 20个十六进制字(4字节)
x/20xb 0x400000 # 20个十六进制字节
# 查看指令
x/20i $pc # 从PC位置显示20条指令
# 查看字符串
x/s string_ptr # 显示字符串
# 查看字符数组
x/50c buffer # 显示50个字符
# 查看栈内容
x/20x $sp # 查看栈顶内容
x/20gx $sp # 查看栈顶(8字节单位)
5.3 自动显示
gdb
复制代码
# 设置自动显示(每次停止时自动打印)
display var
display/x $pc # 自动显示PC(十六进制)
# 查看自动显示列表
info display
# 删除自动显示
undisplay 1 # 删除编号1的自动显示
delete display 1 # 同上
六、线程分析
6.1 查看线程信息
gdb
复制代码
# 列出所有线程
info threads
# 输出示例:
# Id Target Id Frame
# * 1 Thread 0x7ffff7fc0740 (LWP 1234) "myapp" 0x00007ffff7dd5f7d in pthread_join
# 2 Thread 0x7ffff6bc0700 (LWP 1235) "myapp" worker_thread () at worker.c:100
# 3 Thread 0x7ffff63bf700 (LWP 1236) "myapp" 0x00007ffff7dd8ed5 in pthread_cond_wait
# 查看特定线程详细信息
info thread 2
# 查看线程数量
info threads | wc -l
6.2 切换线程
gdb
复制代码
# 切换到指定线程
thread 2
# 切换后查看该线程的调用栈
thread 2
bt
# 查看该线程的寄存器
thread 2
info registers
6.3 批量线程分析
gdb
复制代码
# 显示所有线程的调用栈
thread apply all bt
# 显示所有线程的完整调用栈
thread apply all bt full
# 显示指定范围的线程调用栈
thread apply 1-3 bt
# 对所有线程执行命令
thread apply all info registers
6.4 死锁检测
gdb
复制代码
# 查看所有线程调用栈,找出等待锁的线程
thread apply all bt
# 查看互斥锁状态
print *mutex
print mutex->__data.__owner # 查看锁持有者线程ID
# 查看条件变量
print *cond
七、常见崩溃场景分析
7.1 段错误分析流程
gdb
复制代码
# 1. 加载core文件
gdb ./myapp core.1234
# 2. 查看崩溃信号
info signals
# 3. 查看调用栈
bt
# 4. 切换到崩溃栈帧
frame 0
# 5. 查看源代码
list
# 6. 查看局部变量
info locals
info args
# 7. 查看相关指针是否为空
print ptr
# 8. 查看寄存器
info registers
# 9. 反汇编分析
disassemble
7.2 空指针解引用示例
gdb
复制代码
# 假设崩溃在 data->value 访问
(gdb) bt
#0 0x0000000000400abc in process_data (data=0x0) at main.c:50
#1 0x0000000000400bcd in main () at main.c:100
# 查看崩溃位置
frame 0
list
# 查看指针值
print data
$1 = (Data *) 0x0 # 空指针!
# 查看寄存器
info registers
rax 0x0 0 # 空指针在rax中
# 反汇编确认
disassemble
# mov (%rdi),%rax # 尝试解引用空指针
7.3 数组越界分析
gdb
复制代码
# 查看数组边界
print sizeof(arr)
print sizeof(arr)/sizeof(arr[0]) # 数组长度
# 查看访问的索引
print index
# 查看数组内容
print arr[0]@20
# 使用观察点检测越界
watch arr[100] # 监控越界位置
7.4 栈溢出分析
gdb
复制代码
# 查看栈指针
print $sp
# 查看栈帧信息
info frame
# 查看栈内存
x/50x $sp
# 检查栈保护函数
info functions __stack_chk_fail
7.5 内存释放后使用
gdb
复制代码
# 查看指针值
print ptr
# 查看指针指向的内存
x/20x ptr
# 检查是否为已释放区域(通常为0x0000000000000000或无效地址)
# 使用valgrind或AddressSanitizer更容易检测
八、高级分析技巧
8.1 条件断点与观察点
gdb
复制代码
# 设置观察点(数据变化时停止)
watch global_var # 变量被写入时停止
rwatch global_var # 变量被读取时停止
awatch global_var # 变量被读或写时停止
# 查看观察点
info watchpoints
# 设置条件断点
break main.c:100 if x > 10
break process_data if data == NULL
# 修改现有断点条件
condition 1 count > 100
8.2 内存映射查看
gdb
复制代码
# 查看内存映射
info proc mappings
# 输出示例:
# Start Addr End Addr Size Offset objfile
# 0x400000 0x401000 0x1000 0x0 /home/user/myapp
# 0x600000 0x601000 0x1000 0x0 /home/user/myapp
# 0x7ffff7dd5000 0x7ffff7dfc000 0x27000 0x0 /lib/libc.so.6
# 查看共享库信息
info sharedlibrary
8.3 反向调试
gdb
复制代码
# 启用执行记录
record
record full
# 运行程序
continue
# 反向执行
reverse-continue # 反向继续执行
reverse-step # 反向单步(进入函数)
reverse-next # 反向单步(跳过函数)
reverse-stepi # 反向单条指令
# 查看记录状态
info record
# 停止记录
record stop
8.4 函数调用与变量修改
gdb
复制代码
# 在调试中调用函数
call func(1, 2)
call printf("value = %d\n", x)
# 修改变量值
set var = 10
set *ptr = 0
set {int}0x400000 = 100
# 修改字符串
set {char[10]}buffer = "hello"
# 强制函数返回
return
return 10 # 返回指定值
九、批处理自动化分析
9.1 命令行批处理
bash
复制代码
# 使用-ex参数执行多个命令
arm-linux-gnueabihf-gdb -batch \
-ex "file ./myapp" \
-ex "core-file ./core.1234" \
-ex "bt full" \
-ex "thread apply all bt" \
-ex "info registers" \
> analysis.txt
9.2 GDB脚本文件
gdb
复制代码
# analysis.gdb - 保存为脚本文件
set pagination off # 关闭分页
set confirm off # 关闭确认提示
# 加载文件
file ./myapp
core-file ./core.1234
# 设置日志输出
set logging file analysis.txt
set logging on
# 执行分析
echo === Signal Info ===\n
info signals
echo \n=== Registers ===\n
info registers
echo \n=== Backtrace ===\n
bt full
echo \n=== All Threads ===\n
thread apply all bt
echo \n=== Memory Map ===\n
info proc mappings
echo \n=== Disassembly ===\n
disassemble
# 退出
set logging off
quit
bash
复制代码
# 执行脚本
gdb -x analysis.gdb
9.3 完整自动化分析脚本
bash
复制代码
#!/bin/bash
# auto_analyze.sh
CORE_FILE=$1
BINARY=$2
DEBUG_FILE=$3
if [ -z "$CORE_FILE" ] || [ -z "$BINARY" ]; then
echo "Usage: $0 <core-file> <binary> [debug-file]"
exit 1
fi
GDB="arm-linux-gnueabihf-gdb"
REPORT="analysis_$(basename $CORE_FILE).txt"
echo "=== Crash Analysis Report ===" > $REPORT
echo "Date: $(date)" >> $REPORT
echo "Core: $CORE_FILE" >> $REPORT
echo "Binary: $BINARY" >> $REPORT
echo "" >> $REPORT
$GDB -batch \
-ex "file $BINARY" \
${DEBUG_FILE:+-ex "add-symbol-file $DEBUG_FILE"} \
-ex "core-file $CORE_FILE" \
-ex "set pagination off" \
-ex "echo === Signal Info ===\\n" \
-ex "info signals" \
-ex "echo \\n=== Registers ===\\n" \
-ex "info registers" \
-ex "echo \\n=== Backtrace ===\\n" \
-ex "bt full" \
-ex "echo \\n=== All Threads ===\\n" \
-ex "thread apply all bt" \
-ex "echo \\n=== Memory Map ===\\n" \
-ex "info proc mappings" \
-ex "echo \\n=== Disassembly ===\\n" \
-ex "disassemble" \
>> $REPORT 2>&1
echo "Analysis complete: $REPORT"
十、信号与错误对照表
信号
编号
名称
常见原因
SIGSEGV
11
段错误
空指针解引用、数组越界、释放后使用
SIGABRT
6
中止
abort()调用、assert()失败、内存分配失败
SIGBUS
7
总线错误
内存对齐错误、映射文件访问错误
SIGFPE
8
浮点异常
除零、浮点溢出
SIGILL
4
非法指令
执行损坏的代码、错误的函数指针
SIGPIPE
13
管道破裂
写入已关闭的管道/套接字
十一、GDB命令速查表
命令
简写
说明
file
加载可执行文件
core-file
加载核心转储文件
add-symbol-file
加载调试符号文件
run
r
运行程序
continue
c
继续执行
next
n
单步执行(跳过函数)
step
s
单步执行(进入函数)
finish
fin
执行到函数返回
backtrace
bt
显示调用栈
frame
f
切换/显示栈帧
up
向上移动栈帧
down
向下移动栈帧
print
p
打印变量/表达式
display
设置自动显示
list
l
显示源代码
disassemble
dis
反汇编
info
i
查看各种信息
thread
t
线程操作
break
b
设置断点
watch
wa
设置观察点
x
查看内存
set
修改变量/设置选项
call
调用函数
quit
q
退出GDB
十二、典型分析流程总结
复制代码
1. 加载core文件
gdb ./myapp core.1234
2. 确认崩溃信号
info signals
3. 查看调用栈
bt full
4. 切换到崩溃栈帧
frame 0
5. 查看源代码
list
6. 查看局部变量和参数
info locals
info args
7. 查看相关变量值
print var
print *ptr
8. 查看寄存器
info registers
9. 查看内存
x/20x address
10. 多线程程序查看所有线程
thread apply all bt
11. 分析死锁(如适用)
print *mutex
print mutex->__data.__owner
12. 反汇编分析(如需要)
disassemble