本篇是我們系列文章的最後一篇,前面的文章中大多是在 CLR Runtime
以及 OS
層面進行監控來發現各種可疑的程式問題,除了這兩個層面,其實我們還可以對 SDK 中一些類進行洞察,比如說:
ArrayPool
Http
Socket
Task
更多資料可以看下:https://docs.microsoft.com/en-us/dotnet/core/diagnostics/well-known-event-providers
接下來就來個簡單的拋磚引玉
之所以對 ArrayPool 感興趣,主要還是因為在分析 Dump 的過程中,遇到過幾起 LOH 碎片化問題,比如使用第三方模板生成引擎生成 Html 導致大量臨時性 char[]
, byte[]
,終導致 LOH 破敗不堪,所以最後給出的建議是使用這種池化的 ArrayPool
,如果可以監控池的租借情況,那是不是挺好的? 哈哈,還真有這樣的 ETW,截圖如下:
為了方便講解,先上一段簡單的測試程式碼:
internal class Program
{
static void Main(string[] args)
{
var shared = ArrayPool<int>.Shared;
var rentedArray = shared.Rent(10);
for (int i = 0; i < 10; i++)
{
rentedArray[i] = i + 1;
}
for (int j = 0; j < 10; j++)
{
Console.WriteLine(rentedArray[j]);
}
shared.Return(rentedArray);
Console.ReadKey();
}
}
接下來啟動 Perfview,在 Additional Providers 上輸入:
*System.Buffers.ArrayPoolEventSource:::@StacksEnabled=true
然後開啟 Start Collection
觀察 Array 的租借情況,稍等片刻後,在 Event 中搜尋 ArrayPool
可以看到相關的 ETW 事件,截圖如下:
在 Rent
列的 bufferSize="16"
中可知,當前租借了一個 size=16
的陣列。
HasStack="True" ThreadID="15,060" ProcessorNumber="10" bufferId="32,854,180" bufferSize="16" poolId="27,252,167" bucketId="-1"
因為開啟了 Stack
功能,可以在 Time MSec
列上右鍵選擇 Open Any Stacks
,在彈窗中可以輕鬆找到這個 rent 所在的程式碼,截圖如下:
對 Http 的監控也是由於最近遇到了一個比較頭疼的 dump 有感而發的,一個朋友的 dump 出現了 cpu 100% 的情況,我分析下來發現是程式在短時間內出現了大量的 Http Exception,進一步排查懷疑是 sdk 裡面的異常,由於被吞了所以上層獲取不到,也就找不到是第三方 sdk 哪裡的程式碼塊出的問題。
這裡的找不到或者很難找到是在 WinDBG 場景下,其實藉助 PerfView 還是比較好發現的,途徑就是開啟 System.Net.Http
ETW 事件,它內建了 14 個,太強大了,截圖如下:
為了方便講述,先上一段測試程式碼。
internal class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
{
GetString();
}
Console.ReadLine();
}
static async void GetString()
{
try
{
HttpClient client = new HttpClient();
var html = await client.GetStringAsync("https://cnblogs1.com");
Console.WriteLine(html);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
這段程式碼會丟擲異常,然後在 catch 中給吞掉了,因為拋了異常,就可以觀察它的 RequestFailed
事件,然後找到對應的 RequestStart
事件,再觀察它的呼叫棧即可。
接下來在 PerfView 中設定 *System.Net.Http:::@StacksEnabled=true
,再開啟收集按鈕,稍等片刻點選 Event
面板,蒐集 Http
事件,截圖如下:
從面板中可以清晰的觀察到當前有 5
個請求失敗,並且還帶了關聯的 ActivityID
, 接下來可以找 ActivityID=/#18920/1/29/
對應的 Request/Start
事件。
然後在 Time MSec
列上右鍵點選 Open Any Stacks
按鈕,可以輕鬆的看到,那個 Request/Start
事件是 GetString()
方法觸發的,截圖如下:
總的來說,在 .NET 偵錯領域,讓 PerfView 適當的配合 WinDbg,真的可以 如虎添翼 ,好了,本系列就先寫到這裡,感謝朋友們對本系列的持續關注。