setunhandledexceptionfilter是windows下最轻量可靠的崩溃捕获方式,需在main/winmain开头注册,回调中禁用c++标准库,配合minidumpwritedump、stackwalk64及pdb解析堆栈;linux则用sigaction+backtrace+addr2line,注意编译选项与权限问题。

Windows 下用 SetUnhandledExceptionFilter 捕获崩溃并写入日志
直接用系统 API 拦住未处理异常,是最轻量、最可靠的堆栈捕获方式。它不依赖调试器、不干扰 Release 构建,且能拿到崩溃瞬间的完整上下文。
常见错误现象:std::set_terminate 或 try/catch(...) 完全抓不到访问违规、栈溢出、非法指令这类 SEH 异常;日志里只看到“程序已停止工作”,没堆栈。
- 必须在
main()开头或WinMain()一开始就注册,晚了可能错过早期崩溃 - 回调函数里禁止调用 C++ 标准库(如
std::ofstream、printf),避免重入或堆损坏;改用WriteFile或OutputDebugStringA - 记得用
MiniDumpWriteDump配合dbghelp.dll生成.dmp文件,纯文本堆栈在无符号信息时意义有限
用 StackWalk64 解析崩溃线程的调用栈
光有异常地址没用,得把地址转成函数名+文件行号,否则日志里全是 0x00007ff6a1b2c3f8 这种没法 debug 的值。
使用场景:你已经有崩溃线程的 CONTEXT 和模块信息,现在要逐帧回溯。
- 必须加载对应模块的 PDB(不是 EXE 自带的,是编译时生成的 .pdb 文件),否则
SymFromAddr返回空函数名 -
StackWalk64要求传入正确的 CPU 架构标识(IMAGE_FILE_MACHINE_AMD64或IMAGE_FILE_MACHINE_I386),错一个就走不出第一帧 - 别跳过
SymInitialize的fInvadeProcess = TRUE参数,否则无法读取当前进程的模块符号
Linux 下用 backtrace + addr2line 替代方案
Windows 那套在 Linux 上跑不通,得换一套信号级捕获逻辑。核心是用 sigaction 拦住 SIGSEGV/SIGABRT,再用 GNU 扩展获取栈帧。
性能影响:backtrace 本身很快,但后续符号解析(尤其 addr2line 外部调用)会阻塞主线程,线上服务慎用。
- 编译时必须加
-g -rdynamic,否则backtrace_symbols只返回十六进制地址 -
addr2line -e your_program 0x401234是离线解析推荐方式,比dladdr更准,尤其对内联函数和优化代码 - 注意
fork()后子进程继承信号处理,若用system("addr2line..."),可能因 fork 失败静默丢堆栈
日志路径权限和多进程写入冲突怎么避
崩溃进程往往以受限权限运行(比如 Windows 服务跑在 LocalSystem,Linux 用 nobody 用户),直接往 C:\logs\crash.log 或 /var/log/app/ 写会失败——连打开文件句柄都返回 NULL。
容易踩的坑:用相对路径(如 "./crash.log"),结果日志写到未知目录,或者被杀毒软件拦截。
- Windows 推荐写到
%LOCALAPPDATA%\YourApp\logs\,用SHGetFolderPath获取,确保用户有写权限 - Linux 用
/tmp/yourapp_crash_$$($$是 pid),避免多实例覆盖;崩溃后由守护进程统一归档 - 不要用
fopen(..., "a")然后fprintf—— 文件缓冲区可能未刷盘就进程终止;用open(O_WRONLY|O_APPEND|O_CREAT)+write()更可靠
符号解析和路径权限这两块,最容易在线上环境突然失效——本地测通不代表部署就稳。PDB 文件放哪、日志目录是否存在、SELinux 是否拦了 open,都得实机验证,不能只信开发机结果。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!









