Share via


Uso del streaming con TraceProcessing

Para acceder a los datos, TraceProcessor los carga de forma predeterminada en la memoria a medida que se procesa el seguimiento. Este enfoque de almacenamiento en búfer es fácil de usar, pero puede resultar costoso en términos de uso de memoria.

TraceProcessor también proporciona el elemento trace.UseStreaming(), que admite el acceso a varios tipos de datos de seguimiento mediante streaming (los datos se procesan a medida que se leen desde el archivo de seguimiento, en lugar de almacenarse en búfer en la memoria). Por ejemplo, el tamaño de un seguimiento de llamadas del sistema puede ser bastante grande y almacenar en búfer toda esta lista de llamadas en un seguimiento puede ser bastante costoso.

Acceso a los datos almacenados en búfer

En el código siguiente se muestra cómo se puede acceder a los datos de la llamada del sistema tal y como se hace habitualmente (con el almacenamiento en búfer) a través de 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 los datos de streaming

Con un seguimiento de llamadas del sistema de gran tamaño, intentar almacenar los datos de la llamada del sistema en la memoria puede ser bastante costoso, o incluso imposible. En el código siguiente se muestra cómo acceder a los mismos datos de la llamada del sistema mediante streaming si se reemplaza trace.UseSyscalls() por 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 la primera fase de 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 el streaming con el almacenamiento en búfer: los datos del subproceso se almacenan en búfer antes de que se transmitan los datos de la llamada del sistema. Como resultado, el seguimiento debe leerse dos veces: una para obtener los datos del subproceso almacenados en búfer y otra para acceder a los datos de la llamada del sistema de streaming con los datos del subproceso almacenados en búfer que ahora hay disponibles. Para combinar el streaming y el almacenamiento en búfer de esta manera, en el ejemplo se pasa el elemento ConsumerSchedule.SecondPass al elemento trace.UseStreaming().UseSyscalls(), que hace que el procesamiento de la llamada del sistema se haga en una segunda fase de seguimiento. Al ejecutarse la segunda vez, la devolución de la llamada del sistema puede acceder al resultado pendiente de trace.UseThreads() cuando procesa cada llamada del sistema. Sin este argumento opcional, el streaming de la llamada del sistema se habría ejecutado en el primer seguimiento (solo se haría uno) y el resultado pendiente de trace.UseThreads() aún no estaría disponible. En ese caso, la devolución de llamada seguiría teniendo acceso al valor de ThreadId desde la llamada del sistema, pero no tendría acceso al proceso para el subproceso (porque el subproceso para procesar los datos de vinculación se proporciona a través de otros eventos que es posible que aún no se hayan procesado).

A continuación, enumeramos algunas diferencias clave del uso entre el almacenamiento en búfer y el streaming:

  1. El almacenamiento en búfer devuelve el valor IPendingResult<T>, y el resultado que contiene solo está disponible antes de que el seguimiento se haya procesado. Una vez procesado el seguimiento, los resultados se pueden enumerar mediante técnicas como foreach y LINQ.
  2. El streaming devuelve un valor nulo y, en su lugar, toma un argumento de devolución de llamada. Llama a la devolución de llamada cada vez que un elemento pasa a estar 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 a trace.Process(), cuando los resultados pendientes están disponibles.
  4. El código para procesar datos de streaming aparece antes de la llamada a trace.Process(), como devolución de llamada al método trace.UseStreaming.Use...().
  5. Un consumidor de streaming puede optar por procesar solo una parte de la secuencia y cancelar futuras devoluciones de llamada mediante una llamada a context.Cancel(). Siempre se proporciona una lista completa almacenada en búfer a los consumidores de almacenamiento en búfer.

Datos de streaming correlacionados

