【问题标题】:Missing Stack Frames from ClrStackWalk EventClrStackWalk 事件中缺少堆栈帧
【发布时间】:2020-08-18 08:00:37
【问题描述】:

我尝试使用 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!");
                }
            }
        }
    }
}

但是当我这样做时,我发现几乎所有堆栈帧都丢失了。我总是得到 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>

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

由于我无法访问原始事件,我只有一个不错的类型安全尽管无用的包装器,这使得无法获取 .NET Stackwalk 事件的堆栈帧。 除此之外,当我尝试查找符号时,TraceProcessing 是否还支持 JITed 代码?在 API 级别,我只能在图像级别找到一种方法,这将无法解码 JITed 代码?但是由于 TraceProcessing 可以解码 JITed 调用堆栈,我认为 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 事件?

【问题讨论】:

    标签: c# etw .net-traceprocessing


    【解决方案1】:

    如果清单和事件负载不匹配,自己解析它们可能是最好的解决方法。 (很高兴这对你有用。)

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

    【讨论】:

    • .NET Core 清单位于 github.com/dotnet/runtime/blob/master/src/coreclr/src/vm/…。 .NET Core 不再有 ClrStackwalk 事件,因为只有 Win7/XP 才需要,其中内核的 ETW stackwalker 在 x64 上的 JITed 代码中确实中断了。我想我可以直接向 .NET 提交错误报告?
    • 是的,直接在 .NET 上提交错误可能是最好的选择。不幸的是,我认为 .NET Framework 中的错误修复在这一点上的优先级低于 Core,因此您的解决方法可能最终成为现实的选择。
    【解决方案2】:

    通过解析所有事件,然后获取事件的原始数据并自行存储地址,我让它以某种骇人听闻的方式工作。稍后,当我解析通用事件时,我可以通过时间戳映射相应的事件并解析方法。 托管方法也很好地显示在 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);
            }
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 2021-05-02
      • 1970-01-01
      • 2021-08-06
      • 1970-01-01
      • 1970-01-01
      • 2023-03-03
      • 2019-08-10
      • 2019-02-19
      • 1970-01-01
      相关资源
      最近更新 更多