Skip to content

Commit 232c74e

Browse files
authored
[cDAC] Properly handle reportInteropMD special case (#119825)
* implement RuntimeTypeSystem.HasMDContextArg * update StackWalk.GetMethodDescPtr to account for special case
1 parent f2b7911 commit 232c74e

File tree

7 files changed

+143
-12
lines changed

7 files changed

+143
-12
lines changed

docs/design/datacontracts/RuntimeTypeSystem.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ partial interface IRuntimeTypeSystem : IContract
155155
// A IL Stub method is also a StoredSigMethodDesc, and a NoMetadataMethod
156156
public virtual bool IsILStub(MethodDescHandle methodDesc);
157157

158+
// Return true if a MethodDesc represents an IL stub with a special MethodDesc context arg
159+
public virtual bool HasMDContextArg(MethodDescHandle);
160+
158161
// Return true if a MethodDesc is in a collectible module
159162
public virtual bool IsCollectibleMethod(MethodDescHandle methodDesc);
160163

@@ -882,6 +885,14 @@ And the following enumeration definitions
882885
{
883886
IsLCGMethod = 0x00004000,
884887
IsILStub = 0x00008000,
888+
ILStubTypeMask = 0x000007FF,
889+
}
890+
891+
[Flags]
892+
internal enum ILStubType : uint
893+
{
894+
StubPInvokeVarArg = 0x4,
895+
StubCLRToCOMInterop = 0x6,
885896
}
886897

887898
[Flags]
@@ -1155,6 +1166,23 @@ And the various apis are implemented with the following algorithms
11551166
return ((DynamicMethodDescExtendedFlags)ExtendedFlags).HasFlag(DynamicMethodDescExtendedFlags.IsILStub);
11561167
}
11571168

1169+
public bool HasMDContextArg(MethodDescHandle method)
1170+
{
1171+
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
1172+
1173+
if (methodDesc.Classification != MethodDescClassification.Dynamic)
1174+
{
1175+
return false;
1176+
}
1177+
1178+
uint ExtendedFlags = // Read ExtendedFlags field from StoredSigMethodDesc contract using address methodDescHandle.Address
1179+
ILStubType ilStubType = (ILStubType)(ExtendedFlags & DynamicMethodDescExtendedFlags.ILStubTypeMask);
1180+
bool isCLRToCOMStub = ilStubType == ILStubType.StubCLRToCOMInterop;
1181+
bool isPInvokeVarArgStub = ilStubType == ILStubType.StubPInvokeVarArg;
1182+
1183+
return isCLRToCOMStub || isPInvokeVarArgStub;
1184+
}
1185+
11581186
public ushort GetSlotNumber(MethodDescHandle methodDesc) => _methodDescs[methodDesc.Addres]._desc.Slot;
11591187
```
11601188

docs/design/datacontracts/StackWalk.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ Contracts used:
8585
| --- |
8686
| `ExecutionManager` |
8787
| `Thread` |
88+
| `RuntimeTypeSystem` |
8889

8990

9091
### Stackwalk Algorithm
@@ -359,10 +360,21 @@ string GetFrameName(TargetPointer frameIdentifier);
359360
TargetPointer GetMethodDescPtr(TargetPointer framePtr)
360361
```
361362

