Skip to content

Commit

Permalink
AMD64/ARM64 stackwalking backend
Browse files Browse the repository at this point in the history
  • Loading branch information
Max Charlamb committed Jan 28, 2025
1 parent da73082 commit a794374
Show file tree
Hide file tree
Showing 17 changed files with 498 additions and 278 deletions.
8 changes: 8 additions & 0 deletions src/coreclr/debug/runtimeinfo/datadescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,14 @@ CDAC_TYPE_FIELD(InlinedCallFrame, /*pointer*/, CallerReturnAddress, offsetof(Inl
CDAC_TYPE_FIELD(InlinedCallFrame, /*pointer*/, CalleeSavedFP, offsetof(InlinedCallFrame, m_pCalleeSavedFP))
CDAC_TYPE_END(InlinedCallFrame)

#ifdef FEATURE_EH_FUNCLETS
CDAC_TYPE_BEGIN(SoftwareExceptionFrame)
CDAC_TYPE_SIZE(sizeof(SoftwareExceptionFrame))
CDAC_TYPE_FIELD(SoftwareExceptionFrame, /*T_CONTEXT*/, TargetContext, cdac_data<SoftwareExceptionFrame>::TargetContext)
CDAC_TYPE_FIELD(SoftwareExceptionFrame, /*pointer*/, ReturnAddress, cdac_data<SoftwareExceptionFrame>::ReturnAddress)
CDAC_TYPE_END(SoftwareExceptionFrame)
#endif // FEATURE_EH_FUNCLETS

#define DEFINE_FRAME_TYPE(frameType) \
CDAC_TYPE_BEGIN(frameType) \
CDAC_TYPE_SIZE(sizeof(frameType)) \
Expand Down
2 changes: 0 additions & 2 deletions src/coreclr/unwinder/amd64/unwinder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,8 +352,6 @@ BOOL amd64Unwind(void* pContext, ReadCallback readCallback, GetAllocatedBuffer g
{
HRESULT hr = E_FAIL;

return (long)((CONTEXT*)pContext)->Rip;

OOPStackUnwinderAMD64 unwinder { readCallback, getAllocatedBuffer, getStackWalkInfo };
hr = unwinder.Unwind((CONTEXT*) pContext);

Expand Down
20 changes: 17 additions & 3 deletions src/coreclr/unwinder/arm64/unwinder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,25 @@ typedef struct _ARM64_VFP_STATE
// Macros for accessing memory. These can be overridden if other code
// (in particular the debugger) needs to use them.

#if !defined(DEBUGGER_UNWIND)
#if !defined(DEBUGGER_UNWIND) && !defined(FEATURE_CDAC_UNWINDER)

#define MEMORY_READ_BYTE(params, addr) (*dac_cast<PTR_BYTE>(addr))
#define MEMORY_READ_WORD(params, addr) (*dac_cast<PTR_WORD>(addr))
#define MEMORY_READ_DWORD(params, addr) (*dac_cast<PTR_DWORD>(addr))
#define MEMORY_READ_QWORD(params, addr) (*dac_cast<PTR_UINT64>(addr))

#elif defined(FEATURE_CDAC_UNWINDER)
template<typename T>
T cdacRead(uint64_t addr)
{
T t;
g_pUnwinder->readCallback(addr, &t, sizeof(t));
return t;
}
#define MEMORY_READ_BYTE(params, addr) (cdacRead<BYTE>(addr))
#define MEMORY_READ_WORD(params, addr) (cdacRead<WORD>(addr))
#define MEMORY_READ_DWORD(params, addr) (cdacRead<DWORD>(addr))
#define MEMORY_READ_QWORD(params, addr) (cdacRead<UINT64>(addr))
#endif

//
Expand Down Expand Up @@ -2788,15 +2800,17 @@ BOOL DacUnwindStackFrame(T_CONTEXT *pContext, T_KNONVOLATILE_CONTEXT_POINTERS* p
#endif // DACCESS_COMPILE

#ifdef FEATURE_CDAC_UNWINDER
OOPStackUnwinderArm64* g_pUnwinder;
BOOL arm64Unwind(void* pContext, ReadCallback readCallback, GetAllocatedBuffer getAllocatedBuffer, GetStackWalkInfo getStackWalkInfo)
{
HRESULT hr = E_FAIL;

return (long)((T_CONTEXT*)pContext)->Pc;

OOPStackUnwinderArm64 unwinder { readCallback, getAllocatedBuffer, getStackWalkInfo };
g_pUnwinder = &unwinder;
hr = unwinder.Unwind((T_CONTEXT*) pContext);

g_pUnwinder = nullptr;

return (hr == S_OK);
}
#endif
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/unwinder/baseunwinder.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ class OOPStackUnwinder
getStackWalkInfo(getStackWalkInfo)
{ }


public:
// These functions are marked public because they are called using
// a global instance of OOPStackUnwinder in the ARM64 implementation.
ReadCallback readCallback;
GetAllocatedBuffer getAllocatedBuffer;
GetStackWalkInfo getStackWalkInfo;
Expand Down
28 changes: 18 additions & 10 deletions src/coreclr/vm/frames.h
Original file line number Diff line number Diff line change
Expand Up @@ -1213,10 +1213,18 @@ class SoftwareExceptionFrame : public Frame

virtual void UpdateRegDisplay(const PREGDISPLAY, bool updateFloats = false);

friend struct ::cdac_data<SoftwareExceptionFrame>;

// Keep as last entry in class
DEFINE_VTABLE_GETTER_AND_DTOR(SoftwareExceptionFrame)
};

template<>
struct cdac_data<SoftwareExceptionFrame>
{
static constexpr size_t TargetContext = offsetof(SoftwareExceptionFrame, m_Context);
static constexpr size_t ReturnAddress = offsetof(SoftwareExceptionFrame, m_ReturnAddress);
};
#endif // FEATURE_EH_FUNCLETS

//-----------------------------------------------------------------------
Expand Down Expand Up @@ -1452,10 +1460,10 @@ class HelperMethodFrame : public Frame

LazyMachState m_MachState; // pRetAddr points to the return address and the stack arguments

friend struct ::cdac_data<HelperMethodFrame>;

// Keep as last entry in class
DEFINE_VTABLE_GETTER_AND_CTOR_AND_DTOR(HelperMethodFrame)

friend struct ::cdac_data<HelperMethodFrame>;
};

template<>
Expand Down Expand Up @@ -1547,10 +1555,10 @@ class HelperMethodFrame_1OBJ : public HelperMethodFrame
private:
PTR_OBJECTREF gcPtrs[1];

friend struct ::cdac_data<HelperMethodFrame_1OBJ>;

// Keep as last entry in class
DEFINE_VTABLE_GETTER_AND_CTOR_AND_DTOR(HelperMethodFrame_1OBJ)

friend struct ::cdac_data<HelperMethodFrame_1OBJ>;
};

template<>
Expand Down Expand Up @@ -1618,10 +1626,10 @@ class HelperMethodFrame_2OBJ : public HelperMethodFrame
private:
PTR_OBJECTREF gcPtrs[2];

friend struct ::cdac_data<HelperMethodFrame_2OBJ>;

// Keep as last entry in class
DEFINE_VTABLE_GETTER_AND_CTOR_AND_DTOR(HelperMethodFrame_2OBJ)

friend struct ::cdac_data<HelperMethodFrame_2OBJ>;
};

template<>
Expand Down Expand Up @@ -1694,10 +1702,10 @@ class HelperMethodFrame_3OBJ : public HelperMethodFrame
private:
PTR_OBJECTREF gcPtrs[3];

friend struct ::cdac_data<HelperMethodFrame_3OBJ>;

// Keep as last entry in class
DEFINE_VTABLE_GETTER_AND_CTOR_AND_DTOR(HelperMethodFrame_3OBJ)

friend struct ::cdac_data<HelperMethodFrame_3OBJ>;
};

