通过


转储收集和分析实用工具 (dotnet-dump)

本文适用于:✔️ 版本 3.0.47001 及更高版本

注意

dotnet-dump for macOS 仅支持 .NET 5 及更高版本。

安装

可采用两种方法来下载和安装 dotnet-dump

注意

若要在 x86 应用上使用 dotnet-dump,需要使用相应的 x86 版本的工具。

摘要

dotnet-dump [-h|--help] [--version] <command>

说明

dotnet-dump全局工具是在Windows、Linux 和 macOS 上收集和分析转储的一种方法,无需涉及任何本机调试器。 在 lldb 无法正常运行的平台(如 Alpine Linux)上,此工具非常重要。 借助 dotnet-dump 工具,可以运行 SOS 命令来分析崩溃和垃圾回收器 (GC),但它不是本机调试器,因此不支持显示本机堆栈帧之类的操作。

选项

  • --version

    显示 dotnet-dump 实用工具的版本。

  • -h|--help

    显示命令行帮助。

命令

命令
dotnet-dump 收集
dotnet-dump 分析
dotnet-dump ps

dotnet-dump 收集

从进程捕获转储。

摘要

dotnet-dump collect [-h|--help] [-p|--process-id] [-n|--name] [--type] [-o|--output] [--diag] [--crashreport]

选项

  • -h|--help

    显示命令行帮助。

  • -p|--process-id <PID>

    指定从中收集转储的进程的 ID 号。

  • -n|--name <name>

    指定从中收集转储的进程的名称。

  • --type <Full|Heap|Mini>

    指定转储类型,它确定从进程收集的信息的类型。 有三种类型:

    • Full - 最大的转储,包含所有内存(包括模块映像)。
    • Heap - 大型且相对全面的转储,其中包含模块列表、线程列表、所有堆栈、异常信息、句柄信息和除映射图像以外的所有内存。
    • Mini - 小型转储,其中包含模块列表、线程列表、异常信息和所有堆栈。
    • Triage - 包含模块列表、线程列表、异常信息、所有堆栈和 PII 的小型转储。

    如果未指定,则 Full 为默认类型。

  • -o|--output <output_dump_path>

    应在其中写入收集的转储的完整路径和文件名。 确保运行 dotnet 进程的用户对指定目录具有写入权限。

    如果未指定:

    • 默认为 Windows 上的 .\dump_YYYYMMDD_HHMMSS.dmp
    • 在 Linux 和 macOS 上默认为 ./core_YYYYMMDD_HHMMSS。

    YYYYMMDD 为年/月/日,HHMMSS 为小时/分钟/秒。

  • --diag

    启用转储收集诊断日志记录。

  • --crashreport

    启用故障报告生成。

注意

在 Linux 和 macOS 上,此命令需要目标应用程序和 dotnet-dump 使用同一 TMPDIR 环境变量。 否则,该命令将超时。

注意

若要使用 dotnet-dump 收集转储,需要以与运行目标进程的用户相同的用户身份或以根身份运行。 否则,该工具将无法与目标进程建立连接。

注意

收集完整或堆转储可能会导致 OS 在目标进程的大量虚拟内存中分页。 如果目标进程在具有强制内存限制的容器中运行,则增加的内存使用量可能会导致 OS 在超出限制时终止容器。 我们建议进行测试,以确保内存限制设置得足够高。 另一种选择是在转储收集之前暂时更改或删除限制(如果环境支持这样做)。

dotnet-dump 分析

启动交互式 shell 以了解转储。 shell 接受各种 SOS 命令

摘要

dotnet-dump analyze <dump_path> [-h|--help] [-c|--command]

自变量

  • <dump_path>

    指定要分析的转储文件的路径。

选项

  • -c|--command <debug_command>

    在启动时运行该命令。 此参数的多个实例可用于调用链接命令。 命令将按命令行上提供的顺序运行。 如果希望 dotnet 转储在命令后退出,则上一个命令应为“exit”。

分析 SOS 命令

