动手实现一个适用于.NET Core 的诊断工具

前言

大家可能对诊断工具并不陌生,从大名鼎鼎的 dotTrace,到 .NET CLI 推出的一系列的高效诊断组件(dotnet trace,dotnet sos,dotnet dump)等, 这些工具提升了对程序Debug的能力和效率,可以让开发人员从更高层次的维度来发现程序中的问题。

今天我们针对于.NET Core, 尝试动手实现一个简单的诊断工具,在保证对程序无侵入(不修改代码和配置)的前提下,我们尝试获取程序的运行信息,包括内存,线程,垃圾回收,异常等。

这里可能会有小伙伴说,我可以用C++编写然后利用Profiling API实现,类似于OneAPM,Datadog 自动探针的形式来收集数据,当然也可以,不过今天我们主要用到了 Microsoft.Diagnostics.NETCore.Client,运行时团队给开发人员提供了更简单和友好的组件。

初始化项目

首先,我们需要创建两个.NET Core 的项目,一个是C#的控制台项目,名字叫ConsoleApp,这是我们的诊断程序,另一个是普通的WebAPI,我们需要对这个API项目进行诊断分析。

然后在控制台项目上通过Nuget引入诊断组件,分别是 Microsoft.Diagnostics.NETCore.Client,Microsoft.Diagnostics.Tracing.TraceEvent

1.获取正在运行的程序列表

在无侵入的情况下,我们首先需要获取到运行的dotnet程序,包括进程的名字和PID,在多个dotnet项目中,我们后边都会通过PID来对特定的程序进行诊断。修改ConsoleApp的Program.cs如下,这里主要用到了 GetPublishedProcesses 方法。

class Program
{static void Main(string[] args){if (args.Any()){switch (args[0]){case "ps": PrintProcessStatus(); break; }}}public static void PrintProcessStatus(){var processes = DiagnosticsClient.GetPublishedProcesses().Select(Process.GetProcessById).Where(process => process != null);foreach (var process in processes){Console.WriteLine($"ProcessId: {process.Id}");Console.WriteLine($"ProcessName: {process.ProcessName}");Console.WriteLine($"StartTime: {process.StartTime}");Console.WriteLine($"Threads: {process.Threads.Count}");Console.WriteLine();Console.WriteLine();}}
}

修改完成后,我们用命令行启动项目,WebAPI 项目运行dotnet run命令 , 启动之后,ConsoleApp 再运行 dotnet run ps命令,ps 是我们传入的参数,我们可以在控制台上看到正在运行的进程信息,我们主要会用到pid。

2.获取 GC 信息

我们创建了一个 DiagnosticsClient的实例,在构造函数中传入了processId进程ID,然后开启了一个有关GC信息的会话,最后订阅了CLR相关的事件回调,输出了事件名称EventName到控制台。

static void Main(string[] args)
{if (args.Any()){switch (args[0]){case "ps": PrintProcessStatus(); break;case "runtime": PrintRuntime(int.Parse(args[1])); break;}}
} public static void PrintRuntime(int processId)
{ var providers = new List<EventPipeProvider>(){new ("Microsoft-Windows-DotNETRuntime",EventLevel.Informational, (long)ClrTraceEventParser.Keywords.GC)};var client = new DiagnosticsClient(processId);using (var session = client.StartEventPipeSession(providers, false)){var source = new EventPipeEventSource(session.EventStream);source.Clr.All += (TraceEvent obj) =>{Console.WriteLine(obj.EventName);};try{source.Process();}catch (Exception e){Console.WriteLine(e.ToString());}}
}

接下来,我们修改一下WebAPI的代码,在控制器中的方法中创建了一个集合,并且添加了很多数据。

[HttpGet]
public IEnumerable<WeatherForecast> Get()
{List<string> list = new ();for (int i = 0; i < 1000000; i++){list.Add(i.ToString());} var rng = new Random();return Enumerable.Range(1, 5).Select(index => new WeatherForecast{Date = DateTime.Now.AddDays(index),TemperatureC = rng.Next(-20, 55),Summary = Summaries[rng.Next(Summaries.Length)]}).ToArray();
}

同样,我们首先通过 dotnet run 命令启动WebAPI项目,然后 dotnet run ps 启动ConsoleApp项目,控制台会输出 webapi 项目的进程信息,我这里的pid是3832

