.NET Core 服务诊断工具
前言:
前一篇文中介绍了.NET Core-全局性能诊断工具 的使用方法,那么接下来自己实现一个简单.NET Core的诊断工具。
该工具主要包括:.NET Core 程序进程信息查看、性能计数器结果获取、Dump抓取、Trace 文件生成等一些基本功能
本文主要采用:Microsoft.Diagnostics.NETCore.Client 库来实现相关功能
一、Microsoft.Diagnostics.NETCore.Client 介绍:
简介:
Microsoft.Diagnostics.NETCore.Client(也称为Diagnostics客户端库)是一个托管库,可让您与.NET Core运行时(Core CLR)进行交互以执行各种与诊断相关的任务,
例如:跟踪,请求转储或附加ICorProfiler 。使用此库,您可以编写针对特定情况定制的自己的诊断工具。
安装方式:
Install-Package Microsoft.Diagnostics.NETCore.Client
二、工具实现:
1、创建项目:DiagnosticsTools(.NET 5.0 Winform项目)
添加包引用:
Install-Package Microsoft.Diagnostics.NETCore.Client
Install-Package Microsoft.Diagnostics.Tracing.TraceEvent
项目结构:

调整窗体界面如下:

2、获取当前所有.Net Core 3.0及以上的进程列表
/// <summary>
/// 获取进程状态:.Net Core 3.0及以上进程
/// </summary>
private void PrintProcessStatus()
{
//定位上次记录
int row = dgvPros.CurrentCell == null ? 0 : dgvPros.CurrentCell.RowIndex;
int col = dgvPros.CurrentCell == null ? 0 : dgvPros.CurrentCell.ColumnIndex;
var data = DiagnosticsClient.GetPublishedProcesses()
.Select(Process.GetProcessById)
.Where(process => process != null)
.Select(o => { return new { o.Id, o.ProcessName, o.StartTime, o.Threads.Count }; }); dgvPros.DataSource = data.ToList();
if (dgvPros.Rows.Count > row)
dgvPros.CurrentCell = dgvPros.Rows[row].Cells[col];
}
3、获取当前进程基本信息:
private string GetProInfo(Process info)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("进程影象名:" + info.ProcessName + "\r\n");
stringBuilder.Append("进程ID:" + info.Id + "\r\n");
stringBuilder.Append("启动线程树:" + info.Threads.Count.ToString() + "\r\n");
stringBuilder.Append("CPU占用时间:" + info.TotalProcessorTime.ToString() + "\r\n");
stringBuilder.Append("线程优先级:" + info.PriorityClass.ToString() + "\r\n");
stringBuilder.Append("启动时间:" + info.StartTime.ToLongTimeString() + "\r\n");
stringBuilder.Append("专用内存:" + (info.PrivateMemorySize64 / 1024).ToString() + "K" + "\r\n");
stringBuilder.Append("峰值虚拟内存:" + (info.PeakVirtualMemorySize64 / 1024).ToString() + "K" + "\r\n");
stringBuilder.Append("峰值分页内存:" + (info.PeakPagedMemorySize64 / 1024).ToString() + "K" + "\r\n");
stringBuilder.Append("分页系统内存:" + (info.PagedSystemMemorySize64 / 1024).ToString() + "K" + "\r\n");
stringBuilder.Append("分页内存:" + (info.PagedMemorySize64 / 1024).ToString() + "K" + "\r\n");
stringBuilder.Append("未分页系统内存:" + (info.NonpagedSystemMemorySize64 / 1024).ToString() + "K" + "\r\n");
stringBuilder.Append("物理内存:" + (info.WorkingSet64 / 1024).ToString() + "K" + "\r\n");
stringBuilder.Append("虚拟内存:" + (info.VirtualMemorySize64 / 1024).ToString() + "K");
return stringBuilder.ToString();
}
4、监听进程相关事件(CLR、Dynamic、Kernel等):
/// <summary>
/// 进程运行事件输出:CLR、性能计数器、动态处理(cpu使用率超过90%则抓取dump)
/// </summary>
/// <param name="processId">进程id</param>
/// <param name="threshold">cpu使用率</param>
private void PrintRuntime(int processId, int threshold = 90)
{
if (!diagnosticsCache.ContainsKey(processId))
{
var providers = new List<EventPipeProvider>()
{
new EventPipeProvider("Microsoft-Windows-DotNETRuntime",EventLevel.Informational, (long)ClrTraceEventParser.Keywords.Default),
//性能计数器:间隔时间为1s
new EventPipeProvider("System.Runtime",EventLevel.Informational,(long)ClrTraceEventParser.Keywords.None,
new Dictionary<string, string>() {{ "EventCounterIntervalSec", "1" }})
}; DiagnosticsClient client = new DiagnosticsClient(processId);
diagnosticsCache[processId] = client;
using (EventPipeSession session = client.StartEventPipeSession(providers, false))
{
var source = new EventPipeEventSource(session.EventStream); source.Clr.All += (TraceEvent obj) =>
{
if (dgvPros.CurrentRow != null && obj.ProcessID.Equals(dgvPros.CurrentRow.Cells[0].Value))
{
string msg = $"Clr-{obj.EventName}-";
if (obj.PayloadNames.Length > 0)
{
foreach (var item in obj.PayloadNames)
msg += $"{item}:{ obj.PayloadStringByName(item)}-";
}
TextAppendLine(msg);
}
};
source.Dynamic.All += (TraceEvent obj) =>
{
if (dgvPros.CurrentRow != null && obj.ProcessID.Equals(dgvPros.CurrentRow.Cells[0].Value))
{
string msg = $"Dynamic-{obj.EventName}-{string.Join("|", obj.PayloadNames)}";
//性能计数器事件
if (obj.EventName.Equals("EventCounters"))
{
var payloadFields = (IDictionary<string, object>)(obj.PayloadByName(""));
if (payloadFields != null)
payloadFields = payloadFields["Payload"] as IDictionary<string, object>; if (payloadFields != null)
{
msg = $"Dynamic-{obj.EventName}-{payloadFields["DisplayName"]}:{payloadFields["Mean"]}{payloadFields["DisplayUnits"]}";
TextAppendLine(msg);
}
//如果CPU使用率超过90%抓取dump
if (payloadFields != null && payloadFields["Name"].ToString().Equals("cpu-usage"))
{
double cpuUsage = Double.Parse(payloadFields["Mean"].ToString());
if (cpuUsage > (double)threshold)
{
client.WriteDump(DumpType.Normal, "/tmp/minidump.dmp");
}
}
}
else
{
if (obj.PayloadNames.Length > 0)
{
foreach (var item in obj.PayloadNames)
msg += $"{item}:{ obj.PayloadStringByName(item)}-";
}
TextAppendLine(msg);
}
}
};
source.Kernel.All += (TraceEvent obj) =>
{
if (dgvPros.CurrentRow != null && obj.ProcessID.Equals(dgvPros.CurrentRow.Cells[0].Value))
{
string msg = $"Kernel-{obj.EventName}-{string.Join("|", obj.PayloadNames)}";
TextAppendLine(msg);
}
}; try
{
source.Process();
}
catch (Exception e)
{
string errorMsg = $"错误:{e}";
TextAppendLine(errorMsg);
}
}
}
}
5、Dump抓取功能:
/// <summary>
/// 抓取Dmp文件
/// </summary>
/// <param name="processId"></param>
private void TriggerCoreDump(int processId)
{
saveFileDialog1.Filter = "Dump文件|*.dmp";
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
var client = new DiagnosticsClient(processId);
//Normal = 1,WithHeap = 2,Triage = 3,Full = 4
client.WriteDump(DumpType.Normal, saveFileDialog1.FileName, false);
}
}
参数说明:
转储类型。
Normal:仅包括捕获进程中所有现有线程的所有现有跟踪的堆栈跟踪所需的信息。有限的GC堆内存和信息。WithHeap:包括GC堆和捕获进程中所有现有线程的堆栈跟踪所必需的信息。Triage:仅包括捕获进程中所有现有线程的所有现有跟踪的堆栈跟踪所需的信息。有限的GC堆内存和信息。Full:在此过程中包括所有可访问的内存。原始内存数据包含在末尾,因此可以直接映射初始结构,而无需原始内存信息。此选项可能会导致非常大的转储文件。
6、生成进程指定事件内Trace文件
/// <summary>
/// 写入Trace文件
/// </summary>
/// <param name="processId">进程ID</param>
/// <param name="duration">指定时间范围(单位s)</param>
private void TraceProcessForDuration(int processId, int duration)
{
saveFileDialog1.Filter = "Nettrace文件|*.nettrace";
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
var cpuProviders = new List<EventPipeProvider>()
{
new EventPipeProvider("Microsoft-Windows-DotNETRuntime", EventLevel.Informational, (long)ClrTraceEventParser.Keywords.Default),
new EventPipeProvider("Microsoft-DotNETCore-SampleProfiler", EventLevel.Informational, (long)ClrTraceEventParser.Keywords.None)
};
var client = new DiagnosticsClient(processId);
using (var traceSession = client.StartEventPipeSession(cpuProviders))
{
Task copyTask = Task.Run(async () =>
{
using (FileStream fs = new FileStream(saveFileDialog1.FileName, FileMode.Create, FileAccess.Write))
{
await traceSession.EventStream.CopyToAsync(fs);
}
}); copyTask.Wait(duration * 1000);
traceSession.Stop();
}
}
}
三、运行效果
运行效果如下图:实现相关效果

