Skip to content
Open
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
6 changes: 6 additions & 0 deletions docs/design/datacontracts/StackWalk.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ public interface IStackDataFrameHandle

// True when the interrupting frame represents an active hardware fault.
bool HasFaulted { get; }

// True when the current Frame is either a SoftwareExceptionFrame or FaultingExceptionFrame
bool IsExceptionFrame { get; }

// True when this is the active stack frame.
bool IsActiveFrame { get; }
}

public enum StackWalkState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public interface IStackDataFrameHandle
StackWalkState State { get; }
bool IsInterrupted { get; }
bool HasFaulted { get; }
bool IsExceptionFrame { get; }
bool IsActiveFrame { get; }
}

public enum StackWalkState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ private record StackDataFrameHandle(
bool IsResumableFrame = false,
bool IsActiveFrame = false,
bool IsInterrupted = false,
bool HasFaulted = false) : IStackDataFrameHandle
bool HasFaulted = false,
bool IsExceptionFrame = false) : IStackDataFrameHandle
{ }

private class StackWalkData(IPlatformAgnosticContext context, StackWalkState state, FrameIterator frameIter, ThreadData threadData)
Expand Down Expand Up @@ -117,7 +118,10 @@ public StackDataFrameHandle ToDataFrame()
{
bool isResumable = IsCurrentFrameResumable();
bool isActiveFrame = IsFirst && State == StackWalkState.Frameless;
return new(Context.Clone(), State, FrameIter.CurrentFrameAddress, ThreadData, isResumable, isActiveFrame, IsInterrupted, HasFaulted);
bool isExceptionFrame = State is StackWalkState.Frame or StackWalkState.SkippedFrame
&& FrameIter.IsValid()
&& FrameIter.GetCurrentFrameType() is FrameType.FaultingExceptionFrame or FrameType.SoftwareExceptionFrame;
return new(Context.Clone(), State, FrameIter.CurrentFrameAddress, ThreadData, isResumable, isActiveFrame, IsInterrupted, HasFaulted, isExceptionFrame);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ public sealed unsafe partial class ClrDataStackWalk : IXCLRDataStackWalk
private readonly uint _flags;
private readonly Target _target;
private readonly IXCLRDataStackWalk? _legacyImpl;
private readonly ThreadData _threadData;

private bool _currentFrameIsValid;
private readonly IEnumerator<IStackDataFrameHandle> _dataFrames;
private IEnumerator<IStackDataFrameHandle> _dataFrames;

public ClrDataStackWalk(TargetPointer threadAddr, uint flags, Target target, IXCLRDataStackWalk? legacyImpl)
{
Expand All @@ -29,8 +30,8 @@ public ClrDataStackWalk(TargetPointer threadAddr, uint flags, Target target, IXC
_target = target;
_legacyImpl = legacyImpl;

ThreadData threadData = _target.Contracts.Thread.GetThreadData(_threadAddr);
_dataFrames = _target.Contracts.StackWalk.CreateStackWalk(threadData).GetEnumerator();
_threadData = _target.Contracts.Thread.GetThreadData(_threadAddr);
_dataFrames = _target.Contracts.StackWalk.CreateStackWalk(_threadData).GetEnumerator();

// IEnumerator<T> begins before the first element.
// Call MoveNext() to set _dataFrames.Current to the first element.
Expand Down Expand Up @@ -58,6 +59,13 @@ internal static bool IsLegacyVisible(IStackDataFrameHandle frame)
or StackWalkState.Frame
or StackWalkState.SkippedFrame;

private void Reseed(byte[] context, bool isFirst)
{
_dataFrames.Dispose();
_dataFrames = _target.Contracts.StackWalk.CreateStackWalk(_threadData, context, isFirst).GetEnumerator();
_currentFrameIsValid = MoveNextLegacyVisible();
}

int IXCLRDataStackWalk.GetContext(uint contextFlags, uint contextBufSize, uint* contextSize, [MarshalUsing(CountElementName = "contextBufSize"), Out] byte[] contextBuf)
{
int hr = HResults.S_OK;
Expand Down Expand Up @@ -133,8 +141,60 @@ int IXCLRDataStackWalk.GetFrame(DacComNullableByRef<IXCLRDataFrame> frame)

return hr;
}
int IXCLRDataStackWalk.GetFrameType(uint* simpleType, uint* detailedType)
=> HResults.E_NOTIMPL;
int IXCLRDataStackWalk.GetFrameType(CLRDataSimpleFrameType* simpleType, CLRDataDetailedFrameType* detailedType)
{
int hr = HResults.S_OK;
try
{
if (_currentFrameIsValid)
{
IStackDataFrameHandle dataFrame = _dataFrames.Current;
if (simpleType is not null)
{
*simpleType = dataFrame.State switch
{
StackWalkState.Frameless => CLRDataSimpleFrameType.CLRDATA_SIMPFRAME_MANAGED_METHOD,
StackWalkState.Frame or StackWalkState.SkippedFrame => CLRDataSimpleFrameType.CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE,
_ => CLRDataSimpleFrameType.CLRDATA_SIMPFRAME_UNRECOGNIZED,
};
}

if (detailedType is not null)
{
*detailedType = dataFrame.IsExceptionFrame
? CLRDataDetailedFrameType.CLRDATA_DETFRAME_EXCEPTION_FILTER
: CLRDataDetailedFrameType.CLRDATA_DETFRAME_UNRECOGNIZED;
}
}
else
{
hr = HResults.S_FALSE;
}
}
catch (System.Exception ex)
{
hr = ex.HResult;
}

#if DEBUG
if (_legacyImpl is not null)
{
CLRDataSimpleFrameType simpleTypeLocal = 0;
CLRDataDetailedFrameType detailedTypeLocal = 0;
int hrLocal = _legacyImpl.GetFrameType(&simpleTypeLocal, &detailedTypeLocal);
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
{
if (simpleType is not null)
Debug.Assert(*simpleType == simpleTypeLocal, $"cDAC: {*simpleType:x}, DAC: {simpleTypeLocal:x}");
if (detailedType is not null)
Debug.Assert(*detailedType == detailedTypeLocal, $"cDAC: {*detailedType:x}, DAC: {detailedTypeLocal:x}");
}
}
#endif

return hr;
}
int IXCLRDataStackWalk.GetStackSizeSkipped(ulong* stackSizeSkipped)
=> LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetStackSizeSkipped(stackSizeSkipped) : HResults.E_NOTIMPL;
int IXCLRDataStackWalk.Next()
Expand Down Expand Up @@ -171,18 +231,21 @@ int IXCLRDataStackWalk.Request(uint reqCode, uint inBufferSize, byte* inBuffer,
int hr = HResults.S_OK;
try
{
if (inBufferSize != 0 || inBuffer != null)
throw new ArgumentException("Invalid input buffer parameters");
switch (reqCode)
{
Comment thread
rcj1 marked this conversation as resolved.
case (uint)CLRDataGeneralRequest.CLRDATA_REQUEST_REVISION:
if (outBufferSize != sizeof(uint))
if (inBufferSize != 0 || inBuffer != null || outBufferSize != sizeof(uint) || outBuffer == null)
throw new ArgumentException("Invalid buffer parameters for CLRDATA_REQUEST_REVISION");
*(uint*)outBuffer = 1;
hr = HResults.S_OK;
break;
case (uint)CLRDataStackWalkRequest.CLRDATA_STACK_WALK_REQUEST_SET_FIRST_FRAME:
if (inBufferSize != sizeof(uint) || inBuffer == null || outBufferSize != 0)
throw new ArgumentException("Invalid buffer parameters for CLRDATA_STACK_WALK_REQUEST_SET_FIRST_FRAME");
hr = HResults.S_OK; // no-op for the case where we use this
break;
case DACSTACKPRIV_REQUEST_FRAME_DATA:
if (outBufferSize != sizeof(ulong))
if (inBufferSize != 0 || inBuffer != null || outBufferSize != sizeof(ulong) || outBuffer == null)
throw new ArgumentException("Invalid buffer parameters for DACSTACKPRIV_REQUEST_FRAME_DATA");
if (!_currentFrameIsValid)
throw new ArgumentException("Invalid frame");
Expand All @@ -194,7 +257,7 @@ int IXCLRDataStackWalk.Request(uint reqCode, uint inBufferSize, byte* inBuffer,
hr = HResults.S_OK;
break;
default:
throw new NotImplementedException();
throw new ArgumentException("Invalid request code");
}
}
catch (System.Exception ex)
Expand All @@ -205,26 +268,46 @@ int IXCLRDataStackWalk.Request(uint reqCode, uint inBufferSize, byte* inBuffer,
#if DEBUG
if (_legacyImpl is not null)
{
int hrLocal;
byte[] localOutBuffer = new byte[outBufferSize];
byte[] localOutBuffer = new byte[(int)outBufferSize];
fixed (byte* localOutBufferPtr = localOutBuffer)
{
hrLocal = _legacyImpl.Request(reqCode, inBufferSize, inBuffer, outBufferSize, localOutBufferPtr);
}
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
{
for (int i = 0; i < outBufferSize; i++)
{
Debug.Assert(localOutBuffer[i] == outBuffer[i], $"cDAC: {outBuffer[i]:x}, DAC: {localOutBuffer[i]:x}");
}
int hrLocal = _legacyImpl.Request(reqCode, inBufferSize, inBuffer, outBufferSize, localOutBufferPtr);
Debug.ValidateHResult(hr, hrLocal);
if (hr == HResults.S_OK)
Debug.Assert(new ReadOnlySpan<byte>(outBuffer, (int)outBufferSize).SequenceEqual(localOutBuffer));
}
}
#endif
return hr;
}
int IXCLRDataStackWalk.SetContext(uint contextSize, [In, MarshalUsing(CountElementName = "contextSize")] byte[] context)
=> HResults.E_NOTIMPL;
{
int hr = HResults.S_OK;
try
{
uint platformContextSize = IPlatformAgnosticContext.GetContextForPlatform(_target).Size;
if (context is null || contextSize < platformContextSize)
throw new ArgumentException("Invalid context buffer");

bool isFirst = _currentFrameIsValid && _dataFrames.Current.IsActiveFrame;
Reseed(context, isFirst);
}
catch (System.Exception ex)
{
hr = ex.HResult;
}

if (_legacyImpl is not null)
{
int hrLocal = _legacyImpl.SetContext(contextSize, context);
#if DEBUG
Debug.ValidateHResult(hr, hrLocal);
#endif
}

return hr;
}

int IXCLRDataStackWalk.SetContext2(uint flags, uint contextSize, [In, MarshalUsing(CountElementName = "contextSize")] byte[] context)
=> HResults.E_NOTIMPL;
}
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ int GetContext(
int GetStackSizeSkipped(ulong* stackSizeSkipped);

[PreserveSig]
int GetFrameType(/*CLRDataSimpleFrameType*/ uint* simpleType, /*CLRDataDetailedFrameType*/ uint* detailedType);
int GetFrameType(CLRDataSimpleFrameType* simpleType, CLRDataDetailedFrameType* detailedType);

[PreserveSig]
int GetFrame(DacComNullableByRef<IXCLRDataFrame> frame);
Expand Down Expand Up @@ -979,6 +979,31 @@ public enum CLRDataGeneralRequest : uint
CLRDATA_REQUEST_REVISION = 0xe0000000,
}

public enum CLRDataStackWalkRequest : uint
{
CLRDATA_STACK_WALK_REQUEST_SET_FIRST_FRAME = 0xe1000000,
}

[Flags]
public enum CLRDataStackSetContextFlag : uint
{
CLRDATA_STACK_SET_UNWIND_CONTEXT = 0x00000000,
CLRDATA_STACK_SET_CURRENT_CONTEXT = 0x00000001,
}

public enum CLRDataSimpleFrameType : uint
{
CLRDATA_SIMPFRAME_UNRECOGNIZED = 0x1,
CLRDATA_SIMPFRAME_MANAGED_METHOD = 0x2,
CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE = 0x8,
}

public enum CLRDataDetailedFrameType : uint
{
CLRDATA_DETFRAME_UNRECOGNIZED = 0,
CLRDATA_DETFRAME_EXCEPTION_FILTER = 3,
}

[Flags]
public enum CLRDataExceptionStateFlag : uint
{
Expand Down