Freigeben über


Diagnoseclientbibliothek

Dieser Artikel bezieht sich auf: ✔️ .NET Core 3.0 SDK und höhere Versionen für Ziel-Apps, .NET Standard 2.0 für die 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 Sicherungsbibliothek hinter vielen Diagnosetools wie dotnet-counters, dotnet-trace, dotnet-gcdump, dotnet-dump und dotnet-monitor. Mithilfe dieser Bibliothek können Sie eigene Diagnosetools schreiben, die für Ihr bestimmtes Szenario angepasst wurden.

Sie können Microsoft.Diagnostics.NETCore.Client erhalten, indem Sie PackageReference zu Ihrem Projekt hinzufügen. Das Paket ist auf NuGet.org gehostet.

Die Beispiele in den folgenden Abschnitten zeigen, wie Sie die Microsoft.Diagnostics.NETCore.Client-Bibliothek verwenden. 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 mit dem .NET-Laufzeitanbieter mit dem GC-Schlüsselwort auf Informationsebene starten. Außerdem wird gezeigt, wie Sie die EventPipeEventSource von der TraceEvent-Bibliothek bereitgestellte Klasse verwenden, um die eingehenden Ereignisse zu analysieren und deren Namen in Echtzeit in die 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 cpu-usage von .NET-Runtime veröffentlichten Leistungsindikator überwachen und einen Dump anfordern, wenn die CPU-Auslastung über einen bestimmten Schwellenwert hinausgeht.

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 eine EventPipe-Sitzung für einen bestimmten Zeitraum mit dem Standard-CLR-Tracing-Schlüsselwort sowie dem Beispiel-Profiler ausgelöst wird. Anschließend liest er den Ausgabedatenstrom und schreibt die Bytes in eine Datei. 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 mit der DiagnosticsClient.GetPublishedProcesses API die Namen der .NET-Prozesse ausgeben, 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 beendet wird, 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 Sie einen ICorProfiler über profiler attach an einen Prozess anfügen.

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