Condividi tramite


Usare lo streaming con TraceProcessor

Per impostazione predefinita, TraceProcessor accede ai dati caricandoli in memoria durante l'elaborazione della traccia. Questo approccio di buffering è facile da usare, ma può essere costoso in termini di utilizzo della memoria.

TraceProcessor fornisce anche la traccia. UseStreaming(), che supporta l'accesso a più tipi di dati traccia in modalità streaming (elaborazione dei dati durante la lettura dal file traccia, anziché memorizzare nel buffer i dati in memoria). Ad esempio, una traccia syscalls può essere piuttosto grande e memorizzare nel buffer l'intero elenco di chiamate di sistema in una traccia può essere piuttosto costoso.

Accesso ai dati memorizzati nel buffer

Il codice seguente mostra l'accesso ai dati syscall nel modo normale memorizzato nel buffer tramite 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]}");
            }
        }
    }
}

Accesso ai dati in streaming

Con una traccia syscall di grandi dimensioni, il tentativo di memorizzare nel buffer i dati syscall in memoria può essere piuttosto costoso o potrebbe non essere possibile. Nel codice seguente viene illustrato come accedere agli stessi dati syscall in modalità streaming, sostituendo trace.UseSyscalls() con trace.UseStreaming().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<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]}");
            }
        }
    }
}

Come funziona lo streaming

Per impostazione predefinita, tutti i dati di streaming vengono forniti durante il primo passaggio della traccia e i dati memorizzati nel buffer da altre origini non sono disponibili. L'esempio precedente illustra come combinare lo streaming con il buffering: i dati del thread vengono memorizzati nel buffer prima che i dati syscall vengano trasmessi. Di conseguenza, la traccia deve essere letta due volte, una volta per ottenere i dati del thread memorizzati nel buffer e una seconda volta per accedere ai dati syscall di streaming con i dati thread memorizzati nel buffer ora disponibili. Per combinare streaming e buffering in questo modo, l'esempio passa ConsumerSchedule.SecondPass a trace.UseStreaming().UseSyscalls(), che fa sì che l'elaborazione delle chiamate di sistema venga eseguita in un secondo passaggio attraverso la traccia. Eseguito in un secondo passaggio, il callback syscall può accedere al risultato in sospeso da trace.UseThreads() quando elabora ogni syscall. Senza questo argomento facoltativo, lo streaming syscall sarebbe stato eseguito nel primo passaggio della traccia (ci sarebbe un solo passaggio) e il risultato in sospeso di trace.UseThreads() non sarebbe ancora disponibile. In tal caso, callback avrebbe ancora accesso a ThreadId da syscall, ma non avrebbe accesso al processo per il thread (perché il thread per elaborare i dati di collegamento viene fornito tramite altri eventi che potrebbero non essere ancora stati elaborati).

Alcune differenze principali nell'utilizzo tra buffering e streaming:

  1. Il buffering restituisce un IPendingResult<T> e il risultato che contiene è disponibile solo prima dell'elaborazione della traccia. Dopo l'elaborazione della traccia, i risultati possono essere enumerati usando tecniche come foreach e LINQ.
  2. Lo streaming restituisce void e accetta invece un argomento callback. Richiama callback una volta quando ogni elemento diventa disponibile. Poiché i dati non vengono memorizzati nel buffer, non è mai presente un elenco di risultati da enumerare con foreach o LINQ. Il callback streaming deve memorizzare nel buffer qualsiasi parte dei dati da salvare per l'uso al termine dell'elaborazione.
  3. Il codice per l'elaborazione dei dati memorizzati nel buffer viene visualizzato dopo il richiamo di trace.Process(), quando sono disponibili i risultati in sospeso.
  4. Il codice per l'elaborazione dei dati di streaming viene visualizzato prima del richiamo di trace.Process(), come callback al metodo trace.UseStreaming.Use...().
  5. Un consumer streaming può scegliere di elaborare solo parte dello stream e annullare i callback futuri richiamando context.Cancel(). A un consumer buffering viene sempre fornito un elenco completo e memorizzato nel buffer.

Dati streaming correlati