命令 函数
analyzeoom 显示对 GC 堆进行分配请求时发生的最后 OOM 的信息。
clrmodules 列出进程中的托管模块。
clrstack 仅提供托管代码的堆栈跟踪。
clrthreads 列出正在运行的托管线程。
clru 显示托管方法的批注反汇编。
dreadmemory 转储内存内容。
dbgout 启用/禁用 (-off) 内部 SOS 日志记录。
dso 显示在当前堆栈的边界内找到的所有托管对象。
dumpalc 显示有关指定对象加载到的可回收 AssemblyLoadContext 的详细信息。
dumparray 显示有关托管数组的详细信息。
dumpasync 显示有关垃圾回收堆上异步状态机的信息。
dumpassembly 显示有关程序集的详细信息。
dumpclass 显示有关指定地址处的 EEClass 结构的信息。
dumpconcurrentdictionary 显示并发字典内容。
dumpconcurrentqueue 显示并发队列内容。
dumpdelegate 显示有关委托的信息。
dumpdomain 显示有关所有 AppDomain 或指定 AppDomain 中的所有程序集的信息。
dumpgcdata 显示有关 GC 数据的信息。
dumpgen 显示指定代系的堆内容。
dumpheap 显示有关垃圾回收堆的信息和有关对象的收集统计信息。
dumpil 显示与托管方法关联的公共中间语言(CIL)。
dumplog 将内存中压力日志的内容写入到指定文件。
dumpmd 显示有关指定地址处的 MethodDesc 结构的信息。
dumpmodule 显示有关指定地址处的模块的信息。
dumpmt 显示有关指定地址处的方法表的信息。
dumpobj 显示有关指定地址处的对象的信息。
dumpruntimetypes 查找 GC 堆中的所有 System.RuntimeType 对象,并输出它们引用的类型名称和 MethodTable。
dumpsig 转储由 <sigaddr> <moduleaddr> 指定的方法或字段的签名。
dumpsigelem 转储签名对象的单个元素。
dumpstackobjects 显示在当前堆栈的边界内找到的所有托管对象。
dumpvc 显示有关值类的字段的信息。
eeheap 显示有关内部运行时数据结构所使用的进程内存的信息。
eestack 对进程中的所有线程运行 dumpstack 命令。
eeversion 显示有关运行时和 SOS 版本的信息。
ehinfo 显示 JIT 方法中的异常处理块。
exitquit 退出交互模式。
finalizequeue 显示所有已进行终结注册的对象。
findappdomain 尝试解析 GC 对象的 AppDomain。
gchandles 显示有关进程中的垃圾回收器句柄的统计信息。
gcheapstat 显示有关垃圾回收器的统计信息。
gcinfo 显示方法的 JIT GC 编码。
gcroot 显示有关对指定地址处的对象的引用(或根)的信息。
gcwhere 显示指定地址在 GC 堆中的位置。
histclear 释放由 Hist 命令系列使用的任何资源。
histinit 从保存在调试对象中的压力日志初始化 SOS 结构。
histobj 检查所有压力日志的重定位记录,并显示可能已将地址作为自变量传入的垃圾回收重定位链。
histobjfind 显示在指定地址处引用对象的所有日志项。
histroot 显示与指定根的提升和重定位相关的信息。
histstats 显示压力日志统计信息。
ip2md 显示已 JIT 编译的代码中指定地址处的 MethodDesc 结构。
listnearobj 显示指定地址前后的对象。
logopen 启用控制台文件日志记录。
logclose 禁用控制台文件日志记录。
logging 启用/禁用内部 SOS 日志记录。
lmmodules 显示进程中的本机模块。
name2ee 显示指定模块中的指定类型或方法的 MethodTableEEClass 结构。
objsize 显示指定对象的大小。
parallelstacks 显示与Visual Studio“并行堆栈”面板类似的合并线程堆栈。
pathto 显示从 <root><target> 的 GC 路径。
peprintexception 显示从指定地址处的 Exception 类派生的任何对象的字段并设置这些字段的格式。
rregisters 显示线程的寄存器。
runtimes 列出目标中的运行时或更改默认运行时。
setclrpath 使用 setclrpath <path> 设置加载 coreclr dac/dbi 文件的路径。
setsymbolserver 启用符号服务器支持。
sos 执行各种 coreclr 调试命令。 使用语法 sos <command-name> <args>。 有关详细信息,请参阅“soshelp”。
soshelphelp 显示所有可用命令。
soshelp <command>help <command> 执行指定的命令。
syncblk 显示 SyncBlock 持有者信息。
taskstate 以人类可读格式显示任务状态。
threadpool 显示有关运行时线程池的信息。
threadpoolqueue 显示排队的线程池工作项。
threadstate 整齐输出线程状态的含义。
threads <threadid>setthread <threadid> 设置或显示 SOS 命令的当前线程 ID。
timerinfo 显示有关正在运行的计时器的信息。
token2ee 显示指定令牌和模块的 MethodTable 结构和 MethodDesc 结构。
traverseheap 以 CLR 探查器能够理解的格式将堆信息写入文件。
verifyheap 检查 GC 堆是否有损坏的迹象。
verifyobj 检查作为自变量传递的对象是否有损坏迹象。

