.Net Core服务诊断排查


前言:

 近期在项目中出现了几次服务内存资源占用较高的情况,特回顾梳理下排查过程以及对相应问题的排查方法总结。

一、Dump抓取

 抓取dump的方式有多种,下面介绍几种常用的:

  1. 任务管理器中找到程序进程,右键菜单:创建转存储文件

   注意:需要以程序运行的位数运行任务管理器抓取Dump

  2. DotNet 全局工具:dotnet-dump;工具可适用于Windows、Linux、macOS平台

   a) 安装全局dotnet-dump工具:

dotnet tool install --global dotnet-dump

   b) 使用该工具抓取dump:

dotnet-dump collect [-h|--help] [-p|--process-id] [-n|--name] [--type] [-o|--output] [--diag]
    • -h|--help:显示命令行帮助。
    • -p|--process-id :指定从中收集转储的进程的 ID 号。
    • -n|--name :指定从中收集转储的进程的名称。
    • --type :指定转储类型,它确定从进程收集的信息的类型。 有三种类型:Full - 最大的转储,包含所有内存(包括模块映像)。Heap - 大型且相对全面的转储,其中包含模块列表、线程列表、所有堆栈、异常信息、句柄信息和除映射图像以外的所有内存。Mini - 小型转储,其中包含模块列表、线程列表、异常信息和所有堆栈。如果未指定,则 Full 为默认类型。
    • -o|--output :应在其中写入收集的转储的完整路径和文件名。如果未指定:在 Windows 上默认为 .\dump_YYYYMMDD_HHMMSS.dmp ;在 Linux 上默认为 ./core_YYYYMMDD_HHMMSS ;YYYYMMDD 为年/月/日,HHMMSS 为小时/分钟/秒。
    • --diag:启用转储收集诊断日志记录。

  3、ProcDump抓取Dump工具:自动抓取

    命令语法:

procdump.exe [-mm] [-ma] [-mp] [-mc Mask] [-md Callback_DLL] [-mk]
             [-n Count]
             [-s Seconds]
             [-c|-cl CPU_Usage [-u]]
             [-m|-ml Commit_Usage]
             [-p|-pl Counter_Threshold]
             [-h]
             [-e [1 [-g] [-b]]]
             [-l]
             [-t]
             [-f  Include_Filter, ...]
             [-fx Exclude_Filter, ...]
             [-o]
             [-r [1..5] [-a]]
             [-at Timeout]
             [-wer]
             [-64]
             {
                 {{[-w] Process_Name | Service_Name | PID} [Dump_File | Dump_Folder]}
             |
                 {-x Dump_Folder Image_File [Argument, ...]}
             }

   参数说明:    

参数说明
-a 避免中断。 需要 -r。 如果触发器会导致目标因超出并发转储限制而长时间挂起,将跳过触发器。
-at 避免超时中断。 在 N 秒时取消触发器的集合。
-b 将调试断点视为异常 (否则忽略它们) 。
-c CPU 阈值,用于创建进程的转储。
-cl CPU 阈值,低于该阈值可创建进程的转储。
-d 调用指定 DLL 的名为 MiniDumpCallbackRoutine 的小型转储回调例程。
-e 当进程遇到未经处理异常时编写转储。 包括 1,以在出现第一机会异常时创建转储。
-f 筛选第一个可能异常。 支持通配符 (*) 。 若要仅显示名称而不进行转储,请使用空白 ("") 筛选器。
-fx 筛选 (排除) 异常内容和调试日志记录。 支持通配符。
-g 在托管进程中作为本机调试器运行, (互操作) 。
-h 如果进程有挂起的窗口, (在至少 5 秒未响应窗口消息时写入) 。
-i 将 ProcDump 安装为 AeDebug 事后调试器。 仅支持使用 -ma、-mp、-d 和 -r 作为附加选项。
-k 克隆到 (-r) 或转储收集结束时终止进程
-l 显示进程的调试日志记录。
-m 内存提交阈值(以 MB 为单位)用于创建转储。
-ma 编写包含所有进程内存的转储文件。 默认转储格式仅包含线程和处理信息。
-mc 编写自定义转储文件。 包括由指定的十六进制MINIDUMP_TYPE掩码 (内存) 。
-md 编写回调转储文件。 包括由指定 DLL 的名为 MiniDumpCallbackRoutine 的 MiniDumpWriteDump 回调例程定义的内存。
-mk 同时编写内核转储文件。 包括进程中线程的内核堆栈。 使用克隆 (-r) 时,OS 不支持内核转储 (-mk) 。 使用多个转储大小时,会针对每个转储大小执行内核转储。
-ml 当内存提交低于指定的 MB 值时触发。
-mm 使用默认模式编写 (转储) 。
-mp 使用线程和句柄信息以及所有读/写进程内存编写转储文件。 为了最大程度地减小转储大小,将搜索大于 512MB 的内存区域,如果找到,则排除最大的区域。 内存区域是大小相同的内存分配区域的集合。 删除此内存 (缓存) 将Exchange和SQL Server转储减少 90% 以上。
-n 退出前要写入的转储数。
-o 覆盖现有的转储文件。
-p 超过阈值时,对指定性能计数器触发。 注意:若要在进程有多个实例运行时指定进程计数器,请使用具有以下语法的进程 ID:"\Process (< name > _ < pid >) \counter"
-pl 当性能计数器低于指定值时触发。
-r 使用克隆进行转储。 并发限制是可选的 (默认为 1,最大为 5) 。
警告:高并发值可能会影响系统性能。
- Windows 7:使用反射。 OS 不支持 -e。
- Windows 8.0:使用反射。 OS 不支持 -e。
- Windows 8.1+:使用 PSS。 支持所有触发器类型。
-s 写入转储之前连续秒 (默认值为 10) 。
-t 在进程终止时写入转储。
-u 将 CPU 使用率视为与 -c (一) 。
作为唯一选项,卸载 ProcDump 作为事后调试器。
-w 等待指定的进程启动(如果该进程未运行)。
-wer 将 (最大) 排队到Windows 错误报告。
-x 使用可选参数启动指定的映像。 如果是应用商店应用程序或包,ProcDump 将在下次激活时启动, (激活) 。
-64 默认情况下,在 64 位进程上运行时,ProcDump 将捕获 32 位进程的 32 位Windows。 此选项将替代 以创建 64 位转储。 仅用于 WOW64 子系统调试。
-? 使用 -? -e 查看示例命令行。

   示例:

    当进程在 5 秒钟内 CPU 使用率超过 50% 时,最多写入 2 个名为"w3wp"的进程的微型转储:

