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,这样问题也找到了。

posted on 2022-06-17 15:35  炼金师  阅读(773)  评论(0编辑  收藏  举报

导航