问题描述
我尝试使用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);
}
}
}