Compartir a través de


Uso del streaming con TraceProcessor

De forma predeterminada, TraceProcessor accede a los datos cargandolos en memoria a medida que se procesa el seguimiento. Este enfoque de almacenamiento en búfer es fácil de usar, pero puede ser costoso en términos de uso de memoria.

TraceProcessor también proporciona trace.UseStreaming(), que admite el acceso a varios tipos de datos de seguimiento de forma continua (procesando los datos a medida que se leen del archivo de seguimiento, en vez de almacenar esos datos en la memoria). Por ejemplo, un rastreo de llamadas al sistema puede ser bastante grande, y almacenar toda la lista de estas llamadas en una memoria intermedia puede ser bastante caro.

Acceso a datos almacenados en búfer

El código siguiente muestra cómo acceder a los datos de syscall de la manera normal mediante almacenamiento en búfer usando 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]}");
            }
        }
    }
}

Acceso a datos de streaming

Con una traza de syscalls grande, el intento de almacenar en búfer los datos de las syscalls en la memoria puede ser bastante costoso, o incluso puede no ser posible. En el código siguiente se muestra cómo acceder a los mismos datos de syscall de manera continua, reemplazando 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]}");
            }
        }
    }
}

Funcionamiento del streaming

De forma predeterminada, todos los datos de streaming se proporcionan durante el primer paso a través del seguimiento y los datos almacenados en búfer de otros orígenes no están disponibles. En el ejemplo anterior se muestra cómo combinar streaming con almacenamiento en búfer: los datos del subproceso se almacena en búfer antes de transmitir los datos de syscall. Como resultado, el seguimiento debe leerse dos veces: primero, para obtener los datos de los subprocesos almacenados en búfer, y luego, para acceder a los datos de llamadas al sistema de transmisión con estos datos de subprocesos ya disponibles. Para combinar el streaming y el almacenamiento en búfer de esta manera, el ejemplo pasa ConsumerSchedule.SecondPass a trace.UseStreaming().UseSyscalls(), lo que provoca que el procesamiento de syscalls se realice en un segundo paso a través de la traza. Al ejecutarse en un segundo paso, la devolución de llamada del sistema puede acceder al resultado pendiente de trace.UseThreads() cuando procesa cada llamada al sistema. Sin este argumento opcional, el streaming de syscall se habría ejecutado en la primera pasada a través del seguimiento (solo habría una pasada), y el resultado pendiente del seguimiento de trace.UseThreads() aún no estaría disponible. En ese caso, la devolución de llamada todavía tendría acceso al ThreadId del syscall, pero no tendría acceso al proceso del subproceso (porque los datos de enlace de subproceso a proceso se proporcionan a través de otros eventos que podrían no haberse procesado todavía).

Algunas diferencias clave en el uso entre el almacenamiento en búfer y el streaming:

  1. El almacenamiento en búfer devuelve una IPendingResult<T>y el resultado que contiene solo está disponible antes de que se haya procesado el seguimiento. Una vez que se haya procesado el seguimiento, los resultados se pueden enumerar utilizando técnicas como foreach y LINQ.
  2. El streaming devuelve void y, en su lugar, acepta un argumento de devolución de llamada. Llama al callback una vez cuando cada elemento esté disponible. Dado que los datos no están almacenados en búfer, nunca hay una lista de resultados para enumerar con foreach o LINQ: la devolución de llamada de streaming debe almacenar en búfer cualquier parte de los datos que quiera guardar para su uso una vez completado el procesamiento.
  3. El código para procesar datos almacenados en búfer aparece después de la llamada al seguimiento. Process(), cuando los resultados pendientes están disponibles.
  4. El código para procesar los datos de streaming aparece antes de la llamada a trace.Process(), como callback al método trace.UseStreaming.Use...().
  5. Un consumidor de streaming puede optar por procesar solo parte de la secuencia y cancelar futuras devoluciones de llamada llamando a context.Cancel(). Un consumidor con almacenamiento en búfer siempre recibe una lista completa y almacenada en búfer.

Datos de streaming correlacionados

