C++ 程序在运行时可能会由于 空指针访问、数组越界、非法内存访问、栈溢出 等原因崩溃。为了分析崩溃原因,我们通常会生成 Dump 文件(Windows 的 .dmp
,Linux 的 core
,macOS 的 .crash
),然后用调试工具分析。
Windows 提供了 MiniDumpWriteDump()
API 来生成 MiniDump 文件(.dmp),它可以记录程序崩溃时的内存、线程、异常信息等。
MiniDumpWriteDump()
介绍BOOL MiniDumpWriteDump(
HANDLE hProcess, // 进程句柄
DWORD ProcessId, // 进程 ID
HANDLE hFile, // Dump 文件句柄
MINIDUMP_TYPE DumpType, // Dump 类型
PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, // 异常信息 (可选)
PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, // 额外数据 (可选)
PMINIDUMP_CALLBACK_INFORMATION CallbackParam // 回调函数 (可选)
);
参数 | 作用 |
---|---|
hProcess |
进程句柄(用 GetCurrentProcess() 获取) |
ProcessId |
进程 ID(用 GetCurrentProcessId() 获取) |
hFile |
目标 Dump 文件的句柄(用 CreateFile() 创建) |
DumpType |
Dump 类型(控制记录多少信息) |
ExceptionParam |
异常信息(用于捕获崩溃时的状态,可选) |
UserStreamParam |
额外数据(可为空) |
CallbackParam |
回调函数(可为空) |
类型 | 值(十六进制) | 描述 |
---|---|---|
MiniDumpNormal | 0x00000000 |
默认值,仅包含基本信息(进程、线程、模块) |
MiniDumpWithDataSegs | 0x00000001 |
记录全局变量数据段 |
MiniDumpWithFullMemory | 0x00000002 |
完整 Dump,包含所有进程内存(文件较大) |
MiniDumpWithHandleData | 0x00000004 |
记录所有句柄信息 |
MiniDumpFilterMemory | 0x00000008 |
过滤一些私有的内存区域以减少 Dump 大小 |
MiniDumpScanMemory | 0x00000010 |
扫描进程内存以获取更多信息 |
MiniDumpWithUnloadedModules | 0x00000020 |
记录已卸载的模块 |
MiniDumpWithIndirectlyReferencedMemory | 0x00000040 |
记录被指针间接引用的内存内容 |
MiniDumpFilterModulePaths | 0x00000080 |
仅记录模块路径,不包含完整的模块数据 |
MiniDumpWithProcessThreadData | 0x00000100 |
记录进程/线程额外信息 |
MiniDumpWithPrivateReadWriteMemory | 0x00000200 |
记录进程私有的读写内存 |
MiniDumpWithoutOptionalData | 0x00000400 |
不包含可选数据,减小 Dump 大小 |
MiniDumpWithFullMemoryInfo | 0x00000800 |
记录完整的内存信息 |
MiniDumpWithThreadInfo | 0x00001000 |
记录所有线程的详细信息 |
MiniDumpWithCodeSegs | 0x00002000 |
记录代码段信息 |
MiniDumpWithoutAuxiliaryState | 0x00004000 |
不包含辅助状态信息 |
MiniDumpWithFullAuxiliaryState | 0x00008000 |
记录完整的辅助状态信息 |
MiniDumpWithPrivateWriteCopyMemory | 0x00010000 |
记录写时复制(Copy-on-Write)的私有内存 |
MiniDumpIgnoreInaccessibleMemory | 0x00020000 |
忽略无法访问的内存区域 |
MiniDumpWithTokenInformation | 0x00040000 |
记录进程的 Token 信息(用于权限分析) |
MiniDumpWithModuleHeaders | 0x00080000 |
记录模块的 PE 头信息 |
MiniDumpFilterTriage | 0x00100000 |
仅记录用于故障诊断的最小数据集 |
MiniDumpValidTypeFlags | 0x001FFFFF |
所有可用的 Dump 类型标志位(用于验证 Dump 类型) |
推荐使用 MiniDumpWithDataSegs
| MiniDumpWithThreadInfo
,可以更全面地分析崩溃原因。
在 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() 可能会失败。
#include
#include
#include
#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;
}
可以用 Visual Studio 或 WinDbg 打开 .dmp 文件,查看调用栈、崩溃地址等信息。
Linux 默认会生成 core
文件,记录程序崩溃时的内存状态。
ulimit -c unlimited # 允许生成 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。
#include
#include
#include
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;
}
默认在当前目录 ./core
或 /var/lib/systemd/coredump/
可修改 /proc/sys/kernel/core_pattern
gdb ./your_program core
bt # 查看调用栈
macOS 也可能默认 禁用了 Core Dump,需要运行以下命令启用:
ulimit -c unlimited
macOS 默认将 .crash 文件存放在 ~/Library/Logs/DiagnosticReports/,但可以使用 ulimit -c unlimited 后修改:
echo "/tmp/crash.%e.%p" | sudo tee /proc/sys/kernel/core_pattern
这样崩溃后,Crash Report 文件会存放到 /tmp/ 目录。
#include
#include
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;
}
lldb -c ~/Library/Logs/DiagnosticReports/YourApp.crash
bt # 查看调用栈
参考表:如何判断崩溃原因
现象 | 可能原因 | 解决方法 |
---|---|---|
崩溃在 nullptr 访问 |
访问空指针 | assert(p != nullptr); |
崩溃在数组访问 | 数组越界 | 开启 ASan,检查索引 |
delete 崩溃 |
释放了已释放的内存 | nullptr 赋值后再 delete |
运行一段时间后崩溃 | 内存泄漏或数据竞争 | 启用 Valgrind 或 ThreadSanitizer |
GUI 无响应 | 死锁或 UI 线程阻塞 | 检查 std::mutex 是否死锁 |
对比总结
平台 | 默认 Dump 目录 | 是否需要命令启动 | Dump 文件格式 | 代码生成 Dump | 触发 Dump 的方式 | 如何分析 Dump | 调试工具 |
---|---|---|---|---|---|---|---|
Windows | %LOCALAPPDATA%\CrashDumps\ 或 C:\Users\<用户名>\AppData\Local\CrashDumps\ |
❌ 无需命令 | .dmp |
MiniDumpWriteDump() 生成 .dmp |
进程崩溃自动生成 或 手动调用 MiniDumpWriteDump() |
WinDbg (!analyze -v ),Visual Studio 直接打开 .dmp |
WinDbg, Visual Studio |
Linux | ./core 或 /var/lib/systemd/coredump/ |
✅ 需要 ulimit -c unlimited |
core |
setrlimit(RLIMIT_CORE, &core_limit) 生成 core |
进程崩溃(SIGSEGV 等)自动生成 或 gcore 手动触发 |
gdb <程序> ,或 eu-stack -p core |
gdb |
macOS | ~/Library/Logs/DiagnosticReports/ |
✅ 需要 ulimit -c unlimited |
.crash |
signal(SIGSEGV, handler) 生成 .crash |
进程崩溃(SIGSEGV 等)自动生成 或 lldb -> process save-core 生成 |
lldb -c ,atos -o <可执行文件> -p <进程ID> |
lldb, atos |