微软提供了标准的CLR性能分析类库
https://github.com/Microsoft/clrmd
这个类库是开源的代码。能够获取CLR runtime里面几乎所有的信息。
如何获取clrmd编译后的dll
方法一
在nuget里面搜索clrmd可以很方便的下载
方法二
估计有些同学和我一样喜欢手动挡,那么我这里提供一个下载dll的地址。项目里面只要引用这个dll就能对CLR进行分析诊断了。
https://raw.githubusercontent.com/wsq003/clrmd/master/bin/Microsoft.Diagnostics.Runtime.dll
如何分析CPU消耗
原理
1、操作系统能够记录每个os thread的cpu消耗
2、CLR的managed thread和操作系统的os thread会动态的进行映射
3、通过clrmd可以获取每个managed thread的call stack
4、结合123我们可以分析出asp.net里面哪个线程的代码消耗了最多CPU
思路
在asp.net项目里面新增一个‘一般处理程序ashx’页面,比如diagnose.ashx,在这个页面代码里面记录每个os thread的cpu消耗,每个managed thread的call stack,以及os thread和managed thread的对应关系。
反复调用diagnose.ashx,看看最忙的那个os thread是被哪个managed thread占用的。
参考代码
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
{
int pid = System.Diagnostics.Process.GetCurrentProcess().Id;
var msg = GetCallstack(pid);
context.Response.Write(msg);
}
var ths = Process.GetCurrentProcess().Threads;
foreach (ProcessThread th in ths)
{
var msg = string.Format("{0}-{1:0.0}, {2:0.0}, {3:0.0}\r\n",
th.Id, th.TotalProcessorTime.TotalSeconds, th.PrivilegedProcessorTime.TotalSeconds, th.UserProcessorTime.TotalSeconds);
context.Response.Write(msg);
}
}
string GetCallstack(int pid)
{
try
{
using (var dataTarget = DataTarget.AttachToProcess(pid, 5000, AttachFlag.Passive))
{
var runtime = dataTarget.ClrVersions[0].CreateRuntime();
string info = "";
foreach (var t in runtime.Threads)
{
var cs = t.StackTrace.Select(f =>
{
if (f.Method != null)
{
return f.Method.Type.Name + "." + f.Method.Name;
}
return null;
}).ToArray();
if (cs.Length > 0)
{
info += string.Format("系统线程ID:{0} - CLR线程ID{1}:\r\n", t.OSThreadId, t.ManagedThreadId);
foreach (var line in cs)
{
info += string.Format("{0}\r\n", line);
}
info += "\r\n";
}
}
return info;
}
}
catch (Exception ex)
{
return "获取callstack失败:" + ex.Message;
}
}
补充说明
因为.net framework rumtime的版本问题,要用一个单独的工具去attach其他进程做分析,有两个思路:
1、可以参考java世界的jvmti。让被诊断的进程自己暴露一个接口,把clr的各种诊断数据都吐出来。
2、可以参考clr profiler,在界面上指定clr的版本,然后attach到进程去。