template<>
Expand Down Expand Up @@ -1770,10 +1778,10 @@ class HelperMethodFrame_PROTECTOBJ : public HelperMethodFrame
PTR_OBJECTREF m_pObjRefs;
UINT m_numObjRefs;

friend struct ::cdac_data<HelperMethodFrame_PROTECTOBJ>;

// Keep as last entry in class
DEFINE_VTABLE_GETTER_AND_CTOR_AND_DTOR(HelperMethodFrame_PROTECTOBJ)

friend struct ::cdac_data<HelperMethodFrame_PROTECTOBJ>;
};

template<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,24 @@ namespace Microsoft.Diagnostics.DataContractReader;
/// </remarks>
internal abstract class Target
{
public enum CorDebugPlatform : int
{
CORDB_PLATFORM_WINDOWS_X86 = 0,
CORDB_PLATFORM_WINDOWS_AMD64 = 1,
CORDB_PLATFORM_WINDOWS_IA64 = 2,
CORDB_PLATFORM_MAC_PPC = 3,
CORDB_PLATFORM_MAC_X86 = 4,
CORDB_PLATFORM_WINDOWS_ARM = 5,
CORDB_PLATFORM_MAC_AMD64 = 6,
CORDB_PLATFORM_WINDOWS_ARM64 = 7,
CORDB_PLATFORM_POSIX_AMD64 = 8,
CORDB_PLATFORM_POSIX_X86 = 9,
CORDB_PLATFORM_POSIX_ARM = 10,
CORDB_PLATFORM_POSIX_ARM64 = 11,
CORDB_PLATFORM_POSIX_LOONGARCH64 = 12,
CORDB_PLATFORM_POSIX_RISCV64 = 12
}

/// <summary>
/// Pointer size of the target
/// </summary>
Expand All @@ -28,7 +46,7 @@ internal abstract class Target
public abstract bool IsLittleEndian { get; }

public abstract int GetThreadContext(uint threadId, uint contextFlags, uint contextSize, Span<byte> bufferToFill);
public abstract int GetPlatform(out int platform);
public abstract int GetPlatform(out CorDebugPlatform platform);

/// <summary>
/// Reads a well-known global pointer value from the target process
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,19 +71,26 @@ public override TargetPointer GetUnwindInfo(RangeSection rangeSection, TargetCod
throw new InvalidOperationException("Unable to get NumUnwindInfos");
}

ulong imageBase = rangeSection.Data.RangeBegin;
if (numUnwindInfos == 0)
{
return TargetPointer.Null;
}

for (ulong i = 0; i < numUnwindInfos; i++)
ulong imageBase = rangeSection.Data.RangeBegin;
TargetPointer prevUnwindInfoAddress = unwindInfos;
TargetPointer currUnwindInfoAddress;
for (ulong i = 1; i < numUnwindInfos; i++)
{
TargetPointer unwindInfoAddress = unwindInfos + (i * runtimeFunctionSize);
Data.RuntimeFunction runtimeFunction = Target.ProcessedData.GetOrAdd<Data.RuntimeFunction>(unwindInfoAddress);
if (runtimeFunction.BeginAddress + imageBase <= jittedCodeAddress.Value && runtimeFunction.EndAddress + imageBase >= jittedCodeAddress.Value)
currUnwindInfoAddress = unwindInfos + (i * runtimeFunctionSize);
Data.RuntimeFunction nextRuntimeFunction = Target.ProcessedData.GetOrAdd<Data.RuntimeFunction>(currUnwindInfoAddress);
if (nextRuntimeFunction.BeginAddress + imageBase > jittedCodeAddress.Value)
{
return unwindInfoAddress;
return prevUnwindInfoAddress;
}
prevUnwindInfoAddress = currUnwindInfoAddress;
}

return TargetPointer.Null;
return prevUnwindInfoAddress;
}

