1、什么是core文件
core文件是程序崩溃时,操作系统记录的内存快照,里面包含信息:
程序崩溃时调用的堆栈信息
信号量
变量
符号
内存
各个线程状态
CPU 寄存器信息
2、遇到程序崩溃怎么定位?
1)先看是哪个业务崩溃,找到崩溃业务的微服务;
2)用gdb工具查看core文件,core文件的信号量和调用栈,在哪个函数内崩溃;
3)查看微服务日志,服务重启前后,或者崩溃前后,打印的日志:
对比服务重启前后是否有异常请求、错误码或异常堆栈信息。
4)定位到具体函数内,查看崩溃的那一行,发生了什么,
一般core有可能是数组越界,空指针,非法访问内存 ,子线程未结束主线程提前退出,多个线程访问同一资源未加锁,变量未初始化,使用未赋值的变量等问题,打印日志,分析函数;
可增加:
0)前置准备:确保 core dump 能生成
确保部署环境开启了 core dump( ulimit -c unlimited)
1.5)是否能复现?尝试复现场景
如果是偶发 core,可以尝试:
用相同请求参数重放
在压力测试下复现
用相同环境和配置部署副本再测
2.5)分析 core 时,获取更多上下文信息
使用 info locals、info args、info threads、thread apply all bt 等命令
3、core常见命令
- info locals
查看当前函数的 局部变量 及其值。
📌 用法:
(gdb) info locals
✅ 举例:
void foo() {
int a = 10;
std::string s = "hello";
// 崩溃点
}
调试 core 时定位到 foo() 函数,可以输入:
(gdb) frame 0
(gdb) info locals
输出类似:
a = 10
s = "hello"
作用:快速查看当前帧的所有局部变量,是否有异常值(如空指针、意外 0 值等)
🧠 2. info args
查看当前函数的 参数值
📌 用法:
(gdb) info args
✅ 举例:
void process(int id, Node* ptr) {
// 崩溃点
}
调试 core 时,切到崩溃帧:
(gdb) frame 0
(gdb) info args
输出类似:
id = 42
ptr = (Node *) 0x0 ← 空指针
✔️ 作用:快速判断函数参数传入是否异常,是不是传了 null 等
🧠 3. info threads
查看程序中的 所有线程列表
📌 用法:
(gdb) info threads
✅ 输出示例:
Id Target Id Frame
* 1 Thread 0x7ffff7bc4740 (LWP 1234) main at main.cpp:25
2 Thread 0x7ffff6ab3700 (LWP 1235) worker at worker.cpp:42
3 Thread 0x7ffff60b2700 (LWP 1236) sleeping at <...>
✔️ 作用:
多线程程序崩溃时,能看到 哪个线程崩溃了
可手动切换线程查看其他线程状态
🧠 4. thread apply all bt
对所有线程执行 bt(backtrace),查看每个线程的调用栈
📌 用法:
(gdb) thread apply all bt
✅ 输出示例(多线程栈回溯):
Thread 1 (Thread 0x7f...):
#0 crash_func() at crash.cpp:15
#1 main() at main.cpp:30
Thread 2 (Thread 0x7f...):
#0 __libc_sleep
#1 worker_func() at worker.cpp:42
Thread 3 (Thread 0x7f...):
#0 waiting_func() at wait.cpp:18
✔️ 作用:
一次性查看所有线程栈信息
看看是否是子线程崩溃、死锁、卡住等
非常适合排查多线程场景下的崩溃或卡死问题
命令 作用说明 常用场景
info locals 查看当前函数的局部变量 判断崩溃时变量是否异常
info args 查看当前函数的参数 参数是否为空、非法
info threads 查看程序中所有线程 是否是主线程崩溃?有多少线程
thread apply all bt 查看所有线程的调用栈 多线程崩溃、死锁、竞争分析
4、但如果是发布版本,缺少调试符号信息symbol table,无法使用上述命令进行调试
下面命令帮助定位:
即使没有符号信息,你仍然可以:
工具/命令 用法说明
bt 看栈帧,知道崩溃函数
disas 反汇编当前函数,有时能看出空指针、越界
info registers 看寄存器值,特别是 rip, rsp, rbp, rdi 等
x 查看某个地址的内存内容,比如 x/4x $rsp
thread apply all bt 多线程崩溃定位
1)反汇编:disas
Dump of assembler code for function _ZThn8_NK3Frm4Impl23KernelModuleContextImpl6adm_idEv:
.........
0x00007f614c27d36d <+93>: mov (%rbx),%rax
0x00007f614c27d370 <+96>: mov %rbx,%rdi
0x00007f614c27d373 <+99>: call *0x10(%rax)
0x00007f614c27d376 <+102>: mov 0x38(%rsp),%rax
0x00007f614c27d37b <+107>: sub %fs:0x28,%rax
0x00007f614c27d384 <+116>: jne 0x7f614c27d3c6 <_ZThn8_NK3Frm4Impl23KernelModuleContextImpl6adm_idEv+182>
0x00007f614c27d386 <+118>: mov 0x0(%rbp),%rax
0x00007f614c27d38a <+122>: mov %rbp,%rdi
=> 0x00007f614c27d38d <+125>: mov 0xa0(%rax),%rax 崩溃在这一行
0x00007f614c27d394 <+132>: add $0x48,%rsp
0x00007f614c27d398 <+136>: pop %rbx
0x00007f614c27d399 <+137>: pop %rbp
0x00007f614c27d39a <+138>: jmp *%rax
0x00007f614c27d39c <+140>: nopl 0x0(%rax)
0x00007f614c27d3a0 <+144>: movq $0x0,0x8(%rsp)
0x00007f614c27d3a9 <+153>: mov %rbx,%rdi
反汇编(disassemble)
把机器码(CPU 能执行的二进制指令)翻译回更易读的汇编语言代码。汇编语言是机器码的“助记符”形式,比如:
机器码可能是:0x48 0x89 0xe5
反汇编后是:mov %rsp, %rbp
为什么“反汇编当前函数”有用?
查看底层执行细节
当源码不可用,或者调试符号缺失时,可以通过反汇编看程序在做什么。
排查异常
通过观察汇编指令,比如访问的内存地址,可以推断是不是空指针访问,或者越界访问。
理解代码行为
有些复杂bug,源码看不清楚,反汇编能帮助理解程序具体执行的指令。
举个例子
假设你在调试一个函数,遇到段错误(Segmentation Fault)。
用 disas 查看当前函数的汇编代码,可能看到某条指令访问了一个无效地址,比如 mov (%rax), %rbx,而 %rax 是 0x0,说明空指针访问。
例:
=> 0x00007f614c27d38d <+125>: mov 0xa0(%rax),%rax 这条说明什么
拆开来看:
0x00007f614c27d38d:这是指令在内存中的地址。
<+125>:这是当前函数的偏移量,表示这条指令距离函数起始地址的偏移125字节。
mov 0xa0(%rax), %rax:这是汇编指令,意思是:
从寄存器 rax 指向的内存地址开始偏移 0xa0(即 160 字节)的位置读取数据,然后把读取到的数据放回寄存器 rax。
这条指令说明了什么?
寄存器 %rax 中存的是某个内存地址。
程序要从这个地址向后偏移160字节的位置取值。
取到的值又写回 %rax。
查看rax寄存器的值
info registers rax
rax = 0x7f61490d4758 (十进制:140055814162264)
rbp = 0x562c0e671308
0x7f61490d4758 是rax寄存器存储的地址,这条指令是取rax存储的地址偏移0xa0位,放到rax内
2)info registers 查看寄存器存储的地址,能看出来谁是空指针,
(gdb) info registers
rax 0x7f61490d4758 140055814162264
rbx 0x562c0eedd740 94747229017920
rcx 0x562c0e0e2140 94747214356800
rdx 0x3 3
rsi 0x562c0da3d560 94747207390560
rdi 0x562c0e671308 94747220185864
rbp 0x562c0e671308 0x562c0e671308
rsp 0x7f6141623e00 0x7f6141623e00
r8 0x562c0da3d500 94747207390464
r9 0x20 32
r10 0xc 12
r11 0x7ffe731de080 140730829758592
r12 0x562c0e56a530 94747219109168
r13 0x562c0e56a4d0 94747219109072
r14 0x562c0ee16980 94747228203392
r15 0x7f6141624320 140055685514016
rip 0x7f614c27d38d 0x7f614c27d38d <non-virtual thunk to Frm::Impl::KernelModuleContextImpl::adm_id() const+125>
eflags 0x10246 [ PF ZF IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
看起来rax存储的寄存器的指针不空,可能是个指针
3、查看这个地址对应的内容:
x/16gx 0x7f61490d4758
(gdb) x/gx 0x7f61490d4758 + 0xa0
0x7f61490d47f8 <_ZTVN5Table4CmDc6SONLog25DftTableSchemaFactoryImplE+240>: 0x0000000000000000
这行输出的含义:
0x7f61490d47f8 是内存地址,等于 0x7f61490d4758 + 0xa0
<_ZTVN5Table4CmDc6SONLog25DftTableSchemaFactoryImplE+240> 这个是 GDB 根据符号表识别出来的符号(名称):
这是一个 C++的虚表(vtable)地址,符号名被C++名字修饰(mangled),大致意思是类 Table::CmDc::SONLog::DftTableSchemaFactoryImpl 的虚函数表中的某个位置,偏移 240 字节。
0x0000000000000000 是这个地址处存放的内容,也就是64位的一个值,是 0。
✅ 正确解释:
项目 说明
0x7f61490d47f8 是一个虚函数表(vtable)中某个槽的地址,即 某个虚函数指针存放的位置
0x0000000000000000 是存储在该地址中的值,也就是这个虚函数指针的指向地址(目标地址)(虚函数表的地址)
结论:
为什么会发生 SIGBUS?
重点: 你访问 0xa0(%rax),想读出虚函数表中对应的函数指针(应该是非0地址),但是你发现它是 0。
而虚函数表中的函数指针如果是 0,后面你再间接调用这个地址(即调用空指针函数)时就会发生异常。
但是你报告的信号是 SIGBUS(总线错误),SIGBUS 代表“非法的内存访问”,通常发生于:
访问未映射的内存页
访问映射的内存页,但是不对齐或大小错误(在某些架构)
访问被内核禁止的内存区域(比如文件映射结尾处)
总结
你当前访问的内存是一个虚函数表的位置,且值为 0,不是合法函数地址。
可能的原因:
你的对象已经被销毁(析构),虚函数表被覆盖了
对象未初始化,导致虚表指针为空
内存被误改
直接跳转到 0x0(空指针)会导致崩溃,可能这里是你崩溃的根源。
4、符号
符号名前缀 含义
_ZTV 虚函数表(vtable)
_ZTI 类型信息(typeinfo)
_ZN 命名空间或类名
5、信号
✅ 信号(signal):操作系统用来通知进程发生了某种事件的机制。比如 SIGSEGV, SIGABRT, SIGFPE 等。
💡 常见的 signal 类型(core 文件中可能出现):
信号编号 名称 含义
6 SIGABRT 异常终止(如 abort())
11 SIGSEGV 段错误(访问非法内存)
8 SIGFPE 浮点异常(除零等)
4 SIGILL 非法指令
7 SIGBUS 总线错误
6、程序崩溃
操作系统检测到程序运行时出现了非法或危险的行为,然后发出一个信号(signal),终止该程序的运行。
2265

被折叠的 条评论
为什么被折叠?



