<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          動(dòng)手實(shí)現(xiàn)一個(gè)適用于.NET Core 的診斷工具

          共 7161字,需瀏覽 15分鐘

           ·

          2022-01-20 10:49

          前言

          大家可能對(duì)診斷工具并不陌生,從大名鼎鼎的 dotTrace,到 .NET CLI 推出的一系列的高效診斷組件(dotnet trace,dotnet sos,dotnet dump)等, 這些工具提升了對(duì)程序Debug的能力和效率,可以讓開(kāi)發(fā)人員從更高層次的維度來(lái)發(fā)現(xiàn)程序中的問(wèn)題。

          今天我們針對(duì)于.NET Core, 嘗試動(dòng)手實(shí)現(xiàn)一個(gè)簡(jiǎn)單的診斷工具,在保證對(duì)程序無(wú)侵入(不修改代碼和配置)的前提下,我們嘗試獲取程序的運(yùn)行信息,包括內(nèi)存,線程,垃圾回收,異常等。

          這里可能會(huì)有小伙伴說(shuō),我可以用C++編寫然后利用Profiling API實(shí)現(xiàn),類似于OneAPM,Datadog 自動(dòng)探針的形式來(lái)收集數(shù)據(jù),當(dāng)然也可以,不過(guò)今天我們主要用到了?Microsoft.Diagnostics.NETCore.Client,運(yùn)行時(shí)團(tuán)隊(duì)給開(kāi)發(fā)人員提供了更簡(jiǎn)單和友好的組件。

          初始化項(xiàng)目

          首先,我們需要?jiǎng)?chuàng)建兩個(gè).NET Core 的項(xiàng)目,一個(gè)是C#的控制臺(tái)項(xiàng)目,名字叫ConsoleApp,這是我們的診斷程序,另一個(gè)是普通的WebAPI,我們需要對(duì)這個(gè)API項(xiàng)目進(jìn)行診斷分析。

          然后在控制臺(tái)項(xiàng)目上通過(guò)Nuget引入診斷組件,分別是?Microsoft.Diagnostics.NETCore.Client,Microsoft.Diagnostics.Tracing.TraceEvent

          1.獲取正在運(yùn)行的程序列表

          在無(wú)侵入的情況下,我們首先需要獲取到運(yùn)行的dotnet程序,包括進(jìn)程的名字和PID,在多個(gè)dotnet項(xiàng)目中,我們后邊都會(huì)通過(guò)PID來(lái)對(duì)特定的程序進(jìn)行診斷。修改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(); }
          }}

          修改完成后,我們用命令行啟動(dòng)項(xiàng)目,WebAPI 項(xiàng)目運(yùn)行dotnet run命令 , 啟動(dòng)之后,ConsoleApp 再運(yùn)行?dotnet run ps命令,ps 是我們傳入的參數(shù),我們可以在控制臺(tái)上看到正在運(yùn)行的進(jìn)程信息,我們主要會(huì)用到pid。

          2.獲取 GC 信息

          我們創(chuàng)建了一個(gè) DiagnosticsClient的實(shí)例,在構(gòu)造函數(shù)中傳入了processId進(jìn)程ID,然后開(kāi)啟了一個(gè)有關(guān)GC信息的會(huì)話,最后訂閱了CLR相關(guān)的事件回調(diào),輸出了事件名稱EventName到控制臺(tái)。

          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() { 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()); } }}

          接下來(lái),我們修改一下WebAPI的代碼,在控制器中的方法中創(chuàng)建了一個(gè)集合,并且添加了很多數(shù)據(jù)。

          [HttpGet]public IEnumerable Get(){    List 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();}

          同樣,我們首先通過(guò)?dotnet run?命令啟動(dòng)WebAPI項(xiàng)目,然后?dotnet run ps?啟動(dòng)ConsoleApp項(xiàng)目,控制臺(tái)會(huì)輸出 webapi 項(xiàng)目的進(jìn)程信息,我這里的pid是3832

          然后在控制臺(tái)項(xiàng)目中運(yùn)行?dotnet run runtime 3832, runtime 和 3832 都是我們傳入的參數(shù), 然后開(kāi)啟一個(gè)新的命令行窗口,通過(guò)curl訪問(wèn)幾次webapi的接口,當(dāng)然你也可以在瀏覽器中訪問(wèn),我們發(fā)現(xiàn),在右邊的控制臺(tái)項(xiàng)目輸出了GC的相關(guān)信息, 這里我們只輸出了事件名,實(shí)際上我們可以拿到更多的數(shù)據(jù)信息。

          3.獲取異常信息

          同樣的,我們先修改WebApi項(xiàng)目,手動(dòng)拋出一個(gè)異常。

          [HttpGet]public IEnumerable 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();}

          在控制臺(tái)項(xiàng)目中,我們只需要改動(dòng)一個(gè)Keywords 枚舉,就是把?ClrTraceEventParser.Keywords.GC?改成?ClrTraceEventParser.Keywords.Exception,當(dāng)然這里支持了其他更多的類型。

          修改完成后,我們先啟動(dòng) WebApi 項(xiàng)目,然后在ConsoleApp中先運(yùn)行?dotnet run ps,查看webapi的進(jìn)程id,然后再運(yùn)行?dotnet run runtime 13600, 最后我們通過(guò) curl 命令或者瀏覽器訪問(wèn)webapi的接口,同樣,在右邊的ConsoleApp中,輸出了異常的相關(guān)事件信息。

          在上面的代碼中,我手動(dòng)拋出一個(gè)異常,我們的診斷工具ConsoleApp是可以獲取到相關(guān)的異常信息,那我用try,catch 把異常吃掉呢?它還能捕獲到異常嗎?

          [HttpGet]public IEnumerable 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(); }

          修改代碼后,我們重新運(yùn)行webapi和診斷工具ConsoleApp,訪問(wèn)api接口時(shí),你會(huì)發(fā)現(xiàn),就算我們用try,catch 吃掉了異常,它仍然會(huì)輸出異常信息。

          4. 生成Dump文件

          通過(guò)?Microsoft.Diagnostics.NETCore.Client?組件,我們可以很方便的為程序生生成Dump文件,然后可以用 windbg 工具來(lái)進(jìn)行分析。

          修改控制臺(tái)項(xiàng)目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);}

          修改完成后,啟動(dòng)webapi項(xiàng)目和控制臺(tái)項(xiàng)目,在控制臺(tái)項(xiàng)目中運(yùn)行?dotnet run dump 13288?命令,它會(huì)在webapi的目錄下,生成程序的dump文件

          5.生成 Trace 文件

          同樣,我們可以很方便的生成 Trace 文件,它可以分析到CPU的函數(shù)執(zhí)行耗時(shí)情況,它的格式是.nettrace, 你可以直接用VS 2017及以上或者 PerfView 工具打開(kāi)。

          修改控制臺(tái)項(xiàng)目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() { 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(); }}

          修改完成后,啟動(dòng)webapi項(xiàng)目和控制臺(tái)項(xiàng)目,在控制臺(tái)項(xiàng)目中運(yùn)行?dotnet run trace 13288命令,trace和13288都是參數(shù),它會(huì)在控制臺(tái)項(xiàng)目的目錄下,生成 mytrace.nettrace文件

          我們可以使用VS或者 PerfView 打開(kāi)它

          總結(jié)

          其實(shí)在.NET Core CLI 中,已經(jīng)提供了高度可用的一系列診斷工具,dotnet-trace,dotnet-dump 等等,Microsoft.Diagnostics.NETCore.Client?提供了非常友好和高層次的API,不僅僅是文中這些, 我們可以用C#代碼,來(lái)完成對(duì)CLR層面的一些操作,來(lái)幫助我們發(fā)掘?qū)Τ绦蛟\斷的更多可能性。

          示例代碼都已經(jīng)上傳到 https://github.com/SpringLeee/DiagnosticDemo,覺(jué)得不錯(cuò)的就給我點(diǎn)個(gè)贊吧!



          瀏覽 60
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  无码天天av天天爽 | 亚洲欧美v在线视频 | 中文字幕精品无码网站人口 | 欧美操逼免费毛片视频 | 国产精久久久 |