分析 Dump 入门简明教程

分析 Dump 入门简明教程

在 Windows 操作系统下,内存转储(dump)文件有多种类型,每种类型都有其特定的用途和特点

  1. Windbg (Windows Debugger)
    概述:Windbg 是 Microsoft 提供的强大调试工具,适用于高级用户和开发人员。它可以进行详细的内存分析,包括调用堆栈、线程状态、内存使用情况等。
    特点:支持 mini dump 和 full dump,提供丰富的调试命令和扩展。
  2. Visual Studio
    概述:Visual Studio 集成了内存转储文件的分析功能,适合开发人员使用。
    特点:用户界面友好,可以直接在 IDE 中打开和分析 dump 文件,提供调用堆栈、线程视图和变量检查等功能。
  3. DebugDiag (Debug Diagnostic Tool)
    概述:DebugDiag 是 Microsoft 提供的故障排除工具,专门用于捕获和分析内存转储文件。
    特点:提供自动分析报告,支持崩溃、挂起和内存泄漏的诊断,适合运维和技术支持人员。
  4. ProcDump
    概述:ProcDump 是 Sysinternals 提供的命令行工具,用于创建内存转储文件。
    特点:灵活的触发条件设置,可以捕获特定事件(如 CPU 使用率过高、内存使用过高)时的内存转储。
  5. WinDbg
    概述:WinDbg 是 Microsoft 提供的现代版调试工具,基于 WinDbg,但具有更好的用户界面和性能。
    特点:支持最新的调试技术和扩展,用户界面更加友好,适合初学者和高级用户。
  6. DotMemory
    概述:DotMemory 是 JetBrains 提供的 .NET 内存分析工具。
    特点:专注于内存泄漏和性能问题的诊断,提供详细的内存使用报告和可视化工具。
  7. ANTS Memory Profiler
    概述:ANTS Memory Profiler 是 Redgate 提供的 .NET 内存分析工具。
    特点:提供详细的内存使用报告,支持内存泄漏分析和性能优化。
  8. Memory Analyzer Tool (MAT)
    概述:MAT 是 Eclipse 提供的 Java 内存分析工具,也可以用于分析 .NET 内存转储文件。
    特点:提供详细的内存使用报告和泄漏分析,支持多种格式的内存转储文件。

编写问题程序

internal class Program
{
    private static void Main(string[] args)
    {
        Console.WriteLine("Hello, World!");
       
        Memory();
        CPU();

        Console.ReadKey();
    }

    private static readonly List<byte[]> _list =  new List<byte[]>();

    /// <summary>
    /// Memories this instance
    /// </summary>
    private static void Memory()
    {
        for (var i = 0; i < 10; i++)
        {
            _list.Add(new byte[1024 * 1024 * 10]);
        }
    }

    /// <summary>
    /// Cpus this instance
    /// </summary>
    private static void CPU()
    {
        System.Threading.Tasks.Task.Factory.StartNew(() =>
        {
            while (true)
            {
                Console.WriteLine(_list.Count);
            }
        });
    }
}

生成 dump 文件

使用操作系统的任务管理器

  1. 打开任务管理器:
    • 按 Ctrl + Shift + Esc 快捷键打开任务管理器。
    • 或者,右键点击任务栏并选择“任务管理器”。
  2. 找到目标进程:
    • 在“进程”选项卡中,找到你想要创建内存转储的进程。
  3. 创建内存转储:
    • 右键点击目标进程,选择“创建转储文件”。
    • 任务管理器会开始创建内存转储文件,并显示一个对话框,告知你转储文件的保存位置和大小。

程序崩溃自动抓取

@echo off
echo 正在启用Dump...
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps"
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps" /v DumpFolder /t REG_EXPAND_SZ /d "D:\CrashDump" /f
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps" /v DumpType /t REG_DWORD /d 2 /f
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps" /v DumpCount /t REG_DWORD /d 10 /f
echo Dump已经启用
exit

将上述内存保存为 *.bat就开始了自动抓dmp文件的功能。只要有程序崩溃,就会在指定的目录下生成。

DebugDiag 注册表键值说明

名称 类型 说明 默认值
DumpCount REG_DWORD 最大保留Dump个数 10
DumpType REG_DWORD Dump类型 (1 - Mini dump, 2 - Full dump) 1
DumpFolder REG_EXPAND_SZ Dump文件保存的位置

如果程序崩溃没有产生 dump 文件则可能是没有权限,将目录修改为有权限的目录即可。

