.dmp 文件调试

211 阅读7分钟

vc6.0 mfc 生成 dump

// vc6mfcdumptest.cpp : Defines the entry point for the console application.
//
#define DNC_MINIDUMP
#include "stdafx.h"
#include "vc6mfcdumptest.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// The one and only application object

CWinApp theApp;

using namespace std;

#ifdef DNC_MINIDUMP 
LONG WINAPI DNCUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *pExceptionPointers) 
{ 
	TCHAR szProgramPath[MAX_PATH] = {0};
	if(GetModuleFileName(NULL, szProgramPath, MAX_PATH))
	{
		LPTSTR lpSlash = _tcsrchr(szProgramPath, '\\');
		if(lpSlash)
		{
			*(lpSlash + 1) = '\0';
		}
	}
	time_t t = time(0); 
	char tmp[64]; 
	strftime( tmp, sizeof(tmp), "%Y%m%d-%H%M",localtime(&t) ); 
	
	TCHAR szDumpFile[MAX_PATH] = {0};
	_stprintf(szDumpFile, _T("%s%s.dmp"), szProgramPath, tmp);
	
	HANDLE hDumpFile = CreateFile(szDumpFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL ,NULL);
	MINIDUMP_EXCEPTION_INFORMATION stMiniDumpExceptionInfo;
	stMiniDumpExceptionInfo.ExceptionPointers = pExceptionPointers;
	stMiniDumpExceptionInfo.ThreadId = GetCurrentThreadId();
	stMiniDumpExceptionInfo.ClientPointers = TRUE;
	MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile,
		MiniDumpNormal, &stMiniDumpExceptionInfo, NULL, NULL);
	CloseHandle(hDumpFile);
    return EXCEPTION_EXECUTE_HANDLER;	
} 
#endif


int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
	int nRetCode = 0;

	// initialize MFC and print and error on failure
	if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
	{
		// TODO: change error code to suit your needs
		cerr << _T("Fatal Error: MFC initialization failed") << endl;
		nRetCode = 1;
	}
	else
	{
		// TODO: code your application's behavior here.
	
	#ifdef DNC_MINIDUMP 
		SetUnhandledExceptionFilter(DNCUnhandledExceptionFilter);
	#endif	
		// 使程序崩溃产生 Dump 文件
		int* p = NULL;
		*p = 1;
	}

	return nRetCode;
}

vc++ 生成 dump

#include <iostream>
#include <tchar.h>
#include "Windows.h"
#include "DbgHelp.h"
#pragma comment(lib,"dbghelp.lib")

int GenerateMiniDump(PEXCEPTION_POINTERS pExceptionPointers)
{
    // 定义函数指针
    typedef BOOL(WINAPI* MiniDumpWriteDumpT)(
        HANDLE,
        DWORD,
        HANDLE,
        MINIDUMP_TYPE,
        PMINIDUMP_EXCEPTION_INFORMATION,
        PMINIDUMP_USER_STREAM_INFORMATION,
        PMINIDUMP_CALLBACK_INFORMATION
        );
    // 从 "DbgHelp.dll" 库中获取 "MiniDumpWriteDump" 函数
    MiniDumpWriteDumpT pfnMiniDumpWriteDump = NULL;
    HMODULE hDbgHelp = LoadLibrary(_T("DbgHelp.dll"));
    if (NULL == hDbgHelp)
    {
        return EXCEPTION_CONTINUE_EXECUTION;
    }
    pfnMiniDumpWriteDump = (MiniDumpWriteDumpT)GetProcAddress(hDbgHelp, "MiniDumpWriteDump");

    if (NULL == pfnMiniDumpWriteDump)
    {
        FreeLibrary(hDbgHelp);
        return EXCEPTION_CONTINUE_EXECUTION;
    }
    // 创建 dmp 文件件
    TCHAR szFileName[MAX_PATH] = { 0 };
    TCHAR temp[] = _T("DumpDemo_v1.0");
    TCHAR* szVersion = temp;
    SYSTEMTIME stLocalTime;
    GetLocalTime(&stLocalTime);
    wsprintf(szFileName, L"%s-%04d%02d%02d-%02d%02d%02d.dmp",
        szVersion, stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
        stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond);
    HANDLE hDumpFile = CreateFile(szFileName, GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_WRITE | FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
    if (INVALID_HANDLE_VALUE == hDumpFile)
    {
        FreeLibrary(hDbgHelp);
        return EXCEPTION_CONTINUE_EXECUTION;
    }
    // 写入 dmp 文件
    MINIDUMP_EXCEPTION_INFORMATION expParam;
    expParam.ThreadId = GetCurrentThreadId();
    expParam.ExceptionPointers = pExceptionPointers;
    expParam.ClientPointers = FALSE;
    pfnMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
        hDumpFile, MiniDumpWithDataSegs, (pExceptionPointers ? &expParam : NULL), NULL, NULL);
    // 释放文件
    CloseHandle(hDumpFile);
    FreeLibrary(hDbgHelp);
    return EXCEPTION_EXECUTE_HANDLER;
}