然后在控制台项目中运行 dotnet run runtime 3832, runtime 和 3832 都是我们传入的参数, 然后开启一个新的命令行窗口,通过curl访问几次webapi的接口,当然你也可以在浏览器中访问,我们发现,在右边的控制台项目输出了GC的相关信息, 这里我们只输出了事件名,实际上我们可以拿到更多的数据信息。

3.获取异常信息

同样的,我们先修改WebApi项目,手动抛出一个异常。

[HttpGet]
public IEnumerable<WeatherForecast> Get()
{throw new Exception("error");var rng = new Random();return Enumerable.Range(1, 5).Select(index => new WeatherForecast{Date = DateTime.Now.AddDays(index),TemperatureC = rng.Next(-20, 55),Summary = Summaries[rng.Next(Summaries.Length)]}).ToArray();
}

在控制台项目中,我们只需要改动一个Keywords 枚举,就是把 ClrTraceEventParser.Keywords.GC 改成 ClrTraceEventParser.Keywords.Exception,当然这里支持了其他更多的类型。

修改完成后,我们先启动 WebApi 项目,然后在ConsoleApp中先运行 dotnet run ps,查看webapi的进程id,然后再运行 dotnet run runtime 13600, 最后我们通过 curl 命令或者浏览器访问webapi的接口,同样,在右边的ConsoleApp中,输出了异常的相关事件信息。

在上面的代码中,我手动抛出一个异常,我们的诊断工具ConsoleApp是可以获取到相关的异常信息,那我用try,catch 把异常吃掉呢?它还能捕获到异常吗?

[HttpGet]
public IEnumerable<WeatherForecast> Get()
{try{Convert.ToInt32("sss");}catch (Exception ex){Console.WriteLine(ex.ToString()); }  var rng = new Random();return Enumerable.Range(1, 5).Select(index => new WeatherForecast{Date = DateTime.Now.AddDays(index),TemperatureC = rng.Next(-20, 55),Summary = Summaries[rng.Next(Summaries.Length)]}).ToArray();}

修改代码后,我们重新运行webapi和诊断工具ConsoleApp,访问api接口时,你会发现,就算我们用try,catch 吃掉了异常,它仍然会输出异常信息。

4. 生成Dump文件

通过 Microsoft.Diagnostics.NETCore.Client 组件,我们可以很方便的为程序生生成Dump文件,然后可以用 windbg 工具来进行分析。

修改控制台项目ConsoleApp的Program.cs如下:

 static void Main(string[] args){if (args.Any()){switch (args[0]){case "ps": PrintProcessStatus(); break;case "runtime": PrintRuntime(int.Parse(args[1])); break;case "dump": Dump(int.Parse(args[1])); break;}}
}public static void Dump(int processId)
{var client = new DiagnosticsClient(processId);client.WriteDump(DumpType.Normal, @"mydump.dmp", false);
}

修改完成后,启动webapi项目和控制台项目,在控制台项目中运行 dotnet run dump 13288 命令,它会在webapi的目录下,生成程序的dump文件

5.生成 Trace 文件

同样,我们可以很方便的生成 Trace 文件,它可以分析到CPU的函数执行耗时情况,它的格式是.nettrace, 你可以直接用VS 2017及以上或者 PerfView 工具打开。

修改控制台项目ConsoleApp的Program.cs如下:

static void Main(string[] args)
{if (args.Any()){switch (args[0]){case "ps": PrintProcessStatus(); break;case "runtime": PrintRuntime(int.Parse(args[1])); break;case "dump": Dump(int.Parse(args[1])); break;case "trace": Trace(int.Parse(args[1])); break;}}
}public static void Trace(int processId)
{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.Run(async () =>{using (FileStream fs = new FileStream(@"mytrace.nettrace", FileMode.Create, FileAccess.Write)){await traceSession.EventStream.CopyToAsync(fs);}}).Wait(10 * 1000);traceSession.Stop();}
}

修改完成后,启动webapi项目和控制台项目,在控制台项目中运行 dotnet run trace 13288命令,trace和13288都是参数,它会在控制台项目的目录下,生成 mytrace.nettrace文件

我们可以使用VS或者 PerfView 打开它

总结

其实在.NET Core CLI 中,已经提供了高度可用的一系列诊断工具,dotnet-trace,dotnet-dump 等等,Microsoft.Diagnostics.NETCore.Client 提供了非常友好和高层次的API,不仅仅是文中这些, 我们可以用C#代码,来完成对CLR层面的一些操作,来帮助我们发掘对程序诊断的更多可能性。