注意

有关其他详细信息,请参阅 SOS 调试扩展.NET

dotnet-dump ps

列出可以从中收集转储的 dotnet 进程。 dotnet-dump 版本 6.0.320703 及更高版本还显示每个进程的启动命令行参数(如果可用)。

摘要

dotnet-dump ps [-h|--help]

示例

假设使用命令 dotnet run --configuration Release 启动长时间运行的应用。 在另一个窗口中,运行 dotnet-dump ps 命令。 你将看到如下输出。 命令行参数(如果有)显示在 dotnet-dump 版本 6.0.320703 及更高版本中。

> dotnet-dump ps

  21932 dotnet     C:\Program Files\dotnet\dotnet.exe   run --configuration Release
  36656 dotnet     C:\Program Files\dotnet\dotnet.exe

使用 dotnet-dump

第一步是收集转储。 如果已生成核心转储,则可以跳过此步骤。 作系统或 .NET Core 运行时的内置 dump 生成功能可以创建核心转储。

$ dotnet-dump collect --process-id 1902
Writing minidump to file ./core_20190226_135837
Written 98983936 bytes (24166 pages) to core file
Complete

现在,使用 analyze 命令分析核心转储:

$ dotnet-dump analyze ./core_20190226_135850
Loading core dump: ./core_20190226_135850
Ready to process analysis commands. Type 'help' to list available commands or 'help [command]' to get detailed help on a command.
Type 'quit' or 'exit' to exit the session.
>

此操作会显示一个交互式会话,该会话接受以下类似命令:

> clrstack
OS Thread Id: 0x573d (0)
    Child SP               IP Call Site
00007FFD28B42C58 00007fb22c1a8ed9 [HelperMethodFrame_PROTECTOBJ: 00007ffd28b42c58] System.RuntimeMethodHandle.InvokeMethod(System.Object, System.Object[], System.Signature, Boolean, Boolean)
00007FFD28B42DD0 00007FB1B1334F67 System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo) [/root/coreclr/src/mscorlib/src/System/Reflection/RuntimeMethodInfo.cs @ 472]
00007FFD28B42E20 00007FB1B18D33ED SymbolTestApp.Program.Foo4(System.String) [/home/mikem/builds/SymbolTestApp/SymbolTestApp/SymbolTestApp.cs @ 54]
00007FFD28B42ED0 00007FB1B18D2FC4 SymbolTestApp.Program.Foo2(Int32, System.String) [/home/mikem/builds/SymbolTestApp/SymbolTestApp/SymbolTestApp.cs @ 29]
00007FFD28B42F00 00007FB1B18D2F5A SymbolTestApp.Program.Foo1(Int32, System.String) [/home/mikem/builds/SymbolTestApp/SymbolTestApp/SymbolTestApp.cs @ 24]
00007FFD28B42F30 00007FB1B18D168E SymbolTestApp.Program.Main(System.String[]) [/home/mikem/builds/SymbolTestApp/SymbolTestApp/SymbolTestApp.cs @ 19]
00007FFD28B43210 00007fb22aa9cedf [GCFrame: 00007ffd28b43210]
00007FFD28B43610 00007fb22aa9cedf [GCFrame: 00007ffd28b43610]

查看已终止应用的未经处理的异常:

