.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 <PID>:指定從中收集轉儲的進程的 ID 號。
    • -n|–name <name>:指定從中收集轉儲的進程的名稱。
    • –type <Full|Heap|Mini>:指定轉儲類型,它確定從進程收集的信息的類型。 有三種類型:Full – 最大的轉儲,包含所有內存(包括模塊映像)。Heap – 大型且相對全面的轉儲,其中包含模塊列表、線程列表、所有堆棧、異常信息、句柄信息和除映射圖像以外的所有內存。Mini – 小型轉儲,其中包含模塊列表、線程列表、異常信息和所有堆棧。如果未指定,則 Full 為默認類型。
    • -o|–output <output_dump_path>:應在其中寫入收集的轉儲的完整路徑和文件名。如果未指定:在 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 <dump_path> [-h|--help] [-c|--command]

  SOS命令支持:

命令 函數
soshelp|help 顯示所有可用命令
soshelp|help <command> 執行指定的命令。
exit|quit 退出交互模式。
clrstack <arguments> 僅提供託管代碼的堆棧跟蹤。
clrthreads <arguments> 列出正在運行的託管線程。
dumpasync <arguments> 顯示有關垃圾回收堆上異步狀態機的信息。
dumpassembly <arguments> 顯示有關指定地址處程序集的詳細信息。
dumpclass <arguments> 顯示有關指定地址處的 EEClass 結構的信息。
dumpdelegate <arguments> 顯示有關指定地址處的委託的信息。
dumpdomain <arguments> 顯示所有 AppDomain 和指定域中的所有程序集的信息。
dumpheap <arguments> 顯示有關垃圾回收堆的信息和有關對象的收集統計信息。
dumpil <arguments> 顯示與託管方法關聯的 Microsoft 中間語言 (MSIL)。
dumplog <arguments> 將內存中壓力日誌的內容寫入到指定文件。
dumpmd <arguments> 顯示有關指定地址處的 MethodDesc 結構的信息。
dumpmodule <arguments> 顯示有關指定地址處的模塊的信息。
dumpmt <arguments> 顯示有關指定地址處的 MethodTable 的信息。
dumpobj <arguments> 顯示有關位於指定地址處的對象的信息。
dso|dumpstackobjects <arguments> 顯示在當前堆棧的邊界內找到的所有託管對象。
eeheap <arguments> 顯示有關內部運行時數據結構所使用的進程內存的信息。
finalizequeue <arguments> 顯示所有已進行終結註冊的對象。
gcroot <arguments> 顯示有關對指定地址處的對象的引用(或根)的信息。
gcwhere <arguments> 顯示傳入參數在 GC 堆中的位置。
ip2md <arguments> 顯示 JIT 代碼中指定地址處的 MethodDesc 結構。
histclear <arguments> 釋放由 hist* 命令系列使用的任何資源。
histinit <arguments> 從保存在調試對象中的壓力日誌初始化 SOS 結構。
histobj <arguments> 顯示與 <arguments> 相關的垃圾回收壓力日誌重定位。
histobjfind <arguments> 顯示在指定地址處引用對象的所有日誌項。
histroot <arguments> 顯示與指定根的提升和重定位相關的信息。
lm|modules 顯示進程中的本機模塊。
name2ee <arguments> 顯示 <argument> 的 MethodTable 和 EEClass 結構。
pe|printexception <arguments> 顯示從 Exception 類派生的 <argument> 的任何對象。
setsymbolserver <arguments> 啟用符號服務器支持
syncblk <arguments> 顯示 SyncBlock 持有者信息。
threads|setthread <threadid> 設置或顯示 SOS 命令的當前線程 ID。

   例如:

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

三、案例:

  問題現象:xxx服務請求響應耗時較長,導致業務無法正常開展

  排查過程:

   1、查看內存情況,Oracle連接數量較大

    

   2、查看線程中都為Oracle連接等待:

    

    3、最終確認請求被卡到了Oracle處理

四、總結

  1、在排查服務相關問題時,分析dump方式是最直接的方式;而vs方式分析dump是最直接、方便、簡單的方式。

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

參考:

  windbg命令