搭配 TraceProcessor 使用串流

預設情況下,TraceProcessor 在處理追蹤時透過將資料載入到記憶體中來存取資料。 這種緩衝方法很容易使用,但需要使用大量的記憶體。

TraceProcessor也提供trace.UseStreaming(),它支援以串流方式存取多種類型的追蹤資料 (在從追蹤檔案讀取資料時處理資料,而不是在記憶體中緩衝該資料)。 例如,syscalls 追蹤可能會相當大,而緩衝處理追蹤中整個 syscalls 清單可能會相當昂貴。

存取緩衝資料

以下程式碼顯示了透過 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]}");
            }
        }
    }
}

存取串流資料

對於大型系統呼叫追蹤,嘗試在記憶體中緩衝系統調用資料可能會非常昂貴,甚至是不可能的事情。 以下程式碼將示範如何以串流方式存取相同的系統呼叫資料數,並將 trace.UseSyscalls() 替換為 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]}");
            }
        }
    }
}

串流處理如何運作

根據預設,所有串流資料都會在第一次通過追蹤期間提供,而來自其他來源的緩衝資料則無法使用。 上述範例展示了如何將串流傳輸與緩衝結合,在系統呼叫資料串流之前對執行緒資料進行緩衝。 因此,必須讀取追蹤兩次 - 一次是為了獲取緩衝的執行緒資料,第二次是為了使用現在可用的緩衝執行緒資料來存取串流系統呼叫資料。 為了以這種方式組合串流處理和緩衝,此範例將 ConsumerSchedule.SecondPass 傳遞給trace.UseStreaming().UseSyscalls(),這會導致系統呼叫處理在第二次通過追蹤時發生。 透過在第二遍中執行,系統呼叫回調可以在處理每個系統呼叫時從trace.UseThreads() 存取掛起的結果。 如果沒有這個可選參數,系統呼叫流將在第一次通過追蹤時執行 (只有一次),並且來自 trace.UseThreads() 的待處理結果將不可用。 在這種情況下,回呼仍然可以從系統呼叫存取 ThreadId,但它無法存取執行緒的程序 (因為處理連結資料的執行緒是透過其他可能尚未處理的事件提供的)。

緩衝處理與串流之間使用量的一些主要差異:

  1. 緩衝處理會傳回 IPendingResult<T>,而且保留的結果只有在處理追蹤之前才能使用。 處理追蹤之後,可以使用 foreach 和 LINQ 等技術列舉結果。
  2. 串流會傳回 void,而是採用回呼自變數。 當每個項目變得可用時,它會呼叫回調一次。 由於資料未緩衝處理,因此永遠都沒有任何結果清單可列舉 foreach 或 LINQ – 串流回呼必須緩衝處理完成之後要儲存以供使用的任何部分資料。
  3. 當待處理結果可用時,處理緩衝資料的程式碼出現在呼叫 trace.Process()之後。
  4. 處理流程資料的程式碼出現在呼叫 trace.Process() 之前,作為對 trace.UseStreaming.Use...() 方法的回調。
  5. 流消費者可以選擇僅處理流的一部分,並透過呼叫 context.Cancel() 來取消未來的回調。 緩衝取用者一律會提供完整的緩衝清單。

相關串流資料

有時追蹤資料來自一系列事件 - 例如,系統呼叫是透過單獨的進入和退出事件記錄的,但來自兩個事件的組合資料可能會更有幫助。 方法 trace.UseStreaming().UseSyscalls() 將這兩個事件的資料關聯起來,並在該對可用時提供它。 透過 trace.UseStreaming() 可獲得幾種類型的相關資料:

代碼 描述
trace.UseStreaming().UseContextSwitchData() 串流關聯上下文切換資料 (來自緊湊和非緊湊事件,具有比原始非緊湊事件更準確的 SwitchInThreadId)。
trace.UseStreaming().UseScheduledTasks() 串流相互關聯的排程工作資料。
trace.UseStreaming().UseSyscalls() 串流相互關聯的系統呼叫資料。
trace.UseStreaming().UseWindowInFocus() 串流相互關聯的焦點窗口資料。

獨立串流事件

此外,trace.UseStreaming() 可提供數種不同獨立事件類型的剖析事件:

代碼 描述
trace.UseStreaming().UseLastBranchRecordEvents() 串流解析最後一個分支記錄 (LBR) 事件。
trace.UseStreaming().UseReadyThreadEvents() 串流解析就緒執行緒事件。
trace.UseStreaming().UseThreadCreateEvents() 串流剖析的執行緒建立事件。
trace.UseStreaming().UseThreadExitEvents() 串流剖析的執行緒結束事件。
trace.UseStreaming().UseThreadRundownStartEvents() 串流剖析的執行緒執行啟動事件。
trace.UseStreaming().UseThreadRundownStopEvents() 串流剖析的執行緒執行停止事件。
trace.UseStreaming().UseThreadSetNameEvents() 串流剖析的執行緒集名稱事件。

相關資料的基礎串流事件

最後,trace.UseStreaming() 也提供用來將上述清單中資料相互關聯的基礎事件。 這些基礎事件包括:

代碼 描述 包含在
trace.UseStreaming().UseCompactContextSwitchEvents() 串流剖析的精簡內容交換器事件。 trace.UseStreaming().UseContextSwitchData()
trace.UseStreaming().UseContextSwitchEvents() 串流剖析的內容切換事件。 在某些情況下,SwitchInThreadIds 可能不正確。 trace.UseStreaming().UseContextSwitchData()
trace.UseStreaming().UseFocusChangeEvents() 串流剖析的視窗焦點變更事件。 trace.UseStreaming().UseWindowInFocus()
trace.UseStreaming().UseScheduledTaskStartEvents() 串流剖析的排程工作啟動事件。 trace.UseStreaming().UseScheduledTasks()
trace.UseStreaming().UseScheduledTaskStopEvents() 串流剖析的排程工作停止事件。 trace.UseStreaming().UseScheduledTasks()
trace.UseStreaming().UseScheduledTaskTriggerEvents() 串流剖析的排程工作觸發程式事件。 trace.UseStreaming().UseScheduledTasks()
trace.UseStreaming().UseSessionLayerSetActiveWindowEvents() 串流剖析的會話層設定活動視窗事件。 trace.UseStreaming().UseWindowInFocus()
trace.UseStreaming().UseSyscallEnterEvents() 串流剖析的 syscall 輸入事件。 trace.UseStreaming().UseSyscalls()
trace.UseStreaming().UseSyscallExitEvents() 串流剖析的 syscall 結束事件。 trace.UseStreaming().UseSyscalls()

後續步驟

在本教學課程中,您將了解如何使用串流立即存取追蹤資料並使用更少的記憶體。

下一個步驟是查看從追蹤存取您想要的資料。 查看一些想法的範例。 請注意,並非所有追蹤都包含所有支援的資料類型。