ClrStackWalk事件中缺少堆栈帧

问题描述

我尝试使用TraceProcessing库从具有关联的Clr Stackwalk事件堆栈跟踪的托管异常中获取。原则上,通过解析事件并获取方法地址应该很容易。

using Microsoft.Windows.EventTracing;
using Microsoft.Windows.EventTracing.Events;
using System;
using System.Collections.Generic;
using System.Linq;

namespace TraceProcessingStackDecoding
{
    class Program
    {
        static void Main(string[] args)
        {
            string etlFile = args[0];
            using ITraceProcessor processor = TraceProcessor.Create(etlFile,new TraceProcessorSettings
            {
                AllowLostEvents = true,});

            IPendingResult<IGenericEventDataSource> genericEvents = processor.UseGenericEvents();
            processor.Process();


            const int ClrstackWalkEventId = 82;
            const string DotNetRuntimeProviderName = "Microsoft-Windows-DotNETRuntime";

            foreach (IGenericEvent clrstackWalk in genericEvents.Result.Events.Where( x=> x.ProviderName == DotNetRuntimeProviderName && x.Id == ClrstackWalkEventId))
            {
                IReadOnlyList<Address> stackAddresses = clrstackWalk.Fields["Stack"].AsAddressList;
                uint frameCount = clrstackWalk.Fields["FrameCount"].AsUInt32;

                if( stackAddresses.Count != frameCount)
                {
                    Console.WriteLine($"Error: Address List has only {stackAddresses.Count} entries but expected were {frameCount} entries!");
                }
            }
        }
    }
}

但是当我这样做时,我发现几乎所有Stack框架都丢失了。我总是得到2。如果我没记错的话,它应该返回所有数据,直到事件结束。

Error: Address List has only 2 entries but expected were 34 entries!
Error: Address List has only 2 entries but expected were 35 entries!
Error: Address List has only 2 entries but expected were 35 entries!
Error: Address List has only 2 entries but expected were 35 entries!
Error: Address List has only 2 entries but expected were 36 entries!
Error: Address List has only 2 entries but expected were 37 entries!
Error: Address List has only 2 entries but expected were 64 entries!
Error: Address List has only 2 entries but expected were 30 entries!
Error: Address List has only 2 entries but expected were 77 entries!
Error: Address List has only 2 entries but expected were 77 entries!
Error: Address List has only 2 entries but expected were 31 entries!
Error: Address List has only 2 entries but expected were 77 entries!

Clr Stackwalk事件清单将其定义为:

            <template tid="ClrstackWalk">
                <data name="ClrInstanceID" inType="win:UInt16"/>
                <data name="Reserved1" inType="win:UInt8"/>
                <data name="Reserved2" inType="win:UInt8"/>
                <data name="FrameCount" inType="win:UInt32"/>
                <data name="Stack" count="2" inType="win:Pointer"/>
            </template>

问题可能是您认真对待了count属性。但这不是实际记录事件的方式,并且地址列表几乎是没有固定计数的堆栈的100%动态列表。最好的办法是返回所有数据,直到事件结束为止作为地址列表,如果它是所显示事件中的最后一项。

由于我无法访问原始事件,所以我只有一个不错的类型安全(尽管没有用的包装器),这使得无法获取.NET Stackwalk事件的堆栈框架。 除此之外,当我尝试查找符号时,TraceProcessing还支持JIT代码吗?在API级别,我只能在Image级别找到一个方法,该方法将使得无法解码JIT代码?但是,由于TraceProcessing可以解码JIT调用堆栈,所以我认为在API级别上可能会缺少一些东西。

foreach (Address stackAdr in stackAddresses)
{
    foreach (var image in ev.Process.Images)
    {
        var range = image.AddressRange;
        if (  ( (range.BaseAddress < range.LimitAddress) && (stackAdr > range.BaseAddress && stackAdr < range.LimitAddress)) ||
              ( (range.BaseAddress > range.LimitAddress) && (stackAdr < range.BaseAddress && stackAdr > range.LimitAddress)) )
        {
            IStackSymbol stackSymbol = image.GetSymbol(stackAdr);
            Console.WriteLine(stackSymbol?.FunctionName);
        }
    }
}

这种方法是否可以直接使用JIT代码,还是需要手动解码所有JIT事件?

解决方法

如果清单和事件有效负载不匹配,则自行解析它们可能是最好的解决方法。 (很高兴为您服务。)

最好的选择可能是修复清单-该团队是否有GitHub存储库可在其中提交错误?也许以下存储库中的人会知道吗? https://github.com/dotnet/diagnostics

,

通过解析所有事件,然后获取事件的原始数据,并以我自己的方式存储地址,可以使其以某种骇人听闻的方式工作。稍后,当我解析通用事件时,我可以通过其时间戳映射相应事件并解析方法。 托管方法也很好地显示了JITed代码。

    ...    
     processor.Use(ProcessRawEvents);
    ...

    List<StackEvent> StackEvents = new List<StackEvent>();
    
    class StackEvent
    {
        public TraceTimestamp TimeStamp;
        public IReadOnlyList<Address> Stack;
    }
    
    bool myNeedsStack = false;
    
    
void ProcessRawEvents(EventContext eventContext)
{
    TraceEvent ev = eventContext.Event;

    if (ev.ProviderId == Constants.DotNetRuntimeGuid)
    {
        if (ev.Id == Constants.ExceptionEventId)
        {
            myNeedsStack = true;
        }

        // potentially every exception event is followed by a stackwalk event
        if (myNeedsStack && ev.Id == Constants.ClrStackWalkEventId)
        {
            myNeedsStack = false;

            StackEvent stackEv = new StackEvent()
            {
                TimeStamp = ev.Timestamp,};

            ReadOnlySpan<byte> frameData = ev.Data.Slice(8);
            List<Address> addresses = new List<Address>();
            stackEv.Stack = addresses;

            if (ev.Is32Bit)
            {
                ReadOnlySpan<int> ints  = MemoryMarshal.Cast<byte,int>(frameData);
                    
                foreach(var intAdr in ints)
                {
                    addresses.Add(new Address(intAdr));
                }
            }
            else
            {
                ReadOnlySpan<long> longs = MemoryMarshal.Cast<byte,long>(frameData);
                foreach(var longAdr in longs)
                {
                    addresses.Add(new Address(longAdr));
                }
            }

            StackEvents.Add(stackEv);
        }
    }

}