使用 DebugDiag 创建转储文件

DebugDiag(Debug Diagnostic Tool)是 Microsoft 提供的一款强大的故障排除工具,主要用于捕获和分析应用程序的内存转储文件。它可以用于诊断应用程序崩溃、挂起和内存泄漏等问题。以下是如何使用 DebugDiag 创建和分析内存转储文件的步骤。

  1. 下载 DebugDiag

    • 访问 DebugDiag 下载 DebugDiag 工具。
    • 选择适合你操作系统的版本进行下载。
  2. 安装 DebugDiag

    • 运行下载的安装程序,按照提示完成安装。

创建内存转储文件

  1. 打开 DebugDiag

    • 安装完成后,打开 DebugDiag。默认情况下,你会看到两个工具:DebugDiag Analysis 和 DebugDiag Collection。
  2. 启动 DebugDiag Collection

    • 选择 DebugDiag Collection。
  3. 创建新的规则

    • 在 DebugDiag Collection 中,点击“New Rule”(新建规则)。
    • 选择你想要创建的规则类型。常见的规则类型包括:
      • CrashHang Rule(崩溃/挂起规则):用于捕获应用程序崩溃或挂起时的内存转储。
      • Memory Pressure Rule(内存压力规则):用于捕获应用程序在内存使用达到一定阈值时的内存转储。
      • Performance Counter Rule(性能计数器规则):用于捕获应用程序在特定性能计数器达到阈值时的内存转储。
  4. 配置规则

    • 选择规则类型后,按照向导的提示配置规则。例如,选择“CrashHang Rule”后,选择你要监控的进程。
    • 你可以选择特定的进程或所有进程。
    • 配置转储文件的保存位置和文件名格式。
  5. 启动规则

    • 配置完成后,点击“Finish”(完成)以启动规则。
    • DebugDiag 会开始监控选定的进程,并在满足条件时自动创建内存转储文件。

分析内存转储文件

DebugDiag 分析内存转储文件

image

  1. 打开 DebugDiag Analysis

    • 启动 DebugDiag Analysis。
  2. 加载转储文件

    • 在 DebugDiag Analysis 中,点击“File”(文件) -> “Open Crash Dump”(打开崩溃转储文件)。
    • 选择你创建的 .dmp 文件。
  3. 分析转储文件

    • DebugDiag 会自动加载并分析转储文件。
    • 分析完成后,你会看到一个报告,其中包含关于崩溃或挂起的原因的详细信息。
    • 报告中可能包含调用堆栈、异常信息、线程状态等。

image

注意事项

  • 权限:创建内存转储文件可能需要管理员权限,特别是在对系统进程或受保护的进程进行转储时。
  • 文件大小:内存转储文件可能非常大,特别是对于占用大量内存的应用程序。确保有足够的磁盘空间。
  • 隐私和安全:内存转储文件可能包含敏感信息,如密码、个人数据等。确保妥善保管和处理这些文件。

希望这些步骤和工具能帮助你有效地创建和分析内存转储文件。如果有更多问题或需要进一步的帮助,请随时提问。

Visual Studio 调试内存转储文件

image

可以看到还是很傻瓜化的,快照中说明已经很明显了

WinDbg

WinDbg 是一个强大的调试工具,用于分析 Windows 操作系统和应用程序的崩溃转储文件

安装

下载并安装 WinDbg下载

加载符号文件

加载符号文件,在 WinDbg 中,设置符号文件路径。可以使用 Microsoft 的符号服务器.sympath srvC:\Symbolshttps://msdl.microsoft.com/download/symbols
其中 C:\Symbols 是本地符号缓存目录

在 WinDbg 中,选择 File -> Open Crash Dump,然后选择要分析的转储文件(通常是 .dmp 文件)

************* Preparing the environment for Debugger Extensions Gallery repositories **************
   ExtensionRepository : Implicit
   UseExperimentalFeatureForNugetShare : false
   AllowNugetExeUpdate : false
   NonInteractiveNuget : true
   AllowNugetMSCredentialProviderInstall : false
   AllowParallelInitializationOfLocalRepositories : true

   EnableRedirectToV8JsProvider : false

   -- Configuring repositories
      ----> Repository : LocalInstalled, Enabled: true
      ----> Repository : UserExtensions, Enabled: true

>>>>>>>>>>>>> Preparing the environment for Debugger Extensions Gallery repositories completed, duration 0.000 seconds

************* Waiting for Debugger Extensions Gallery to Initialize **************

