共用方式為


搭配 TraceProcessor 使用串流

根據預設,TraceProcessor 會藉由在處理追蹤時將數據載入記憶體來存取數據。 這種緩衝處理方法很容易使用,但在記憶體使用量方面可能很昂貴。

TraceProcessor 也提供追蹤。UseStreaming(),它支援以串流方式存取多種追蹤數據類型(在從追蹤檔案讀取時處理數據,而不是緩衝記憶體中的數據)。 例如,系統調用追蹤可能會相當大,而緩衝處理追蹤中整個系統調用清單可能會相當耗費資源。

存取緩衝數據

以下程式碼顯示如何通過 trace.UseSyscalls() 以正常且緩衝的方式存取 syscall 數據。

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]}");
            }
        }
    }
}

存取串流數據

使用大型的系統呼叫追蹤時,嘗試將系統呼叫數據緩衝在記憶體中可能會非常昂貴,甚至可能無法做到。 下列程式碼示範如何以串流方式存取相同的 syscall 數據,將 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]}");
            }
        }
    }
}

串流的運作方式

根據預設,所有串流數據都會在第一次通過追蹤期間提供,而來自其他來源的緩衝數據則無法使用。 上述範例示範如何結合串流與緩衝處理 – 線程數據會在串流處理 syscall 數據之前進行緩衝處理。 因此,追蹤記錄必須讀取兩次—第一次取得緩衝線程資料,第二次在緩衝線程資料已可用的情況下存取串流 syscalls 資料。 為了以這種方式結合串流和緩衝處理,此範例會將 ConsumerSchedule.SecondPass 傳遞至 trace.UseStreaming().UseSyscalls(),這會導致系統呼叫處理在追蹤的第二次掃描中發生。 藉由在第二個階段中執行,syscall 回呼可以在 trace.UseThreads() 處理每個 syscall 時,存取待處理的結果。 如果沒有這個可選參數,syscall 串流會在追蹤的第一遍中執行(只會執行一次),並且來自 trace.UseThreads() 的待定結果尚未可用。 在此情況下,回呼函式仍然可以從 syscall 存取 ThreadId,但無法存取線程所屬的進程(因為線程與進程的連結資料是透過可能尚未處理的其他事件來提供)。

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

  1. 緩衝操作會傳回 IPendingResult<T>,並且只有在處理追蹤之前,該緩衝所保留的結果才可用。 處理追蹤之後,可以使用 foreach 和 LINQ 等技術列舉結果。
  2. 串流會回傳 void,而是採用回呼函數作為參數。 每當一個項目變得可用時,它就呼叫一次回呼函數。 由於資料未經緩衝,因此永遠無法取得可供 foreach 或 LINQ 列舉的結果列表—串流回呼需要自行緩衝處理欲儲存的部分資料,以便在處理完成後使用。
  3. 處理緩衝數據的代碼會在呼叫 trace.Process() 之後出現,這時待處理結果已可使用。
  4. 作為 trace.UseStreaming.Use...() 方法的回呼,處理串流數據的程式代碼會出現在呼叫 trace.Process() 之前。
  5. 串流取用者可以選擇只處理數據流的一部分,並藉由呼叫 context.Cancel() 來取消未來的回呼。 緩衝取用者總是會收到一份完整的緩衝列表。

相互關聯的串流數據

有時,追蹤數據會以一連串的事件形式記錄,例如,syscalls 會透過個別的 enter 和 exit 事件來記錄,但這兩個事件合併後的數據可能更有用。 方法 trace.UseStreaming().UseSyscalls() 將這兩個事件的數據關聯並在配對可用時提供。 有幾個類型的相互關聯數據可透過追蹤取得。UseStreaming():

程式碼 說明
追蹤。UseStreaming().UseContextSwitchData() 串流來自緊密和非緊密事件的相關上下文切換數據,並提供比原始非緊密事件更精確的 SwitchInThreadIds。
跟蹤。UseStreaming()。UseScheduledTasks() 串流相互關聯的排程工作數據。
跟蹤。UseStreaming()。UseSyscalls() 串流相關聯的系統呼叫資料。
跟蹤。UseStreaming()。UseWindowInFocus() 串流相互關聯的焦點窗口數據。

獨立串流活動

此外,追蹤。UseStreaming() 提供數種不同獨立事件類型的剖析事件:

程式碼 說明
Trace.UseStreaming().UseLastBranchRecordEvents() 解析最後一個分支記錄(LBR)事件的數據流。
追蹤.UseStreaming().UseReadyThreadEvents() 串流剖析的就緒線程事件。
跟蹤。UseStreaming()。UseThreadCreateEvents() 串流解析的線程創建事件。
跟蹤。UseStreaming()。UseThreadExitEvents() 串流剖析的線程結束事件。
跟蹤。UseStreaming()。UseThreadRundownStartEvents() 串流剖析的線程執行啟動事件。
跟蹤。UseStreaming()。UseThreadRundownStopEvents() 流解析线程触发的停止事件。
追蹤。UseStreaming().UseThreadSetNameEvents() 串流剖析的線程集名稱事件。

相關數據的底層串流事件

最後,trace.UseStreaming() 也提供了將上述清單中數據相互關聯所需的基礎事件。 這些基礎事件包括:

程式碼 說明 包含於
追蹤.UseStreaming().UseCompactContextSwitchEvents() 串流解析精簡上下文切換事件。 追蹤。UseStreaming().UseContextSwitchData()
跟蹤。UseStreaming()。UseContextSwitchEvents() 串流剖析的內容切換事件。 在某些情況下,SwitchInThreadIds 可能不正確。 追蹤。UseStreaming().UseContextSwitchData()
追蹤.UseStreaming().UseFocusChangeEvents() 串流剖析的視窗焦點變更事件。 跟蹤。UseStreaming()。UseWindowInFocus()
跟蹤。UseStreaming()。UseScheduledTaskStartEvents() 串流解析排程工作啟動事件。 跟蹤。UseStreaming()。UseScheduledTasks()
跟蹤。UseStreaming()。UseScheduledTaskStopEvents() 串流解析程序中的排程任務停止事件。 跟蹤。UseStreaming()。UseScheduledTasks()
跟蹤。UseStreaming()。UseScheduledTaskTriggerEvents() 排程工作解析流時觸發事件。 跟蹤。UseStreaming()。UseScheduledTasks()
跟蹤。UseStreaming()。UseSessionLayerSetActiveWindowEvents() 串流解析的會話層設定活動視窗的事件。 跟蹤。UseStreaming()。UseWindowInFocus()
跟蹤。UseStreaming()。UseSyscallEnterEvents() 串流解析 syscall 進入事件。 跟蹤。UseStreaming()。UseSyscalls()
跟蹤。UseStreaming()。UseSyscallExitEvents() 串流剖析的 syscall 結束事件。 跟蹤。UseStreaming()。UseSyscalls()

後續步驟

在本教學課程中,您已瞭解如何使用串流來立即存取追蹤數據,以及使用較少的記憶體。

下一步是查看並存取您在追蹤中想要的數據。 請查看 範例 以獲取一些想法。 請注意,並非所有追蹤都包含所有支援的數據類型。