Diagnostische clientbibliotheek

Dit artikel is van toepassing op: ✔️ .NET Core 3.0 SDK en latere versies voor doel-apps, .NET Standard 2.0 om de bibliotheek te gebruiken.

Microsoft.Diagnostics.NETCore.Client (ook wel bekend als de Diagnostische clientbibliotheek) is een beheerde bibliotheek waarmee u kunt communiceren met .NET Core Runtime (CoreCLR) voor verschillende diagnostische taken, zoals tracering via EventPipe, het aanvragen van een dump of het koppelen van een ICorProfiler. Deze bibliotheek is de back-upbibliotheek achter veel diagnostische hulpprogramma's, zoals dotnet-counters, dotnet-trace, dotnet-gcdump, dotnet-dump en dotnet-monitor. Met deze bibliotheek kunt u uw eigen diagnostische hulpprogramma's schrijven die zijn aangepast voor uw specifieke scenario.

U kunt Microsoft.Diagnostics.NETCore.Client verkrijgen door een PackageReference aan uw project toe te voegen. Het pakket wordt gehost op NuGet.org.

In de voorbeelden in de volgende secties ziet u hoe u microsoft.Diagnostics.NETCore.Client-bibliotheek gebruikt. In sommige van deze voorbeelden ziet u ook hoe u de nettoladingen van gebeurtenissen parseert met behulp van de TraceEvent-bibliotheek .

Koppelen aan een proces en alle GC-gebeurtenissen afdrukken

Dit fragment laat zien hoe u een EventPipe-sessie start met behulp van de .NET Runtime-provider met het trefwoord GC op informatieniveau. Ook ziet u hoe u de EventPipeEventSource klasse van de TraceEvent-bibliotheek gebruikt om de binnenkomende gebeurtenissen te parseren en hun namen in realtime af te drukken naar de console.

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

Een kerndump schrijven

In dit voorbeeld ziet u hoe u de verzameling van een kerndump activeert met behulp van 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");
    }
}

Een kerndump activeren wanneer het CPU-gebruik hoger is dan een drempelwaarde

In dit voorbeeld ziet u hoe u de cpu-usage teller bewaakt die is gepubliceerd door de .NET-runtime en een dump aanvraagt wanneer het CPU-gebruik hoger is dan een bepaalde drempelwaarde.

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

Een CPU-tracering activeren voor een bepaald aantal seconden

In dit voorbeeld ziet u hoe u een EventPipe-sessie voor een bepaalde periode activeert met het standaard-CLR-traceringswoord en de sample profiler. Daarna wordt de uitvoerstroom gelezen en worden de bytes naar een bestand geschreven. In wezen wordt dotnet-trace dit intern gebruikt om een traceringsbestand te schrijven.

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 dit voorbeeld ziet u hoe u api gebruikt DiagnosticsClient.GetPublishedProcesses om de namen van de .NET-processen af te drukken die een diagnostisch IPC-kanaal hebben gepubliceerd.

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

Gebeurtenissen parseren in realtime

In dit voorbeeld ziet u een voorbeeld waarin we twee taken maken, een die de gebeurtenissen parseert die live komen en EventPipeEventSource één waarmee de console-invoer wordt gelezen voor een gebruiker die het programma aangeeft dat het programma wordt beëindigd. Als de doel-app wordt afgesloten voordat de gebruiker op Enter drukt, wordt de app correct afgesloten. inputTask Anders verzendt u de opdracht Stoppen naar de pijp en sluit u deze probleemloos af.

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

Een ICorProfiler-profiler koppelen

In dit voorbeeld ziet u hoe u een ICorProfiler koppelt aan een proces via profiler attach.

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