A volte dati traccia vengono inseriti in una sequenza di eventi, ad esempio, le syscall vengono registrate tramite eventi di immissione e uscita separati, ma i dati combinati di entrambi gli eventi possono essere più utili. Il metodo trace.UseStreaming().UseSyscalls() correla i dati di entrambi questi eventi e li fornisce man mano che la coppia diventa disponibile. Alcuni tipi di dati correlati sono disponibili tramite trace.UseStreaming():

Codice Descrizione
trace.UseStreaming().UseContextSwitchData() Flussi dati di switch contestuali correlati (da eventi compattati e non compattati, con switchInThreadId più accurati rispetto agli eventi non elaborati non compattati).
trace.UseStreaming().UseScheduledTasks() Flussi di dati correlati a attività pianificate.
trace.UseStreaming().UseSyscalls() Flussi di dati correlati a chiamata di sistema.
trace.UseStreaming().UseWindowInFocus() Flussi di dati correlati alla finestra in stato attivo.

Eventi di streaming autonomi

Inoltre, trace.UseStreaming() fornisce eventi analizzati per diversi tipi di eventi autonomi:

Codice Descrizione
trace.UseStreaming().UseLastBranchRecordEvents() Flussi di eventi Last Branch Record (LBR) analizzati.
trace.UseStreaming().UseReadyThreadEvents() Flussi di eventi thread pronti analizzati.
trace.UseStreaming().UseThreadCreateEvents() Flussi di eventi che creano thread analizzato.
trace.UseStreaming().UseThreadExitEvents() Flussi di eventi in uscita thread analizzato.
trace.UseStreaming().UseThreadRundownStartEvents() Flussi gli eventi di avvio rundown del thread analizzato.
trace.UseStreaming().UseThreadRundownStopEvents() Flussi gli eventi di arresto rundown del thread analizzato.
trace.UseStreaming().UseThreadSetNameEvents() Flussi di eventi del nome del set thread analizzato.

Eventi di streaming sottostanti per dati correlati

Infine, trace.UseStreaming() fornisce anche gli eventi sottostanti usati per correlare i dati nell'elenco precedente. Questi eventi sottostanti sono:

Codice Descrizione Incluso in
trace.UseStreaming().UseCompactContextSwitchEvents() Flussi eventi di cambio di contesto compatto analizzati. trace.UseStreaming().UseContextSwitchData()
trace.UseStreaming().UseContextSwitchEvents() Flussi di eventi di cambio contesto analizzati. SwitchInThreadIds potrebbe non essere accurato in alcuni casi. trace.UseStreaming().UseContextSwitchData()
trace.UseStreaming().UseFocusChangeEvents() Flussi di eventi di modifica della finestra in stato attivo analizzata. trace.UseStreaming().UseWindowInFocus()
trace.UseStreaming().UseScheduledTaskStartEvents() Flussi di eventi di avvio attività pianificata analizzata. trace.UseStreaming().UseScheduledTasks()
trace.UseStreaming().UseScheduledTaskStopEvents() Flussi di eventi di arresto attività pianificata analizzata. trace.UseStreaming().UseScheduledTasks()
trace.UseStreaming().UseScheduledTaskTriggerEvents() Flussi di eventi di trigger di attività pianificate analizzate. trace.UseStreaming().UseScheduledTasks()
trace.UseStreaming().UseSessionLayerSetActiveWindowEvents() Flussi di eventi della finestra attiva del set di livelli di sessione analizzati. trace.UseStreaming().UseWindowInFocus()
trace.UseStreaming().UseSyscallEnterEvents() Flussi di eventi syscall analizzati. trace.UseStreaming().UseSyscalls()
trace.UseStreaming().UseSyscallExitEvents() Flussi ed eventi in uscita syscall analizzati. trace.UseStreaming().UseSyscalls()

Passaggi successivi

In questo tutorial è stato illustrato come usare lo streaming per accedere immediatamente ai dati traccia e usare meno memoria.

Il passaggio successivo consiste nell'esaminare i dati desiderati dalle tracce. Esaminare gli esempi per alcune idee. Si noti che non tutte le tracce includono tutti i tipi di dati supportati.