既定では、TraceProcessor は、トレースの処理時にメモリに読み込むことでデータにアクセスします。 このバッファリング アプローチは使いやすいですが、メモリ使用量の面ではコストがかかる場合があります。
TraceProcessor はトレースも提供します。UseStreaming() は、ストリーミング方式で複数の種類のトレース データへのアクセスをサポートします (メモリ内のデータをバッファー処理するのではなく、トレース ファイルからデータを読み取る際にデータを処理します)。 たとえば、syscalls トレースは非常に大きくなる可能性があり、トレース内の syscall のリスト全体をバッファー処理すると、非常にコストがかかる場合があります。
バッファー内のデータへのアクセス
次のコードは、トレースを介して通常のバッファー方式で syscall データにアクセスする方法を示しています。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]}");
}
}
}
}
ストリーミング データへのアクセス
大規模な syscalls トレースでは、syscall データをメモリにバッファー処理しようとすると、非常にコストがかかる場合や、不可能な場合もあります。 次のコードは、同じ 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 データがストリーミングされる前にバッファーされます。 その結果、トレースは2回読み取る必要があります。1回目はバッファーされたスレッドデータを取得し、2回目はそのデータを用いてストリーミングのsyscallデータにアクセスします。 この方法でストリーミングとバッファリングを組み合わせるために、この例では ConsumerSchedule.SecondPass をトレースに渡します。UseStreaming()。UseSyscalls() は、syscall 処理がトレースを通過する 2 番目のパスで発生します。 2 番目のパスで実行することで、syscall コールバックはトレースから保留中の結果にアクセスできます。各 syscall を処理する場合は UseThreads() を使用します。 この省略可能な引数がない場合、syscall ストリーミングはトレースを通じて最初のパスだけで実行され(通常は複数のパスがありますが、今回の場合は 1 つだけです)、trace.UseThreads() からの保留されている結果はまだ利用可能ではありません。 その場合、コールバックは syscall から ThreadId に引き続きアクセスできますが、スレッドのプロセスにはアクセスできません (リンク データを処理するスレッドは、まだ処理されていない可能性がある他のイベントを介して提供されるため)。
バッファリングとストリーミングの使用方法の主な違いは次のとおりです。
- バッファリングは IPendingResult<T> を返し、保持される結果はトレースが処理される前にのみ使用できます。 トレースが処理された後は、foreach や LINQ などの手法を使用して結果を列挙できます。
- ストリーミングは void を返し、代わりにコールバック引数を受け取ります。 各項目が使用可能になると、コールバックが 1 回呼び出されます。 データはバッファー処理されないため、foreach または LINQ を使用して列挙する結果の一覧はありません。ストリーミング コールバックは、処理が完了した後に使用するために保存するデータの任意の部分をバッファーする必要があります。
- バッファーに格納されたデータを処理するためのコードは、トレースの呼び出しの後に表示されます。Process() : 保留中の結果が使用可能な場合。
- ストリーミングデータを処理するためのコードは、trace.Process() の呼び出しの前に表示され、trace.UseStreaming.Use...() メソッドのコールバックとして使用されます。
- ストリーミング コンシューマーは、コンテキストを呼び出すことによって、ストリームの一部のみを処理し、将来のコールバックを取り消すことができます。Cancel()。 バッファリング コンシューマーには、常に完全なバッファーリストが提供されます。
相関のあるストリーミングデータ
トレース データが一連のイベントに含まれる場合があります。たとえば、syscall は個別の入退出イベントを介してログに記録されますが、両方のイベントの結合されたデータがより役に立つ場合があります。 メソッド trace.UseStreaming().UseSyscalls() は、これらのイベントの両方のデータを関連付け、ペアのデータが使用可能になる都度提供します。 トレースを使用して、いくつかの種類の相関データを使用できます。UseStreaming():
コード | 説明 |
---|---|
跡。UseStreaming()。UseContextSwitchData() | ストリームは、より正確な SwitchInThreadIds を使用して、関連するコンテキスト切り替えデータを提供します(コンパクトおよび非コンパクトイベントから、未加工の非コンパクトイベントよりも正確です)。 |
跡。UseStreaming()。UseScheduledTasks() | 関連付けられたスケジュールされたタスク データをストリーミングします。 |
跡。UseStreaming()。UseSyscalls() | 相関システム呼び出しデータをストリームします。 |
トレース.UseStreaming().UseWindowInFocus() | 関連付けられたウィンドウインフォーカス データをストリーミングします。 |
スタンドアロン ストリーミング イベント
さらに、トレースUseStreaming() は、さまざまな種類のスタンドアロン イベントに対して解析されたイベントを提供します。
コード | 説明 |
---|---|
跡。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 enter イベントをストリームします。 | 跡。UseStreaming()。UseSyscalls() |
跡。UseStreaming()。UseSyscallExitEvents() | 解析された syscall 終了イベントをストリームします。 | 跡。UseStreaming()。UseSyscalls() |
次のステップ
このチュートリアルでは、ストリーミングを使用してトレース データにすぐにアクセスし、使用するメモリを減らす方法について説明しました。
次の手順では、トレースから必要とするデータにアクセスします。 いくつかのアイデアについては、 サンプル を参照してください。 すべてのトレースに、サポートされているすべての種類のデータが含まれているわけではないことに注意してください。
Windows developer