找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1944

积分

0

好友

263

主题
发表于 昨天 04:56 | 查看: 9| 回复: 0

线上服务跑得好好的,突然报警:进程异常退出,产生 core 文件。 日志里只有一句冷冰冰的 Segmentation fault (core dumped),重启能暂时缓解,但没人敢保证下次不会再炸。

这种场景,大多数 C/C++ 工程师都不陌生。真正拉开差距的,不是“会不会用 GDB”,而是能不能在最短时间内,从 core 文件里还原出问题现场

这篇文章不讲花活,只讲一次真实可落地的 GDB 崩溃分析流程。

一、先搞清楚:Core Dump 到底是什么

当进程因为非法内存访问、除零、assert 失败等原因异常终止时,内核会把进程当时的内存、寄存器、线程栈等信息保存成一个 core 文件。

它不是日志,而是一个“时间被冻结的进程快照”。

前提条件很重要: 如果线上没生成 core,后面的一切都是空谈。

ulimit -c
  • 输出 0:不会生成 core
  • 输出 unlimited 或正数:允许生成

生产环境通常需要显式打开,并指定 core 路径:

ulimit -c unlimited
echo \"/data/core/core.%e.%p\" > /proc/sys/kernel/core_pattern

二、第一步:用对 GDB 的打开方式

永远不要只用 core 文件启动 GDB。

正确姿势:

gdb ./your_binary core.xxx

原因很简单: 没有可执行文件,GDB 无法还原符号信息,看到的全是地址。

如果线上是 release 版本,记住一个底线要求:

必须保留带符号的 binary 或单独的 debug 文件

否则你只能对着一堆 ?? 发呆。

三、最关键的一条命令:bt

进入 GDB 后,第一件事不是看代码,而是:

bt

或更完整一点:

bt full

一份“健康”的 backtrace,至少应该包含:

  • 明确的崩溃函数
  • 清晰的调用路径
  • 关键参数和值

例如:

#0  std::vector<int>::operator[] (this=0x0, __n=3)
#1  Foo::process() at foo.cpp:42
#2  worker_thread() at worker.cpp:88

光这一眼,已经能判断出八成问题:空指针调用成员函数

四、栈不是一条线,而是一棵树

很多人只看 #0,这是典型的新手误区。

真正有价值的,往往在 #1 ~ #3

建议的阅读顺序:

  1. #0:确认“死因”
  2. #1:找到“谁把你送走的”
  3. #2/#3:定位业务入口

例如一个经典的 use-after-free:

#0  strlen () from libc.so.6
#1  std::string::length()
#2  Logger::log(std::string const&)
#3  RequestHandler::handle()

这时候不要被 strlen 迷惑,它只是最后一刀。 真正的问题是:string 内部指针已经悬空

五、多线程程序,别忘了看线程

线上服务几乎都是多线程的。

info threads

切换线程:

thread 3
bt

很多 crash 表面发生在“某个线程”,但真正的根因可能是:

  • 另一个线程提前释放了内存
  • 没加锁的共享对象
  • 生命周期管理错误

如果 core 里只有一个线程在跑,而其他线程都卡在锁上,这本身就是重要线索。

六、结合源码,不要迷信“这一行崩了”

GDB 指到的源码行,只说明在那一刻访问了非法内存,不等于那一行写错了。

一个典型例子:

void Foo::process()
{
    if (ptr_) {
        ptr_->do_something();
    }
}

崩在 ptr_->do_something(),但真正的问题可能是:

  • ptr_ 在别的线程被释放
  • ptr_ 指向的对象已析构
  • 对象内部状态被破坏

GDB 告诉你“哪里死的”,不是“为什么会死”

七、善用寄存器和局部变量

当 backtrace 不够直观时,可以继续深挖:

frame 1
info locals
info args

很多线上问题,最终都是靠一个“异常值”揪出来的:

  • vector size 为负
  • index 明显越界
  • 指针值看起来像野地址(0xdeadbeef、0xcccccccc)

这些都是真实踩过的坑。

八、如果 backtrace 已经烂掉了怎么办

你可能会遇到:

  • 栈被踩坏
  • 回溯只剩 libc
  • bt 直接中断

这通常意味着更严重的内存破坏

这类问题,core 只能提供方向,真正的解法往往是:

  • 结合 ASan / UBSan 复现
  • 加强运行时检查
  • 缩小问题范围后重新抓 core

别指望一次 GDB 就“通灵成功”。

九、一个工程师该形成的习惯

真正成熟的工程实践里,GDB 分析不是“救火技能”,而是日常能力:

  • 编译选项里默认保留符号
  • 崩溃第一时间拉 core,而不是盲目重启
  • 用堆栈说话,而不是猜

当你能冷静地从 core 文件里,把事故过程一步步还原出来,你对代码的掌控力,会明显上一个台阶。

线上崩溃不可怕,可怕的是看不懂它留下的线索。 GDB 和 core dump,本质上是在帮你复盘一场已经发生的事故。

看得懂的人,问题就已经解决了一半。

如果你在调试 C++ 程序或分析复杂系统问题上有什么心得,欢迎来 云栈社区 交流分享。




上一篇:无需逆向工程,利用算法助手快速破解移动应用加解密与签名验证
下一篇:得物电商App智能巡检实践:基于AI视觉大模型的UI自动化质量保障方案
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-14 14:15 , Processed in 0.362413 second(s), 38 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表