Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion docs/design/datacontracts/StackWalk.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ string GetFrameName(TargetPointer frameIdentifier);

// Gets the method desc pointer associated with the given Frame.
TargetPointer GetMethodDescPtr(TargetPointer framePtr);

// Gets the method desc pointer associated with a given IStackDataFrameHandle
TargetPointer GetMethodDescPtr(IStackDataFrameHandle stackDataFrameHandle);
```

## Version 1
Expand Down Expand Up @@ -347,7 +350,7 @@ TargetPointer GetFrameAddress(IStackDataFrameHandle stackDataFrameHandle);
string GetFrameName(TargetPointer frameIdentifier);
```

`GetMethodDescPtr` returns the method desc pointer associated with a Frame. If not applicable, it returns TargetPointer.Null.
`GetMethodDescPtr(TargetPointer framePtr)` returns the method desc pointer associated with a Frame. If not applicable, it returns TargetPointer.Null.
* For FramedMethodFrame and most of its subclasses the methoddesc is accessible as a pointer field on the object (MethodDescPtr). The two exceptions are PInvokeCalliFrame (no valid method desc) and StubDispatchFrame.
* StubDispatchFrame's MD may be either found on MethodDescPtr, or if this field is null, we look it up using a method table (RepresentativeMTPtr) and MT slot (RepresentativeSlot).
* InlinedCallFrame also has a field from which we draw the method desc; however, we must first do some validation that the data in this field is valid.
Expand All @@ -356,6 +359,14 @@ string GetFrameName(TargetPointer frameIdentifier);
TargetPointer GetMethodDescPtr(TargetPointer framePtr)
```

`GetMethodDescPtr(IStackDataFrameHandle stackDataFrameHandle)` returns the method desc pointer associated with a `IStackDataFrameHandle`. Note this can either be at a capital 'F' frame or a managed frame unlike the above API which works only at capital 'F' frames.
This API is implemeted as follows:
1. Try to get the current frame address with `GetFrameAddress`. If the address is not null, return `GetMethodDescPtr(<frameAddress>)`.
2. Check if the current context IP is a managed context using the ExecutionManager contract. If it is a managed contet, use the ExecutionManager context to find the related MethodDesc and return the pointer to it.
```csharp
TargetPointer GetMethodDescPtr(IStackDataFrameHandle stackDataFrameHandle)
```

### x86 Specifics

The x86 platform has some major differences to other platforms. In general this stems from the platform being older and not having a defined unwinding codes. Instead, to unwind managed frames, we rely on GCInfo associated with JITted code. For the unwind, we do not defer to a 'Windows like' native unwinder, instead the custom unwinder implementation was ported to managed code.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public interface IStackWalk : IContract
TargetPointer GetFrameAddress(IStackDataFrameHandle stackDataFrameHandle) => throw new NotImplementedException();
string GetFrameName(TargetPointer frameIdentifier) => throw new NotImplementedException();
TargetPointer GetMethodDescPtr(TargetPointer framePtr) => throw new NotImplementedException();
TargetPointer GetMethodDescPtr(IStackDataFrameHandle stackDataFrameHandle) => throw new NotImplementedException();
}

public struct StackWalk : IStackWalk
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,16 @@ internal DacStreams_1(Target target)
public string? StringFromEEAddress(TargetPointer address)
{
// We use the data subsystem to handle caching results from processing this data
var dictionary = _target.ProcessedData.GetOrAdd<DacStreams_1_Data>(0).EEObjectToString;

dictionary.TryGetValue(address, out string? result);
return result;
try
{
var dictionary = _target.ProcessedData.GetOrAdd<DacStreams_1_Data>(0).EEObjectToString;
dictionary.TryGetValue(address, out string? result);
return result;
}
catch (VirtualReadException)
{
return null;
}
}

internal sealed class DacStreams_1_Data : IData<DacStreams_1_Data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,23 +187,6 @@ private IPlatformFrameHandler GetFrameHandler(IPlatformAgnosticContext context)
};
}

private static bool InlinedCallFrameHasFunction(Data.InlinedCallFrame frame, Target target)
{
if (target.PointerSize == 4)
{
return frame.Datum != TargetPointer.Null && (frame.Datum.Value & 0x1) != 0;
}
else
{
return ((long)frame.Datum.Value & ~0xffff) != 0;
}
}

private static bool InlinedCallFrameHasActiveCall(Data.InlinedCallFrame frame)
{
return frame.CallerReturnAddress != TargetPointer.Null;
}

public static TargetPointer GetMethodDescPtr(TargetPointer framePtr, Target target)
{
Data.Frame frame = target.ProcessedData.GetOrAdd<Data.Frame>(framePtr);
Expand Down Expand Up @@ -247,4 +230,21 @@ public static TargetPointer GetMethodDescPtr(TargetPointer framePtr, Target targ
return TargetPointer.Null;
}
}

private static bool InlinedCallFrameHasFunction(Data.InlinedCallFrame frame, Target target)
{
if (target.PointerSize == 4)
{
return frame.Datum != TargetPointer.Null && (frame.Datum.Value & 0x1) != 0;
}
else
{
return ((long)frame.Datum.Value & ~0xffff) != 0;
}
}

private static bool InlinedCallFrameHasActiveCall(Data.InlinedCallFrame frame)
{
return frame.CallerReturnAddress != TargetPointer.Null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,27 @@ TargetPointer IStackWalk.GetFrameAddress(IStackDataFrameHandle stackDataFrameHan
string IStackWalk.GetFrameName(TargetPointer frameIdentifier)
=> FrameIterator.GetFrameName(_target, frameIdentifier);

TargetPointer IStackWalk.GetMethodDescPtr(TargetPointer framePtr)
=> FrameIterator.GetMethodDescPtr(framePtr, _target);

TargetPointer IStackWalk.GetMethodDescPtr(IStackDataFrameHandle stackDataFrameHandle)
{
StackDataFrameHandle handle = AssertCorrectHandle(stackDataFrameHandle);

// if we are at a capital F Frame, we can get the method desc from the frame
// TODO(cdac): Support special fReportInteropMD case https://github.com/dotnet/runtime/issues/119724
TargetPointer framePtr = ((IStackWalk)this).GetFrameAddress(handle);
if (framePtr != TargetPointer.Null)
return ((IStackWalk)this).GetMethodDescPtr(framePtr);

// otherwise try to get the method desc from the IP
if (!IsManaged(handle.Context.InstructionPointer, out CodeBlockHandle? codeBlockHandle))
return TargetPointer.Null;

IExecutionManager eman = _target.Contracts.ExecutionManager;
return eman.GetMethodDesc(codeBlockHandle.Value);
}

private bool IsManaged(TargetPointer ip, [NotNullWhen(true)] out CodeBlockHandle? codeBlockHandle)
{
IExecutionManager eman = _target.Contracts.ExecutionManager;
Expand Down Expand Up @@ -205,11 +226,6 @@ private unsafe void FillContextFromThread(IPlatformAgnosticContext context, Thre
context.FillFromBuffer(buffer);
}

TargetPointer IStackWalk.GetMethodDescPtr(TargetPointer framePtr)
{
return FrameIterator.GetMethodDescPtr(framePtr, _target);
}

private static StackDataFrameHandle AssertCorrectHandle(IStackDataFrameHandle stackDataFrameHandle)
{
if (stackDataFrameHandle is not StackDataFrameHandle handle)
Expand Down
127 changes: 127 additions & 0 deletions src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataFrame.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// 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.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using Microsoft.Diagnostics.DataContractReader.Contracts;

namespace Microsoft.Diagnostics.DataContractReader.Legacy;

[GeneratedComClass]
internal sealed unsafe partial class ClrDataFrame : IXCLRDataFrame, IXCLRDataFrame2
{
private readonly Target _target;
private readonly IXCLRDataFrame? _legacyImpl;
private readonly IXCLRDataFrame2? _legacyImpl2;

private readonly IStackDataFrameHandle _dataFrame;

public ClrDataFrame(Target target, IStackDataFrameHandle dataFrame, IXCLRDataFrame? legacyImpl)
{
_target = target;
_legacyImpl = legacyImpl;
_legacyImpl2 = legacyImpl as IXCLRDataFrame2;

_dataFrame = dataFrame;
}

// IXCLRDataFrame implementation
int IXCLRDataFrame.GetFrameType(uint* simpleType, uint* detailedType)
=> _legacyImpl is not null ? _legacyImpl.GetFrameType(simpleType, detailedType) : HResults.E_NOTIMPL;

int IXCLRDataFrame.GetContext(
uint contextFlags,
uint contextBufSize,
uint* contextSize,
[Out, MarshalUsing(CountElementName = nameof(contextBufSize))] byte[] contextBuf)
=> _legacyImpl is not null ? _legacyImpl.GetContext(contextFlags, contextBufSize, contextSize, contextBuf) : HResults.E_NOTIMPL;

int IXCLRDataFrame.GetAppDomain(void** appDomain)
=> _legacyImpl is not null ? _legacyImpl.GetAppDomain(appDomain) : HResults.E_NOTIMPL;

int IXCLRDataFrame.GetNumArguments(uint* numArgs)
=> _legacyImpl is not null ? _legacyImpl.GetNumArguments(numArgs) : HResults.E_NOTIMPL;

int IXCLRDataFrame.GetArgumentByIndex(
uint index,
void** arg,
uint bufLen,
uint* nameLen,
char* name)
=> _legacyImpl is not null ? _legacyImpl.GetArgumentByIndex(index, arg, bufLen, nameLen, name) : HResults.E_NOTIMPL;

int IXCLRDataFrame.GetNumLocalVariables(uint* numLocals)
=> _legacyImpl is not null ? _legacyImpl.GetNumLocalVariables(numLocals) : HResults.E_NOTIMPL;

int IXCLRDataFrame.GetLocalVariableByIndex(
uint index,
void** localVariable,
uint bufLen,
uint* nameLen,
char* name)
=> _legacyImpl is not null ? _legacyImpl.GetLocalVariableByIndex(index, localVariable, bufLen, nameLen, name) : HResults.E_NOTIMPL;

int IXCLRDataFrame.GetCodeName(
uint flags,
uint bufLen,
uint* nameLen,
char* nameBuf)
=> _legacyImpl is not null ? _legacyImpl.GetCodeName(flags, bufLen, nameLen, nameBuf) : HResults.E_NOTIMPL;

int IXCLRDataFrame.GetMethodInstance(out IXCLRDataMethodInstance? method)
{
int hr = HResults.S_OK;
method = null;

IXCLRDataMethodInstance? legacyMethod = null;
if (_legacyImpl is not null)
{
int hrLocal = _legacyImpl.GetMethodInstance(out legacyMethod);
if (hrLocal < 0)
return hrLocal;
}

try
{
IStackWalk stackWalk = _target.Contracts.StackWalk;
IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;

TargetPointer methodDesc = stackWalk.GetMethodDescPtr(_dataFrame);

if (methodDesc == TargetPointer.Null)
throw Marshal.GetExceptionForHR(/*E_NOINTERFACE*/ HResults.COR_E_INVALIDCAST)!;

MethodDescHandle mdh = rts.GetMethodDescHandle(methodDesc);
TargetPointer appDomain = _target.ReadPointer(
_target.ReadGlobalPointer(Constants.Globals.AppDomain));

method = new ClrDataMethodInstance(_target, mdh, appDomain, legacyMethod);
}
catch (System.Exception ex)
{
hr = ex.HResult;
}

return hr;
}

int IXCLRDataFrame.Request(
uint reqCode,
uint inBufferSize,
byte* inBuffer,
uint outBufferSize,
byte* outBuffer)
=> _legacyImpl is not null ? _legacyImpl.Request(reqCode, inBufferSize, inBuffer, outBufferSize, outBuffer) : HResults.E_NOTIMPL;

int IXCLRDataFrame.GetNumTypeArguments(uint* numTypeArgs)
=> _legacyImpl is not null ? _legacyImpl.GetNumTypeArguments(numTypeArgs) : HResults.E_NOTIMPL;

int IXCLRDataFrame.GetTypeArgumentByIndex(uint index, void** typeArg)
=> _legacyImpl is not null ? _legacyImpl.GetTypeArgumentByIndex(index, typeArg) : HResults.E_NOTIMPL;

// IXCLRDataFrame2 implementation
int IXCLRDataFrame2.GetExactGenericArgsToken(void** genericToken)
=> _legacyImpl2 is not null ? _legacyImpl2.GetExactGenericArgsToken(genericToken) : HResults.E_NOTIMPL;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Text;
using Microsoft.Diagnostics.DataContractReader.Contracts;

namespace Microsoft.Diagnostics.DataContractReader.Legacy;
Expand Down Expand Up @@ -110,7 +111,82 @@ int IXCLRDataMethodInstance.GetTokenAndScope(uint* token, void** /*IXCLRDataModu
}

int IXCLRDataMethodInstance.GetName(uint flags, uint bufLen, uint* nameLen, char* nameBuf)
=> _legacyImpl is not null ? _legacyImpl.GetName(flags, bufLen, nameLen, nameBuf) : HResults.E_NOTIMPL;
{
int hr = HResults.S_OK;

try
{
IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;

if (flags != 0)
throw new ArgumentException();

bool fallbackToUnknown = false;
StringBuilder sb = new();
try
{
TypeNameBuilder.AppendMethodInternal(
_target,
sb,
_methodDesc,
TypeNameFormat.FormatSignature |
TypeNameFormat.FormatNamespace |
TypeNameFormat.FormatFullInst);
}
catch
{
string? fallbackName = _target.Contracts.DacStreams.StringFromEEAddress(_methodDesc.Address);
if (fallbackName != null)
{
sb.Clear();
sb.Append(fallbackName);
}
else
{
sb.Clear();
sb.Append("Unknown");
fallbackToUnknown = true;
}
}

OutputBufferHelpers.CopyStringToBuffer(nameBuf, bufLen, nameLen, sb.ToString());

if (!fallbackToUnknown && nameBuf != null && bufLen < sb.Length + 1)
{
hr = HResults.S_FALSE;
}
}
catch (System.Exception ex)
{
hr = ex.HResult;
}

#if DEBUG
if (_legacyImpl is not null)
{
uint nameLenLocal = 0;
char[] nameBufLocal = new char[bufLen > 0 ? bufLen : 1];
int hrLocal;
fixed (char* pNameBufLocal = nameBufLocal)
{
hrLocal = _legacyImpl.GetName(flags, bufLen, &nameLenLocal, nameBuf is null ? null : pNameBufLocal);
}

Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}");
if (nameLen is not null)
Debug.Assert(nameLenLocal == *nameLen, $"cDAC: {*nameLen:x}, DAC: {nameLenLocal:x}");

if (nameBuf is not null)
{
string dacName = new string(nameBufLocal, 0, (int)nameLenLocal - 1);
string cdacName = new string(nameBuf);
Debug.Assert(dacName == cdacName, $"cDAC: {cdacName}, DAC: {dacName}");
}
}
#endif

return hr;
}

int IXCLRDataMethodInstance.GetFlags(uint* flags)
=> _legacyImpl is not null ? _legacyImpl.GetFlags(flags) : HResults.E_NOTIMPL;
Expand Down
Loading