使用visual studio对dmp文件进行调试(winform)

发布于:2025-02-21 ⋅ 阅读:(137) ⋅ 点赞:(0)

网上有很多关于使用vs对dmp的调试教程,不过大多比较简略,为便于日后查阅,综合自己调研的过程,特此书写整个调试流程。

  • 终极目标:根据dmp文件,找到程序异常位置
  • 调试工具:visual studio 2022 enterprise
  • 准备文件:*.exe,*.pdb,*.dmp

崩溃示例代码

模拟windows下崩溃的程序,最优解就是采用winform进行开发。在VS中新建一个winform程序(此步骤略)。为模拟异常,随便拖一个按钮,在其按钮事件里来一个除0的异常即可:

        private void btnTest1_Click(object sender, EventArgs e)
        {
            int d = 1;
            int s = 0;
            float re = d / s;
        }

生成dmp文件

生成dmp的方式有很多种,这里采用在程序里写生成dmp的代码,实现代码如下:

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

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

然后再program.cs的main方法里直接引入如下语句,即可在程序崩溃时导出dmp文件:

 AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler((obj, args) => MiniDump.TryDump("WindowsFormsApp1"+ DateTime.Now.ToString("yyyyMMddHHmmss") + ".dmp", MiniDump.Option.WithFullMemory));

Visual Studio 设置

打开vs,在最上方菜单栏选择 “调试”→“选项”:
在这里插入图片描述
将 【启用“仅我的代码”】取消选中(如果不设置这步,dmp调试会中断):
在这里插入图达到片描述

调试步骤

  1. 将exe文件,pdb文件和dmp文件全部放置在同一个文件夹中:在这里插入图片描述

  2. 打开vs,并将pdb拖入到vs中,vs中显示如下:
    在这里插入图片描述

  3. 再点击右侧的“设置符号路径”:
    在这里插入图片描述

  4. 弹出的选项页面中,在这里设置pdb的路径,并选中:
    在这里插入图片描述

  5. 在这里设置符号路径,然后点击“加载所有符号”(我这里因为加载过了,所以是灰显):
    在这里插入图片描述
    第一次加载会比较慢,需要从网络端下载符号集,大概1个小时之内会加载完毕,耐心等待即可。

  6. 以上全部设置完毕,再回到主页面,点击“使用 混合 进行测试”:

  7. 会进入到如下页面,在这里可以看到代码“return exp.ExceptionPointers == IntPtr.Zero ”,不过这种还是太笼统:
    在这里插入图片描述

  8. 关注右下方部分,右键最左侧箭头所指向的最上方的这一行,选择“在代码图中显示调用堆栈”:
    在这里插入图片描述

  9. 即可看到具体的调用堆栈信息:
    在这里插入图片描述
    在这里可以明显的看到是在btnTest1_click方法中出了问题。

  10. 继续关注右下方的调用堆栈面板,找到btnTest1_click所属行:
    在这里插入图片描述

  11. 点击当前行,即可看到产出异常的具体位置:在这里插入图片描述

  12. 点击右侧的“分析”按钮,也可以查到具体的异常信息:
    在这里插入图片描述
    在这里插入图片描述

至此,我们完成了dmp的分析全部过程。


网站公告

今日签到

点亮在社区的每一天
去签到