A veces, los datos de seguimiento se incluyen en una secuencia de eventos; por ejemplo, las llamadas del sistema se registran a través de eventos de entrada y salida independientes, pero los datos combinados de ambos eventos pueden resultar más útiles. El método trace.UseStreaming().UseSyscalls() correlaciona los datos de ambos eventos y los proporciona cuando el par pasa a estar disponible. Hay algunos tipos de datos correlacionados disponibles a través de trace.UseStreaming():

Código Descripción
trace.UseStreaming().UseContextSwitchData() Datos de cambio de contexto correlacionados por secuencias (de eventos compactos y no compactos con valores de SwitchInThreadIds más precisos que los eventos no compactos sin procesar).
trace.UseStreaming().UseScheduledTasks() Transmite datos correlacionados de tareas programadas.
trace.UseStreaming().UseSyscalls() Transmite datos correlacionados de llamadas del sistema.
trace.UseStreaming().UseWindowInFocus() Transmite datos correlacionados de la ventana enfocada.

Eventos de streaming independientes

Además, trace.UseStreaming() proporciona eventos de análisis para varios tipos de eventos independientes diferentes:

Código Descripción
trace.UseStreaming().UseLastBranchRecordEvents() Transmite eventos analizados del último registro de rama (LBR).
trace.UseStreaming().UseReadyThreadEvents() Transmite eventos de subproceso listos para analizar.
trace.UseStreaming().UseThreadCreateEvents() Transmite eventos analizados de creación de subprocesos.
trace.UseStreaming().UseThreadExitEvents() Transmite eventos analizados de salida de subprocesos.
trace.UseStreaming().UseThreadRundownStartEvents() Transmite eventos analizados de inicio del resumen de los subprocesos.
trace.UseStreaming().UseThreadRundownStopEvents() Transmite eventos analizados de detención del resumen de los subprocesos.
trace.UseStreaming().UseThreadSetNameEvents() Transmite eventos analizados del nombre del conjunto de subprocesos.

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 los siguientes:

Código Descripción Incluido en
trace.UseStreaming().UseCompactContextSwitchEvents() Transmite eventos analizados de cambio de contexto compacto. trace.UseStreaming().UseContextSwitchData()
trace.UseStreaming().UseContextSwitchEvents() Transmite eventos analizados de cambio de contexto. Es posible que SwitchInThreadIds no sea preciso en algunos casos. trace.UseStreaming().UseContextSwitchData()
trace.UseStreaming().UseFocusChangeEvents() Transmite eventos analizados de cambio de enfoque de la ventana. trace.UseStreaming().UseWindowInFocus()
trace.UseStreaming().UseScheduledTaskStartEvents() Transmite eventos analizados de inicio de tareas programadas. trace.UseStreaming().UseScheduledTasks()
trace.UseStreaming().UseScheduledTaskStopEvents() Transmite eventos analizados de detención de tareas programadas. trace.UseStreaming().UseScheduledTasks()
trace.UseStreaming().UseScheduledTaskTriggerEvents() Transmite eventos analizados de desencadenamiento de tareas programadas. trace.UseStreaming().UseScheduledTasks()
trace.UseStreaming().UseSessionLayerSetActiveWindowEvents() Transmite eventos analizados de la ventana activa del conjunto de niveles de sesión. trace.UseStreaming().UseWindowInFocus()
trace.UseStreaming().UseSyscallEnterEvents() Transmite eventos analizados de entrada de llamadas del sistema. trace.UseStreaming().UseSyscalls()
trace.UseStreaming().UseSyscallExitEvents() Transmite eventos analizados de salida de llamadas del sistema. trace.UseStreaming().UseSyscalls()

Pasos siguientes

En este tutorial, ha aprendido a usar el streaming para acceder a los datos de seguimiento de inmediato, así como a utilizar menos memoria.

En el siguiente paso, verá cómo puede obtener acceso a los datos que quiere desde los seguimientos. Consulte los ejemplos para obtener algunas ideas. Tenga en cuenta que no todos los seguimientos incluyen todos los tipos de datos admitidos.