Compartilhar via


Usar o streaming com o TraceProcessor

Por padrão, o TraceProcessor acessa os dados carregando-os na memória à medida que o rastreamento é processado. Essa abordagem de buffer é fácil de usar, mas pode ser cara em termos de uso de memória.

O TraceProcessor também fornece o trace.UseStreaming(), que dá suporte ao acesso a vários tipos de dados de rastreamento por streaming (processando os dados conforme eles são lidos do arquivo de rastreamento, em vez de armazená-los em buffer na memória). Por exemplo, um rastreamento de syscalls pode ser muito grande, e o armazenamento em buffer de toda a lista de syscalls em um rastreamento pode ser muito caro.

Como acessar os dados armazenados em buffer

O seguinte código mostra como acessar os dados de syscall da maneira normal e em buffer por meio do 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]}");
            }
        }
    }
}

Como acessar os dados de streaming

Com um rastreamento grande de syscalls, tentar armazenar em buffer os dados de syscall na memória pode ser muito caro ou talvez nem seja possível. O seguinte código mostra como acessar os mesmos dados de syscall por streaming, substituindo o trace.UseSyscalls() pelo 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]}");
            }
        }
    }
}

Como funciona o streaming

Por padrão, todos os dados de streaming são fornecidos durante a primeira passagem do rastreamento, e os dados armazenados em buffer de outras fontes não ficam disponíveis. O exemplo acima mostra como combinar o streaming com o armazenamento em buffer: os dados de thread são armazenados em buffer antes do streaming dos dados de syscall. Como resultado, o rastreamento precisa ser lido duas vezes: uma vez para obter os dados de thread armazenados em buffer e uma segunda vez para acessar os dados de syscall de streaming com os dados de thread armazenados em buffer agora disponíveis. Para combinar o streaming e o buffer dessa maneira, o exemplo transmite ConsumerSchedule.SecondPass para o trace.UseStreaming().UseSyscalls(), que faz com que o processamento de syscall ocorra em uma segunda passagem pelo rastreamento. Durante a execução em uma segunda passagem, o retorno de chamada de syscall pode acessar o resultado pendente do trace.UseThreads() quando ele processa cada syscall. Sem esse argumento opcional, o streaming de syscall é executado na primeira passagem do rastreamento (há apenas uma passagem) e o resultado pendente do trace.UseThreads() ainda não fica disponível. Nesse caso, o retorno de chamada ainda tem acesso à ThreadId do syscall, mas não tem acesso ao processo do thread (porque o thread usado para processar os dados de vinculação é fornecido por meio de outros eventos que ainda podem não ter sido processados).

Algumas diferenças importantes de uso entre o buffer e o streaming:

  1. O buffer retorna um IPendingResult<T>, e o resultado que ele contém fica disponível somente antes que o rastreamento seja processado. Depois que o rastreamento é processado, os resultados podem ser enumerados com técnicas como foreach e LINQ.
  2. O streaming retorna nulo e, em vez disso, usa um argumento de retorno de chamada. Ele chama o retorno de chamada uma vez à medida que cada item fica disponível. Como os dados não são armazenados em buffer, nunca há uma lista de resultados para enumeração com foreach ou LINQ. O retorno de chamada de streaming precisa armazenar em buffer qualquer parte dos dados que deseja salvar para uso após a conclusão do processamento.
  3. O código para processamento de dados armazenados em buffer é exibido após a chamada ao trace.Process(), quando os resultados pendentes ficam disponíveis.
  4. O código para processamento dos dados de streaming é exibido antes da chamada ao trace.Process(), como um retorno de chamada ao método trace.UseStreaming.Use…().
  5. Um consumidor de streaming pode optar por processar apenas parte do fluxo e cancelar os retornos de chamada futuros chamando context.Cancel(). Um consumidor de buffer sempre recebe uma lista completa armazenada em buffer.

Dados de streaming correlacionados

