Favicon

Write minidump

Peponi1/14/20253m

C#
Win32dbghelp.dllMiniDumpWriteDump

1. Introduction

SW 운용 중 예상치 못한 exception은 언제나 발생한다. 만약 *.pdb 파일과 함께 실행중이라면 윈도우 이벤트 로그를 통해 예외가 발생한 코드 라인, 대략적인 정보를 확인할 수 있다. 이 때, dump 파일의 정보가 있으면 해당 시점의 SW 상태를 자세히 알 수 있어 분석이 빠르게 진행된다.

2. Example

아래는 구현할 *Dump() 메서드의 간략한 정보이다.

MethodMiniDumpType
WriteMiniDump()MiniDumpType.Normal
WriteFullDump()MiniDumpType.Normal
MiniDumpType.WithFullMemory
MiniDumpType.WithHandleData
MiniDumpType.WithProcessThreadData
MiniDumpType.WithThreadInfo
MiniDumpType.WithCodeSegs

아래는 MiniDump 클래스 예시이다.

using System.Diagnostics;
using System.Runtime.InteropServices;
 
namespace MiniDumpTest;
 
/// <summary>
/// <code>
/// Non-UI thread : AppDomain.CurrentDomain.UnhandledException
/// WinForm UI thread : Application.ThreadException
/// WPF UI thread : Application.Current.DispatcherUnhandledException
/// ASP.NET HttpApplication request exception : HttpApplication.Error
/// </code>
/// </summary>
public static class MiniDumpWriter
{
    [Flags]
    private enum MiniDumpType
    {
        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
    }
 
    [StructLayout(LayoutKind.Sequential, Pack = 4)]
    private struct MinidumpExceptionInformation
    {
        public uint ThreadId;
        public IntPtr ExceptionPointers;
        public int ClientPointers;
    }
 
    [DllImport("dbghelp.dll", EntryPoint = "MiniDumpWriteDump",
        CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode,
        ExactSpelling = true, SetLastError = true)]
    private static extern bool MiniDumpWriteDump(
                            IntPtr hProcess,
                            Int32 processId,
                            IntPtr fileHandle,
                            uint dumpType,
                            ref MinidumpExceptionInformation exceptionInfo,
                            IntPtr userInfo,
                            IntPtr extInfo);
 
    [DllImport("kernel32.dll")]
    private static extern uint GetCurrentThreadId();
 
    public static void WriteMiniDump()
    {
        var dumpPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $@"{DateTime.Now.ToString("yyMMdd-HHmmss")}.mini.dmp");
        var dumpType = MiniDumpType.Normal;
        
        WriteDump(dumpPath, (uint)dumpType);
    }
 
    public static void WriteFullDump()
    {
        var dumpPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $@"{DateTime.Now.ToString("yyMMdd-HHmmss")}.full.dmp");
        var dumpType = MiniDumpType.Normal |
                       MiniDumpType.WithFullMemory |
                       MiniDumpType.WithHandleData |
                       MiniDumpType.WithProcessThreadData |
                       MiniDumpType.WithThreadInfo |
                       MiniDumpType.WithCodeSegs;
        
        WriteDump(dumpPath, (uint)dumpType);
    }
 
    private static void WriteDump(string dumpPath, uint dumpType)
    {
        MinidumpExceptionInformation exceptionInfo = new();
        exceptionInfo.ClientPointers = 1;
        exceptionInfo.ExceptionPointers = Marshal.GetExceptionPointers();
        exceptionInfo.ThreadId = GetCurrentThreadId();
 
        using (FileStream stream = new FileStream(dumpPath, FileMode.Create))
        {
            Process process = Process.GetCurrentProcess();
 
            MiniDumpWriteDump(process.Handle,
                              process.Id,
                              stream.SafeFileHandle.DangerousGetHandle(),
                              dumpType,
                              ref exceptionInfo,
                              IntPtr.Zero,
                              IntPtr.Zero);
        }
    }
}

다음은 dump 파일을 남기는 예시이다.

using MiniDumpTest;
 
namespace MiniDumpTest.Tests;
 
[TestClass]
public class Minidump
{
    [TestMethod]
    public void Dump()
    {
        try
        {
            // Assume an exception occurred.
            throw new Exception("Test");
        }
        catch (Exception ex)
        {
            // Write dump files
            MiniDumpWriter.WriteMiniDump();
            MiniDumpWriter.WriteFullDump();
        }
    }
}

MiniDumpWriter 클래스의 comment는 처리되지 않은 예외에 대한 이벤트의 목록이다. (Application start 이전에 이벤트 핸들러를 등록해야 한다.)

  1. Non-UI thread : AppDomain.CurrentDomain.UnhandledException
  2. WinForm UI thread : Application.ThreadException
  3. WPF UI thread : Application.Current.DispatcherUnhandledException
  4. ASP.NET HttpApplication request exception : HttpApplication.Error

이를 이용하여 UI 또는 non-UI thread에서 발생하는 처리되지 않은 예외를 dump로 상세히 기록할 수 있다.

3. 참조 자료