Skip to content

Commit f397c83

Browse files
Copilotrcj1
andauthored
[cDAC]: convert DacDbi GetRcwCachedInterfacePointers to EnumerateRcwCachedInterfacePointers and implement in cDAC (#128341)
* Convert `GetRcwCachedInterfacePointers` to `EnumerateRcwCachedInterfacePointers` * Create template for these add-to-list callbacks --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com> Co-authored-by: Rachel Jarvi <rachel.jarvi@gmail.com>
1 parent 11cfc90 commit f397c83

10 files changed

Lines changed: 159 additions & 86 deletions

File tree

src/coreclr/debug/daccess/dacdbiimpl.cpp

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3593,42 +3593,39 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::IsRcw(VMPTR_Object vmObject, OUT
35933593
#endif // FEATURE_COMINTEROP
35943594
}
35953595

3596-
HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetRcwCachedInterfacePointers(VMPTR_Object vmObject, BOOL bIInspectableOnly, OUT DacDbiArrayList<CORDB_ADDRESS> * pDacItfPtrs)
3596+
HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::EnumerateRcwCachedInterfacePointers(VMPTR_Object vmObject, FP_RCW_INTERFACE_CALLBACK fpCallback, CALLBACK_DATA pUserData)
35973597
{
35983598
#ifdef FEATURE_COMINTEROP
35993599

36003600
DD_ENTER_MAY_THROW;
36013601

3602+
if (fpCallback == NULL)
3603+
return E_POINTER;
3604+
36023605
HRESULT hr = S_OK;
36033606
EX_TRY
36043607
{
3605-
3606-
Object* objPtr = vmObject.GetDacPtr();
3607-
3608-
InlineSArray<TADDR, INTERFACE_ENTRY_CACHE_SIZE> rgUnks;
3609-
36103608
PTR_RCW pRCW = GetRcwFromVmptrObject(vmObject);
36113609
if (pRCW != NULL)
36123610
{
3613-
pRCW->GetCachedInterfacePointers(bIInspectableOnly, &rgUnks);
3614-
3615-
pDacItfPtrs->Alloc(rgUnks.GetCount());
3616-
3617-
for (COUNT_T i = 0; i < rgUnks.GetCount(); ++i)
3611+
RCW::CachedInterfaceEntryIterator it = pRCW->IterateCachedInterfacePointers();
3612+
while (it.Next())
36183613
{
3619-
(*pDacItfPtrs)[i] = (CORDB_ADDRESS)(rgUnks[i]);
3614+
PTR_MethodTable pMT = dac_cast<PTR_MethodTable>((TADDR)(it.GetEntry()->m_pMT.Load()));
3615+
if (pMT != NULL)
3616+
{
3617+
TADDR taUnk = (TADDR)(it.GetEntry()->m_pUnknown.Load());
3618+
if (taUnk != NULL)
3619+
{
3620+
fpCallback((CORDB_ADDRESS)taUnk, pUserData);
3621+
}
3622+
}
36203623
}
3621-
3622-
}
3623-
else
3624-
{
3625-
pDacItfPtrs->Alloc(0);
36263624
}
36273625
}
36283626
EX_CATCH_HRESULT(hr);
36293627
return hr;
36303628
#else
3631-
pDacItfPtrs->Alloc(0);
36323629
return S_OK;
36333630
#endif // FEATURE_COMINTEROP
36343631
}

src/coreclr/debug/daccess/dacdbiimpl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ class DacDbiInterfaceImpl :
314314
// retrieves the list of interfaces pointers implemented by vmObject, as it is known at
315315
// the time of the call (the list may change as new interface types become available
316316
// in the runtime)
317-
HRESULT STDMETHODCALLTYPE GetRcwCachedInterfacePointers(VMPTR_Object vmObject, BOOL bIInspectableOnly, OUT DacDbiArrayList<CORDB_ADDRESS> * pDacItfPtrs);
317+
HRESULT STDMETHODCALLTYPE EnumerateRcwCachedInterfacePointers(VMPTR_Object vmObject, FP_RCW_INTERFACE_CALLBACK fpCallback, CALLBACK_DATA pUserData);
318318

319319
private:
320320
// Given a pointer to a managed function, obtain the method desc for it.

src/coreclr/debug/di/divalue.cpp

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2802,6 +2802,16 @@ HRESULT CordbObjectValue::GetCachedInterfaceTypes(
28022802
#endif
28032803
}
28042804

2805+
#if defined(FEATURE_COMINTEROP)
2806+
namespace
2807+
{
2808+
void RcwInterfacePointerCallback(CORDB_ADDRESS itfPtr, CALLBACK_DATA pUserData)
2809+
{
2810+
CallbackAccumulator<CORDB_ADDRESS>::From(pUserData)->Push(itfPtr);
2811+
}
2812+
}
2813+
#endif // FEATURE_COMINTEROP
2814+
28052815
HRESULT CordbObjectValue::GetCachedInterfacePointers(
28062816
BOOL bIInspectableOnly,
28072817
ULONG32 celt,
@@ -2825,25 +2835,29 @@ HRESULT CordbObjectValue::GetCachedInterfacePointers(
28252835
HRESULT hr = S_OK;
28262836
ULONG32 cItfs = 0;
28272837

2828-
// retrieve interface types
2829-
2830-
CORDB_ADDRESS objAddr = m_valueHome.GetAddress();
2838+
CallbackAccumulator<CORDB_ADDRESS> acc;
28312839

2832-
DacDbiArrayList<CORDB_ADDRESS> dacItfPtrs;
2833-
EX_TRY
2840+
// The RCW interface pointer cache only holds non-IInspectable interfaces, so when the caller
2841+
// asks for IInspectable-only pointers the result is always empty.
2842+
if (!bIInspectableOnly)
28342843
{
2835-
IDacDbiInterface* pDAC = GetProcess()->GetDAC();
2836-
VMPTR_Object vmObj;
2837-
IfFailThrow(pDAC->GetObject(objAddr, &vmObj));
2844+
CORDB_ADDRESS objAddr = m_valueHome.GetAddress();
28382845

2839-
// retrieve type info from LS
2840-
IfFailThrow(pDAC->GetRcwCachedInterfacePointers(vmObj, bIInspectableOnly, &dacItfPtrs));
2846+
EX_TRY
2847+
{
2848+
IDacDbiInterface* pDAC = GetProcess()->GetDAC();
2849+
VMPTR_Object vmObj;
2850+
IfFailThrow(pDAC->GetObject(objAddr, &vmObj));
2851+
2852+
IfFailThrow(pDAC->EnumerateRcwCachedInterfacePointers(vmObj, &RcwInterfacePointerCallback, &acc));
2853+
}
2854+
EX_CATCH_HRESULT(hr);
2855+
IfFailRet(hr);
2856+
IfFailRet(acc.hrError);
28412857
}
2842-
EX_CATCH_HRESULT(hr);
2843-
IfFailRet(hr);
28442858

28452859
// synthesize CordbType instances
2846-
cItfs = (ULONG32)dacItfPtrs.Count();
2860+
cItfs = (ULONG32)acc.items.Size();
28472861

28482862
if (pcEltFetched != NULL && ptrs == NULL)
28492863
{
@@ -2859,7 +2873,7 @@ HRESULT CordbObjectValue::GetCachedInterfacePointers(
28592873
if (ptrs != NULL && *pcEltFetched > 0)
28602874
{
28612875
for (ULONG32 i = 0; i < *pcEltFetched; ++i)
2862-
ptrs[i] = dacItfPtrs[i];
2876+
ptrs[i] = acc.items[i];
28632877
}
28642878

28652879
return (*pcEltFetched == celt ? S_OK : S_FALSE);

src/coreclr/debug/di/process.cpp

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2190,38 +2190,19 @@ HRESULT CordbProcess::GetGCHeapInformation(COR_HEAPINFO *pHeapInfo)
21902190

21912191
namespace
21922192
{
2193-
struct HeapSegmentAccumulator
2194-
{
2195-
CQuickArrayList<COR_SEGMENT> segments;
2196-
HRESULT hrError;
2197-
};
2198-
21992193
void HeapSegmentCallback(
22002194
CORDB_ADDRESS rangeStart,
22012195
CORDB_ADDRESS rangeEnd,
22022196
int generation,
22032197
ULONG heap,
22042198
CALLBACK_DATA pUserData)
22052199
{
2206-
HeapSegmentAccumulator *acc =
2207-
reinterpret_cast<HeapSegmentAccumulator*>(pUserData);
2208-
2209-
if (FAILED(acc->hrError))
2210-
return;
2211-
22122200
COR_SEGMENT s;
22132201
s.start = rangeStart;
22142202
s.end = rangeEnd;
22152203
s.type = (CorDebugGenerationTypes)generation;
22162204
s.heap = heap;
2217-
HRESULT hr = S_OK;
2218-
EX_TRY
2219-
{
2220-
acc->segments.Push(s);
2221-
}
2222-
EX_CATCH_HRESULT(hr);
2223-
if (FAILED(hr))
2224-
acc->hrError = hr;
2205+
CallbackAccumulator<COR_SEGMENT>::From(pUserData)->Push(s);
22252206
}
22262207
}
22272208

@@ -2236,18 +2217,17 @@ HRESULT CordbProcess::EnumerateHeapRegions(ICorDebugHeapSegmentEnum **ppRegions)
22362217

22372218
EX_TRY
22382219
{
2239-
HeapSegmentAccumulator acc;
2240-
acc.hrError = S_OK;
2220+
CallbackAccumulator<COR_SEGMENT> acc;
22412221

22422222
hr = GetDAC()->EnumerateHeapSegments(&HeapSegmentCallback, &acc);
22432223
if (SUCCEEDED(hr) && FAILED(acc.hrError))
22442224
hr = acc.hrError;
22452225

22462226
if (SUCCEEDED(hr))
22472227
{
2248-
if (acc.segments.Size() != 0)
2228+
if (acc.items.Size() != 0)
22492229
{
2250-
CordbHeapSegmentEnumerator *segEnum = new CordbHeapSegmentEnumerator(this, acc.segments.Ptr(), (DWORD)acc.segments.Size());
2230+
CordbHeapSegmentEnumerator *segEnum = new CordbHeapSegmentEnumerator(this, acc.items.Ptr(), (DWORD)acc.items.Size());
22512231
GetContinueNeuterList()->Add(this, segEnum);
22522232
hr = segEnum->QueryInterface(__uuidof(ICorDebugHeapSegmentEnum), (void**)ppRegions);
22532233
}

src/coreclr/debug/di/rspriv.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,45 @@ struct MachineInfo;
6969
#define WriteProcessMemory DONT_USE_WRITEPROCESS_MEMORY
7070

7171

72+
//-----------------------------------------------------------------------------
73+
// CallbackAccumulator<T>
74+
//
75+
// Helper for FP_*_CALLBACK consumers on the DI side that need to collect a
76+
// list of values produced by the DAC and surface a single HRESULT. The
77+
// callback can call Push() without worrying about exceptions - the first
78+
// failure is captured in hrError and subsequent pushes are short-circuited.
79+
// After the enumeration call returns, the caller should check both the DAC's
80+
// returned HRESULT and acc.hrError, then consume acc.items.
81+
//-----------------------------------------------------------------------------
82+
template <typename T>
83+
struct CallbackAccumulator
84+
{
85+
CQuickArrayList<T> items;
86+
HRESULT hrError;
87+
88+
CallbackAccumulator() : hrError(S_OK) { }
89+
90+
void Push(const T& item)
91+
{
92+
if (FAILED(hrError))
93+
return;
94+
HRESULT hr = S_OK;
95+
EX_TRY
96+
{
97+
items.Push(item);
98+
}
99+
EX_CATCH_HRESULT(hr);
100+
if (FAILED(hr))
101+
hrError = hr;
102+
}
103+
104+
static CallbackAccumulator* From(CALLBACK_DATA pUserData)
105+
{
106+
return reinterpret_cast<CallbackAccumulator*>(pUserData);
107+
}
108+
};
109+
110+
72111
/* ------------------------------------------------------------------------- *
73112
* Forward class declarations
74113
* ------------------------------------------------------------------------- */

src/coreclr/debug/inc/dacdbiinterface.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1709,10 +1709,13 @@ IDacDbiInterface : public IUnknown
17091709
// Check whether the argument is a runtime callable wrapper.
17101710
virtual HRESULT STDMETHODCALLTYPE IsRcw(VMPTR_Object vmObject, OUT BOOL * pResult) = 0;
17111711

1712-
// retrieves the list of interfaces pointers implemented by vmObject, as it is known at
1713-
// the time of the call (the list may change as new interface types become available
1714-
// in the runtime)
1715-
virtual HRESULT STDMETHODCALLTYPE GetRcwCachedInterfacePointers(VMPTR_Object vmObject, BOOL bIInspectableOnly, OUT DacDbiArrayList<CORDB_ADDRESS> * pDacItfPtrs) = 0;
1712+
// Callback invoked for each cached interface pointer held by an RCW.
1713+
typedef void (*FP_RCW_INTERFACE_CALLBACK)(CORDB_ADDRESS itfPtr, CALLBACK_DATA pUserData);
1714+
1715+
// Enumerates the interface pointers cached by the RCW associated with vmObject, as known at
1716+
// the time of the call (the list may change as new interface types become available in the
1717+
// runtime). If vmObject is not an RCW the enumeration is empty. The callback must not throw.
1718+
virtual HRESULT STDMETHODCALLTYPE EnumerateRcwCachedInterfacePointers(VMPTR_Object vmObject, FP_RCW_INTERFACE_CALLBACK fpCallback, CALLBACK_DATA pUserData) = 0;
17161719

17171720
// ----------------------------------------------------------------------------
17181721
// functions to get information about reference/handle referents for ICDValue

src/coreclr/inc/dacdbi.idl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ typedef void (*FP_MODULE_ENUMERATION_CALLBACK)(VMPTR_Assembly vmAssembly, CALLBA
144144
typedef void (*FP_THREAD_ENUMERATION_CALLBACK)(VMPTR_Thread vmThread, CALLBACK_DATA pUserData);
145145
typedef BOOL (*FP_INTERNAL_FRAME_ENUMERATION_CALLBACK)(FramePointer fpFrame, CALLBACK_DATA pUserData);
146146
typedef void (*FP_HEAPSEGMENT_CALLBACK)(CORDB_ADDRESS rangeStart, CORDB_ADDRESS rangeEnd, int generation, ULONG heap, CALLBACK_DATA pUserData);
147+
typedef void (*FP_RCW_INTERFACE_CALLBACK)(CORDB_ADDRESS itfPtr, CALLBACK_DATA pUserData);
147148

148149

149150
//
@@ -335,7 +336,7 @@ interface IDacDbiInterface : IUnknown
335336
HRESULT IsExceptionObject([in] VMPTR_Object vmObject, [out] BOOL * pResult);
336337
HRESULT GetStackFramesFromException([in] VMPTR_Object vmObject, [out] DacDbiArrayList_DacExceptionCallStackData * pDacStackFrames);
337338
HRESULT IsRcw([in] VMPTR_Object vmObject, [out] BOOL * pResult);
338-
HRESULT GetRcwCachedInterfacePointers([in] VMPTR_Object vmObject, [in] BOOL bIInspectableOnly, [out] DacDbiArrayList_CORDB_ADDRESS * pDacItfPtrs);
339+
HRESULT EnumerateRcwCachedInterfacePointers([in] VMPTR_Object vmObject, [in] FP_RCW_INTERFACE_CALLBACK fpCallback, [in] CALLBACK_DATA pUserData);
339340

340341
// Object Data
341342
HRESULT GetTypedByRefInfo([in] CORDB_ADDRESS pTypedByRef, [out] struct DebuggerIPCE_ObjectData * pObjectData);

src/coreclr/vm/runtimecallablewrapper.h

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -272,27 +272,6 @@ struct RCW
272272
return m_cbRefCount;
273273
}
274274

275-
void GetCachedInterfacePointers(BOOL bIInspectableOnly,
276-
SArray<TADDR> * rgItfPtrs)
277-
{
278-
LIMITED_METHOD_DAC_CONTRACT;
279-
280-
CachedInterfaceEntryIterator it = IterateCachedInterfacePointers();
281-
while (it.Next())
282-
{
283-
PTR_MethodTable pMT = dac_cast<PTR_MethodTable>((TADDR)(it.GetEntry()->m_pMT.Load()));
284-
if (pMT != NULL &&
285-
(!bIInspectableOnly))
286-
{
287-
TADDR taUnk = (TADDR)(it.GetEntry()->m_pUnknown.Load());
288-
if (taUnk != NULL)
289-
{
290-
rgItfPtrs->Append(taUnk);
291-
}
292-
}
293-
}
294-
}
295-
296275
LPVOID GetVTablePtr() { LIMITED_METHOD_CONTRACT; return m_vtablePtr; }
297276

298277
// Remoting aware QI that will attempt to re-unmarshal on object disconnect.

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/DacDbiImpl.cs

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1934,8 +1934,68 @@ public int IsRcw(ulong vmObject, Interop.BOOL* pResult)
19341934
return hr;
19351935
}
19361936

1937-
public int GetRcwCachedInterfacePointers(ulong vmObject, Interop.BOOL bIInspectableOnly, nint pDacItfPtrs)
1938-
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetRcwCachedInterfacePointers(vmObject, bIInspectableOnly, pDacItfPtrs) : HResults.E_NOTIMPL;
1937+
#if DEBUG
1938+
[ThreadStatic]
1939+
private static List<ulong>? _debugEnumerateRcwCachedInterfacePointers;
1940+
1941+
private static List<ulong> DebugEnumerateRcwCachedInterfacePointers
1942+
=> _debugEnumerateRcwCachedInterfacePointers ??= new();
1943+
1944+
[UnmanagedCallersOnly]
1945+
private static void EnumerateRcwCachedInterfacePointersDebugCallback(ulong itfPtr, nint _)
1946+
{
1947+
DebugEnumerateRcwCachedInterfacePointers.Add(itfPtr);
1948+
}
1949+
#endif
1950+
1951+
public int EnumerateRcwCachedInterfacePointers(ulong vmObject, delegate* unmanaged<ulong, nint, void> fpCallback, nint pUserData)
1952+
{
1953+
int hr = HResults.S_OK;
1954+
List<ulong> itfPtrs = new();
1955+
try
1956+
{
1957+
if (fpCallback is null)
1958+
throw new ArgumentNullException(nameof(fpCallback));
1959+
1960+
IObject obj = _target.Contracts.Object;
1961+
_ = obj.GetBuiltInComData(new TargetPointer(vmObject), out TargetPointer rcw, out _, out _);
1962+
if (rcw != TargetPointer.Null)
1963+
{
1964+
IBuiltInCOM builtInCom = _target.Contracts.BuiltInCOM;
1965+
foreach ((TargetPointer methodTable, TargetPointer unknown) in builtInCom.GetRCWInterfaces(rcw))
1966+
{
1967+
if (methodTable != TargetPointer.Null && unknown != TargetPointer.Null)
1968+
itfPtrs.Add(unknown.Value);
1969+
}
1970+
}
1971+
1972+
foreach (ulong itfPtr in itfPtrs)
1973+
fpCallback(itfPtr, pUserData);
1974+
}
1975+
catch (System.Exception ex)
1976+
{
1977+
hr = ex.HResult;
1978+
}
1979+
1980+
#if DEBUG
1981+
if (_legacy is not null)
1982+
{
1983+
DebugEnumerateRcwCachedInterfacePointers.Clear();
1984+
delegate* unmanaged<ulong, nint, void> debugCallbackPtr = &EnumerateRcwCachedInterfacePointersDebugCallback;
1985+
int hrLocal = _legacy.EnumerateRcwCachedInterfacePointers(vmObject, debugCallbackPtr, 0);
1986+
Debug.ValidateHResult(hr, hrLocal);
1987+
1988+
if (hr == HResults.S_OK)
1989+
{
1990+
List<ulong> legacyItfPtrs = DebugEnumerateRcwCachedInterfacePointers;
1991+
Debug.Assert(itfPtrs.SequenceEqual(legacyItfPtrs),
1992+
$"cDAC: [{string.Join(",", itfPtrs.Select(p => $"0x{p:x}"))}], DAC: [{string.Join(",", legacyItfPtrs.Select(p => $"0x{p:x}"))}]");
1993+
}
1994+
DebugEnumerateRcwCachedInterfacePointers.Clear();
1995+
}
1996+
#endif
1997+
return hr;
1998+
}
19391999

19402000
public int GetTypedByRefInfo(ulong pTypedByRef, nint pObjectData)
19412001
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetTypedByRefInfo(pTypedByRef, pObjectData) : HResults.E_NOTIMPL;

src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/Dbi/IDacDbiInterface.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ public unsafe partial interface IDacDbiInterface
580580
int IsRcw(ulong vmObject, Interop.BOOL* pResult);
581581

582582
[PreserveSig]
583-
int GetRcwCachedInterfacePointers(ulong vmObject, Interop.BOOL bIInspectableOnly, nint pDacItfPtrs);
583+
int EnumerateRcwCachedInterfacePointers(ulong vmObject, /*FP_RCW_INTERFACE_CALLBACK*/ delegate* unmanaged<ulong, nint, void> fpCallback, nint pUserData);
584584

585585
[PreserveSig]
586586
int GetTypedByRefInfo(ulong pTypedByRef, nint pObjectData);

0 commit comments

Comments
 (0)