Freigeben über


Verwenden von Streaming mit TraceProcessor

Standardmäßig greift TraceProcessor auf Daten zu, indem sie während der Verarbeitung der Ablaufverfolgung in den Arbeitsspeicher geladen werden. Dieser Pufferungsansatz ist einfach zu verwenden, kann aber hinsichtlich der Speicherauslastung teuer sein.

TraceProcessor stellt auch „trace.UseStreaming()“ bereit, die den Streamingzugriff auf mehrere Arten von Ablaufverfolgungsdaten unterstützt (Verarbeitung der Daten, während sie aus der Ablaufverfolgungsdatei gelesen werden, anstatt diese Daten im Arbeitsspeicher zu puffern). Beispielsweise kann eine Ablaufverfolgung von Systemaufrufen (Syscalls) sehr umfangreich sein, und das Puffern der gesamten Liste der Systemaufrufe in einer Ablaufverfolgung kann sehr teuer sein.

Zugreifen auf gepufferte Daten

Der folgende Code zeigt den normalen, gepufferten Zugriff auf Systemaufrufdaten mittels „trace.UseSyscalls()“:

using Microsoft.Windows.EventTracing;
using Microsoft.Windows.EventTracing.Processes;
using Microsoft.Windows.EventTracing.Syscalls;
using System;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        if (args.Length != 1)
        {
            Console.Error.WriteLine("Usage: <trace.etl>");
            return;
        }

        using (ITraceProcessor trace = TraceProcessor.Create(args[0]))
        {
            IPendingResult<ISyscallDataSource> pendingSyscallData = trace.UseSyscalls();

            trace.Process();

            ISyscallDataSource syscallData = pendingSyscallData.Result;

            Dictionary<IProcess, int> syscallsPerCommandLine = new Dictionary<IProcess, int>();

            foreach (ISyscall syscall in syscallData.Syscalls)
            {
                IProcess process = syscall.Thread?.Process;

                if (process == null)
                {
                    continue;
                }

                if (!syscallsPerCommandLine.ContainsKey(process))
                {
                    syscallsPerCommandLine.Add(process, 0);
                }

                ++syscallsPerCommandLine[process];
            }

            Console.WriteLine("Process Command Line: Syscalls Count");

            foreach (IProcess process in syscallsPerCommandLine.Keys)
            {
                Console.WriteLine($"{process.CommandLine}: {syscallsPerCommandLine[process]}");
            }
        }
    }
}

Zugreifen auf Streamingdaten

Bei einer umfangreichen Ablaufverfolgung von Systemaufrufen kann der Versuch, die Systemaufrufdaten im Arbeitsspeicher zu puffern, sehr teuer oder sogar unmöglich sein. Der folgende Code zeigt, wie Sie auf dieselben Systemaufrufdaten per Streaming zugreifen, wobei Sie „trace.UseSyscalls()“ durch „trace.UseStreaming().UseSyscalls()“ ersetzen:

using Microsoft.Windows.EventTracing;
using Microsoft.Windows.EventTracing.Processes;
using Microsoft.Windows.EventTracing.Syscalls;
using System;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        if (args.Length != 1)
        {
            Console.Error.WriteLine("Usage: <trace.etl>");
            return;
        }

        using (ITraceProcessor trace = TraceProcessor.Create(args[0]))
        {
            IPendingResult<IThreadDataSource> pendingThreadData = trace.UseThreads();

            Dictionary<IProcess, int> syscallsPerCommandLine = new Dictionary<IProcess, int>();

            trace.UseStreaming().UseSyscalls(ConsumerSchedule.SecondPass, context =>
            {
                Syscall syscall = context.Data;
                IProcess process = syscall.GetThread(pendingThreadData.Result)?.Process;

                if (process == null)
                {
                    return;
                }

                if (!syscallsPerCommandLine.ContainsKey(process))
                {
                    syscallsPerCommandLine.Add(process, 0);
                }

                ++syscallsPerCommandLine[process];
            });

            trace.Process();

            Console.WriteLine("Process Command Line: Syscalls Count");

            foreach (IProcess process in syscallsPerCommandLine.Keys)
            {
                Console.WriteLine($"{process.CommandLine}: {syscallsPerCommandLine[process]}");
            }
        }
    }
}

Funktionsweise von Streaming