LONG WINAPI ExceptionFilter(LPEXCEPTION_POINTERS lpExceptionInfo)
{
    // 这里做一些异常的过滤或提示
    if (IsDebuggerPresent())
    {
        return EXCEPTION_CONTINUE_SEARCH;
    }
    return GenerateMiniDump(lpExceptionInfo);
}

int main()
{
    // 加入崩溃dump文件功能
    SetUnhandledExceptionFilter(ExceptionFilter);
    // 使程序崩溃产生 Dump 文件
    int* p = NULL;
    *p = 1;
    
}

image.png

参考:

visual studio 下 C++生成dump文件_vc++ minidumpwritedump-CSDN博客

c# 生成 .dmp

生成方式1:DumpHelper.cs 帮助类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace NetDumpTest
{
    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;
        }

    }
}

生成方式2:MiniDump.cs 帮助类

using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace NetDumpTest
{
    public class MiniDump
    {
        public static void AutoGenerateDumpWhenCrash()
        {

            //参考文档:https://docs.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps?redirectedfrom=MSDN

            //注册表里,添加【程序崩溃后,自动生成dump文件配置】
            //注册表需要admin权限!
            try
            {
                var outputDmpPath = AppDomain.CurrentDomain.BaseDirectory + "Dump";
                if (!Directory.Exists(outputDmpPath))
                {
                    Directory.CreateDirectory(outputDmpPath);
                }

                var fileName = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
                var processName = fileName.Substring(fileName.LastIndexOf('\\') + 1);

                //注册表地址
                string regPath = @"SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps\" + processName;
                var reg = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);//Registry64防止注册表重定向到wow64

                var subKey = reg.CreateSubKey(regPath);



                subKey.SetValue("DumpCount", 1, RegistryValueKind.DWord);//dump文件个数
                subKey.SetValue("DumpFolder", outputDmpPath, RegistryValueKind.ExpandString);//dump文件目录
                subKey.SetValue("DumpType", 1, RegistryValueKind.DWord);//dump文件类型

            }
            catch (Exception ex)
            {
                //CLoggerTools.Warning2File(CLoggerFileName.strException, "{0},{1},{2}", "UI.Program", ex.Message, ex.StackTrace.Replace("\r\n", ""));
                return;
            }


        }
        // 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);
            }
        }
    }
}

生成.dmp

namespace NetDumpTest
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //生成方式1
            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(
    (obj, args) => DumpHelper.WriteDump($"Error_{DateTime.Now.ToString("yyyy_M_dd_HH_mm")}.dmp")
);
            //生成方式2
            MiniDump.AutoGenerateDumpWhenCrash();

            int[] a = { 1,2};
            a[10] = 10;         //异常
            

        }
    }
}

参考:

.NET -- 使用Dump文件分析异常_c# dump-CSDN博客

vs 调试 debug

  1. vs 打开 .dmp文件

image.png

image.png

  1. 配置源码位置

image.png

image.png

image.png

C:\Users\32763\Desktop\workspace\cpp\DumpTest\DumpTest
  1. 本机调试

image.png

  1. 定位异常

c++

image.png

c#

image.png

备注: c# 调试时,需要完整加载 miscrosoft 符号,否则无法加载至源码:

image.png

参考:

使用VS调试Dump文件-CSDN博客使用VS调试Dump文件-CSDN博客

windbg 调试 dmp

  1. windbg 操作页面介绍

Windbg调试(使用方法)_windbg怎么用-CSDN博客

  1. 配置路径

image.png

  • Open Crash Dump... : .dmp文件
  • Symbol File Path ... : 符号文件.pdb
  • Source File Path ... : 源代码
  1. 常用指令
!analyze -v    # 查看数据
~*kbn        # 查看所有线程

参考:

  • .dmp文件调试参考:

windbg使用超详细教程(我是新手,大佬轻虐) - 知乎 (zhihu.com)

  • 附加进程调试参考:

windbg使用教程(调试异常及死锁等)_windbg 教程-CSDN博客