• .NET -- 使用Dump文件分析异常


    目录

    1. Dump文件

    2. 程序崩溃时自动生成Dump文件

    2.1 注册表生成

    2.2 代码生成

    3. 手动生成Dump文件

    3.1 任务管理器生成

    3.2 VS生成

    4. Dump文件调试分析

    4.1 简易崩溃测试代码

    4.2 VS2022调试

    4.3 非本机测试


    1. Dump文件

    Dump文件是进程的内存镜像。可以把程序的执行状态通过调试器保存到dump文件中。主要是用来在系统中出现异常或者崩溃的时候来生成dump文件,然后用调试器进行调试,这样就可以快速定位到程序崩溃位置,对问题进行排查。

    2. 程序崩溃时自动生成Dump文件

    2.1 注册表生成

    Win + R 输入regedit打开注册表

    找到如下项:

    计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps

     新建如下:

    ​ 

    说明:

    DumpCount: 文件数量      ( 值设为0即禁用此功能)
    DumpFolder:存储位置    (Dump文件名:(应用程序名称全称.进程号.dmp))
    DumpType:   dmp文件类型  (1:mini 2:full)

                            mini:只包含某个线程和部分模块的信息。

                            full: 包含了某个进程完整的地址空间数据,以及许多用于调试的信息 

    2.2 代码生成

    使用win32 api :MiniDumpWriteDump  实现

    封装了DumpHelper类

    1. using System.Runtime.InteropServices;
    2. namespace DumpTest
    3. {
    4. public class DumpHelper
    5. {
    6. [Flags]
    7. public enum DumpType : uint
    8. {
    9. // From dbghelp.h:
    10. MiniDumpNormal = 0x00000000, //只包含调用栈相关信息
    11. MiniDumpWithDataSegs = 0x00000001, //包含已加载的模块的数据段信息,比如全局变量
    12. MiniDumpWithFullMemory = 0x00000002, //包含全部可访问的内存
    13. MiniDumpWithHandleData = 0x00000004, //包含句柄信息
    14. MiniDumpFilterMemory = 0x00000008, //过滤一些敏感信息,保护重建调用栈需要的信息
    15. MiniDumpScanMemory = 0x00000010, //扫描,以包含引用内存
    16. MiniDumpWithUnloadedModules = 0x00000020, //包含最近被卸载的模块信息
    17. MiniDumpWithIndirectlyReferencedMemory = 0x00000040,//包含未直接引用的内存
    18. MiniDumpFilterModulePaths = 0x00000080, //过滤某块的路径信息
    19. MiniDumpWithProcessThreadData = 0x00000100, //包含完整的进程和线程信息
    20. MiniDumpWithPrivateReadWriteMemory = 0x00000200, //包含页面属性为 PAGE_READWRITE 的页面
    21. MiniDumpWithoutOptionalData = 0x00000400, //不包含可选数据
    22. MiniDumpWithFullMemoryInfo = 0x00000800, //包含内存区信息
    23. MiniDumpWithThreadInfo = 0x00001000, //包含线程状态信息
    24. MiniDumpWithCodeSegs = 0x00002000, //包含所有代码和有关的内存段
    25. MiniDumpWithoutAuxiliaryState = 0x00004000, //关闭辅助内存收集
    26. MiniDumpWithFullAuxiliaryState = 0x00008000, //使用所有的内存收集器
    27. MiniDumpWithPrivateWriteCopyMemory = 0x00010000, //包含页面属性为 PAGE_WRITECOPY 的页面
    28. MiniDumpIgnoreInaccessibleMemory = 0x00020000, //忽略不可访问的页面
    29. MiniDumpValidTypeFlags = 0x0003ffff, //包含安全令牌相关信息
    30. };
    31. //typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
    32. // DWORD ThreadId;
    33. // PEXCEPTION_POINTERS ExceptionPointers;
    34. // BOOL ClientPointers;
    35. //} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
    36. [StructLayout(LayoutKind.Sequential, Pack = 4)] // Pack=4 is important! So it works also for x64!
    37. struct MiniDumpExceptionInformation
    38. {
    39. public uint ThreadId;
    40. public IntPtr ExceptioonPointers;
    41. [MarshalAs(UnmanagedType.Bool)]
    42. public bool ClientPointers;
    43. }
    44. //BOOL
    45. //WINAPI
    46. //MiniDumpWriteDump(
    47. // __in HANDLE hProcess, :要转储的进程句柄。
    48. // __in DWORD ProcessId, :要转储的进程ID。
    49. // __in HANDLE hFile, :通过 CreateFile() 等 API 打开的,用来保存 dump 的文件句柄。
    50. // __in MINIDUMP_TYPE DumpType, :转储类型。此参数会直接影响转储文件的大小
    51. // __in_opt PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, :指向异常信息结构 MiniDumpExceptionInformation 的指针。如果本参数为 NULL,则转储文件中不会包含异常信息。
    52. // __in_opt PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, :指向用户自定义信息结构的指针
    53. // __in_opt PMINIDUMP_CALLBACK_INFORMATION CallbackParam :指向回调例程 MINIDUMP_CALLBACK_INFORMATION 的指针。如果此参数为NULL,转储过程中不会执行任何回调例程。
    54. // );
    55. [DllImport("dbghelp.dll",EntryPoint = "MiniDumpWriteDump",CallingConvention = CallingConvention.StdCall,CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
    56. static extern bool MiniDumpWriteDump(
    57. IntPtr hProcess,
    58. uint processId,
    59. IntPtr hFile,
    60. uint dumpType,
    61. ref MiniDumpExceptionInformation expParam,
    62. IntPtr userStreamParam,
    63. IntPtr callbackParam);
    64. [DllImport("kernel32.dll", EntryPoint = "GetCurrentThreadId", ExactSpelling = true)]
    65. static extern uint GetCurrentThreadId();
    66. [DllImport("kernel32.dll", EntryPoint = "GetCurrentProcess", ExactSpelling = true)]
    67. static extern IntPtr GetCurrentProcess();
    68. [DllImport("kernel32.dll", EntryPoint = "GetCurrentProcessId", ExactSpelling = true)]
    69. static extern uint GetCurrentProcessId();
    70. public static bool WriteDump(string fileName)
    71. {
    72. return Write(fileName, DumpType.MiniDumpNormal);
    73. }
    74. public static bool Write(string fileName, DumpType dumpType)
    75. {
    76. var path = Path.Combine(Environment.CurrentDirectory, fileName);
    77. using var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None);
    78. MiniDumpExceptionInformation exp;
    79. exp.ThreadId = GetCurrentThreadId();
    80. exp.ClientPointers = false;
    81. exp.ExceptioonPointers = Marshal.GetExceptionPointers();
    82. bool bRet = MiniDumpWriteDump(
    83. GetCurrentProcess(),
    84. GetCurrentProcessId(),
    85. fs.SafeFileHandle.DangerousGetHandle(),
    86. (uint)dumpType,
    87. ref exp,
    88. IntPtr.Zero,
    89. IntPtr.Zero);
    90. return bRet;
    91. }
    92. }
    93. }

    调用:

    当某个系统异常未被捕获时,调用方法生成Dump文件

    1. AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(
    2. (obj, args) => DumpHelper.WriteDump($"Error_{DateTime.Now.ToString("yyyy_M_dd_HH_mm")}.dmp")
    3. );

    结果:

    代码出现异常导致程序崩溃时,会在程序的工作目录下自动生成Dump文件,如图:

    文件1:DumpType:MiniDumpWithFullMemory

    文件2:DumpType:MiniDumpNormal

    可以看出Dump文件保存的内容不同,文件大小差异巨大。

    3. 手动生成Dump文件

    3.1 任务管理器生成

    打开任务管理器 --> 找到对应进程 --> 右击选择创建转储文件

    创建成功如图:

    3.2 VS生成

    在代码中断时,调试 --> 将转储另存为 --> 设置文件路径&文件名

    4. Dump文件调试分析

    4.1 简易崩溃测试代码

    1. var lists=new List<string>() {"a","b","c"};
    2. Console.WriteLine(lists[3]);

    4.2 VS2022调试

    运行DumpTest.exe,程序崩溃后,自动生成Dump文件

    用VS打开.dmp文件

    ​ 

    点击右侧"使用混合进行调试" -- > 就定位到异常位置,如图

     得出结论:列表引用超范围的异常

    4.3 非本机测试

    若将Dump文件拷贝到另一台PC上调试时,则需要将以下3个文件都拷下来,放在同一目录下,必须保证pdb与出问题的exe是同一时间生成的。

    参考:

    【1】 MiniDumpWriteDump function (minidumpapiset.h) - Win32 apps | Microsoft Learn

    【2】Dump文件的生成和使用_思影影思的博客-CSDN博客_dump

    【3】C#使用MiniDump捕获异常 - 你不知道的浪漫 - 博客园 (cnblogs.com)

    【4】向大厂看齐!为自己的程序增加自动转储的功能! - 知乎 (zhihu.com)

  • 相关阅读:
    技术干货|昇思MindSpore 1.5版本中的亲和算法库——MindSpore Boost
    Django模型的字段类型
    OpenGL_Learn08(坐标系统与3D空间)
    CY3/5/7/FITC荧光素标记乳糖/蜜二糖/单乙酰氨基半乳糖
    使用Spring Cache实现广告缓存并基于RabbitMQ实现双写一致
    LeetCode 1359. Count All Valid Pickup and Delivery Options【动态规划,组合数学】1722
    R语言使用plot函数可视化数据散点图,使用text函数在可视化图像的指定位置添加文本标签、并指定字体类型为serif
    【GPU】显卡内存不足及监控GPU使用情况
    如何准确高效的对电商数据进行分析
    遗传算法GA求解非连续函数问题
  • 原文地址:https://blog.csdn.net/a549742320/article/details/127790466