当前位置: 首页 > news >正文

跨平台 C++ 程序崩溃调试与 Dump 文件分析

前言

C++ 程序在运行时可能会由于 空指针访问、数组越界、非法内存访问、栈溢出 等原因崩溃。为了分析崩溃原因,我们通常会生成 Dump 文件(Windows 的 .dmp,Linux 的 core,macOS 的 .crash),然后用调试工具分析。


1. Windows: MiniDump 生成 .dmp 文件

Windows 提供了 MiniDumpWriteDump() API 来生成 MiniDump 文件(.dmp),它可以记录程序崩溃时的内存、线程、异常信息等。

1.1 MiniDumpWriteDump() 介绍

BOOL MiniDumpWriteDump(HANDLE hProcess,             							// 进程句柄DWORD ProcessId,             							// 进程 IDHANDLE hFile,                							// Dump 文件句柄MINIDUMP_TYPE DumpType,     							// Dump 类型PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, 		// 异常信息 (可选)PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, 	// 额外数据 (可选)PMINIDUMP_CALLBACK_INFORMATION CallbackParam      	// 回调函数 (可选)
);

1.2 参数解析

参数作用
hProcess进程句柄(用 GetCurrentProcess() 获取)
ProcessId进程 ID(用 GetCurrentProcessId() 获取)
hFile目标 Dump 文件的句柄(用 CreateFile() 创建)
DumpTypeDump 类型(控制记录多少信息)
ExceptionParam异常信息(用于捕获崩溃时的状态,可选)
UserStreamParam额外数据(可为空)
CallbackParam回调函数(可为空)

1.3 Dump 类型(MINIDUMP_TYPE)

类型值(十六进制)描述
MiniDumpNormal0x00000000默认值,仅包含基本信息(进程、线程、模块)
MiniDumpWithDataSegs0x00000001记录全局变量数据段
MiniDumpWithFullMemory0x00000002完整 Dump,包含所有进程内存(文件较大)
MiniDumpWithHandleData0x00000004记录所有句柄信息
MiniDumpFilterMemory0x00000008过滤一些私有的内存区域以减少 Dump 大小
MiniDumpScanMemory0x00000010扫描进程内存以获取更多信息
MiniDumpWithUnloadedModules0x00000020记录已卸载的模块
MiniDumpWithIndirectlyReferencedMemory0x00000040记录被指针间接引用的内存内容
MiniDumpFilterModulePaths0x00000080仅记录模块路径,不包含完整的模块数据
MiniDumpWithProcessThreadData0x00000100记录进程/线程额外信息
MiniDumpWithPrivateReadWriteMemory0x00000200记录进程私有的读写内存
MiniDumpWithoutOptionalData0x00000400不包含可选数据,减小 Dump 大小
MiniDumpWithFullMemoryInfo0x00000800记录完整的内存信息
MiniDumpWithThreadInfo0x00001000记录所有线程的详细信息
MiniDumpWithCodeSegs0x00002000记录代码段信息
MiniDumpWithoutAuxiliaryState0x00004000不包含辅助状态信息
MiniDumpWithFullAuxiliaryState0x00008000记录完整的辅助状态信息
MiniDumpWithPrivateWriteCopyMemory0x00010000记录写时复制(Copy-on-Write)的私有内存
MiniDumpIgnoreInaccessibleMemory0x00020000忽略无法访问的内存区域
MiniDumpWithTokenInformation0x00040000记录进程的 Token 信息(用于权限分析)
MiniDumpWithModuleHeaders0x00080000记录模块的 PE 头信息
MiniDumpFilterTriage0x00100000仅记录用于故障诊断的最小数据集
MiniDumpValidTypeFlags0x001FFFFF所有可用的 Dump 类型标志位(用于验证 Dump 类型)

推荐使用 MiniDumpWithDataSegs | MiniDumpWithThreadInfo,可以更全面地分析崩溃原因。

1.4 自定义 Dump 文件存储路径

在 Windows 上,我们可以自定义 Dump 文件的存储路径,例如存放到 C:\Dumps\ 目录下:

std::string dumpPath = "C:\\Dumps\\crash_dump.dmp";  
HANDLE hFile = CreateFileA(dumpPath.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

确保 C:\Dumps\ 目录存在,否则 CreateFileA() 可能会失败。

1.5 Windows 代码示例

#include <windows.h>
#include <dbghelp.h>
#include <iostream>#pragma comment(lib, "dbghelp.lib")void CreateDump(const std::string& dumpPath, EXCEPTION_POINTERS* pExceptionInfo) {HANDLE hFile = CreateFileA(dumpPath.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);if (hFile != INVALID_HANDLE_VALUE) {MINIDUMP_EXCEPTION_INFORMATION dumpInfo;dumpInfo.ThreadId = GetCurrentThreadId();dumpInfo.ExceptionPointers = pExceptionInfo;dumpInfo.ClientPointers = TRUE;MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpWithDataSegs | MiniDumpWithThreadInfo, &dumpInfo, NULL, NULL);CloseHandle(hFile);}
}LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS* pExceptionInfo) {std::string dumpPath = "C:\\Dumps\\crash_dump.dmp";CreateDump(dumpPath, pExceptionInfo);return EXCEPTION_EXECUTE_HANDLER;
}int main() {SetUnhandledExceptionFilter(ExceptionHandler);// 触发崩溃int* p = nullptr;*p = 10;return 0;
}

1.6 Dump 分析

可以用 Visual Studio 或 WinDbg 打开 .dmp 文件,查看调用栈、崩溃地址等信息。


2. Linux: Core Dump 生成

Linux 默认会生成 core 文件,记录程序崩溃时的内存状态。