>>>>>>>>>>>>> Waiting for Debugger Extensions Gallery to Initialize completed, duration 0.062 seconds
   ----> Repository : UserExtensions, Enabled: true, Packages count: 0
   ----> Repository : LocalInstalled, Enabled: true, Packages count: 41

Microsoft (R) Windows Debugger Version 10.0.27553.1004 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.


Loading Dump File [D:\dump\DumpTest.DMP]
User Mini Dump File with Full Memory: Only application data is available

WARNING: Path element is empty

************* Path validation summary **************
Response                         Time (ms)     Location
Deferred                                       SRV*C:\symbolscache*http://msdl.microsoft.com/download/symbols
Deferred                                       srv*
WARNING: Path element is empty
Symbol search path is: SRV*C:\symbolscache*http://msdl.microsoft.com/download/symbols;;srv*
Executable search path is: 
Windows 10 Version 22631 MP (20 procs) Free x64
Product: WinNt, suite: SingleUserTS Personal
Edition build lab: 22621.1.amd64fre.ni_release.220506-1250
Debug session time: Fri Nov 22 15:13:03.000 2024 (UTC + 8:00)
System Uptime: 0 days 5:03:15.026
Process Uptime: 0 days 0:00:32.000
................................................

+------------------------------------------------------------------------+
| This target supports Hardware-enforced Stack Protection. A HW based    |
| "Shadow Stack" may be available to assist in debugging and analysis.   |
| See aka.ms/userhsp for more info.                                      |
|                                                                        |
| dps @ssp                                                               |
|                                                                        |
+------------------------------------------------------------------------+

For analysis of this file, run !analyze -v
wow64cpu!CpupSyscallStub+0x13:
00000000`003a1cf3 c3              ret

可以看到 WinDbg 已经加载了转储文件,并准备好进行分析

载入SOS.dll扩展命令模块

.load C:\Windows\Microsoft.NET\Framework64\v4.0.30319\sos.dll

!analyze -v

!analyze -v 是一个用于分析崩溃转储文件的命令,它提供了详细的崩溃信息,包括异常类型、堆栈跟踪、寄存器值等。

0:000> !analyze -v
*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************


KEY_VALUES_STRING: 1

    Key  : Analysis.Elapsed.mSec
    Value: 89839

    Key  : Analysis.IO.Other.Mb
    Value: 11

    Key  : Analysis.IO.Read.Mb
    Value: 0

    Key  : Analysis.IO.Write.Mb
    Value: 77

    Key  : Analysis.Init.Elapsed.mSec
    Value: 102744

    Key  : Analysis.Memory.CommitPeak.Mb
    Value: 170

    Key  : CLR.BuiltBy
    Value: NET481REL1LAST_C

    Key  : CLR.Engine
    Value: CLR

    Key  : CLR.Version
    Value: 4.8.9282.0

    Key  : Failure.Bucket
    Value: BREAKPOINT_80000003_wow64cpu.dll!CpupSyscallStub

    Key  : Failure.Hash
    Value: {289a12fb-10fd-bb5b-3edc-4567046643a5}

    Key  : Timeline.OS.Boot.DeltaSec
    Value: 18195

    Key  : Timeline.Process.Start.DeltaSec
    Value: 32

    Key  : WER.OS.Branch
    Value: ni_release

    Key  : WER.OS.Version
    Value: 10.0.22621.1

    Key  : WER.Process.Version
    Value: 1.0.0.0


FILE_IN_CAB:  DumpTest.DMP

NTGLOBALFLAG:  0

APPLICATION_VERIFIER_FLAGS:  0

EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 0000000000000000
   ExceptionCode: 80000003 (Break instruction exception)
  ExceptionFlags: 00000000
NumberParameters: 0

FAULTING_THREAD:  000134fc

PROCESS_NAME:  DumpTest.exe

ERROR_CODE: (NTSTATUS) 0x80000003 - {    }

EXCEPTION_CODE_STR:  80000003

STACK_TEXT:  
00000000`0063e808 00000000`003a192c     : 00000023`77006bcc 00007ffa`e4c70023 00000000`00000000 00000000`00000000 : wow64cpu!CpupSyscallStub+0x13
00000000`0063e810 00000000`003a1d75     : 00000000`0073f850 00007ffa`e4c7cb78 00000000`00392008 00007ffa`e4c7ec97 : wow64cpu!DeviceIoctlFileFault+0x31
00000000`0063e8c0 00007ffa`e4c7ea8d     : 00000000`005a0000 00000000`0063f300 00000000`00000000 00000000`0063f300 : wow64cpu!BTCpuSimulate+0xbb5
00000000`0063e900 00007ffa`e4c7e18d     : 00000000`00000000 00000000`00958360 00000000`00000000 00000000`00000000 : wow64!RunCpuSimulation+0xd
00000000`0063e930 00007ffa`e65ee7db     : 00000000`00390080 00007ffa`e664b760 00000000`0059f000 00007ffa`e6647b28 : wow64!Wow64LdrpInitialize+0x12d
00000000`0063ebe0 00007ffa`e65daad6     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000001 : ntdll!LdrpInitializeProcess+0x174b
00000000`0063efb0 00007ffa`e65844e3     : 00000000`0063f300 00007ffa`e6510000 00000000`0059f050 00000000`005a27ee : ntdll!_LdrpInitialize+0x565ba
00000000`0063f030 00007ffa`e658440e     : 00000000`0063f300 00000000`00000000 00000000`0063f300 00000000`00000000 : ntdll!LdrpInitializeInternal+0x6b
00000000`0063f2b0 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!LdrInitializeThunk+0xe


