可以,但需显式设置 catch throw 和 set step-mode on;gdb 默认只停首次 throw,二次 throw; 仅在未优化(-O0 -g)、同线程内且异常状态未被清除时被捕获。

gdb 能否捕获 throw 后又被 catch 再 throw 的异常?
可以,但默认不触发断点。C++ 中二次抛出(throw;)不会重新构造异常对象,而是沿用当前栈帧的异常对象;gdb 默认只停在首次 throw,对 throw; 无感知——除非你显式设置 catch throw 并启用 set follow-fork-mode child(若涉及 fork)或确保调试器能跟踪异常传播路径。
必须启用的 gdb 设置:catch throw 和 set step-mode on
catch throw 是关键,它让 gdb 在每次进入 C++ 异常抛出点中断(包括 throw;),但要注意:
- 它捕获的是
__cxa_throw底层调用,不是语法层面的throw关键字,所以即使throw;也命中 - 若线程中抛出后立刻被同一线程
catch再throw;,gdb 会停在第二次__cxa_throw,前提是没被优化掉(加-O0 -g编译) -
set step-mode on防止next跳过异常传播过程(比如跳进std::rethrow_exception或跨栈帧的throw;) - 多线程下需配合
info threads和thread apply all bt确认哪个线程真正触发了重抛
常见陷阱:优化、线程分离与异常对象生命周期
三个最常导致“看不到二次抛出”的原因:
-
-O2及以上可能内联catch块或消除冗余throw;,务必用-O0 -g重编译 - 若用
std::thread且未join()或detach(),主线程退出时子线程被强制终止,gdb 来不及响应异常事件;调试时建议加thread.join()或用pthread_create+pthread_join显式同步 -
std::current_exception()捕获后再std::rethrow_exception()属于“异常指针转发”,catch throw不触发——此时应改用catch catch观察捕获点,再结合print $_exception(gdb 10.2+)或x/10i $pc查看是否进了__cxa_rethrow
快速验证是否真卡在 throw;:用 bt + frame 看栈帧语义
当 catch throw 触发后,别急着 continue:
立即学习“C++免费学习笔记(深入)”;
- 执行
bt,确认顶层帧是否为__cxa_throw,第二帧是否指向你的catch块末尾(即throw;所在行) - 用
frame 1切到上一帧,再list,看是否精准定位到throw;行;如果不是,说明编译器把异常传播优化成跳转了,得关优化重试 - 若看到多个线程同时停在
__cxa_throw,用thread <n>切换,再info registers对比$rdi/$rsi(传给__cxa_throw的异常对象地址和 type_info)是否一致——相同地址说明是同一异常重抛,不同则可能是并发抛出
多线程异常重抛本身不保证顺序,throw; 的语义依赖当前栈帧的异常状态,而该状态在线程切换时并不自动传递;实际调试中,最容易忽略的是——你以为在 A 线程里 throw;,其实早被 B 线程的 catch 清除了异常标志,结果什么都没抛出去。


