> pe -lines
Exception object: 00007fb18c038590
Exception type:   System.Reflection.TargetInvocationException
Message:          Exception has been thrown by the target of an invocation.
InnerException:   System.Exception, Use !PrintException 00007FB18C038368 to see more.
StackTrace (generated):
SP               IP               Function
00007FFD28B42DD0 0000000000000000 System.Private.CoreLib.dll!System.RuntimeMethodHandle.InvokeMethod(System.Object, System.Object[], System.Signature, Boolean, Boolean)
00007FFD28B42DD0 00007FB1B1334F67 System.Private.CoreLib.dll!System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo)+0xa7 [/root/coreclr/src/mscorlib/src/System/Reflection/RuntimeMethodInfo.cs @ 472]
00007FFD28B42E20 00007FB1B18D33ED SymbolTestApp.dll!SymbolTestApp.Program.Foo4(System.String)+0x15d [/home/mikem/builds/SymbolTestApp/SymbolTestApp/SymbolTestApp.cs @ 54]
00007FFD28B42ED0 00007FB1B18D2FC4 SymbolTestApp.dll!SymbolTestApp.Program.Foo2(Int32, System.String)+0x34 [/home/mikem/builds/SymbolTestApp/SymbolTestApp/SymbolTestApp.cs @ 29]
00007FFD28B42F00 00007FB1B18D2F5A SymbolTestApp.dll!SymbolTestApp.Program.Foo1(Int32, System.String)+0x3a [/home/mikem/builds/SymbolTestApp/SymbolTestApp/SymbolTestApp.cs @ 24]
00007FFD28B42F30 00007FB1B18D168E SymbolTestApp.dll!SymbolTestApp.Program.Main(System.String[])+0x6e [/home/mikem/builds/SymbolTestApp/SymbolTestApp/SymbolTestApp.cs @ 19]

StackTraceString: <none>
HResult: 80131604

分析内存泄漏和分配

当应用保存对不再需要的对象的引用时,将发生内存泄漏,从而阻止垃圾回收器回收内存。 用于 dotnet-dump 识别内存泄漏、查找最大对象,并了解内存的使用情况。

有关调试内存泄漏的完整演练,请参阅 Debug .NET

标识最大的对象

使用 dumpheap 包含选项的 -stat 命令可查看堆上对象的摘要,按总大小排序:

> dumpheap -stat

Statistics:
              MT    Count    TotalSize Class Name
00007f6c1eeefba8      576        59904 System.Reflection.RuntimeMethodInfo
00007f6c1dc021c8     1749        95696 System.SByte[]
00000000008c9db0     3847       116080      Free
00007f6c1e784a18      175       128640 System.Char[]
00007f6c1dbf5510      217       133504 System.Object[]
00007f6c1dc014c0      467       416464 System.Byte[]
00007f6c21625038        6      4063376 testwebapi.Controllers.Customer[]
00007f6c20a67498   200000      4800000 testwebapi.Controllers.Customer
00007f6c1dc00f90   206770     19494060 System.String
Total 428516 objects

此输出显示哪些类型消耗最多的内存。 在此示例中, System.String 对象消耗约 19 MB,对象 Customer 消耗约 4.8 MB。

按命名空间或程序集标识对象

若要查找哪些模块或命名空间占用内存,请使用 -type 具有部分类型名称的选项来筛选结果:

> dumpheap -type MyCompany.Data -stat

Statistics:
              MT    Count    TotalSize Class Name
00007f6c21625038    15000      3600000 MyCompany.Data.CustomerRecord
00007f6c21625040     8000      2560000 MyCompany.Data.OrderHistory
00007f6c21625048     2000       960000 MyCompany.Data.ProductCache
Total 25000 objects, 7120000 bytes

此方法可帮助你确定代码库哪些部分负责内存消耗。

查找最大实例化数

若要查看哪些类型具有最多实例,而不考虑总大小,请查看输出中的dumpheap -stat“计数”列。 实例计数较高的对象可能表示创建或缓存问题效率低下:

> dumpheap -stat

Statistics:
              MT    Count    TotalSize Class Name
00007f6c1dc00f90   206770     19494060 System.String
00007f6c20a67498   200000      4800000 testwebapi.Controllers.Customer
00007f6c1dc021c8     1749        95696 System.SByte[]

此示例显示 206,770 String 个实例和 200,000 Customer 个实例。

使用 gcroot 分析对象引用

识别大型或大量对象后,用于 gcroot 找出为什么对象未被垃圾回收。 该 gcroot 命令显示从 GC 根到特定对象的引用链:

> dumpheap -mt 00007f6c20a67498
         Address               MT     Size
00007f6ad09421f8 00007f6c20a67498       24
...

> gcroot 00007f6ad09421f8