Standardmäßig werden alle Streamingdaten während des ersten Durchlaufs der Ablaufverfolgung bereitgestellt, und gepufferte Daten aus anderen Quellen sind nicht verfügbar. Das obige Beispiel zeigt, wie Streaming mit Pufferung kombiniert wird: Threaddaten werden vor dem Streaming von Systemaufrufdaten gepuffert. Daraus resultiert, dass die Ablaufverfolgung zweimal gelesen werden muss – einmal, um die gepufferte Threaddaten abzurufen, und ein zweites Mal, um mit den gepufferten Threaddaten, die jetzt verfügbar sind, auf Systemaufruf-Streamingdaten zuzugreifen. Um Streaming und Pufferung auf diese Weise zu kombinieren, übergibt das Beispiel „ConsumerSchedule.SecondPass“ an „trace.UseStreaming().UseSyscalls()“, wodurch die Systemaufrufverarbeitung in einem zweiten Durchlauf der Ablaufverfolgung erfolgt. Durch die Ausführung in einem zweiten Durchlauf kann der Systemaufruf-Rückruf auf das ausstehende Ergebnis von „trace.UseThreads()“ zugreifen, wenn der jeweilige Systemaufruf verarbeitet wird. Ohne dieses optionale Argument wäre das Systemaufrufstreaming im ersten Durchlauf der Ablaufverfolgung ausgeführt worden (es gäbe nur einen Durchlauf), und das ausstehende Ergebnis von „trace.UseThreads()“ wäre noch nicht verfügbar. In diesem Fall hätte der Rückruf weiterhin Zugriff auf die ThreadId aus dem Systemaufruf, aber keinen Zugriff auf den Prozess für den Thread (da der Thread zum Verarbeiten von Verknüpfungsdaten über andere Ereignisse bereitgestellt wird, die möglicherweise noch nicht verarbeitet wurden).

Einige wichtige Unterschiede hinsichtlich der Verwendung zwischen Pufferung und Streaming:

  1. Die Pufferung gibt ein IPendingResult<T> zurück, und das darin enthaltene Ergebnis ist nur verfügbar, bevor die Ablaufverfolgung verarbeitet wurde. Nachdem die Ablaufverfolgung verarbeitet wurde, können die Ergebnisse mit Methoden wie „foreach“ und LINQ aufgezählt werden.
  2. Das Streaming gibt „void“ (leer) zurück und akzeptiert stattdessen ein Rückrufargument. Es ruft den Rückruf jeweils einmal auf, sobald jedes Element verfügbar wird. Da die Daten nicht gepuffert werden, gibt es nie eine Liste mit Ergebnissen, die mit „foreach“ oder LINQ aufgezählt werden könnten. Der Streamingrückruf muss den Teil der Daten puffern, den er nach Abschluss der Verarbeitung zur Verwendung speichern möchte.
  3. Der Code für die Verarbeitung gepufferter Daten steht hinter dem Aufruf von „trace.Process()“, wenn die ausstehenden Ergebnisse verfügbar sind.
  4. Der Code für die Verarbeitung von Streamingdaten wird vor dem Aufruf von „trac.Process()“ als Rückruf der „trace.UseStreaming.Use...()“-Methode angezeigt.
  5. Ein Streamingconsumer kann sich entscheiden, nur einen Teil des Streams zu verarbeiten, und zukünftige Rückrufe abbrechen, indem er „context.Cancel()“ aufruft. Einem Pufferungsconsumer wird immer eine vollständige, gepufferte Liste bereitgestellt.

Korrelierte Streamingdaten

Manchmal werden Ablaufverfolgungsdaten als Abfolge von Ereignissen geliefert, z. B. werden Systemaufrufe über separate enter- und exit-Ereignisse (Eintritt/Beendigung) protokolliert, wobei aber die kombinierten Daten aus beiden Ereignissen hilfreicher sein können. Die Methode „trace.UseStreaming().UseSyscalls()“ korreliert die Daten dieser beiden Ereignisse und stellt sie bereit, sobald das Paar verfügbar wird. Es sind einige Arten von korrelierten Daten über „trace.UseStreaming()“ verfügbar:

Code BESCHREIBUNG
trace.UseStreaming().UseContextSwitchData() Streamt korrelierte Daten zu Kontextwechseln (von kompakten und nicht kompakten Ereignissen mit genaueren SwitchInThreadIds als rohe nicht kompakte Ereignisse).
trace.UseStreaming().UseScheduledTasks() Streamt korrelierte Daten geplanter Aufgaben.
trace.UseStreaming().UseSyscalls() Streamt korrelierte Daten von Systemaufrufen.
trace.UseStreaming().UseWindowInFocus() Streamt korrelierte Daten zu „Fenster im Fokus“.

Eigenständige Streamingereignisse

Darüber hinaus stellt „trace.UseStreaming()“ analysierte Ereignisse für eine Reihe verschiedener eigenständiger Ereignistypen bereit:

Code BESCHREIBUNG
trace.UseStreaming().UseLastBranchRecordEvents() Streamt analysierte LBR-Ereignisse (Last Branch Record).
trace.UseStreaming().UseReadyThreadEvents() Streamt analysierte „Thread bereit“-Ereignisse.
trace.UseStreaming().UseThreadCreateEvents() Streamt analysierte „Thread erstellen“-Ereignisse.
trace.UseStreaming().UseThreadExitEvents() Streamt analysierte „Thread beenden“-Ereignisse.
trace.UseStreaming().UseThreadRundownStartEvents() Streamt analysierte „Thread-Rundown starten“-Ereignisse.
trace.UseStreaming().UseThreadRundownStopEvents() Streamt analysierte „Thread-Rundown beenden“-Ereignisse.
trace.UseStreaming().UseThreadSetNameEvents() Streamt analysierte „Thread Namen festlegen“-Ereignisse.

Zugrunde liegende Streamingereignisse für korrelierte Daten

Schließlich stellt „trace.UseStreaming()“ auch die zugrunde liegenden Ereignisse bereit, die zum Korrelieren der Daten in der obigen Liste verwendet werden. Di zugrunde liegenden Ereignisse sind folgende:

Code BESCHREIBUNG Teil von
trace.UseStreaming().UseCompactContextSwitchEvents() Streamt analysierte kompakte Ereignisse von Kontextwechseln. trace.UseStreaming().UseContextSwitchData()
trace.UseStreaming().UseContextSwitchEvents() Streamt analysierte Ereignisse von Kontextwechseln. SwitchInThreadIds sin in manchen Fällen eventuell nicht genau. trace.UseStreaming().UseContextSwitchData()
trace.UseStreaming().UseFocusChangeEvents() Streamt analysierte Ereignisse von Änderungen des Fensterfokus. trace.UseStreaming().UseWindowInFocus()
trace.UseStreaming().UseScheduledTaskStartEvents() Streamt analysierte Ereignisse geplanter Aufgabenstarts. trace.UseStreaming().UseScheduledTasks()
trace.UseStreaming().UseScheduledTaskStopEvents() Streamt analysierte Ereignisse geplanter Aufgabenbeendigungen. trace.UseStreaming().UseScheduledTasks()
trace.UseStreaming().UseScheduledTaskTriggerEvents() Streamt analysierte Ereignisse geplanter Aufgabentrigger. trace.UseStreaming().UseScheduledTasks()
trace.UseStreaming().UseSessionLayerSetActiveWindowEvents() Streams analysierte Ereignisse von „Fenster als aktiv festlegen“ auf Sitzungsebene. trace.UseStreaming().UseWindowInFocus()
trace.UseStreaming().UseSyscallEnterEvents() Streamt analysierte Ereignisse von Systemaufrufeintritten. trace.UseStreaming().UseSyscalls()
trace.UseStreaming().UseSyscallExitEvents() Streamt analysierte Ereignisse von Systemaufrufbeendigungen. trace.UseStreaming().UseSyscalls()

Nächste Schritte

In diesem Tutorial haben Sie erfahren, wie Sie Streaming verwenden, um sofort auf Ablaufverfolgungsdaten zuzugreifen und weniger Arbeitsspeicher zu verwenden.

Der nächste Schritt besteht darin, auf die Daten zuzugreifen, die Sie aus Ihren Ablaufverfolgungen erhalten möchten. Sehen Sie sich die Beispiele an, um ein paar Ideen zu erhalten. Beachten Sie, dass nicht alle Ablaufverfolgungen alle unterstützten Datentypen enthalten.