如何使用 TraceProcessor 读取 VirtualAllocations?

How To Read VirtualAllocations With TraceProcessor?

启用 VirtualAlloc Tracing 后,如何使用 TraceProcessor 取回 VirtualAlloc 事件?

在Microsoft.Windows.EventTracing.Memory中我只找到

但是没有提到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);
    }
}