SYMBOL_NAME:  wow64cpu!CpupSyscallStub+13

MODULE_NAME: wow64cpu

IMAGE_NAME:  wow64cpu.dll

STACK_COMMAND:  dt ntdll!LdrpLastDllInitializer BaseDllName ; dt ntdll!LdrpFailureData ; ~0s; .ecxr ; kb

FAILURE_BUCKET_ID:  BREAKPOINT_80000003_wow64cpu.dll!CpupSyscallStub

OS_VERSION:  10.0.22621.1

BUILDLAB_STR:  ni_release

OSPLATFORM_TYPE:  x64

OSNAME:  Windows 10

IMAGE_VERSION:  10.0.22621.2506

FAILURE_ID_HASH:  {289a12fb-10fd-bb5b-3edc-4567046643a5}

Followup:     MachineOwner
---------

执行 !threadpool 看是否是问题根源

0:000> !threadpool
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for clr.dll - 
PDB symbol for clr.dll not loaded
CPU utilization: 0%
Worker Thread: Total: 0 Running: 0 Idle: 0 MaxLimit: 0 MinLimit: 0
Work Request in Queue: 0
--------------------------------------
Number of Timers: 0
--------------------------------------
Completion Port Thread:Total: 0 Free: 0 MaxFree: 0 CurrentLimit: 0 MaxLimit: 1000 MinLimit: 0

CPU utilization 0%说明不是 ThreadPool 的问题

执行 !runaway 看线程的执行时间

0:000> !runaway
 User Mode Time
  Thread       Time
   4:714       0 days 0:00:00.358
   3:38c       0 days 0:00:00.000
   2:b94       0 days 0:00:00.000
   1:8bc       0 days 0:00:00.000
   0:8ec       0 days 0:00:00.000

执行时间最长的是线程 4,执行了 0 days 0:00:00.358

执行 ~4 s 将当前线程切换到线程4