private TargetPointer FindMethodCode(RangeSection rangeSection, TargetCodePointer jittedCodeAddress)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers;
/// AMD64-specific thread context.
/// </summary>
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct AMD64Context : IContext
internal struct AMD64Context : IContext
{
[Flags]
public enum ContextFlagsValues : uint
Expand All @@ -32,8 +32,47 @@ public enum ContextFlagsValues : uint
public static uint Size => 0x4d0;
public static uint DefaultContextFlags => (uint)ContextFlagsValues.CONTEXT_FULL;

public readonly TargetPointer StackPointer => new(Rsp);
public readonly TargetPointer InstructionPointer => new(Rip);
public TargetPointer StackPointer
{
readonly get => new(Rsp);
set => Rsp = value.Value;
}
public TargetPointer InstructionPointer
{
readonly get => new(Rip);
set => Rip = value.Value;
}
public TargetPointer FramePointer
{
readonly get => new(Rbp);
set => Rbp = value.Value;
}

public void Unwind(Target target)
{
Unwinder.AMD64Unwind(ref this, target);
}

public unsafe void ReadFromAddress(Target target, TargetPointer address)
{
Span<byte> buffer = new byte[Size];
target.ReadBuffer(address, buffer);
Span<AMD64Context> structSpan = MemoryMarshal.CreateSpan(ref this, sizeof(AMD64Context));
Span<byte> byteSpan = MemoryMarshal.Cast<AMD64Context, byte>(structSpan);
if (buffer.Length > sizeof(AMD64Context))
{
buffer.Slice(0, sizeof(AMD64Context)).CopyTo(byteSpan);
}
else
{
buffer.CopyTo(byteSpan);
}
}

public void Clear()
{
this = default;
}

public override string ToString()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;

namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers;

/// <summary>
/// ARM64-specific thread context.
/// </summary>
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct ARM64Context : IContext
internal struct ARM64Context : IContext
{
[Flags]
public enum ContextFlagsValues : uint
Expand All @@ -29,8 +31,71 @@ public enum ContextFlagsValues : uint

public static uint DefaultContextFlags => (uint)ContextFlagsValues.CONTEXT_FULL;

public readonly TargetPointer StackPointer => new(Sp);
public readonly TargetPointer InstructionPointer => new(Pc);
public TargetPointer StackPointer
{
readonly get => new(Sp);
set => Sp = value.Value;
}
public TargetPointer InstructionPointer
{
readonly get => new(Pc);
set => Pc = value.Value;
}
public TargetPointer FramePointer
{
readonly get => new(Fp);
set => Fp = value.Value;
}

public void Unwind(Target target)
{
Unwinder.ARM64Unwind(ref this, target);
}

public void Clear()
{
this = default;
}

public unsafe void ReadFromAddress(Target target, TargetPointer address)
{
Span<byte> buffer = new byte[Size];
target.ReadBuffer(address, buffer);
Span<ARM64Context> structSpan = MemoryMarshal.CreateSpan(ref this, sizeof(ARM64Context));
Span<byte> byteSpan = MemoryMarshal.Cast<ARM64Context, byte>(structSpan);
if (buffer.Length > sizeof(ARM64Context))
{
buffer.Slice(0, sizeof(ARM64Context)).CopyTo(byteSpan);
}
else
{
buffer.CopyTo(byteSpan);
}
}

public override string ToString()
{
StringBuilder sb = new();
foreach (FieldInfo fieldInfo in typeof(AMD64Context).GetFields())
{
switch (fieldInfo.GetValue(this))
{
case ulong v:
sb.AppendLine($"{fieldInfo.Name} = {v:x16}");
break;
case uint v:
sb.AppendLine($"{fieldInfo.Name} = {v:x8}");
break;
case ushort v:
sb.AppendLine($"{fieldInfo.Name} = {v:x4}");
break;
default:
sb.AppendLine($"{fieldInfo.Name} = {fieldInfo.GetValue(this)}");
continue;
}
}
return sb.ToString();
}

// Control flags

Expand Down
Loading

0 comments on commit a794374

Please sign in to comment.