기본적으로 TraceProcessor는 추적이 처리될 때 메모리에 로드하여 데이터에 액세스합니다. 이 버퍼링 방법은 사용하기 쉽지만 메모리 사용량 측면에서 비용이 많이 들 수 있습니다.
TraceProcessor는 trace.UseStreaming()을 제공하며, 이는 여러 유형의 추적 데이터를 스트리밍 방식으로 액세스할 수 있도록 지원합니다 (추적 파일에서 데이터를 읽을 때 바로 처리하며, 메모리에 그 데이터를 버퍼링하지 않습니다). 예를 들어, syscalls 추적은 매우 클 수 있으며, 추적에서 전체 syscalls 목록을 버퍼링하면 비용이 많이 들 수 있습니다.
버퍼링된 데이터 액세스
다음 코드는 추적을 통해 일반 버퍼링된 방식으로 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]}");
}
}
}
}
스트리밍 데이터 액세스
큰 syscall 추적을 사용하면 메모리에서 syscall 데이터를 버퍼링하려고 하면 비용이 많이 들거나 불가능할 수도 있습니다. 다음 코드는 trace.UseSyscalls()를 trace.UseStreaming().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<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 데이터가 스트리밍되기 전에 스레드 데이터가 버퍼링됩니다. 따라서 버퍼링된 스레드 데이터를 가져오기 위해 한 번, 이제 사용 가능한 버퍼링된 스레드 데이터를 사용하여 스트리밍 syscall 데이터에 액세스하기 위해 두 번째로 추적을 읽어야 합니다. 이러한 방식으로 스트리밍과 버퍼링을 결합하기 위해, 이 예제에서는 ConsumerSchedule.SecondPass를 trace.UseStreaming().UseSyscalls()에 전달하여, 추적의 두 번째 패스를 통해 syscall 처리가 이루어지도록 합니다. 두 번째 패스에서 실행할 때, syscall 콜백은 syscall을 처리할 때 trace.UseThreads()로부터 보류 중인 결과에 접근할 수 있습니다. 이 선택적 인수가 없으면 syscall 스트리밍은 추적을 통과하는 첫 번째 패스에서 실행되어야 하고(단일 패스만 있다면), 추적의 UseThreads()로부터의 대기 중인 결과는 아직 사용할 수 없습니다. 이 경우 콜백은 여전히 syscall에서 ThreadId에 액세스할 수 있지만 스레드에 대한 프로세스에 액세스할 수 없습니다(연결 데이터를 처리하는 스레드는 아직 처리되지 않았을 수 있는 다른 이벤트를 통해 제공되기 때문).
버퍼링과 스트리밍 간 사용의 몇 가지 주요 차이점:
- 버퍼링은 IPendingResult <T>를 반환하며, 보류된 결과는 추적이 처리되기 전에만 사용할 수 있습니다. 추적이 처리된 후 foreach 및 LINQ와 같은 기술을 사용하여 결과를 열거할 수 있습니다.
- 스트리밍은 void를 반환하고 대신 콜백 인수를 사용합니다. 각 항목을 사용할 수 있게 되면 콜백을 한 번 호출합니다. 데이터는 버퍼링되지 않으므로 foreach 또는 LINQ를 사용하여 열거할 결과 목록이 없습니다. 스트리밍 콜백은 처리가 완료된 후 사용하기 위해 저장하려는 데이터의 어떤 부분을 버퍼링해야 합니다.
- 버퍼링된 데이터를 처리하는 코드는 trace.Process() 호출 후, 보류 중인 결과를 사용할 수 있을 때 나타납니다.
- trace.Process() 호출 전에, trace.UseStreaming.Use...() 메서드의 콜백으로 스트리밍 데이터를 처리하는 코드가 나타납니다.
- 스트리밍 소비자는 컨텍스트를 호출하여 스트림의 일부만 처리하고 이후 콜백을 취소하도록 선택할 수 있습니다. Cancel(). 버퍼링 소비자는 항상 완전하고 버퍼링된 목록을 받습니다.
상호 관련된 스트리밍 데이터
경우에 따라 추적 데이터가 일련의 이벤트로 제공됩니다. 예를 들어 syscall은 별도의 Enter 및 Exit 이벤트를 통해 기록되지만 두 이벤트의 결합된 데이터가 더 유용할 수 있습니다. 메서드 trace.UseStreaming().UseSyscalls()는 두 이벤트의 데이터를 연결하고 쌍이 사용 가능해지면 이를 제공합니다. 추적을 통해 몇 가지 유형의 상관 관계 데이터를 사용할 수 있습니다. UseStreaming():
코드 | 설명 |
---|---|
추적.UseStreaming().UseContextSwitchData() | 상호 관련된 컨텍스트 스위치 데이터를 스트림합니다(압축 이벤트 및 압축되지 않은 이벤트에서 원시 비콤팩트 이벤트보다 더 정확한 SwitchInThreadIds 사용). |
추적.UseStreaming().UseScheduledTasks() | 상호 관련된 예약된 작업 데이터를 스트리밍합니다. |
추적.UseStreaming().UseSyscalls() | 상호 관련된 시스템 호출 데이터를 스트리밍합니다. |
자취. UseStreaming(). UseWindowInFocus() | 포커스된 창의 상호 관련 데이터를 스트리밍합니다. |
독자적 스트리밍 이벤트
또한 trace.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을 스트리밍하여 이벤트를 입력합니다. | 추적.UseStreaming().UseSyscalls() |
자취. UseStreaming(). UseSyscallExitEvents() | 구문이 분석된 시스템 호출 종료 이벤트를 스트리밍합니다. | 추적.UseStreaming().UseSyscalls() |
다음 단계
이 자습서에서는 스트리밍을 사용하여 추적 데이터에 즉시 액세스하고 메모리를 적게 사용하는 방법을 알아보았습니다.
다음 단계는 추적에서 원하는 데이터에 액세스하는 것입니다. 몇 가지 아이디어를 위해 샘플을 살펴보세요. 모든 추적에 모든 지원되는 데이터 유형이 포함되지는 않습니다.
Windows developer