procdump -ma -s 5 -n 2 -c 50 w3wp

二、Dump分析

  根据前面几方式得到的Dump文件,下一步就是对Dump进行分析确认问题原因:

 1、Windbug Preview 分析:

  Windbg Preview 是windows平台上的一款相当强大的调试工具,可以从msdn网站下载得到,最新版本包含在windows sdk中,默认会被安装在C:\Program Files\Debugging Tools for Windows 目录中,可以直接把这个目录打包复制到其它机器上使用。

  Windbug常用命令:

    • !analyze -v    自动分析dump
    • kv    查看栈回溯
    • .ecxr    显示当前异常上下文
    • .cxr    切换异常帧上下文
    • .exr    显示异常信息
    • .frame    设置当前栈帧
    • dv    显示当前栈帧局部变量
    • dd    显示内存中的数据
    • r    查看寄存器
    • lmvm    查看模块详细信息
    • r    可以显示系统崩溃时的寄存器,和最后的命令状态
    • dd    显示当前内存地址,dd 参数:显示参数处的内存
    • u    可以显示反汇编的指令
    • kb    显示call stack 内容

 2、VS分析Dump:

  a) 使用VS打开dump文件:

 可以看到该dump的基本信息,接下来设置符号文件:

   

b) 分析Dump中内存情况:调试托管内存

 

    可以看到当前托管资源中内存占用情况,如果有多个dump还可以对比dump中内存增长变化

   查看对象内容:

c) 查看当前运行的进程列表:(设置了符号文件后可以查看当前堆栈信息,以及跳转到代码逻辑)

  

 3、dotnet-dump分析:

  分析命令:

dotnet-dump analyze  [-h|--help] [-c|--command]

  SOS命令支持:

命令函数
soshelp|help 显示所有可用命令
soshelp|help 执行指定的命令。
exit|quit 退出交互模式。
clrstack 仅提供托管代码的堆栈跟踪。
clrthreads 列出正在运行的托管线程。
dumpasync 显示有关垃圾回收堆上异步状态机的信息。
dumpassembly 显示有关指定地址处程序集的详细信息。
dumpclass 显示有关指定地址处的 EEClass 结构的信息。
dumpdelegate 显示有关指定地址处的委托的信息。
dumpdomain 显示所有 AppDomain 和指定域中的所有程序集的信息。
dumpheap 显示有关垃圾回收堆的信息和有关对象的收集统计信息。
dumpil 显示与托管方法关联的 Microsoft 中间语言 (MSIL)。
dumplog 将内存中压力日志的内容写入到指定文件。
dumpmd 显示有关指定地址处的 MethodDesc 结构的信息。
dumpmodule 显示有关指定地址处的模块的信息。
dumpmt 显示有关指定地址处的 MethodTable 的信息。
dumpobj 显示有关位于指定地址处的对象的信息。
dso|dumpstackobjects 显示在当前堆栈的边界内找到的所有托管对象。
eeheap 显示有关内部运行时数据结构所使用的进程内存的信息。
finalizequeue 显示所有已进行终结注册的对象。
gcroot 显示有关对指定地址处的对象的引用(或根)的信息。
gcwhere 显示传入参数在 GC 堆中的位置。
ip2md 显示 JIT 代码中指定地址处的 MethodDesc 结构。
histclear 释放由 hist* 命令系列使用的任何资源。
histinit 从保存在调试对象中的压力日志初始化 SOS 结构。
histobj 显示与  相关的垃圾回收压力日志重定位。
histobjfind 显示在指定地址处引用对象的所有日志项。
histroot 显示与指定根的提升和重定位相关的信息。
lm|modules 显示进程中的本机模块。
name2ee 显示  的 MethodTable 和 EEClass 结构。
pe|printexception 显示从 Exception 类派生的  的任何对象。
setsymbolserver 启用符号服务器支持
syncblk 显示 SyncBlock 持有者信息。
threads|setthread 设置或显示 SOS 命令的当前线程 ID。

   例如:

dotnet-dump analyze "w3wp (2).DMP"

三、案例:

  问题现象:xxx服务请求响应耗时较长,导致业务无法正常开展

  排查过程:

   1、查看内存情况,Oracle连接数量较大

   2、查看线程中都为Oracle连接等待:

    3、最终确认请求被卡到了Oracle处理

四、总结

  1、在排查服务相关问题时,分析dump方式是最直接的方式;而vs方式分析dump是最直接、方便、简单的方式。

  2、通过windbug分析dump是最完整的方式

参考:

  windbg命令