示例代码都已经上传到 https://github.com/SpringLeee/DiagnosticDemo,觉得不错的就给我点个赞吧!

最后欢迎扫码关注我们的公众号 【全球技术精选】,专注国外优秀博客的翻译和开源项目分享。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/302611.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android 强制下线功能,Android学习之基础知识八—Android广播机制实践(实现强制下线功能)...

强制下线功能算是比较常见的了&#xff0c;很多的应用程序都具备这个功能&#xff0c;比如你的QQ号在别处登录了&#xff0c;就会将你强制挤下线。实现强制下线功能的思路比较简单&#xff0c;只需要在界面上弹出一个对话框&#xff0c;让用户无法进行任何操作&#xff0c;必须…

USB权限的设置

USB设备给我们日常生活中带了很多的方便&#xff0c;能够在不同的不同的移动设备中传递数据。但也给我们的数据安全带来了隐患&#xff0c;它可以拷贝走我们计算机中很机密的信息&#xff01;通过下面的操作后&#xff0c;将使我们有用的信息增加更大安全性&#xff0c;至小也能…

漫谈高数——泰勒级数的物理意义

全世界有3.14 % 的人已经关注了数据与算法之美高等数学干吗要研宄级数问题&#xff1f;是为了把简单的问题弄复杂来表明自己的高深&#xff1f; No,是为了把各种简单的问题/复杂的问题&#xff0c;他们的求解过程用一种通用的方法来表示。提一个问题&#xff0c;99*99等于多少&…

保持学习,从这几个公众号开始!

全世界有3.14 % 的人已经关注了数据与算法之美关注了几百个公众号无目的的看文章却如鸡肋一般食之无味弃之可惜你是否也觉得时间被浪费&#xff1f;生命被辜负了&#xff1f;点击关注下面几个公众号它们每天更新有态度、有温度的原创文字每一篇推文都值得你点开长按二维码&…

回顾 | 在 GitHub 上贡献到开源项目

点击蓝字关注我们精彩回顾GitHub 是世界上最大的开源社区&#xff0c;拥有数百万个开源项目。GitHub 的优势之一是在项目上进行协作非常容易。在本研讨会中&#xff0c;我们将介绍在 GitHub 上找到开源项目并为其做出贡献的难易程度。我们将向您展示如何创建拉取请求&#xff0…

html assign无效,Object.assign的一些用法

Object.assign的一些用法2020/11/12 20:19:03  字体&#xff1a;大 中 小 浏览 1289 我要评论需要定制网站程序、公众号程序、微信小程序可以联系&#xff1a;565449214或者加微信13961347334(备注&#xff1a;需要技术)或者在处理一些技术问题时&#xff0c;有什么不懂的地…

R语言和 Python —— 一个错误的分裂

全世界有3.14 % 的人已经关注了数据与算法之美最近有一些文章提出与年龄相关的问题&#xff1a;“崭露头角的年轻数据科学家们是学习R语言还是Python更好?”答案似乎都是“视情况而定”&#xff0c;在现实中没有必要在R和Python中做出选择&#xff0c;因为你两个都用得到。推荐…

快速修改HTML5,HTML5无刷新修改URL(示例代码)

HTML5新添加了两个api分别是pushState和replaceState&#xff0c;DOM中的window对象通过window.history方法提供了对浏览器历史记录的读取&#xff0c;可以在用户的访问记录中前进和后退&#xff0c;我们可以开始操作这个历史记录堆栈。实例一、通过pushState修改URL通过这句代…

这一平台只要把握住风口期,自己就能当老板!

我是电商珠珠 短视频渐渐走进大家的视野&#xff0c;改变了大家的日常娱乐方式。从19年开始&#xff0c;抖音开始发展电商平台-抖音小店。 在改变大家娱乐方式的同时&#xff0c;还将直播电商的热度掀了起来&#xff0c;由此改变了大家的购物方式&#xff0c;给大家带来了方便…

如何用TensorFlow实现人工智能?

自 2015 年 11 月 9 号发布之后&#xff0c;TensorFlow 逐渐成为人工智能领域最广泛运用的深度学习框架。那么TensorFlow框架到底是什么&#xff1f;TensorFlow 是一个大规模机器学习的开源框架&#xff0c;提供了多种深度神经网络的支持。不仅 Google 在自己的产品线使用 Tens…

