Diagnoseclientbibliothek

Dieser Artikel gilt für: ✔️ .NET Core 3.0 SDK und neuere Versionen für Ziel-Apps und .NET Standard 2.0 zur Verwendung der Bibliothek.

Microsoft.Diagnostics.NETCore.Client (auch als Diagnoseclientbibliothek bezeichnet) ist eine verwaltete Bibliothek, mit der Sie mit .NET Core-Runtime (CoreCLR) für verschiedene Diagnoseaufgaben interagieren können, z. B. Ablaufverfolgung über EventPipe, Anforderung eines Speicherabbilds oder Anfügen einer ICorProfiler-Instanz. Diese Bibliothek ist die unterstützende Bibliothek vieler Diagnosetools wie dotnet-counters, dotnet-trace, dotnet-gcdump, dotnet-dump und dotnet-monitor. Mit dieser Bibliothek können Sie eigene Diagnosetools schreiben, die für Ihr spezielles Szenario angepasst sind.

Sie können Microsoft.Diagnostics.NETCore.Client erwerben, indem Sie Ihrem Projekt einen PackageReference hinzufügen. Das Paket wird auf NuGet.org gehostet.

Die Beispiele in den folgenden Abschnitten zeigen die Verwendung der Microsoft.Diagnostics.NETCore.Client-Bibliothek. Einige dieser Beispiele zeigen auch die Analyse der Ereignisnutzlasten mithilfe der TraceEvent-Bibliothek.

Anfügen an einen Prozess und Drucken aller GC-Ereignisse

Dieser Codeausschnitt zeigt, wie Sie eine EventPipe-Sitzung mithilfe des .NET-Runtimeanbieters mit dem GC-Schlüsselwort auf Informationsebene starten. Außerdem wird gezeigt, wie Sie die von der TraceEvent-Bibliothek bereitgestellte EventPipeEventSource-Klasse verwenden, um die eingehenden Ereignisse zu analysieren und ihre Namen in Echtzeit auf der Konsole zu drucken.

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

Schreiben eines Kernspeicherabbilds

In diesem Beispiel wird gezeigt, wie die Auflistung eines Kernspeicherabbilds mithilfe von DiagnosticsClient ausgelöst wird.

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");
    }
}

Auslösen eines Kernspeicherabbilds, wenn die CPU-Auslastung einen Schwellenwert überschreitet

In diesem Beispiel wird gezeigt, wie Sie den von der .NET-Runtime veröffentlichten cpu-usage-Indikator überwachen und ein Speicherabbild anfordern, wenn die CPU-Auslastung einen bestimmten Schwellenwert überschreitet.

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

Auslösen einer CPU-Ablaufverfolgung für eine bestimmte Anzahl von Sekunden

In diesem Beispiel wird gezeigt, wie Sie eine EventPipe-Sitzung für einen bestimmten Zeitraum mit dem Standard-CLR-Ablaufverfolgungsschlüsselwort und dem Beispielprofiler auslösen. Anschließend wird der Ausgabestream gelesen, und die Bytes werden in eine Datei geschrieben. Dies ist im Wesentlichen das, was dotnet-trace intern zum Schreiben einer Ablaufverfolgungsdatei verwendet.

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

In diesem Beispiel wird gezeigt, wie Sie mithilfe der DiagnosticsClient.GetPublishedProcesses-API die Namen der .NET-Prozesse drucken, die einen Diagnose-IPC-Kanal veröffentlicht haben.

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}");
        }
    }
}

Analysieren von Ereignissen in Echtzeit

Im folgenden Beispiel werden zwei Aufgaben erstellt: eine, die die live eingehenden Ereignisse mit EventPipeEventSource analysiert, und eine, die die Konsoleneingabe für eine Benutzereingabe zum Beenden des Programms liest. Wenn die Ziel-App vorhanden ist, bevor der Benutzer die EINGABETASTE drückt, wird die App ordnungsgemäß beendet. Andernfalls sendet inputTask den Stop-Befehl an die Pipe und wird ordnungsgemäß beendet.

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

Anfügen eines ICorProfiler-Profilers

In diesem Beispiel wird gezeigt, wie ein ICorProfiler mit „profiler attach“ an einen Prozess angefügt wird.

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