362-
`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.
363+
`GetMethodDescPtr(IStackDataFrameHandle stackDataFrameHandle)` returns the method desc pointer associated with a `IStackDataFrameHandle`. Note there are two major differences between this API and the one above that operates on a TargetPointer.
364+
* This API can either be at a capital 'F' frame or a managed frame unlike the TargetPointer overload which only works at capital 'F' frames.
365+
* This API handles the special ReportInteropMD case which happens under the following conditions
366+
1. The dataFrame is at an `InlinedCallFrame`
367+
2. The dataFrame is in a `SW_SKIPPED_FRAME` state
368+
3. The InlinedCallFrame's return address is managed code
369+
4. The InlinedCallFrame's return address method has a MDContext arg
370+
371+
In this case, we report the actual interop MethodDesc. A pointer to the MethodDesc immediately follows the InlinedCallFrame in memory.
363372
This API is implemeted as follows:
364-
1. Try to get the current frame address with `GetFrameAddress`. If the address is not null, return `GetMethodDescPtr(<frameAddress>)`.
365-
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.
373+
1. Try to get the current frame address `framePtr` with `GetFrameAddress`.
374+
2. If the address is not null, compute `reportInteropMD` as listed above. Otherwise skip to step 5.
375+
3. If `reportInteropMD`, dereference the pointer immediately following the InlinedCallFrame and return that value.
376+
4. If `!reportIteropMD`, return `GetMethodDescPtr(framePtr)`.
377+
5. Check if the current context IP is a managed context using the ExecutionManager contract. If it is a managed context, use the ExecutionManager context to find the related MethodDesc and return the pointer to it.
366378
```csharp
367379
TargetPointer GetMethodDescPtr(IStackDataFrameHandle stackDataFrameHandle)
368380
```

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ public interface IRuntimeTypeSystem : IContract
175175
// A IL Stub method is also a StoredSigMethodDesc, and a NoMetadataMethod
176176
bool IsILStub(MethodDescHandle methodDesc) => throw new NotImplementedException();
177177

178+
// Return true if a MethodDesc represents an IL stub with a special MethodDesc context arg
179+
bool HasMDContextArg(MethodDescHandle methodDesc) => throw new NotImplementedException();
180+
178181
bool IsCollectibleMethod(MethodDescHandle methodDesc) => throw new NotImplementedException();
179182
bool IsVersionable(MethodDescHandle methodDesc) => throw new NotImplementedException();
180183

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ internal enum DynamicMethodDescExtendedFlags : uint
7878
{
7979
IsLCGMethod = 0x00004000,
8080
IsILStub = 0x00008000,
81+
ILStubTypeMask = 0x000007FF,
82+
}
83+
84+
[Flags]
85+
internal enum ILStubType : uint
86+
{
87+
StubPInvokeVarArg = 0x4,
88+
StubCLRToCOMInterop = 0x6,
8189
}
8290

8391
// on MethodDescChunk.FlagsAndTokenRange
@@ -254,6 +262,10 @@ private DynamicMethodDesc(Target target, TargetPointer methodDescPointer)
254262

255263
public bool IsDynamicMethod => ExtendedFlags.HasFlag(DynamicMethodDescExtendedFlags.IsLCGMethod);
256264
public bool IsILStub => ExtendedFlags.HasFlag(DynamicMethodDescExtendedFlags.IsILStub);
265+
public ILStubType ILStubType => (ILStubType)(ExtendedFlags & DynamicMethodDescExtendedFlags.ILStubTypeMask);
266+
public bool IsCLRToCOMStub => ILStubType == ILStubType.StubCLRToCOMInterop;
267+
public bool IsPInvokeVarArgStub => ILStubType == ILStubType.StubPInvokeVarArg;
268+
public bool HasMDContextArg => IsCLRToCOMStub || IsPInvokeVarArgStub;
257269
}
258270

259271
private sealed class StoredSigMethodDesc : IData<StoredSigMethodDesc>
@@ -904,6 +916,18 @@ public bool IsILStub(MethodDescHandle methodDescHandle)
904916
return AsDynamicMethodDesc(methodDesc).IsILStub;
905917
}
906918

919+
public bool HasMDContextArg(MethodDescHandle methodDescHandle)
920+
{
921+
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];
922+
923+
if (methodDesc.Classification != MethodClassification.Dynamic)
924+
{
925+
return false;
926+
}
927+
928+
return AsDynamicMethodDesc(methodDesc).HasMDContextArg;
929+
}
930+
907931
private MethodTable GetOrCreateMethodTable(MethodDesc methodDesc)
908932
{
909933
// Ensures that the method table is valid, created, and cached

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,13 @@ public bool IsInlineCallFrameWithActiveCall()
144144
return false;
145145
}
146146
Data.InlinedCallFrame inlinedCallFrame = target.ProcessedData.GetOrAdd<Data.InlinedCallFrame>(currentFramePointer);
147-
return inlinedCallFrame.CallerReturnAddress != 0;
147+
return InlinedCallFrameHasActiveCall(inlinedCallFrame);
148+
}
149+
150+
public static bool IsInlinedCallFrame(Target target, TargetPointer framePointer)
151+
{
152+
Data.Frame frame = target.ProcessedData.GetOrAdd<Data.Frame>(framePointer);
153+
return GetFrameType(target, frame.Identifier) == FrameType.InlinedCallFrame;
148154
}
149155

150156
public static string GetFrameName(Target target, TargetPointer frameIdentifier)
@@ -187,7 +193,7 @@ private IPlatformFrameHandler GetFrameHandler(IPlatformAgnosticContext context)
187193
};
188194
}
189195

190-
public static TargetPointer GetMethodDescPtr(TargetPointer framePtr, Target target)
196+
public static TargetPointer GetMethodDescPtr(Target target, TargetPointer framePtr)
191197
{
192198
Data.Frame frame = target.ProcessedData.GetOrAdd<Data.Frame>(framePtr);
193199
FrameType frameType = GetFrameType(target, frame.Identifier);
@@ -231,6 +237,21 @@ public static TargetPointer GetMethodDescPtr(TargetPointer framePtr, Target targ
231237
}
232238
}
233239

240+
public static TargetPointer GetReturnAddress(Target target, TargetPointer framePtr)
241+
{
242+
Data.Frame frame = target.ProcessedData.GetOrAdd<Data.Frame>(framePtr);
243+
FrameType frameType = GetFrameType(target, frame.Identifier);
244+
switch (frameType)
245+
{
246+
case FrameType.InlinedCallFrame:
247+
Data.InlinedCallFrame inlinedCallFrame = target.ProcessedData.GetOrAdd<Data.InlinedCallFrame>(frame.Address);
248+
return InlinedCallFrameHasActiveCall(inlinedCallFrame) ? inlinedCallFrame.CallerReturnAddress : TargetPointer.Null;
249+
default:
250+
// NotImplemented for other frame types
251+
return TargetPointer.Null;
252+
}
253+
}
254+
234255
private static bool InlinedCallFrameHasFunction(Data.InlinedCallFrame frame, Target target)
235256
{
236257
if (target.PointerSize == 4)

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -176,23 +176,60 @@ string IStackWalk.GetFrameName(TargetPointer frameIdentifier)
176176
=> FrameIterator.GetFrameName(_target, frameIdentifier);
177177

178178
TargetPointer IStackWalk.GetMethodDescPtr(TargetPointer framePtr)
179-
=> FrameIterator.GetMethodDescPtr(framePtr, _target);
179+
=> FrameIterator.GetMethodDescPtr(_target, framePtr);
180180

181181
TargetPointer IStackWalk.GetMethodDescPtr(IStackDataFrameHandle stackDataFrameHandle)
182182
{
183183
StackDataFrameHandle handle = AssertCorrectHandle(stackDataFrameHandle);
184+
IExecutionManager eman = _target.Contracts.ExecutionManager;
184185

185186
// if we are at a capital F Frame, we can get the method desc from the frame
186-
// TODO(cdac): Support special fReportInteropMD case https://github.com/dotnet/runtime/issues/119724
187187
TargetPointer framePtr = ((IStackWalk)this).GetFrameAddress(handle);
188188
if (framePtr != TargetPointer.Null)
189-
return ((IStackWalk)this).GetMethodDescPtr(framePtr);
189+
{
190+
// reportInteropMD if
191+
// 1) we are an InlinedCallFrame
192+
// 2) the StackDataFrame is at a SW_SKIPPED_FRAME state
193+
// 3) the return address is managed
194+
// 4) the return address method has a MDContext arg
195+
bool reportInteropMD = false;
196+
197+
if (FrameIterator.IsInlinedCallFrame(_target, framePtr) &&
198+
handle.State == StackWalkState.SW_SKIPPED_FRAME)
199+
{
200+
IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;
201+
202+
// FrameIterator.GetReturnAddress is currently only implemented for InlinedCallFrame
203+
// This is fine as this check is only needed for that frame type
204+
TargetPointer returnAddress = FrameIterator.GetReturnAddress(_target, framePtr);
205+
if (eman.GetCodeBlockHandle(returnAddress.Value) is CodeBlockHandle cbh)
206+
{
207+
MethodDescHandle returnMethodDesc = rts.GetMethodDescHandle(eman.GetMethodDesc(cbh));
208+
reportInteropMD = rts.HasMDContextArg(returnMethodDesc);
209+
}
210+
}
211+
212+
if (reportInteropMD)
213+
{
214+
// Special reportInteropMD case
215+
// This can't be handled in the GetMethodDescPtr(TargetPointer) because it relies on
216+
// the state of the stack walk (SW_SKIPPED_FRAME) which is not available there.
217+
// The MethodDesc pointer immediately follows the InlinedCallFrame
218+
TargetPointer methodDescPtr = framePtr + _target.GetTypeInfo(DataType.InlinedCallFrame).Size
219+
?? throw new InvalidOperationException("InlinedCallFrame type size is not defined.");
220+
return _target.ReadPointer(methodDescPtr);
221+
}
222+
else
223+
{
224+
// Standard case
225+
return ((IStackWalk)this).GetMethodDescPtr(framePtr);
226+
}
227+
}
190228

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

195-
IExecutionManager eman = _target.Contracts.ExecutionManager;
196233
return eman.GetMethodDesc(codeBlockHandle.Value);
197234
}
198235

src/native/managed/cdac/mscordaccore_universal/Legacy/ClrDataFrame.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,11 @@ int IXCLRDataFrame.GetMethodInstance(out IXCLRDataMethodInstance? method)
7575
int hr = HResults.S_OK;
7676
method = null;
7777

78+
int hrLocal = HResults.S_OK;
7879
IXCLRDataMethodInstance? legacyMethod = null;
7980
if (_legacyImpl is not null)
8081
{
81-
int hrLocal = _legacyImpl.GetMethodInstance(out legacyMethod);
82-
if (hrLocal < 0)
83-
return hrLocal;
82+
hrLocal = _legacyImpl.GetMethodInstance(out legacyMethod);
8483
}
8584

8685
try
@@ -104,6 +103,13 @@ int IXCLRDataFrame.GetMethodInstance(out IXCLRDataMethodInstance? method)
104103
hr = ex.HResult;
105104
}
106105

106+
#if DEBUG
107+
if (_legacyImpl is not null)
108+
{
109+
Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}");
110+
}
111+
#endif
112+
107113
return hr;
108114
}
109115

0 commit comments

Comments
 (0)