如何使用 TraceProcessor 读取 VirtualAllocations?
How To Read VirtualAllocations With TraceProcessor?
启用 VirtualAlloc Tracing 后,如何使用 TraceProcessor 取回 VirtualAlloc 事件?
在Microsoft.Windows.EventTracing.Memory中我只找到
- IHeapAllocation
- IHeapSnapshot
- IReferenceSetAccessedPage
- IWorkingSetEntry
- ...
但是没有提到VirtualAlloc的东西。
相关说明:使用此库解析 .NET ETW 事件有多难。 TraceEvent 库对 .NET 事件有很好的支持,但我不清楚我应该如何扩展 TraceProcessor。 TraceProcessor 的 .NET 事件是否在路线图上?
(直到最近我还在研究 TraceProcessor 库。)
正如您所注意到的,我认为我们目前没有对 VirtualAlloc 事件的内置支持。我会让当前的团队优先考虑,但我们过去没有收到对 .NET 事件的重大请求。
对于 VirtualAlloc(和 .NET 事件),是的,您可以自己解析这些事件。参见:
https://docs.microsoft.com/en-us/windows/apps/trace-processing/extensibility
查看概览。您需要了解 VirtualAlloc 事件的格式才能知道如何解析它,以及这些事件的提供者 ID/事件 ID/版本。
现在有几个人问了,下面是获取 VirtualAlloc/VirtualFree 事件数据的示例:
using Microsoft.Windows.EventTracing;
using Microsoft.Windows.EventTracing.Processes;
using Microsoft.Windows.EventTracing.Symbols;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
class Program
{
static int Main(string[] args)
{
if (args.Length != 1)
{
Console.Error.WriteLine("Usage: VirtualAllocFree.exe <trace.etl>");
return 1;
}
try
{
Run(args[0]);
}
catch (Exception exception)
{
Console.Error.WriteLine(exception);
return exception.HResult;
}
return 0;
}
static void Run(string tracePath)
{
using (ITraceProcessor trace = TraceProcessor.Create(tracePath))
{
Guid kernelMemoryProviderId = new Guid("3d6fa8d3-fe05-11d0-9dda-00c04fd7ba7c");
IPendingResult<IStackDataSource> pendingStackDataSource = trace.UseStacks();
IPendingResult<IProcessDataSource> pendingProcessDataSource = trace.UseProcesses();
List<VirtualAllocOrFreeEvent> virtualAllocOrFreeEvents = new List<VirtualAllocOrFreeEvent>();
TraceEventCallback handleKernelMemoryEvent = (EventContext eventContext) =>
{
ClassicEvent classicEvent = eventContext.Event.AsClassicEvent;
if (classicEvent.Version < 2)
{
return;
}
int eventId = classicEvent.Id;
const int virtualAllocEventId = 98;
const int virtualFreeEventId = 99;
if (eventId != virtualAllocEventId && eventId != virtualFreeEventId)
{
return;
}
VirtualAlloc64EventData eventData;
if (classicEvent.Is32Bit)
{
if (classicEvent.Data.Length != Marshal.SizeOf<VirtualAlloc32EventData>())
{
throw new InvalidTraceDataException("Invalid virtual alloc/free event.");
}
VirtualAlloc32EventData thunk = MemoryMarshal.Read<VirtualAlloc32EventData>(classicEvent.Data);
eventData.Base = thunk.Base;
eventData.Size = thunk.Size;
eventData.ProcessId = thunk.ProcessId;
eventData.Flags = thunk.Flags;
}
else
{
if (classicEvent.Data.Length != Marshal.SizeOf<VirtualAlloc64EventData>())
{
throw new InvalidTraceDataException("Invalid virtual alloc/free event.");
}
eventData = MemoryMarshal.Read<VirtualAlloc64EventData>(classicEvent.Data);
}
AddressRange addressRange = new AddressRange(new Address(eventData.Base),
unchecked((long)eventData.Size));
int processId = unchecked((int)eventData.ProcessId);
VirtualAllocFlags flags = eventData.Flags;
TraceTimestamp timestamp = classicEvent.Timestamp;
int threadId = classicEvent.ThreadId.Value;
virtualAllocOrFreeEvents.Add(new VirtualAllocOrFreeEvent(addressRange, processId, flags, timestamp,
threadId, pendingProcessDataSource, pendingStackDataSource));
};
trace.Use(new Guid[] { kernelMemoryProviderId }, handleKernelMemoryEvent);
IPendingResult<ISymbolDataSource> pendingSymbolDataSource = trace.UseSymbols();
trace.Process();
pendingSymbolDataSource.Result
.LoadSymbolsForConsoleAsync(SymCachePath.Automatic, SymbolPath.Automatic).GetAwaiter().GetResult();
Console.WriteLine($"Total virtual alloc/free events: {virtualAllocOrFreeEvents.Count}");
}
}
struct VirtualAlloc64EventData
{
public ulong Base;
public ulong Size;
public uint ProcessId;
public VirtualAllocFlags Flags;
}
struct VirtualAlloc32EventData
{
#pragma warning disable CS0649
public uint Base;
public uint Size;
public uint ProcessId;
public VirtualAllocFlags Flags;
#pragma warning restore CS0649
}
// See:
// https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc
// and:
// https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualfree
[Flags]
enum VirtualAllocFlags : uint
{
None = 0,
Commit = 0x1000,
Reserve = 0x2000,
Decommit = 0x4000,
Release = 0x8000,
Reset = 0x80000,
TopDown = 0x100000,
WriteWatch = 0x200000,
Physical = 0x400000,
ResetUndo = 0x1000000,
LargePages = 0x20000000
}
class VirtualAllocOrFreeEvent
{
readonly IPendingResult<IProcessDataSource> pendingProcessDataSource;
readonly IPendingResult<IStackDataSource> pendingStackDataSource;
readonly int threadId;
public VirtualAllocOrFreeEvent(AddressRange addressRange, int processId, VirtualAllocFlags flags,
TraceTimestamp timestamp, int threadId, IPendingResult<IProcessDataSource> pendingProcessDataSource,
IPendingResult<IStackDataSource> pendingStackDataSource)
{
this.pendingProcessDataSource = pendingProcessDataSource;
this.pendingStackDataSource = pendingStackDataSource;
this.threadId = threadId;
AddressRange = addressRange;
ProcessId = processId;
Flags = flags;
Timestamp = timestamp;
}
public AddressRange AddressRange { get; }
public int ProcessId { get; }
public VirtualAllocFlags Flags { get; }
public TraceTimestamp Timestamp { get; }
public IStackSnapshot Stack => pendingStackDataSource.Result.GetStack(Timestamp, threadId);
public IProcess Process => pendingProcessDataSource.Result.GetProcess(Timestamp, ProcessId);
}
}
启用 VirtualAlloc Tracing 后,如何使用 TraceProcessor 取回 VirtualAlloc 事件?
在Microsoft.Windows.EventTracing.Memory中我只找到
- IHeapAllocation
- IHeapSnapshot
- IReferenceSetAccessedPage
- IWorkingSetEntry
- ...
但是没有提到VirtualAlloc的东西。
相关说明:使用此库解析 .NET ETW 事件有多难。 TraceEvent 库对 .NET 事件有很好的支持,但我不清楚我应该如何扩展 TraceProcessor。 TraceProcessor 的 .NET 事件是否在路线图上?
(直到最近我还在研究 TraceProcessor 库。)
正如您所注意到的,我认为我们目前没有对 VirtualAlloc 事件的内置支持。我会让当前的团队优先考虑,但我们过去没有收到对 .NET 事件的重大请求。
对于 VirtualAlloc(和 .NET 事件),是的,您可以自己解析这些事件。参见:
https://docs.microsoft.com/en-us/windows/apps/trace-processing/extensibility
查看概览。您需要了解 VirtualAlloc 事件的格式才能知道如何解析它,以及这些事件的提供者 ID/事件 ID/版本。
现在有几个人问了,下面是获取 VirtualAlloc/VirtualFree 事件数据的示例:
using Microsoft.Windows.EventTracing;
using Microsoft.Windows.EventTracing.Processes;
using Microsoft.Windows.EventTracing.Symbols;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
class Program
{
static int Main(string[] args)
{
if (args.Length != 1)
{
Console.Error.WriteLine("Usage: VirtualAllocFree.exe <trace.etl>");
return 1;
}
try
{
Run(args[0]);
}
catch (Exception exception)
{
Console.Error.WriteLine(exception);
return exception.HResult;
}
return 0;
}
static void Run(string tracePath)
{
using (ITraceProcessor trace = TraceProcessor.Create(tracePath))
{
Guid kernelMemoryProviderId = new Guid("3d6fa8d3-fe05-11d0-9dda-00c04fd7ba7c");
IPendingResult<IStackDataSource> pendingStackDataSource = trace.UseStacks();
IPendingResult<IProcessDataSource> pendingProcessDataSource = trace.UseProcesses();
List<VirtualAllocOrFreeEvent> virtualAllocOrFreeEvents = new List<VirtualAllocOrFreeEvent>();
TraceEventCallback handleKernelMemoryEvent = (EventContext eventContext) =>
{
ClassicEvent classicEvent = eventContext.Event.AsClassicEvent;
if (classicEvent.Version < 2)
{
return;
}
int eventId = classicEvent.Id;
const int virtualAllocEventId = 98;
const int virtualFreeEventId = 99;
if (eventId != virtualAllocEventId && eventId != virtualFreeEventId)
{
return;
}
VirtualAlloc64EventData eventData;
if (classicEvent.Is32Bit)
{
if (classicEvent.Data.Length != Marshal.SizeOf<VirtualAlloc32EventData>())
{
throw new InvalidTraceDataException("Invalid virtual alloc/free event.");
}
VirtualAlloc32EventData thunk = MemoryMarshal.Read<VirtualAlloc32EventData>(classicEvent.Data);
eventData.Base = thunk.Base;
eventData.Size = thunk.Size;
eventData.ProcessId = thunk.ProcessId;
eventData.Flags = thunk.Flags;
}
else
{
if (classicEvent.Data.Length != Marshal.SizeOf<VirtualAlloc64EventData>())
{
throw new InvalidTraceDataException("Invalid virtual alloc/free event.");
}
eventData = MemoryMarshal.Read<VirtualAlloc64EventData>(classicEvent.Data);
}
AddressRange addressRange = new AddressRange(new Address(eventData.Base),
unchecked((long)eventData.Size));
int processId = unchecked((int)eventData.ProcessId);
VirtualAllocFlags flags = eventData.Flags;
TraceTimestamp timestamp = classicEvent.Timestamp;
int threadId = classicEvent.ThreadId.Value;
virtualAllocOrFreeEvents.Add(new VirtualAllocOrFreeEvent(addressRange, processId, flags, timestamp,
threadId, pendingProcessDataSource, pendingStackDataSource));
};
trace.Use(new Guid[] { kernelMemoryProviderId }, handleKernelMemoryEvent);
IPendingResult<ISymbolDataSource> pendingSymbolDataSource = trace.UseSymbols();
trace.Process();
pendingSymbolDataSource.Result
.LoadSymbolsForConsoleAsync(SymCachePath.Automatic, SymbolPath.Automatic).GetAwaiter().GetResult();
Console.WriteLine($"Total virtual alloc/free events: {virtualAllocOrFreeEvents.Count}");
}
}
struct VirtualAlloc64EventData
{
public ulong Base;
public ulong Size;
public uint ProcessId;
public VirtualAllocFlags Flags;
}
struct VirtualAlloc32EventData
{
#pragma warning disable CS0649
public uint Base;
public uint Size;
public uint ProcessId;
public VirtualAllocFlags Flags;
#pragma warning restore CS0649
}
// See:
// https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc
// and:
// https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualfree
[Flags]
enum VirtualAllocFlags : uint
{
None = 0,
Commit = 0x1000,
Reserve = 0x2000,
Decommit = 0x4000,
Release = 0x8000,
Reset = 0x80000,
TopDown = 0x100000,
WriteWatch = 0x200000,
Physical = 0x400000,
ResetUndo = 0x1000000,
LargePages = 0x20000000
}
class VirtualAllocOrFreeEvent
{
readonly IPendingResult<IProcessDataSource> pendingProcessDataSource;
readonly IPendingResult<IStackDataSource> pendingStackDataSource;
readonly int threadId;
public VirtualAllocOrFreeEvent(AddressRange addressRange, int processId, VirtualAllocFlags flags,
TraceTimestamp timestamp, int threadId, IPendingResult<IProcessDataSource> pendingProcessDataSource,
IPendingResult<IStackDataSource> pendingStackDataSource)
{
this.pendingProcessDataSource = pendingProcessDataSource;
this.pendingStackDataSource = pendingStackDataSource;
this.threadId = threadId;
AddressRange = addressRange;
ProcessId = processId;
Flags = flags;
Timestamp = timestamp;
}
public AddressRange AddressRange { get; }
public int ProcessId { get; }
public VirtualAllocFlags Flags { get; }
public TraceTimestamp Timestamp { get; }
public IStackSnapshot Stack => pendingStackDataSource.Result.GetStack(Timestamp, threadId);
public IProcess Process => pendingProcessDataSource.Result.GetProcess(Timestamp, ProcessId);
}
}