2.1 启用 Core Dump(需要命令启动)

ulimit -c unlimited   # 允许生成 core dump 文件

2.2 自定义 Core Dump 存储路径

可以修改 /proc/sys/kernel/core_pattern 来改变 Core Dump 存放路径,例如:

echo "/tmp/core.%e.%p" | sudo tee /proc/sys/kernel/core_pattern

这样崩溃后,Core Dump 文件将存放到 /tmp/ 目录,并包含程序名和进程 ID。

2.3 代码示例

#include <iostream>
#include <csignal>
#include <sys/resource.h>void EnableCoreDump() {struct rlimit core_limit = { RLIM_INFINITY, RLIM_INFINITY };setrlimit(RLIMIT_CORE, &core_limit);
}void Crash() {int* p = nullptr;*p = 42;
}int main() {EnableCoreDump();Crash();return 0;
}

2.4 Dump 生成路径

  • 默认在当前目录 ./core

  • 或 /var/lib/systemd/coredump/

  • 可修改 /proc/sys/kernel/core_pattern

2.5 Dump 分析

gdb ./your_program core
bt  # 查看调用栈

3. macOS: Crash Report(需要命令启动)

3.1 启用 Core Dump(需要命令启动)

macOS 也可能默认 禁用了 Core Dump,需要运行以下命令启用:

ulimit -c unlimited

3.2 自定义 Core Dump 存储路径

macOS 默认将 .crash 文件存放在 ~/Library/Logs/DiagnosticReports/,但可以使用 ulimit -c unlimited 后修改:

echo "/tmp/crash.%e.%p" | sudo tee /proc/sys/kernel/core_pattern

这样崩溃后,Crash Report 文件会存放到 /tmp/ 目录。

3.3 代码示例

#include <iostream>
#include <csignal>void Crash() {int* p = nullptr;*p = 42;
}int main() {signal(SIGSEGV, [](int signum) {std::cerr << "Segmentation fault caught!" << std::endl;exit(1);});Crash();return 0;
}

3.4 Dump 分析

lldb -c ~/Library/Logs/DiagnosticReports/YourApp.crash
bt  # 查看调用栈

结论

参考表:如何判断崩溃原因

现象可能原因解决方法
崩溃在 nullptr 访问访问空指针assert(p != nullptr);
崩溃在数组访问数组越界开启 ASan,检查索引
delete 崩溃释放了已释放的内存nullptr 赋值后再 delete
运行一段时间后崩溃内存泄漏或数据竞争启用 ValgrindThreadSanitizer
GUI 无响应死锁或 UI 线程阻塞检查 std::mutex 是否死锁

对比总结

平台默认 Dump 目录是否需要命令启动Dump 文件格式代码生成 Dump触发 Dump 的方式如何分析 Dump调试工具
Windows%LOCALAPPDATA%\CrashDumps\C:\Users\<用户名>\AppData\Local\CrashDumps\❌ 无需命令.dmpMiniDumpWriteDump() 生成 .dmp进程崩溃自动生成 或 手动调用 MiniDumpWriteDump()WinDbg (!analyze -v),Visual Studio 直接打开 .dmpWinDbg, Visual Studio
Linux./core/var/lib/systemd/coredump/✅ 需要 ulimit -c unlimitedcoresetrlimit(RLIMIT_CORE, &core_limit) 生成 core进程崩溃(SIGSEGV 等)自动生成 或 gcore 手动触发gdb <程序> <core>,或 eu-stack -p coregdb
macOS~/Library/Logs/DiagnosticReports/✅ 需要 ulimit -c unlimited.crashsignal(SIGSEGV, handler) 生成 .crash进程崩溃(SIGSEGV 等)自动生成 或 lldb -> process save-core 生成lldb -c <crash文件>atos -o <可执行文件> -p <进程ID>lldb, atos
http://www.lryc.cn/news/546578.html

相关文章:

  • 缺陷VS质量:为何软件缺陷是质量属性的致命对立面?
  • 伍[5],伺服电机,电流环,速度环,位置环
  • RuntimeError: CUDA error: device-side assert triggered
  • 清华大学Deepseek第六版AIGC发展研究3.0(共186页,附PDF下载)
  • SpringBoot生成唯一ID的方式
  • 通俗易懂的分类算法之K近邻详解
  • CSDN markdown 操作指令等
  • 【linux】文件与目录命令 - uniq
  • 零信任沙箱:为网络安全筑牢“隔离墙”
  • 【金融量化】Ptrade中交易环境支持的业务类型
  • 【Java---数据结构】链表 LinkedList
  • 紧跟 Web3 热潮,RuleOS 如何成为行业新宠?
  • CC++的内存管理
  • Spark核心之02:RDD、算子分类、常用算子
  • 【Resis实战分析】Redis问题导致页面timeout知识点分析
  • 单一职责原则(设计模式)
  • 生理信号概念
  • 安卓内存泄露之DMA-BUF异常增长:Android Studio镜像引起DMA内存泄露
  • android13打基础: 控件checkbox
  • AI应用测试:遇到类ChatGPT的流式接口要如何压测?
  • React面试葵花宝典之二
  • 在日常生活、工作中deepseek能帮我们解决哪些问题
  • 【Java】IO流
  • HTML第三节
  • Visual Studio 2022安装问题解决,提示无法安装Microsoft.VisualStudio.Community.Msi
  • 【代码分享】基于IRM和RRT*的无人机路径规划方法详解与Matlab实现
  • MybatisPlus从入门到精通
  • el-table input textarea 文本域 自适应高度,切换分页滚动失效处理办法
  • 基于Windows11的DockerDesktop安装和布署方法简介
  • ffmpeg源码编译支持cuda