本文共 5243 字,大约阅读时间需要 17 分钟。
在软件开发的日常工作中,诊断工具是开发人员不可或缺的助手。从 dotTrace 到 .NET CLI 推出的高效诊断组件(dotnet trace、dotnet sos、dotnet dump),这些工具大大提升了程序调试的效率,让开发人员从更高层次发现问题。
今天,我们尝试动手实现一个简单的诊断工具。目标是在不修改代码和配置的前提下,获取程序运行信息,包括内存、线程、垃圾回收和异常等。我们将使用 Microsoft.Diagnostics.NETCore.Client 这个友好的组件来实现。
首先,我们创建两个 .NET Core 项目:
在 ConsoleApp 中通过 NuGet 引入必要的诊断组件:
Install-Package Microsoft.Diagnostics.NETCore.ClientInstall-Package Microsoft.Diagnostics.Tracing.TraceEvent
在无侵入情况下,我们首先获取运行的 dotnet 程序,包括进程名称和 PID。
修改 ConsoleApp 的 Program.cs:
class Program{ static void Main(string[] args) { if (args.Any()) { switch (args[0]) { case "ps": PrintProcessStatus(); break; } } } private 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(); } }} 运行 dotnet run ps 查看正在运行的进程信息。
创建一个 DiagnosticsClient 实例,获取 GC 信息并输出事件名称。
修改 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; } }}private static void PrintRuntime(int processId){ var providers = new List { new EventPipeProvider { Name = "Microsoft-Windows-DotNETRuntime", Level = EventLevel.Informational, Keywords = (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()); } }} 运行 dotnet run runtime 3832 并通过浏览器或 curl 访问 WebAPI 接口。
在 WebAPI 中抛出异常,测试诊断工具的捕获能力。
修改 WebAPI 的 Get 方法:
[HttpGet]public IEnumerableGet(){ try { 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(); } catch (Exception ex) { Console.WriteLine(ex.ToString()); }}
在 ConsoleApp 中,修改 PrintRuntime 方法的 Keywords 为 ClrTraceEventParser.Keywords.Exception。
运行 dotnet run runtime 13600 并访问 WebAPI 接口。
使用 DiagnosticsClient 生成 dump 文件,便于后续分析。
修改 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; } }}private static void Dump(int processId){ var client = new DiagnosticsClient(processId); client.WriteDump(DumpType.Normal, @"mydump.dmp", false);} 运行 dotnet run dump 13288 生成 dump 文件。
同样使用 DiagnosticsClient 生成 trace 文件,分析 CPU 函数执行耗时。
修改 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; } }}private static void Trace(int processId){ var cpuProviders = new List { new EventPipeProvider { Name = "Microsoft-Windows-DotNETRuntime", Level = EventLevel.Informational, Keywords = (long)ClrTraceEventParser.Keywords.Default }, new EventPipeProvider { Name = "Microsoft-DotNETCore-SampleProfiler", Level = EventLevel.Informational, Keywords = (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(); }} 运行 dotnet run trace 13288 生成 trace 文件。
.NET Core CLI 提供了强大的诊断工具,Microsoft.Diagnostics.NETCore.Client 让我们可以从高层次操作 CLR。通过生成 dump 和 trace 文件,我们可以深入分析程序性能和异常。
这只是一个简单的实现示例,实际应用中可以根据需要扩展功能。如有疑问欢迎留言讨论!
转载地址:http://ngeyz.baihongyu.com/