c#程序生成dmp文件并查看调试
前人写的项目频繁闪退,实在找不到原因,所以想到了以前公司服务端使用dmp日志排查问题的方法
c#程序要生成dmp文件的话,需要使用一个辅助工具类,下面上代码:
using System; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; public class MiniDump { // Taken almost verbatim from http://blog.kalmbach-software.de/2008/12/13/writing-minidumps-in-c/ [Flags] public enum Option : uint { // From dbghelp.h: Normal = 0x00000000, WithDataSegs = 0x00000001, WithFullMemory = 0x00000002, WithHandleData = 0x00000004, FilterMemory = 0x00000008, ScanMemory = 0x00000010, WithUnloadedModules = 0x00000020, WithIndirectlyReferencedMemory = 0x00000040, FilterModulePaths = 0x00000080, WithProcessThreadData = 0x00000100, WithPrivateReadWriteMemory = 0x00000200, WithoutOptionalData = 0x00000400, WithFullMemoryInfo = 0x00000800, WithThreadInfo = 0x00001000, WithCodeSegs = 0x00002000, WithoutAuxiliaryState = 0x00004000, WithFullAuxiliaryState = 0x00008000, WithPrivateWriteCopyMemory = 0x00010000, IgnoreInaccessibleMemory = 0x00020000, ValidTypeFlags = 0x0003ffff, } enum ExceptionInfo { None, Present } //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 ExceptionPointers; [MarshalAs(UnmanagedType.Bool)] public bool ClientPointers; } //BOOL //WINAPI //MiniDumpWriteDump( // __in HANDLE hProcess, // __in DWORD ProcessId, // __in HANDLE hFile, // __in MINIDUMP_TYPE DumpType, // __in_opt PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, // __in_opt PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, // __in_opt PMINIDUMP_CALLBACK_INFORMATION CallbackParam // ); // Overload requiring MiniDumpExceptionInformation [DllImport("dbghelp.dll", EntryPoint = "MiniDumpWriteDump", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] static extern bool MiniDumpWriteDump(IntPtr hProcess, uint processId, SafeHandle hFile, uint dumpType, ref MiniDumpExceptionInformation expParam, IntPtr userStreamParam, IntPtr callbackParam); // Overload supporting MiniDumpExceptionInformation == NULL [DllImport("dbghelp.dll", EntryPoint = "MiniDumpWriteDump", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] static extern bool MiniDumpWriteDump(IntPtr hProcess, uint processId, SafeHandle hFile, uint dumpType, IntPtr expParam, IntPtr userStreamParam, IntPtr callbackParam); [DllImport("kernel32.dll", EntryPoint = "GetCurrentThreadId", ExactSpelling = true)] static extern uint GetCurrentThreadId(); static bool Write(SafeHandle fileHandle, Option options, ExceptionInfo exceptionInfo) { Process currentProcess = Process.GetCurrentProcess(); IntPtr currentProcessHandle = currentProcess.Handle; uint currentProcessId = (uint)currentProcess.Id; MiniDumpExceptionInformation exp; exp.ThreadId = GetCurrentThreadId(); exp.ClientPointers = false; exp.ExceptionPointers = IntPtr.Zero; if (exceptionInfo == ExceptionInfo.Present) { exp.ExceptionPointers = Marshal.GetExceptionPointers(); } return exp.ExceptionPointers == IntPtr.Zero ? MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint)options, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero) : MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint)options, ref exp, IntPtr.Zero, IntPtr.Zero); } static bool Write(SafeHandle fileHandle, Option dumpType) { return Write(fileHandle, dumpType, ExceptionInfo.None); } public static Boolean TryDump(String dmpPath, Option dmpType = Option.Normal) { var path = Path.Combine(Environment.CurrentDirectory, dmpPath); var dir = Path.GetDirectoryName(path); if (dir != null && !Directory.Exists(dir)) { Directory.CreateDirectory(dir); } using (var fs = new FileStream(path, FileMode.Create)) { return Write(fs.SafeFileHandle, dmpType); } } }
然后在程序报错的地方写上
MiniDump.TryDump( DateTime.Now.ToString("yyyyMMddHHmmss")+".dmp", MiniDump.Option.WithFullMemory)
例子:
比如说是winfrom程序,就在main函数的最上面写上下面这句话,因为是闪退,所以写在未捕获事件里面
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler((obj, args) => MiniDump.TryDump( DateTime.Now.ToString("yyyyMMddHHmmss")+".dmp", MiniDump.Option.WithFullMemory));
比如说在catch里面页面写上上面的代码。这样程序在实际运行时候如果发生错误,程序会生成一个xxxx.dmp的文件,然后我们打开vs -> 打开文件 -> 打开我们刚刚生成的dmp文件,会显示如下界面:
选择"调试托管内存", 点击调试窗口的"调用堆栈"。到这里我们就能查看程序崩溃的原因了,如下图:
补充一点,如果是在线程中报错的话就看不到具体的堆栈信息了,不过也侧面说明了线程方法里没加try catch,这样问题也找到了。