Использование потоковой передачи с помощью 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]}");
}
}
}
}
Как работает потоковая передача
По умолчанию все потоковые данные предоставляются во время первого прохода по трассировке, а буферизованные данные из других источников недоступны. В приведенном выше примере показано, как объединить потоковую передачу с буферизацией — потоковые данные буферизуются до потоковой передачи данных системных вызовов. В результате трассировка должна быть прочитана дважды — один раз для получения данных буферизованного потока, а второй раз — для доступа к потоковой передаче данных системных вызовов с помощью буферизованных данных потоков. Чтобы объединить потоковую передачу и буферизацию таким образом, в примере передается ConsumerSchedule.SecondPass в trace.UseStreaming().UseSyscalls(). В результате обработка системных вызовов происходит во второй проход по трассировке. Выполняя второй проход, обратный вызов системных вызовов может получить доступ к ожидающему результату из trace.UseThreads() при обработке каждого системного вызова. Без этого необязательного аргумента потоковая передача системных вызовов была бы выполнена в первом проходе через трассировку (был бы только один проход), и отложенный результат trace.UseThreads() был бы недоступен. В этом случае обратный вызов по-прежнему будет иметь доступ к ThreadId из системного вызова, но у него не будет доступа к процессу потока (поскольку поток для обработки данных связывания предоставляется через другие события, которые, возможно, еще не обработаны).
Некоторые ключевые отличия в использовании между буферизацией и потоковой обработкой:
- Буферизация возвращает IPendingResult<T>, и результат доступен только перед обработкой трассировки. После обработки трассировки результаты можно перечислить с помощью таких методов, как foreach и LINQ.
- Потоковая передача возвращает пустое значение и принимает аргумент обратного вызова. Она вызывает обратный вызов по мере того, как каждый элемент становится доступным. Так как данные не буферизуются, список результатов для перечисления с помощью foreach или LINQ не создается — потоковый обратный вызов должен буферизовать ту часть данных, которую нужно сохранить для использования после завершения обработки.
- Код для обработки буферизованных данных появляется после вызова функции trace.Process(), если ожидающие результаты доступны.
- Код для обработки потоковых данных отображается перед вызовом функции trace.Process() в качестве обратного вызова метода trace.UseStreaming.Use...().
- Потребитель потоковой передачи может выбрать обработку только части потока и отменить последующие обратные вызовы, вызвав 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() | События последней записи ветви, проанализированные в потоках. |
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() | События входа системных вызовов, проанализированные потоками. | trace.UseStreaming().UseSyscalls() |
trace.UseStreaming().UseSyscallExitEvents() | События выхода системных вызовов, проанализированные потоками. | trace.UseStreaming().UseSyscalls() |
Next Steps
Из этого руководства вы узнали, как использовать потоковую передачу для немедленного доступа к данным трассировки и экономии памяти.
Следующий шаг — доступ к нужным данным из трассировок. Ознакомьтесь с примерами, чтобы получить представление. Не все трассировки включают в себя все поддерживаемые типы данных.
Windows developer