目录
Dump文件是进程的内存镜像。可以把程序的执行状态通过调试器保存到dump文件中。主要是用来在系统中出现异常或者崩溃的时候来生成dump文件,然后用调试器进行调试,这样就可以快速定位到程序崩溃位置,对问题进行排查。
Win + R 输入regedit打开注册表
找到如下项:
计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps
新建如下:
说明:
DumpCount: 文件数量 ( 值设为0即禁用此功能)
DumpFolder:存储位置 (Dump文件名:(应用程序名称全称.进程号.dmp))
DumpType: dmp文件类型 (1:mini 2:full)mini:只包含某个线程和部分模块的信息。
full: 包含了某个进程完整的地址空间数据,以及许多用于调试的信息
使用win32 api :MiniDumpWriteDump 实现
封装了DumpHelper类
- using System.Runtime.InteropServices;
-
- namespace DumpTest
- {
- public class DumpHelper
- {
- [Flags]
- public enum DumpType : uint
- {
- // From dbghelp.h:
- MiniDumpNormal = 0x00000000, //只包含调用栈相关信息
- MiniDumpWithDataSegs = 0x00000001, //包含已加载的模块的数据段信息,比如全局变量
- MiniDumpWithFullMemory = 0x00000002, //包含全部可访问的内存
- MiniDumpWithHandleData = 0x00000004, //包含句柄信息
- MiniDumpFilterMemory = 0x00000008, //过滤一些敏感信息,保护重建调用栈需要的信息
- MiniDumpScanMemory = 0x00000010, //扫描,以包含引用内存
- MiniDumpWithUnloadedModules = 0x00000020, //包含最近被卸载的模块信息
- MiniDumpWithIndirectlyReferencedMemory = 0x00000040,//包含未直接引用的内存
- MiniDumpFilterModulePaths = 0x00000080, //过滤某块的路径信息
- MiniDumpWithProcessThreadData = 0x00000100, //包含完整的进程和线程信息
- MiniDumpWithPrivateReadWriteMemory = 0x00000200, //包含页面属性为 PAGE_READWRITE 的页面
- MiniDumpWithoutOptionalData = 0x00000400, //不包含可选数据
- MiniDumpWithFullMemoryInfo = 0x00000800, //包含内存区信息
- MiniDumpWithThreadInfo = 0x00001000, //包含线程状态信息
- MiniDumpWithCodeSegs = 0x00002000, //包含所有代码和有关的内存段
- MiniDumpWithoutAuxiliaryState = 0x00004000, //关闭辅助内存收集
- MiniDumpWithFullAuxiliaryState = 0x00008000, //使用所有的内存收集器
- MiniDumpWithPrivateWriteCopyMemory = 0x00010000, //包含页面属性为 PAGE_WRITECOPY 的页面
- MiniDumpIgnoreInaccessibleMemory = 0x00020000, //忽略不可访问的页面
- MiniDumpValidTypeFlags = 0x0003ffff, //包含安全令牌相关信息
- };
-
- //typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
- // DWORD ThreadId;
- // PEXCEPTION_POINTERS ExceptionPointers;
- // BOOL ClientPointers;
- //} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
- [StructLayout(LayoutKind.Sequential, Pack = 4)] // Pack=4 is important! So it works also for x64!
- struct MiniDumpExceptionInformation
- {
- public uint ThreadId;
- public IntPtr ExceptioonPointers;
- [MarshalAs(UnmanagedType.Bool)]
- public bool ClientPointers;
- }
-
- //BOOL
- //WINAPI
- //MiniDumpWriteDump(
- // __in HANDLE hProcess, :要转储的进程句柄。
- // __in DWORD ProcessId, :要转储的进程ID。
- // __in HANDLE hFile, :通过 CreateFile() 等 API 打开的,用来保存 dump 的文件句柄。
- // __in MINIDUMP_TYPE DumpType, :转储类型。此参数会直接影响转储文件的大小
- // __in_opt PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, :指向异常信息结构 MiniDumpExceptionInformation 的指针。如果本参数为 NULL,则转储文件中不会包含异常信息。
- // __in_opt PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, :指向用户自定义信息结构的指针
- // __in_opt PMINIDUMP_CALLBACK_INFORMATION CallbackParam :指向回调例程 MINIDUMP_CALLBACK_INFORMATION 的指针。如果此参数为NULL,转储过程中不会执行任何回调例程。
- // );
- [DllImport("dbghelp.dll",EntryPoint = "MiniDumpWriteDump",CallingConvention = CallingConvention.StdCall,CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
- static extern bool MiniDumpWriteDump(
- IntPtr hProcess,
- uint processId,
- IntPtr hFile,
- uint dumpType,
- ref MiniDumpExceptionInformation expParam,
- IntPtr userStreamParam,
- IntPtr callbackParam);
-
- [DllImport("kernel32.dll", EntryPoint = "GetCurrentThreadId", ExactSpelling = true)]
- static extern uint GetCurrentThreadId();
-
- [DllImport("kernel32.dll", EntryPoint = "GetCurrentProcess", ExactSpelling = true)]
- static extern IntPtr GetCurrentProcess();
-
- [DllImport("kernel32.dll", EntryPoint = "GetCurrentProcessId", ExactSpelling = true)]
- static extern uint GetCurrentProcessId();
-
- public static bool WriteDump(string fileName)
- {
- return Write(fileName, DumpType.MiniDumpNormal);
- }
- public static bool Write(string fileName, DumpType dumpType)
- {
- var path = Path.Combine(Environment.CurrentDirectory, fileName);
- using var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None);
- MiniDumpExceptionInformation exp;
- exp.ThreadId = GetCurrentThreadId();
- exp.ClientPointers = false;
- exp.ExceptioonPointers = Marshal.GetExceptionPointers();
- bool bRet = MiniDumpWriteDump(
- GetCurrentProcess(),
- GetCurrentProcessId(),
- fs.SafeFileHandle.DangerousGetHandle(),
- (uint)dumpType,
- ref exp,
- IntPtr.Zero,
- IntPtr.Zero);
- return bRet;
- }
- }
- }
调用:
当某个系统异常未被捕获时,调用方法生成Dump文件
- AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(
- (obj, args) => DumpHelper.WriteDump($"Error_{DateTime.Now.ToString("yyyy_M_dd_HH_mm")}.dmp")
- );
结果:
代码出现异常导致程序崩溃时,会在程序的工作目录下自动生成Dump文件,如图:
文件1:DumpType:MiniDumpWithFullMemory
文件2:DumpType:MiniDumpNormal
可以看出Dump文件保存的内容不同,文件大小差异巨大。
打开任务管理器 --> 找到对应进程 --> 右击选择创建转储文件
创建成功如图:
在代码中断时,调试 --> 将转储另存为 --> 设置文件路径&文件名
- var lists=new List<string>() {"a","b","c"};
- Console.WriteLine(lists[3]);
运行DumpTest.exe,程序崩溃后,自动生成Dump文件
用VS打开.dmp文件
点击右侧"使用混合进行调试" -- > 就定位到异常位置,如图
得出结论:列表引用超范围的异常
若将Dump文件拷贝到另一台PC上调试时,则需要将以下3个文件都拷下来,放在同一目录下,必须保证pdb与出问题的exe是同一时间生成的。
参考:
【1】 MiniDumpWriteDump function (minidumpapiset.h) - Win32 apps | Microsoft Learn
【2】Dump文件的生成和使用_思影影思的博客-CSDN博客_dump