Às vezes, os dados de rastreamento vêm em uma sequência de eventos. Por exemplo, os syscalls são registrados em log por meio de eventos de entrada e saída separados, mas os dados combinados de ambos os eventos podem ser mais úteis. O método trace.UseStreaming().UseSyscalls() correlaciona os dados desses dois eventos e fornece-os à medida que o par fica disponível. Alguns tipos de dados correlacionados estão disponíveis por meio do trace.UseStreaming():

Código Descrição
trace.UseStreaming().UseContextSwitchData() Gera fluxos de dados da alternância de contexto correlacionados (de eventos compactos e não compactos, com os SwitchInThreadIds mais precisos do que os eventos brutos não compactos).
trace.UseStreaming().UseScheduledTasks() Gera fluxos de dados da tarefa agendada correlacionados.
trace.UseStreaming().UseSyscalls() Gera fluxos de dados da chamada do sistema correlacionados.
trace.UseStreaming().UseWindowInFocus() Gera fluxos de dados da janela em foco correlacionados.

Eventos de streaming autônomos

Além disso, o trace.UseStreaming() fornece eventos analisados para vários tipos de eventos autônomos diferentes:

Código Descrição
trace.UseStreaming().UseLastBranchRecordEvents() Gera fluxos dos eventos LBR (último registro de ramificação) analisados.
trace.UseStreaming().UseReadyThreadEvents() Gera fluxos dos eventos de thread prontos analisados.
trace.UseStreaming().UseThreadCreateEvents() Gera fluxos dos eventos de criação de thread analisados.
trace.UseStreaming().UseThreadExitEvents() Gera fluxos dos eventos de saída de thread analisados.
trace.UseStreaming().UseThreadRundownStartEvents() Gera fluxos dos eventos de início de encerramento de thread analisados.
trace.UseStreaming().UseThreadRundownStopEvents() Gera fluxos dos eventos de parada de encerramento de thread analisados.
trace.UseStreaming().UseThreadSetNameEvents() Gera fluxos dos eventos de nome do conjunto de threads analisados.

Eventos de streaming subjacentes para dados correlacionados

Por fim, o trace.UseStreaming() também fornece os eventos subjacentes usados para correlacionar os dados na lista acima. Esses eventos subjacentes são:

Código Descrição Incluído em
trace.UseStreaming().UseCompactContextSwitchEvents() Gera fluxos dos eventos de alternância de contexto compacto analisados. trace.UseStreaming().UseContextSwitchData()
trace.UseStreaming().UseContextSwitchEvents() Gera fluxos dos eventos de alternância de contexto analisados. O SwitchInThreadIds pode não ser preciso em alguns casos. trace.UseStreaming().UseContextSwitchData()
trace.UseStreaming().UseFocusChangeEvents() Gera fluxos dos eventos de alteração de foco da janela analisados. trace.UseStreaming().UseWindowInFocus()
trace.UseStreaming().UseScheduledTaskStartEvents() Gera fluxos dos eventos de início de tarefa agendada analisados. trace.UseStreaming().UseScheduledTasks()
trace.UseStreaming().UseScheduledTaskStopEvents() Gera fluxos dos eventos de parada de tarefa agendada analisados. trace.UseStreaming().UseScheduledTasks()
trace.UseStreaming().UseScheduledTaskTriggerEvents() Gera fluxos dos eventos de gatilho de tarefa agendada analisados. trace.UseStreaming().UseScheduledTasks()
trace.UseStreaming().UseSessionLayerSetActiveWindowEvents() Gera fluxos dos eventos de janela ativa do conjunto de camadas de sessão analisados. trace.UseStreaming().UseWindowInFocus()
trace.UseStreaming().UseSyscallEnterEvents() Gera fluxos dos eventos de entrada de syscall analisados. trace.UseStreaming().UseSyscalls()
trace.UseStreaming().UseSyscallExitEvents() Gera fluxos dos eventos de saída de syscall analisados. trace.UseStreaming().UseSyscalls()

Próximas etapas

Neste tutorial, você aprendeu a usar o streaming para acessar os dados de rastreamento imediatamente e usar menos memória.

A próxima etapa será procurar acessar os dados que você deseja nos rastreamentos. Veja os exemplos para obter algumas ideias. Observe que nem todos os rastreamentos incluem todos os tipos de dados compatíveis.