0:004> ~4 s
ntdll!ZwRequestWaitReplyPort+0xa:
00000000`776400da c3              ret

执行 !clrstack 显示当前线程的调用堆栈

0:004> !clrstack
OS Thread Id: 0xa68 (4)
Child SP         IP               Call Site
000000001c4feb68 00000000776400da [NDirectMethodFrameStandalone: 000000001c4feb68] System.IO.__ConsoleStream.WriteFile(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
000000001c4feb10 000007fef12c34a1 DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)*** WARNING: Unable to verify checksum for mscorlib.ni.dll
*** ERROR: Module load completed but symbols could not be loaded for mscorlib.ni.dll

000000001c4fec30 000007fef127cf9f System.IO.__ConsoleStream.WriteFileNative(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Int32, Int32 ByRef)
000000001c4fec90 000007fef127cefa System.IO.__ConsoleStream.Write(Byte[], Int32, Int32)
000000001c4fecf0 000007fef1253a09 System.IO.StreamWriter.Flush(Boolean, Boolean)
000000001c4fed50 000007fef1a77b3d System.IO.TextWriter+SyncTextWriter.WriteLine(Int32)
000000001c4feda0 000007ff0016030d ConsoleApplication1.Program.<Cpu>b__0()*** WARNING: Unable to verify checksum for ConsoleApplication1.exe
 [f:\Documents\Visual Studio 2012\Projects\2012test\ConsoleApplication1\Program.cs @ 23]
000000001c4fede0 000007fef121181c System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
000000001c4fee40 000007fef121172b System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
000000001c4fee90 000007fef12a6f2d System.Threading.ThreadHelper.ThreadStart()
000000001c4ff2e8 000007fef33a10b4 [GCFrame: 000000001c4ff2e8] 
000000001c4ff6d0 000007fef33a10b4 [DebuggerU2MCatchHandlerFrame: 000000001c4ff6d0] 

执行 !eeheap -gc 查看托管堆的总信息

0:004> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x0000000002a26220
generation 1 starts at 0x0000000002a21cf0
generation 2 starts at 0x0000000002a11000
ephemeral segment allocation context: none
         segment             begin         allocated  size
0000000002a10000  0000000002a11000  0000000002af2238  0xe1238(922168)
Large object heap starts at 0x0000000012a11000
         segment             begin         allocated  size
0000000012a10000  0000000012a11000  0000000018e171e0  0x64061e0(104882656)
Total Size:              Size: 0x64e7418 (105804824) bytes.
------------------------------
GC Heap Size:    Size: 0x64e7418 (105804824) bytes.

执行 !dumpheap -min 200 -stat 获取占用堆内存的各对象的统计信息

0:004> !dumpheap -min 200 -stat
total 0 objects
Statistics:
              MT    Count    TotalSize Class Name
000007fef1369750        1          216 System.AppDomain
000007fef13745d0        2          432 System.Globalization.NumberFormatInfo
000007fef1373148        1          432 System.Collections.Generic.Dictionary`2+Entry[[System.Type, mscorlib],[System.Security.Policy.EvidenceTypeDescriptor, mscorlib]][]
000007fef136b7b0        2         1056 System.Globalization.CultureData
000007fef136c7e8        2         1128 System.Int32[]
000007fef136b328        3         3256 System.Char[]
000007fef136d110        1         4752 System.Collections.Generic.Dictionary`2+Entry[[System.String, mscorlib],[System.String, mscorlib]][]
0000000000664be0       22        31784      Free
000007fef136ae78        5        34072 System.Object[]
000007fef1370bc0       11    104858384 System.Byte[]
Total 50 objects

byte[] 占用了 104858384 字节,大约 100MB

执行 !dumpheap -type Byte[] -min 200 看各Byte数组占用堆内存的详细信息

0:004> !dumpheap -type Byte[] -min 200
         Address               MT     Size
0000000002a25290 000007fef1370bc0      544     
0000000012a17048 000007fef1370bc0 10485784     
0000000013417060 000007fef1370bc0 10485784     
0000000013e17078 000007fef1370bc0 10485784     
00000000148170a8 000007fef1370bc0 10485784     
00000000152170d8 000007fef1370bc0 10485784     
0000000015c17108 000007fef1370bc0 10485784     
0000000016617138 000007fef1370bc0 10485784     
0000000017017168 000007fef1370bc0 10485784     
0000000017a17198 000007fef1370bc0 10485784     
00000000184171c8 000007fef1370bc0 10485784     
total 0 objects
Statistics:
              MT    Count    TotalSize Class Name
000007fef1370bc0       11    104858384 System.Byte[]
Total 11 objects

选取一个地址0000000012a17048

执行 !gcroot <Byte数组对象地址> 看对象引用关系

0:004> !gcroot 0000000012a17048 
Note: Roots found on stacks may be false positives. Run "!help gcroot" for
more info.
Scan Thread 0 OSTHread a54
RSP:54e958:Root:  0000000002a21e60(System.Threading.ThreadStart)->
  0000000002a21cb0(ConsoleApplication1.Program)->
  0000000002a21cc8(System.Collections.Generic.List`1[[System.Byte[], mscorlib]])->
  0000000002a21dc0(System.Byte[][])->
  0000000012a17048(System.Byte[])

定位到 List 对象

执行 !do <Program对象地址> 查看对象的详细信息

0:004> !do 0000000002a21cb0
Name:        ConsoleApplication1.Program
MethodTable: 000007ff00044140
EEClass:     000007ff00152350
Size:        24(0x18) bytes
File:        C:\Users\Administrator\Desktop\ConsoleApplication1\ConsoleApplication1.exe
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
000007ff00032600  4000001        8 ...yte[], mscorlib]]  0 instance 0000000002a21cc8 _list
posted @ 2022-08-01 11:11  陈大欠  阅读(978)  评论(0编辑  收藏  举报