gdb崩溃时停在出错行需编译带-g且禁用高优化;多线程段错误关键要执行info threads和thread apply all bt查所有线程栈,重点排查悬垂指针、对象生命周期错配及虚函数调用前vtable被覆写等问题。

直接用 gdb 启动程序,崩溃时它就能停在出错那一行——前提是编译带 -g 且没开高优化。多线程段错误真正难的不是“看不到栈”,而是崩的线程未必是当前线程,也未必留下完整调用栈。
崩溃后立刻查所有线程栈,别只看当前线程
段错误发生时,gdb 默认只显示触发信号的线程栈,但问题往往出在另一个早已 return 或已销毁对象的线程里。必须立刻执行:
-
info threads:看哪些线程状态是running或stopped(*标记的是当前线程,但不一定是肇事者) -
thread apply all bt:一次性打印所有线程的回溯,重点找那些栈顶是clone、start_thread或明显已脱离业务函数的线程 - 对可疑线程,用
thread <n>切换过去,再执行bt full查局部变量和寄存器值,确认是否访问了已析构对象或悬垂指针
检查 std::thread 构造参数是否捕获了危险引用
最常见的源头不是锁,而是线程生命周期和对象生命周期错配。比如:
-
std::thread{&MyClass::work, this}:如果this指向的实例在新线程启动前就析构了,后续访问就是段错误 - lambda 捕获局部变量地址:
[&local_var]{ /* use local_var */ }(),线程执行时local_var早已出作用域 -
std::thread t{func}; t.detach();:t析构时若未join()或detach(),会调用std::terminate(),某些环境表现为段错误而非明确终止
解决方法:改用 std::shared_ptr<MyClass> 管理对象,并在 lambda 中捕获 shared_ptr,确保对象存活期覆盖线程运行期。
立即学习“C++免费学习笔记(深入)”;
用 AddressSanitizer 配合 gdb 定位 UAF 和越界
gdb 只能告诉你“哪一行崩了”,但 ASan 能告诉你“为什么崩”。编译时加:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
-fsanitize=address -fno-omit-frame-pointer -g- 链接时也加
-fsanitize=address
运行时报错会直接指出:heap-use-after-free 的分配位置、释放位置、非法访问位置,以及涉及的所有线程调用栈。例如看到 by thread T2 而 T1 已释放内存,就不用猜了。
注意:ASan 会禁用部分优化,线程检测有开销;若怀疑误报,可加 -shared-libsan 并确保运行时环境装有对应 sanitizer runtime。
别忽略主线程没崩但其他线程已损坏对象的情况
有些段错误是“间接结果”:比如线程 A 数据竞争覆写了对象的 vtable 指针,线程 B 后续调用虚函数时跳转到非法地址。这时 bt 显示的是 B 崩溃点,但根源在 A 的竞态写入。
此时要结合:
-
info registers看rip是否指向非法地址 -
x/20i $rip查看崩溃点附近的汇编,确认是不是虚函数调用指令(如call QWORD PTR [rax+0x8]) - 回溯中若出现
std::vector::push_back、std::string::assign等内部操作突然崩,大概率是之前某处破坏了堆元数据或对象布局
这种问题单靠 gdb 很难定位,必须配合 ASan 或 ThreadSanitizer 才能抓到原始竞态点。

















