本文适用于:✔️ .NET Core 3.0 SDK 及更高版本用于目标应用,使用库需要 .NET Standard 2.0。
Microsoft.Diagnostics.NETCore.Client
(也称为诊断客户端库)是一个托管库,可用于与 .NET Core 运行时(CoreCLR)进行交互以执行各种诊断相关任务,例如通过 EventPipe 进行事件跟踪、请求转储或附加 ICorProfiler
。 此库是许多诊断工具背后的支持库,例如 dotnet-counters、dotnet-trace、dotnet-gcdump、dotnet-dump 和 dotnet-monitor。 使用此库,可以编写为特定方案自定义的自己的诊断工具。
可以通过向项目添加 a 来获取 PackageReference
。 包托管在 NuGet.org
上。
以下部分中的示例演示如何使用 Microsoft.Diagnostics.NETCore.Client 库。 其中一些示例还演示如何使用 TraceEvent 库分析事件有效负载。
附加到进程并打印出所有 GC 事件
此代码片段演示如何在信息级别使用 .NET 运行时提供程序 和 GC 关键字启动 EventPipe 会话。 它还演示如何使用 EventPipeEventSource
TraceEvent 库 提供的类分析传入事件并将其名称实时输出到控制台。
using Microsoft.Diagnostics.NETCore.Client;
using Microsoft.Diagnostics.Tracing;
using Microsoft.Diagnostics.Tracing.EventPipe;
using Microsoft.Diagnostics.Tracing.Parsers;
using System;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
public class RuntimeGCEventsPrinter
{
public static void PrintRuntimeGCEvents(int processId)
{
var providers = new List<EventPipeProvider>()
{
new EventPipeProvider("Microsoft-Windows-DotNETRuntime",
EventLevel.Informational, (long)ClrTraceEventParser.Keywords.GC)
};
var client = new DiagnosticsClient(processId);
using (EventPipeSession session = client.StartEventPipeSession(providers, false))
{
var source = new EventPipeEventSource(session.EventStream);
source.Clr.All += (TraceEvent obj) => Console.WriteLine(obj.ToString());
try
{
source.Process();
}
catch (Exception e)
{
Console.WriteLine("Error encountered while processing events");
Console.WriteLine(e.ToString());
}
}
}
}
编写核心转储
此示例演示了如何使用 触发DiagnosticsClient
的集合。
using Microsoft.Diagnostics.NETCore.Client;
public partial class Dumper
{
public static void TriggerCoreDump(int processId)
{
var client = new DiagnosticsClient(processId);
client.WriteDump(DumpType.Normal, "/tmp/minidump.dmp");
}
}
当 CPU 使用率超过设定的阈值时触发核心转储
此示例演示了如何监视 .NET 运行时发布的 cpu-usage
计数器,并在 CPU 使用率超出特定阈值时请求转储。
using Microsoft.Diagnostics.NETCore.Client;
using Microsoft.Diagnostics.Tracing;
using Microsoft.Diagnostics.Tracing.EventPipe;
using Microsoft.Diagnostics.Tracing.Parsers;
using System;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
public partial class Dumper
{
public static void TriggerDumpOnCpuUsage(int processId, int threshold)
{
var providers = new List<EventPipeProvider>()
{
new EventPipeProvider(
"System.Runtime",
EventLevel.Informational,
(long)ClrTraceEventParser.Keywords.None,
new Dictionary<string, string>
{
["EventCounterIntervalSec"] = "1"
}
)
};
var client = new DiagnosticsClient(processId);
using (var session = client.StartEventPipeSession(providers))
{
var source = new EventPipeEventSource(session.EventStream);
source.Dynamic.All += (TraceEvent obj) =>
{
if (obj.EventName.Equals("EventCounters"))
{
var payloadVal = (IDictionary<string, object>)(obj.PayloadValue(0));
var payloadFields = (IDictionary<string, object>)(payloadVal["Payload"]);
if (payloadFields["Name"].ToString().Equals("cpu-usage"))
{
double cpuUsage = Double.Parse(payloadFields["Mean"].ToString());
if (cpuUsage > (double)threshold)
{
client.WriteDump(DumpType.Normal, "/tmp/minidump.dmp");
}
}
}
};
try
{
source.Process();
}
catch (Exception) {}
}
}
}
在给定的秒数内触发 CPU 跟踪
此示例演示如何使用默认 CLR 跟踪关键字和示例探查器在特定时间段内触发 EventPipe 会话。 之后,它会读取输出流并将字节写入文件。 本质上讲,这就是 dotnet-trace
在内部用于编写跟踪文件的内容。
using Microsoft.Diagnostics.Tracing;
using Microsoft.Diagnostics.Tracing.Parsers;
using Microsoft.Diagnostics.NETCore.Client;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.IO;
using System.Threading.Tasks;
public partial class Tracer
{
public void TraceProcessForDuration(int processId, int duration, string traceName)
{
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(traceName, FileMode.Create, FileAccess.Write))
{
await traceSession.EventStream.CopyToAsync(fs);
}
});
Task.WhenAny(copyTask, Task.Delay(TimeSpan.FromMilliseconds(duration * 1000)));
traceSession.Stop();
}
}
}
打印已发布诊断通道的进程名称
此示例演示如何使用 DiagnosticsClient.GetPublishedProcesses
API 打印发布诊断 IPC 通道的 .NET 进程的名称。
using Microsoft.Diagnostics.NETCore.Client;
using System;
using System.Diagnostics;
using System.Linq;
public class ProcessTracker
{
public static void PrintProcessStatus()
{
var processes = DiagnosticsClient.GetPublishedProcesses()
.Select(Process.GetProcessById)
.Where(process => process != null);
foreach (var process in processes)
{
Console.WriteLine($"{process.ProcessName}");
}
}
}
实时分析事件
此示例演示了一个示例,在该示例中,我们创建了两个任务,一个任务用于通过 EventPipeEventSource
分析实时传入的事件,另一个任务用于读取用户输入的控制台输入信号,以使程序结束。 如果用户按 Enter 之前目标应用退出,应用会正常退出。 否则, inputTask
会将 Stop 命令发送到管道并正常退出。
using Microsoft.Diagnostics.NETCore.Client;
using Microsoft.Diagnostics.Tracing;
using Microsoft.Diagnostics.Tracing.EventPipe;
using Microsoft.Diagnostics.Tracing.Parsers;
using System;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.Threading.Tasks;
public partial class Tracer
{
public static void PrintEventsLive(int processId)
{
var providers = new List<EventPipeProvider>()
{
new EventPipeProvider("Microsoft-Windows-DotNETRuntime",
EventLevel.Informational, (long)ClrTraceEventParser.Keywords.Default)
};
var client = new DiagnosticsClient(processId);
using (var session = client.StartEventPipeSession(providers, false))
{
Task streamTask = Task.Run(() =>
{
var source = new EventPipeEventSource(session.EventStream);
source.Clr.All += (TraceEvent obj) => Console.WriteLine(obj.EventName);
try
{
source.Process();
}
// NOTE: This exception does not currently exist. It is something that needs to be added to TraceEvent.
catch (Exception e)
{
Console.WriteLine("Error encountered while processing events");
Console.WriteLine(e.ToString());
}
});
Task inputTask = Task.Run(() =>
{
Console.WriteLine("Press Enter to exit");
while (Console.ReadKey().Key != ConsoleKey.Enter)
{
Task.Delay(TimeSpan.FromMilliseconds(100));
}
session.Stop();
});
Task.WaitAny(streamTask, inputTask);
}
}
}
附加 ICorProfiler 探查器
此示例演示了如何通过探查器附加将 ICorProfiler 附加到进程。
using System;
using Microsoft.Diagnostics.NETCore.Client;
public class Profiler
{
public static void AttachProfiler(int processId, Guid profilerGuid, string profilerPath)
{
var client = new DiagnosticsClient(processId);
client.AttachProfiler(TimeSpan.FromSeconds(10), profilerGuid, profilerPath);
}
}