A veces, los datos de seguimiento se producen en una secuencia de eventos; por ejemplo, las llamadas de sys se registran a través de eventos de entrada y salida independientes, pero los datos combinados de ambos eventos pueden ser más útiles. El método trace.UseStreaming().UseSyscalls() correlaciona los datos de ambos eventos y los proporciona a medida que la pareja de datos está disponible. Hay algunos tipos de datos correlacionados disponibles a través de trace.UseStreaming().

Código Descripción
traza.UseStreaming().UseContextSwitchData() Transmite datos sobre el cambio de contexto correlacionado (de eventos compactos y no compactos, con SwitchInThreadIds más precisos que los eventos no compactos sin formato).
rastro. UseStreaming(). UseScheduledTasks() Transmite datos de tareas programadas correlacionadas.
rastro. UseStreaming(). UseSyscalls() Transmite los datos de llamadas del sistema correlacionados.
rastro. UseStreaming(). UseWindowInFocus() Transmite datos de ventana en foco correlacionados.

Eventos de streaming independientes

Además, seguimiento.UseStreaming() proporciona eventos analizados para varios tipos diferentes de eventos independientes.

Código Descripción
rastro. UseStreaming(). UseLastBranchRecordEvents() Transmite eventos de última rama registrada (LBR) que han sido analizados.
Trace.UseStreaming().UseReadyThreadEvents() Transmite eventos de subprocesos ya analizados y listos.
rastro. UseStreaming(). UseThreadCreateEvents() Transmite eventos de creación de subprocesos analizados.
rastro. UseStreaming(). UseThreadExitEvents() Transmite eventos de salida de subprocesos analizados.
rastro. UseStreaming(). UseThreadRundownStartEvents() Transmite eventos de inicio de la ejecución de subprocesos analizados.
rastro. UseStreaming(). UseThreadRundownStopEvents() Transmite eventos de parada de finalización de subprocesos.
rastro. UseStreaming(). UseThreadSetNameEvents() Transmite eventos de nombre del conjunto de subprocesos analizados.

Eventos de streaming subyacentes para datos correlacionados

Por último, trace. UseStreaming() también proporciona los eventos subyacentes que se usan para correlacionar los datos de la lista anterior. Estos eventos subyacentes son:

Código Descripción Incluido en
rastro. UseStreaming(). UseCompactContextSwitchEvents() Transmite eventos de cambio de contexto compacto analizados. traza.UseStreaming().UseContextSwitchData()
rastro. UseStreaming(). UseContextSwitchEvents() Transmite eventos de cambio de contexto analizados. Es posible que SwitchInThreadIds no sea preciso en algunos casos. traza.UseStreaming().UseContextSwitchData()
rastro. UseStreaming(). UseFocusChangeEvents() Transmitir eventos analizados de cambio de foco de ventana. rastro. UseStreaming(). UseWindowInFocus()
rastro. UseStreaming(). UseScheduledTaskStartEvents() Transmite eventos de inicio de tareas programadas analizados. rastro. UseStreaming(). UseScheduledTasks()
rastro. UseStreaming(). UseScheduledTaskStopEvents() Transmite eventos de detención de tareas programadas analizados. rastro. UseStreaming(). UseScheduledTasks()
rastro. UseStreaming(). UseScheduledTaskTriggerEvents() Flujos de eventos de activación de tareas programadas analizados. rastro. UseStreaming(). UseScheduledTasks()
rastro. UseStreaming(). UseSessionLayerSetActiveWindowEvents() Transmite eventos de ventana activa configurados en la capa de sesión. rastro. UseStreaming(). UseWindowInFocus()
traza.UseStreaming().UseSyscallEnterEvents() Los flujos analizados de eventos de entrada de llamadas al sistema. rastro. UseStreaming(). UseSyscalls()
rastro. UseStreaming(). UseSyscallExitEvents() Transmite eventos de salida de syscall analizados. rastro. UseStreaming(). UseSyscalls()

Pasos siguientes

En este tutorial, ha aprendido a usar streaming para acceder a los datos de seguimiento inmediatamente y a usar menos memoria.

El siguiente paso es acceder a los datos que desea desde sus rastros. Examine los ejemplos para obtener algunas ideas. Tenga en cuenta que no todos los seguimientos incluyen todos los tipos de datos admitidos.