Udostępnij przez


Używanie przesyłania strumieniowego z TraceProcessor

Domyślnie funkcja TraceProcessor uzyskuje dostęp do danych przez załadowanie ich do pamięci podczas przetwarzania śledzenia. Takie podejście buforowania jest łatwe w użyciu, ale może być kosztowne pod względem użycia pamięci.

TraceProcessor udostępnia także funkcję UseStreaming(), która umożliwia dostęp do wielu typów danych śladu w sposób strumieniowy (przetwarzanie danych w miarę ich odczytywania z pliku śladu, zamiast buforowania tych danych w pamięci). Na przykład ślad wywołań systemowych może być dość duży, a buforowanie całej listy wywołań systemowych w śladzie może być dość kosztowne.

Uzyskiwanie dostępu do danych buforowanych

Poniższy kod przedstawia dostęp do danych syscall w normalny, buforowany sposób za pośrednictwem 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]}");
            }
        }
    }
}

Uzyskiwanie dostępu do danych przesyłanych strumieniowo

W przypadku dużego śladu wywołań systemowych próba buforowania danych wywołań w pamięci może być dość kosztowna lub może nawet okazać się niemożliwa. Poniższy kod pokazuje, jak uzyskać dostęp do tych samych danych syscall w sposób strumieniowy, zastępując trace.UseSyscalls() funkcją 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]}");
            }
        }
    }
}

Jak działa przesyłanie strumieniowe

Domyślnie wszystkie dane przesyłane strumieniowo są udostępniane podczas pierwszego przejścia przez ślad, a buforowane dane z innych źródeł nie są dostępne. W powyższym przykładzie pokazano, jak połączyć przesyłanie strumieniowe z buforowaniem — dane wątku są buforowane przed strumieniowym przesyłaniem danych dotyczących wywołań systemowych. W rezultacie śledzenie musi być odczytane dwa razy – raz, aby uzyskać buforowane dane wątku, a drugi raz, aby uzyskać dostęp do danych systemowych przesyłanych strumieniowo, mając już dostępne buforowane dane wątku. Aby połączyć przesyłanie strumieniowe i buforowanie w ten sposób, przykład przekazuje element ConsumerSchedule.SecondPass do funkcji trace.UseStreaming().UseSyscalls(), co powoduje, że przetwarzanie syscall odbywa się w drugim przejściu przez ślad. Po uruchomieniu w drugim przebiegu, wywołanie zwrotne syscall może uzyskać dostęp do oczekujących wyników z funkcji trace.UseThreads() podczas przetwarzania każdego wywołania systemowego. Bez tego opcjonalnego argumentu, przesyłanie strumieniowe wywołań systemowych uruchomiłoby się podczas pierwszego przetwarzania śladu (byłoby tylko jedno przetwarzanie), a oczekiwany wynik z funkcji trace.UseThreads() nie byłby jeszcze dostępny. W takim przypadku funkcja zwrotna nadal będzie miała dostęp do identyfikatora ThreadId z syscall, ale nie będzie miała dostępu do procesu, do którego należy wątek (ponieważ powiązanie wątku z procesem jest dostarczane za pośrednictwem innych zdarzeń, które mogły nie zostać jeszcze przetworzone).

Niektóre kluczowe różnice w użyciu między buforowaniem i przesyłaniem strumieniowym:

  1. Buforowanie zwraca IPendingResult<T>, a wynik, który jest przechowywany, jest dostępny tylko przed przetworzeniem śledzenia. Po przetworzeniu śladu wyniki można wyliczyć przy użyciu technik, takich jak foreach i LINQ.
  2. Przesyłanie strumieniowe zwraca void i zamiast tego przyjmuje funkcję zwrotną. Wywołuje wywołanie zwrotne raz, gdy każdy element stanie się dostępny. Ponieważ dane nie są buforowane, nigdy nie ma listy wyników, które można wyliczyć za pomocą foreach lub LINQ – wywołanie zwrotne przesyłania strumieniowego musi buforować te części danych, które chce zapisać do użycia po zakończeniu przetwarzania.
  3. Kod przetwarzania buforowanych danych pojawia się po wywołaniu trace.Process(), gdy dostępne są oczekujące wyniki.
  4. Kod przetwarzania danych przesyłanych strumieniowo pojawia się przed wywołaniem trace.Process(), jako funkcja zwrotna metody trace.UseStreaming.Use...().
  5. Odbiorca przesyłania strumieniowego może przetworzyć tylko część strumienia i anulować przyszłe wywołania zwrotne, wywołując context.Cancel(). Konsument buforujący zawsze otrzymuje pełną, buforowaną listę.

Skorelowane dane przesyłane strumieniowo