总结:
通过微软提供的 Microsoft.Diagnostics.NETCore.Client 比较简单的就实现了这些功能,当然注册的事件里面还有很多信息分析等着去解锁。
这只是走出了简单的第一步,后续还任重而道远
其他:
参考:https://github.com/dotnet/diagnostics
源码:https://github.com/cwsheng/DiagnosticsTools
.NET Core 服务诊断工具的更多相关文章
- 动手实现一个适用于.NET Core 的诊断工具
前言 大家可能对诊断工具并不陌生,从大名鼎鼎的 dotTrace,到 .NET CLI 推出的一系列的高效诊断组件(dotnet trace,dotnet sos,dotnet dump)等, 这些工 ...
- 轻量级.Net Core服务注册工具CodeDi发布啦
为什么做这么一个工具 因为我们的系统往往时面向接口编程的,所以在开发Asp .net core项目的时候,一定会有大量大接口及其对应的实现要在ConfigureService注册到ServiceCol ...
- .Net Core服务诊断排查
前言: 近期在项目中出现了几次服务内存资源占用较高的情况,特回顾梳理下排查过程以及对相应问题的排查方法总结. 一.Dump抓取 抓取dump的方式有多种,下面介绍几种常用的: 1. 任务管理器中找到程 ...
- .NET Core-全局性能诊断工具
前言: 现在.NET Core 上线后,不可避免的会出现各种问题,如内存泄漏.CPU占用高.接口处理耗时较长等问题.这个时候就需要快速准确的定位问题,并解决. 这时候就可以使用.NET Core 为开 ...
- 中小研发团队架构实践之生产环境诊断工具WinDbg 三分钟学会.NET微服务之Polly 使用.Net Core+IView+Vue集成上传图片功能 Fiddler原理~知多少? ABP框架(asp.net core 2.X+Vue)模板项目学习之路(一) C#程序中设置全局代理(Global Proxy) WCF 4.0 使用说明 如何在IIS上发布,并能正常访问
中小研发团队架构实践之生产环境诊断工具WinDbg 生产环境偶尔会出现一些异常问题,WinDbg或GDB是解决此类问题的利器.调试工具WinDbg如同医生的听诊器,是系统生病时做问题诊断的逆向分析工具 ...
- .NET Core CLI 的性能诊断工具介绍
前言 开发人员的.NET Core项目上线后,经常会出现各种问题,内存泄漏,CPU 100%,处理时间长等, 这个时候就需要快速并准确的发现问题,并解决问题, 除了项目本身的日志记录外,NET Cor ...
- (转)权威支持: 选择正确的 WebSphere 诊断工具
权威支持: 选择正确的 WebSphere 诊断工具 原文:https://www.ibm.com/developerworks/cn/websphere/techjournal/0807_supau ...
- 使用诊断工具观察 Microsoft.Extensions.DependencyInjection 2.x 版本的内存占用
目录 准备工作 大量接口与实现类的生成 elasticsearch+kibana+apm asp.net core 应用 请求与快照 Kibana 上的请求记录 请求耗时的分析 请求内存的分析 第2次 ...
- Microsoft.Extensions.DependencyInjection 之二:使用诊断工具观察内存占用
目录 准备工作 大量接口与实现类的生成 elasticsearch+kibana+apm asp.net core 应用 请求与快照 Kibana 上的请求记录 请求耗时的分析 请求内存的分析 第2次 ...
随机推荐
- Spring 声明式事务与编程式事务详解
本文转载自IBM开发者论坛:https://developer.ibm.com/zh/articles/os-cn-spring-trans 根据自己的学习理解有所调整,用于学习备查. 事务管理对于企 ...
- DenseNet的个人总结
DenseNet这篇论文是在ResNet之后一年发表的,由于ResNet在当时引起了很大的轰动,所以DenseNet也将ResNet作为了主要的对比方法,读起来还是比较容易的,全篇只有两个数学公式,也 ...
- Java 并发工具类 CountDownLatch、CyclicBarrier、Semaphore、Exchanger
本文部分摘自<Java 并发编程的艺术> CountDownLatch CountDownLatch 允许一个或多个线程等待其他线程完成操作.假设现有一个需求:我们需要解析一个 Excel ...
- 【秒懂音视频开发】12_播放WAV
对于WAV文件来说,可以直接使用ffplay命令播放,而且不用像PCM那样增加额外的参数.因为WAV的文件头中已经包含了相关的音频参数信息. ffplay in.wav 接下来演示一下如何使用SDL播 ...
- python函数之有参装饰器
一.为什么要有有参装饰器? 来看之前的无参装饰器 # 无参装饰器 def outter(func): def wrapper(*args,**kwargs): start = time.time() ...
- PureStudy:学科知识分享——个人网站开发全解
PureStudy:学科知识分享--个人网站开发全解 项目描述 PureStudy,学科知识分享网站. 学生可以使用这个网站,来浏览相应学科的知识点.学习总结,获取相关的资料.此外,他们可以选择上传文 ...
- 使用KeepAlived来实现高可用的DR模型
环境 VMware 16 CentOS8 相关软件 keepalived ipvsadm httpd 准备工作 准备四个节点,如上图,Node01 ~ Node04, 本文默认你会在VMWare上安装 ...
- ClickHouse性能优化?试试物化视图
一.前言 ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS):目前我们使用CH作为实时数仓用于统计分析,在做性能优化的时候使用了 物化视图 这一特性作为优化手段,本文主 ...
- HTML5本地存储 localStorage操作使用详解
1.html5几种存储形式 本地存储(localStorage && sessionStorage) 离线缓存(application cache) indexedDB 和 webSQ ...
- 梳理一下最近准备蓝桥杯时学习DP问题的想法
学习时间不长,记录的只是学习过程的思路和想法,不能保证正确,代码可以在acwing上AC. 01背包问题: 1.首先是简单的01背包问题 2.先确定状态,f[i][j]表示有第i件物品,时间为j的最大 ...