|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using Microsoft.Diagnostics.DataContractReader.Data;
namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers;
/// <summary>
/// Abstract base class providing platform agnostic functionality for handling frames.
/// </summary>
internal abstract class BaseFrameHandler(Target target, IPlatformAgnosticContext context)
{
protected readonly Target _target = target;
protected readonly FrameHelpers _frameHelpers = new FrameHelpers(target);
private readonly IPlatformAgnosticContext _context = context;
public virtual void HandleInlinedCallFrame(InlinedCallFrame inlinedCallFrame)
{
// if the caller return address is 0, then the call is not active
// and we should not update the context
if (inlinedCallFrame.CallerReturnAddress == 0)
{
return;
}
_context.InstructionPointer = inlinedCallFrame.CallerReturnAddress;
_context.StackPointer = inlinedCallFrame.CallSiteSP;
_context.FramePointer = inlinedCallFrame.CalleeSavedFP;
}
public virtual void HandleSoftwareExceptionFrame(SoftwareExceptionFrame softwareExceptionFrame)
{
IPlatformAgnosticContext otherContextHolder = IPlatformAgnosticContext.GetContextForPlatform(_target);
otherContextHolder.ReadFromAddress(_target, softwareExceptionFrame.TargetContext);
UpdateCalleeSavedRegistersFromOtherContext(otherContextHolder);
_context.InstructionPointer = otherContextHolder.InstructionPointer;
_context.StackPointer = otherContextHolder.StackPointer;
}
public virtual void HandleTransitionFrame(FramedMethodFrame framedMethodFrame)
{
Data.TransitionBlock transitionBlock = _target.ProcessedData.GetOrAdd<Data.TransitionBlock>(framedMethodFrame.TransitionBlockPtr);
if (_target.GetTypeInfo(DataType.TransitionBlock).Size is not uint transitionBlockSize)
{
throw new InvalidOperationException("TransitionBlock size is not set");
}
_context.InstructionPointer = transitionBlock.ReturnAddress;
_context.StackPointer = framedMethodFrame.TransitionBlockPtr + transitionBlockSize;
Data.CalleeSavedRegisters calleeSavedRegisters = _target.ProcessedData.GetOrAdd<Data.CalleeSavedRegisters>(transitionBlock.CalleeSavedRegisters);
UpdateFromRegisterDict(calleeSavedRegisters.Registers);
}
public virtual void HandleFuncEvalFrame(FuncEvalFrame funcEvalFrame)
{
Data.DebuggerEval debuggerEval = _target.ProcessedData.GetOrAdd<Data.DebuggerEval>(funcEvalFrame.DebuggerEvalPtr);
// No context to update if the eval doesn't use a hijack (exception or interpreter path).
if (!debuggerEval.EvalUsesHijack)
{
return;
}
_context.ReadFromAddress(_target, debuggerEval.TargetContext);
}
public virtual void HandleResumableFrame(ResumableFrame frame)
{
_context.ReadFromAddress(_target, frame.TargetContextPtr);
}
public virtual void HandleFaultingExceptionFrame(FaultingExceptionFrame frame)
{
_context.ReadFromAddress(_target, frame.TargetContext);
}
public virtual void HandleTailCallFrame(TailCallFrame tailCallFrame)
{
throw new InvalidOperationException("TailCallFrame handling is not implemented on the target platform.");
}
protected void UpdateFromRegisterDict(IReadOnlyDictionary<string, TargetNUInt> registers)
{
foreach ((string name, TargetNUInt value) in registers)
{
if (!_context.TrySetRegister(name, value))
{
throw new InvalidOperationException($"Unexpected register {name} found when trying to update context");
}
}
}
protected void UpdateCalleeSavedRegistersFromOtherContext(IPlatformAgnosticContext otherContext)
{
foreach (string name in _target.GetTypeInfo(DataType.CalleeSavedRegisters).Fields.Keys)
{
if (!otherContext.TryReadRegister(name, out TargetNUInt value))
{
throw new InvalidOperationException($"Unexpected register {name} in callee saved registers");
}
if (!_context.TrySetRegister(name, value))
{
throw new InvalidOperationException($"Unexpected register {name} in callee saved registers");
}
}
}
protected Data.Frame? GetNextFrame(TargetPointer currentFrameAddress)
{
Data.Frame current = _target.ProcessedData.GetOrAdd<Data.Frame>(currentFrameAddress);
if (current.Next == TargetPointer.Null)
return null;
ulong terminator = _target.PointerSize == 8 ? ulong.MaxValue : uint.MaxValue;
if (current.Next.Value == terminator)
return null;
return _target.ProcessedData.GetOrAdd<Data.Frame>(current.Next);
}
}
|