一不小心,知乎炸了!

阅读本文大概需要7分钟。昨天晚上加完班发完原创文章&#xff1a;如何成为一个搞垮公司的程序员&#xff1f;就准备回家了&#xff0c;突然发现知乎一堆咨询消息&#xff0c;看了看原来是之前回答的一个问题突然火了&#xff0c;就是这个&#xff1a;好家伙&#xff0c;一天不到…

[转载]Android Layout标签之-viewStub,requestFocus,merge,include

定义Android Layout(XML)时&#xff0c;有四个比较特别的标签是非常重要的&#xff0c;其中有三个是与资源复用有关&#xff0c;分别是<viewStub/>, <requestFocus />, <merge /> and<include />。可是以往我们所接触的案例或者官方文档的例子都没有着…

查询2021抚顺高考成绩,2021年抚顺高考状元是谁分数多少分,历年抚顺高考状元名单...

2020年抚顺一年一度的高考考试已经结束&#xff0c;今年抚顺高考状元是谁呢&#xff0c;抚顺高考状元出自哪个高中学校&#xff0c;文理科分数是多少分&#xff0c;一起来了解。一、2020年抚顺高考状元名单资料2020年抚顺高考状元名单和学校相关信息&#xff0c;截至目前发文时…

Android菜单详解——理解android中的Menu

前言 今天看了pro android 3中menu这一章&#xff0c;对Android的整个menu体系有了进一步的了解&#xff0c;故整理下笔记与大家分享。 PS&#xff1a;强烈推荐《Pro Android 3》&#xff0c;是我至今为止看到的最好的一本android书&#xff0c;中文版出到《精通Android 2》。 …

2021北京高考适应性测试成绩排名查询,2021届适应性考试学生成绩排名

1开启前照灯远光时仪表板上(如图所示)亮起。查看本题分析2在道路上跟车行驶时&#xff0c;跟车距离不是主要的&#xff0c;只须保持与前车相等的速度&#xff0c;即可防止发生追尾事故。查看本题分析3驾驶机动车在高速公路要按照限速标志标明的车速行驶。查看本题分析4这个标志…

.NET之Docker部署详细流程

开篇语自己从头开始走一遍docker部署.net的流程&#xff0c;作为一种学习总结&#xff0c;以及后续会写一些在该基础之上的文章。本次示例环境&#xff1a;vs2019、net5、docker、postman创建项目本次事例代码是用过vs2019创建的ASP.NET Core Web API项目image.png目标框架是.N…

平凡而又神奇的贝叶斯方法

全世界有3.14 % 的人已经关注了数据与算法之美概率论只不过是把常识用数学公式表达了出来。 ——拉普拉斯记得读本科的时候&#xff0c;最喜欢到城里的计算机书店里面去闲逛&#xff0c;一逛就是好几个小时&#xff1b;有一次&#xff0c;在书店看到一本…

d.ts文件可以注释html吗,如何编写一个d.ts文件的步骤详解_旧店_前端开发者

前言本文主要讲怎么写一个typescript的描述文件(以d.ts结尾的文件名&#xff0c;比如xxx.d.ts)。最近开始从之前也从网上面也找了一些资料&#xff0c;但还是看的云里雾里模糊不清&#xff0c;经过一段摸索&#xff0c;将摸索的结果记录下来&#xff0c;也希望可以给别人一个参…

function里面可以写function吗_氨基酸可以和蜂蜜一起喝吗?蜂蜜里面有氨基酸吗?...

氨基酸可以和蜂蜜一起喝吗?蜂蜜里面有氨基酸吗?氨基酸是我们人体不可或缺的营养物质&#xff0c;那氨基酸这种物质是否可以搭配蜂蜜一同食用?不少朋友都存在类似的疑惑&#xff0c;氨基酸会对蜂蜜产生不良的影响吗?其实&#xff0c;蜂蜜中本身也包含不少的氨基酸&#xff0…

通过Dapr实现一个简单的基于.net的微服务电商系统(九)——一步一步教你如何撸Dapr之OAuth2授权-百度版...

目录&#xff1a;一、通过Dapr实现一个简单的基于.net的微服务电商系统二、通过Dapr实现一个简单的基于.net的微服务电商系统(二)——通讯框架讲解三、通过Dapr实现一个简单的基于.net的微服务电商系统(三)——一步一步教你如何撸Dapr四、通过Dapr实现一个简单的基于.net的微服…