Thread 3f68:
    00007F6795BB58A0 00007F6C1D7D0745 testwebapi.Controllers.CustomerCache.GetAll()
        rbx:  (interior)
            ->  00007F6BDFFFF038 System.Object[]
            ->  00007F69D0033570 testwebapi.Controllers.Processor
            ->  00007F69D0033588 testwebapi.Controllers.CustomerCache
            ->  00007F69D00335A0 System.Collections.Generic.List`1[[testwebapi.Controllers.Customer]]
            ->  00007F6C000148A0 testwebapi.Controllers.Customer[]
            ->  00007F6AD0942258 testwebapi.Controllers.Customer

Found 1 root.

此输出显示对象 Customer 由对象 CustomerCache 保存,这有助于识别代码中泄漏的来源。

按对象大小分析内存

-min使用和-max选项按大小筛选对象:

> dumpheap -min 100000 -stat

Statistics:
              MT    Count    TotalSize Class Name
00007f6c21625038        6      4063376 testwebapi.Controllers.Customer[]
00007f6c1dc014c0       12       416464 System.Byte[]
Total 18 objects

此命令仅显示大于 100,000 字节的对象,帮助你专注于最大的内存使用者。

查找死锁

用于 dotnet-dump 诊断线程被阻止等待资源的死锁情况。 有关完整的死锁调试演练,请参阅 debug .NET中的死锁。

列出所有线程

threads使用命令查看所有托管线程:

> threads
*0 0x1DBFF (121855)
 1 0x1DC01 (121857)
 2 0x1DC02 (121858)
 ...

检查线程堆栈

用于 clrstack -all 查看所有线程的调用堆栈:

> clrstack -all

查找多个线程在或类似同步基元上 Monitor.Enter 被阻止的模式。

查找锁所有者

syncblk使用命令查看哪些线程持有锁以及哪些线程正在等待:

> syncblk
Index         SyncBlock MonitorHeld Recursion Owning Thread Info          SyncBlock Owner
   43 00000246E51268B8          603         1 0000024B713F4E30 5634  28   00000249654b14c0 System.Object
   44 00000246E5126908            3         1 0000024B713F47E0 51d4  29   00000249654b14d8 System.Object

MonitorHeld 列显示等待锁定的线程数。 “拥有线程信息”列显示哪个线程拥有锁。

高级内存分析方案

比较多个转储

若要了解一段时间内的内存增长,请收集多个转储并将其进行比较:

  1. 收集基线转储: dotnet-dump collect -p <pid> -o baseline.dmp
  2. 让应用运行和使用更多内存。
  3. 收集第二个转储: dotnet-dump collect -p <pid> -o after.dmp
  4. 分析转储并比较 dumpheap -stat 结果。

查找第二个转储中具有明显更多实例或更大总大小的类型。

分析特定对象类型的内存

若要转储特定类型的所有实例,请执行以下作:

> dumpheap -type Customer
         Address               MT     Size
00007f6ad09421f8 00007f6c20a67498       24
00007f6ad0942210 00007f6c20a67498       24
...

然后,用于 dumpobj 检查各个对象:

> dumpobj 00007f6ad09421f8
Name:        testwebapi.Controllers.Customer
MethodTable: 00007f6c20a67498
EEClass:     00007f6c21625000
Size:        24(0x18) bytes
File:        /app/testwebapi.dll
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
00007f6c1dc00f90  4000001        8        System.String  0 instance 00007f6ad09421f0 Name
00007f6c1dbf4c18  4000002       10         System.Int32  1 instance               42 Id

在 Docker 容器中收集转储

dotnet-dump 需要 ptrace 容器中的功能。 授予它们的一种常见方法是使用 --cap-add=SYS_PTRACE.. 根据环境,可能还需要调整容器的 seccomp 配置文件。 请参阅 转储: 有关诊断容器安全配置问题的常见问题解答。

若要在没有 .NET SDK 的生产映像中安装 dotnet-dump,请使用 install 部分中的 direct 下载链接,或使用 multi-stage Docker build 从 SDK 映像复制工具二进制文件。 有关完整的容器诊断指南,请参阅 在 Linux 容器中收集诊断

排查转储集合问题

转储集合要求进程能够调用 ptrace。 如果收集转储时遇到问题,则运行的环境可能配置为了限制此类调用。 请参阅转储:常见问题解答,了解常见问题的故障排除提示和潜在解决方案。

另请参阅