TraceProcessor でストリーミングを使用する

既定では、TraceProcessor ではトレースが処理されるときにデータをメモリに読み込むことによって、そのデータにアクセスします。 このバッファリング アプローチの使用は簡単ですが、メモリ使用量の観点からはコストが高くなる可能性があります。

TraceProcessor では trace.UseStreaming() も提供しており、これにより、複数の種類のトレース データにストリーミング方式 (データをメモリにバッファリングするのではなく、トレース ファイルから読み取られたかのようにそのデータを処理する) でアクセスできます。 たとえば、システム コール トレースは非常に大きくなる場合があり、トレース内のシステム コールのリスト全体をバッファリングすると負荷が非常に高くなる可能性があります。

バッファリングされたデータへのアクセス

次のコードは、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]}");
            }
        }
    }
}

ストリーミングのしくみ

既定では、すべてのストリーミング データはそのトレースの最初の通過中に提供され、他のソースからのバッファリングされたデータは利用できません。 上の例では、ストリーミングをバッファリングと組み合わせる方法を示しています。システム コール データがストリーミングされる前に、スレッド データがバッファリングされます。 その結果、トレースは、1 回目はバッファリングされたスレッド データを取得するためと、2 回目は利用できるようになったバッファリングされたスレッド データを使用してシステム コールのストリーミング データにアクセスするための、2 回読み取られます。 ストリーミングとバッファリングをこの方法で組み合わせるために、その例では ConsumerSchedule.SecondPass を trace.UseStreaming().UseSyscalls() に渡し、それによってシステム コールの処理がトレースの 2 回目の通過時に発生します。 2 番目のパスで実行することで、各システム コールが処理されるときに、システム コールのコールバックで trace.UseThreads() からの保留中の結果にアクセスできるようになります。 このオプションの引数がないと、システム コールのストリーミングはトレースの最初の通過時に実行され (1 つのパスのみになり)、そのときは trace.UseThreads() からの保留中の結果はまだ利用できません。 このケースでも、コールバックではシステム コールからの ThreadId にアクセスできたものの、そのスレッドの処理にはアクセスできません (リンクされているデータを処理するスレッドは、まだ処理されていない他のイベントを介して提供されるからです)。

バッファリングとストリーミングの使用には、いくつかの重要な違いがあります。

  1. バッファリングでは IPendingResult<T> を返し、そこで保持される結果は、トレースが処理される前にのみ利用できます。 トレースが処理された後は、その結果を foreach や LINQ などの手法を使用して列挙できます。
  2. ストリーミングでは void を返し、代わりにコールバック引数を取ります。 各項目が使用可能になると、コールバックが 1 回呼び出されます。 データはバッファリングされないため、foreach や LINQ を使用して列挙する結果のリストは一切存在しません。ストリーミングのコールバックでは、プロセスが完了した後に使用するために保存する必要があるのがデータのどの部分であっても、バッファリングする必要があります。
  3. バッファリングされたデータを処理するためのコードは、trace.Process() の呼び出しの後、保留中の結果が使用できるようになると表示されます。
  4. ストリーミング データを処理するためのコードは、trace.Process() の呼び出しの前に、trace.UseStreaming.Use...() メソッドのコールバックとして表示されます。
  5. ストリーミング コンシューマーは、ストリームの一部のみを処理することを選択し、将来のコールバックを context.Cancel() を呼び出すことでキャンセルできます。 バッファリング コンシューマーには常に、バッファリングされた完全なリストが提供されます。

相関ストリーミング データ

トレース データは、1 つの連続したイベントで入ってくる場合があります。たとえば、システム コールは別個の開始イベントと終了イベントを介してログに記録されるものの、両方のイベントからの組み合わさったデータのほうが便利なことがあります。 メソッド trace.UseStreaming().UseSyscalls() によって、それらの両方のイベントからのデータが相互に関連付けられ、ペアとして提供されるようになります。 いくつかの種類の相関データは、trace.UseStreaming() を介して入手できます。

コード 説明
trace.UseStreaming().UseContextSwitchData() (未加工の非コンパクトなイベントよりも正確な SwitchInThreadId が備わったコンパクトなイベントおよびや非コンパクトなイベントからの) コンテキストの切り替えに関わる相関データをストリームします。
trace.UseStreaming().UseScheduledTasks() スケジュールが設定されたタスクに関する相関データをストリームします。
trace.UseStreaming().UseSyscalls() システム コールに関する相関データをストリームします。
trace.UseStreaming().UseWindowInFocus() フォーカスが当たっているウィンドウに関する相関データをストリームします。

スタンドアロンのストリーミング イベント

さらに、trace.UseStreaming() にはさまざまなスタンドアロンのイベントの種類に対応した解析イベントがあります。

コード 説明
trace.UseStreaming().UseLastBranchRecordEvents() Last Branch Record (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() コンテキストの切り替えに関する解析イベントをストリームします。 SwitchInThreadId は、場合によっては正確でない場合があります。 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() システム コールの開始に関する解析イベントをストリームします。 trace.UseStreaming().UseSyscalls()
trace.UseStreaming().UseSyscallExitEvents() システム コールの終了に関する解析イベントをストリームします。 trace.UseStreaming().UseSyscalls()

次の手順

このチュートリアルでは、ストリーミングを使用してすぐにトレース データにアクセスし、使用するメモリをより少なくする方法について説明しました。

次の手順では、トレースから必要なデータにアクセスする方法を見て行きます。 いくつかのアイデアについては、サンプルを参照してください。 トレースに含まれているデータがすべてサポートされている種類のデータとは限らないのでご注意ください。