PerfView专题 (第十二篇):对 C# 下的 SDK 类库进行监控(大结局)
一:背景
本篇是我们系列文章的最后一篇,前面的文章中大多是在 CLR Runtime
以及 OS
层面进行监控来发现各种可疑的程序问题,除了这两个层面,其实我们还可以对 SDK 中一些类进行洞察,比如说:
-
ArrayPool
-
Http
-
Socket
-
Task
更多资料可以看下:https://docs.microsoft.com/en-us/dotnet/core/diagnostics/well-known-event-providers
接下来就来个简单的抛砖引玉
二:如何洞察
1. ArrayPool 监控
之所以对 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 所在的代码,截图如下:
2. Http 监控
对 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()
方法触发的,截图如下:
3. 总结
总的来说,在 .NET 调试领域,让 PerfView 适当的配合 WinDbg,真的可以 如虎添翼 ,好了,本系列就先写到这里,感谢朋友们对本系列的持续关注。