Czasami dane trasowania pojawiają się w sekwencji zdarzeń — na przykład wywołania systemowe są rejestrowane oddzielnie dla zdarzeń rozpoczęcia i zakończenia, jednak połączenie danych z obu zdarzeń może być bardziej użyteczne. Metoda trace.UseStreaming().UseSyscalls() koreluje dane z obu tych zdarzeń i udostępnia je w miarę ich dostępności. Poprzez trace.UseStreaming() dostępne są kilka typów skorelowanych danych.

Kod Opis
ślad. UseStreaming(). UseContextSwitchData() Strumieniuje skorelowane dane przełączania kontekstu (z kompaktowych i niezwartych zdarzeń, z dokładniejszymi identyfikatorami SwitchInThreadIds niż surowe wydarzenia niezwarte).
ślad. UseStreaming(). UseScheduledTasks() Strumienie skorelowanych danych dotyczących zaplanowanych zadań.
ślad. UseStreaming(). UseSyscalls() Strumienie skorelowanych danych wywołań systemowych.
ślad. UseStreaming(). UseWindowInFocus() Strumienie danych skorelowanych z fokusowaniem na oknie.

Autonomiczne zdarzenia przesyłania strumieniowego

Ponadto trace.UseStreaming() udostępnia analizowane zdarzenia dla wielu różnych autonomicznych typów zdarzeń:

Kod Opis
ślad. UseStreaming(). UseLastBranchRecordEvents() Strumienie przetwarzały zdarzenia ostatniego rekordu gałęzi (LBR).
ślad.UseStreaming().UseReadyThreadEvents() Strumienie analizowane są pod kątem gotowych zdarzeń wątków.
ślad.UżyjStrumieniowania().UżyjZdarzeńTworzeniaWątku() Strumienie analizowane wątki tworzą zdarzenia.
ślad.UseStreaming().UseThreadExitEvents() Strumienie analizowały zdarzenia wyjścia wątku.
Trace.UseStreaming().UseThreadRundownStartEvents() Strumienie analizują zdarzenia rozpoczęcia uruchamiania wątku.
ślad. UseStreaming(). UseThreadRundownStopEvents() Strumienie analizowane zdarzenia zatrzymania uruchamiania wątku.
ślad. UseStreaming(). UseThreadSetNameEvents() Strumienie analizują zdarzenia dotyczące zmiany nazw zestawów wątków.

Podstawowe zdarzenia przesyłania strumieniowego dla skorelowanych danych

Na koniec, Trace.UseStreaming() udostępnia także podstawowe zdarzenia używane do korelowania danych na powyższej liście. Te zdarzenia bazowe to:

Kod Opis Uwzględnione w
ślad. UseStreaming(). UseCompactContextSwitchEvents() Strumienie analizują kompaktowe zdarzenia przełączania kontekstu. ślad. UseStreaming(). UseContextSwitchData()
ślad.UseStreaming().UseContextSwitchEvents() Strumienie analizują zdarzenia przełączania kontekstu. W niektórych przypadkach identyfikatory SwitchInThreadId mogą nie być dokładne. ślad. UseStreaming(). UseContextSwitchData()
ślad. UseStreaming(). UseFocusChangeEvents() Strumienie analizowanych zdarzeń zmiany fokusu okna. ślad. UseStreaming(). UseWindowInFocus()
ślad. UseStreaming(). UseScheduledTaskStartEvents() Strumienie analizują zdarzenia rozpoczęcia zaplanowanego zadania. ślad. UseStreaming(). UseScheduledTasks()
ślad. UseStreaming(). UseScheduledTaskStopEvents() Analizuje strumienie zdarzeń zatrzymania zaplanowanych zadań. ślad. UseStreaming(). UseScheduledTasks()
ślad. UseStreaming(). UseScheduledTaskTriggerEvents() Strumienie interpretują zdarzenia wyzwalające zaplanowane zadania. ślad. UseStreaming(). UseScheduledTasks()
ślad. UseStreaming(). UseSessionLayerSetActiveWindowEvents() Strumienie parują zdarzenia ustawień aktywnego okna w warstwie sesji. ślad. UseStreaming(). UseWindowInFocus()
ślad. UseStreaming(). UseSyscallEnterEvents() Strumienie analizują zdarzenia wejścia syscall. ślad. UseStreaming(). UseSyscalls()
ślad. UseStreaming(). UseSyscallExitEvents() Strumienie analizują zdarzenia zakończenia wywołań systemowych. ślad. UseStreaming(). UseSyscalls()

Dalsze kroki

W tym samouczku nauczyłeś się, jak używać przesyłu strumieniowego do uzyskiwania dostępu do danych śledzenia natychmiast i zużywając mniej pamięci.

Następnym krokiem jest wyszukanie dostępu do żądanych danych ze śledzenia. Spójrz na przykłady , aby znaleźć kilka pomysłów. Należy pamiętać, że nie wszystkie ślady obejmują wszystkie obsługiwane typy danych.