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
33 changes: 15 additions & 18 deletions src/coreclr/debug/daccess/dacdbiimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3616,42 +3616,39 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::IsRcw(VMPTR_Object vmObject, OUT
#endif // FEATURE_COMINTEROP
}

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

DD_ENTER_MAY_THROW;

if (fpCallback == NULL)
return E_POINTER;

HRESULT hr = S_OK;
EX_TRY
{

Object* objPtr = vmObject.GetDacPtr();

InlineSArray<TADDR, INTERFACE_ENTRY_CACHE_SIZE> rgUnks;

PTR_RCW pRCW = GetRcwFromVmptrObject(vmObject);
if (pRCW != NULL)
{
pRCW->GetCachedInterfacePointers(bIInspectableOnly, &rgUnks);

pDacItfPtrs->Alloc(rgUnks.GetCount());

for (COUNT_T i = 0; i < rgUnks.GetCount(); ++i)
RCW::CachedInterfaceEntryIterator it = pRCW->IterateCachedInterfacePointers();
while (it.Next())
{
(*pDacItfPtrs)[i] = (CORDB_ADDRESS)(rgUnks[i]);
PTR_MethodTable pMT = dac_cast<PTR_MethodTable>((TADDR)(it.GetEntry()->m_pMT.Load()));
if (pMT != NULL)
{
TADDR taUnk = (TADDR)(it.GetEntry()->m_pUnknown.Load());
if (taUnk != NULL)
{
fpCallback((CORDB_ADDRESS)taUnk, pUserData);
}
}
}

}
else
{
pDacItfPtrs->Alloc(0);
}
}
EX_CATCH_HRESULT(hr);
return hr;
#else
pDacItfPtrs->Alloc(0);
return S_OK;
#endif // FEATURE_COMINTEROP
}
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/debug/daccess/dacdbiimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ class DacDbiInterfaceImpl :
// retrieves the list of interfaces pointers implemented by vmObject, as it is known at
// the time of the call (the list may change as new interface types become available
// in the runtime)
HRESULT STDMETHODCALLTYPE GetRcwCachedInterfacePointers(VMPTR_Object vmObject, BOOL bIInspectableOnly, OUT DacDbiArrayList<CORDB_ADDRESS> * pDacItfPtrs);
HRESULT STDMETHODCALLTYPE EnumerateRcwCachedInterfacePointers(VMPTR_Object vmObject, FP_RCW_INTERFACE_CALLBACK fpCallback, CALLBACK_DATA pUserData);

private:
// Given a pointer to a managed function, obtain the method desc for it.
Expand Down
42 changes: 28 additions & 14 deletions src/coreclr/debug/di/divalue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2802,6 +2802,16 @@ HRESULT CordbObjectValue::GetCachedInterfaceTypes(
#endif
}

#if defined(FEATURE_COMINTEROP)
namespace
{
void RcwInterfacePointerCallback(CORDB_ADDRESS itfPtr, CALLBACK_DATA pUserData)
{
CallbackAccumulator<CORDB_ADDRESS>::From(pUserData)->Push(itfPtr);
}
}
#endif // FEATURE_COMINTEROP

HRESULT CordbObjectValue::GetCachedInterfacePointers(
BOOL bIInspectableOnly,
ULONG32 celt,
Expand All @@ -2825,25 +2835,29 @@ HRESULT CordbObjectValue::GetCachedInterfacePointers(
HRESULT hr = S_OK;
ULONG32 cItfs = 0;

// retrieve interface types

CORDB_ADDRESS objAddr = m_valueHome.GetAddress();
CallbackAccumulator<CORDB_ADDRESS> acc;

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

// retrieve type info from LS
IfFailThrow(pDAC->GetRcwCachedInterfacePointers(vmObj, bIInspectableOnly, &dacItfPtrs));
EX_TRY
{
IDacDbiInterface* pDAC = GetProcess()->GetDAC();
VMPTR_Object vmObj;
IfFailThrow(pDAC->GetObject(objAddr, &vmObj));

IfFailThrow(pDAC->EnumerateRcwCachedInterfacePointers(vmObj, &RcwInterfacePointerCallback, &acc));
}
EX_CATCH_HRESULT(hr);
IfFailRet(hr);
IfFailRet(acc.hrError);
}
EX_CATCH_HRESULT(hr);
IfFailRet(hr);

// synthesize CordbType instances
cItfs = (ULONG32)dacItfPtrs.Count();
cItfs = (ULONG32)acc.items.Size();

if (pcEltFetched != NULL && ptrs == NULL)
{
Expand All @@ -2859,7 +2873,7 @@ HRESULT CordbObjectValue::GetCachedInterfacePointers(
if (ptrs != NULL && *pcEltFetched > 0)
{
for (ULONG32 i = 0; i < *pcEltFetched; ++i)
ptrs[i] = dacItfPtrs[i];
ptrs[i] = acc.items[i];
}

return (*pcEltFetched == celt ? S_OK : S_FALSE);
Expand Down
28 changes: 4 additions & 24 deletions src/coreclr/debug/di/process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2190,38 +2190,19 @@ HRESULT CordbProcess::GetGCHeapInformation(COR_HEAPINFO *pHeapInfo)

namespace
{
struct HeapSegmentAccumulator
{
CQuickArrayList<COR_SEGMENT> segments;
HRESULT hrError;
};

void HeapSegmentCallback(
CORDB_ADDRESS rangeStart,
CORDB_ADDRESS rangeEnd,
int generation,
ULONG heap,
CALLBACK_DATA pUserData)
{
HeapSegmentAccumulator *acc =
reinterpret_cast<HeapSegmentAccumulator*>(pUserData);

if (FAILED(acc->hrError))
return;

COR_SEGMENT s;
s.start = rangeStart;
s.end = rangeEnd;
s.type = (CorDebugGenerationTypes)generation;
s.heap = heap;
HRESULT hr = S_OK;
EX_TRY
{
acc->segments.Push(s);
}
EX_CATCH_HRESULT(hr);
if (FAILED(hr))
acc->hrError = hr;
CallbackAccumulator<COR_SEGMENT>::From(pUserData)->Push(s);
}
}

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

EX_TRY
{
HeapSegmentAccumulator acc;
acc.hrError = S_OK;
CallbackAccumulator<COR_SEGMENT> acc;

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

if (SUCCEEDED(hr))
{
if (acc.segments.Size() != 0)
if (acc.items.Size() != 0)
{
CordbHeapSegmentEnumerator *segEnum = new CordbHeapSegmentEnumerator(this, acc.segments.Ptr(), (DWORD)acc.segments.Size());
CordbHeapSegmentEnumerator *segEnum = new CordbHeapSegmentEnumerator(this, acc.items.Ptr(), (DWORD)acc.items.Size());
GetContinueNeuterList()->Add(this, segEnum);
hr = segEnum->QueryInterface(__uuidof(ICorDebugHeapSegmentEnum), (void**)ppRegions);
}
Expand Down
39 changes: 39 additions & 0 deletions src/coreclr/debug/di/rspriv.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,45 @@ struct MachineInfo;
#define WriteProcessMemory DONT_USE_WRITEPROCESS_MEMORY


//-----------------------------------------------------------------------------
// CallbackAccumulator<T>
//
// Helper for FP_*_CALLBACK consumers on the DI side that need to collect a
// list of values produced by the DAC and surface a single HRESULT. The
// callback can call Push() without worrying about exceptions - the first
// failure is captured in hrError and subsequent pushes are short-circuited.
// After the enumeration call returns, the caller should check both the DAC's
// returned HRESULT and acc.hrError, then consume acc.items.
//-----------------------------------------------------------------------------
template <typename T>
struct CallbackAccumulator
{
CQuickArrayList<T> items;
HRESULT hrError;

CallbackAccumulator() : hrError(S_OK) { }

void Push(const T& item)
{
if (FAILED(hrError))
return;
HRESULT hr = S_OK;
EX_TRY
{
items.Push(item);
}
EX_CATCH_HRESULT(hr);
if (FAILED(hr))
hrError = hr;
}

static CallbackAccumulator* From(CALLBACK_DATA pUserData)
{
return reinterpret_cast<CallbackAccumulator*>(pUserData);
}
};


/* ------------------------------------------------------------------------- *
* Forward class declarations
* ------------------------------------------------------------------------- */
Expand Down
11 changes: 7 additions & 4 deletions src/coreclr/debug/inc/dacdbiinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -1712,10 +1712,13 @@ IDacDbiInterface : public IUnknown
// Check whether the argument is a runtime callable wrapper.
virtual HRESULT STDMETHODCALLTYPE IsRcw(VMPTR_Object vmObject, OUT BOOL * pResult) = 0;

// retrieves the list of interfaces pointers implemented by vmObject, as it is known at
// the time of the call (the list may change as new interface types become available
// in the runtime)
virtual HRESULT STDMETHODCALLTYPE GetRcwCachedInterfacePointers(VMPTR_Object vmObject, BOOL bIInspectableOnly, OUT DacDbiArrayList<CORDB_ADDRESS> * pDacItfPtrs) = 0;
// Callback invoked for each cached interface pointer held by an RCW.
typedef void (*FP_RCW_INTERFACE_CALLBACK)(CORDB_ADDRESS itfPtr, CALLBACK_DATA pUserData);

// Enumerates the interface pointers cached by the RCW associated with vmObject, as known at
// the time of the call (the list may change as new interface types become available in the
// runtime). If vmObject is not an RCW the enumeration is empty. The callback must not throw.
virtual HRESULT STDMETHODCALLTYPE EnumerateRcwCachedInterfacePointers(VMPTR_Object vmObject, FP_RCW_INTERFACE_CALLBACK fpCallback, CALLBACK_DATA pUserData) = 0;

// ----------------------------------------------------------------------------
// functions to get information about reference/handle referents for ICDValue
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/inc/dacdbi.idl
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ typedef void (*FP_MODULE_ENUMERATION_CALLBACK)(VMPTR_Assembly vmAssembly, CALLBA
typedef void (*FP_THREAD_ENUMERATION_CALLBACK)(VMPTR_Thread vmThread, CALLBACK_DATA pUserData);
typedef BOOL (*FP_INTERNAL_FRAME_ENUMERATION_CALLBACK)(FramePointer fpFrame, CALLBACK_DATA pUserData);
typedef void (*FP_HEAPSEGMENT_CALLBACK)(CORDB_ADDRESS rangeStart, CORDB_ADDRESS rangeEnd, int generation, ULONG heap, CALLBACK_DATA pUserData);
typedef void (*FP_RCW_INTERFACE_CALLBACK)(CORDB_ADDRESS itfPtr, CALLBACK_DATA pUserData);


//
Expand Down Expand Up @@ -336,7 +337,7 @@ interface IDacDbiInterface : IUnknown
HRESULT IsExceptionObject([in] VMPTR_Object vmObject, [out] BOOL * pResult);
HRESULT GetStackFramesFromException([in] VMPTR_Object vmObject, [out] DacDbiArrayList_DacExceptionCallStackData * pDacStackFrames);
HRESULT IsRcw([in] VMPTR_Object vmObject, [out] BOOL * pResult);
HRESULT GetRcwCachedInterfacePointers([in] VMPTR_Object vmObject, [in] BOOL bIInspectableOnly, [out] DacDbiArrayList_CORDB_ADDRESS * pDacItfPtrs);
HRESULT EnumerateRcwCachedInterfacePointers([in] VMPTR_Object vmObject, [in] FP_RCW_INTERFACE_CALLBACK fpCallback, [in] CALLBACK_DATA pUserData);

// Object Data
HRESULT GetTypedByRefInfo([in] CORDB_ADDRESS pTypedByRef, [out] struct DebuggerIPCE_ObjectData * pObjectData);
Expand Down
21 changes: 0 additions & 21 deletions src/coreclr/vm/runtimecallablewrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,27 +272,6 @@ struct RCW
return m_cbRefCount;
}

void GetCachedInterfacePointers(BOOL bIInspectableOnly,
SArray<TADDR> * rgItfPtrs)
{
LIMITED_METHOD_DAC_CONTRACT;

CachedInterfaceEntryIterator it = IterateCachedInterfacePointers();
while (it.Next())
{
PTR_MethodTable pMT = dac_cast<PTR_MethodTable>((TADDR)(it.GetEntry()->m_pMT.Load()));
if (pMT != NULL &&
(!bIInspectableOnly))
{
TADDR taUnk = (TADDR)(it.GetEntry()->m_pUnknown.Load());
if (taUnk != NULL)
{
rgItfPtrs->Append(taUnk);
}
}
}
}

LPVOID GetVTablePtr() { LIMITED_METHOD_CONTRACT; return m_vtablePtr; }

// Remoting aware QI that will attempt to re-unmarshal on object disconnect.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1599,8 +1599,68 @@ public int IsRcw(ulong vmObject, Interop.BOOL* pResult)
return hr;
}

public int GetRcwCachedInterfacePointers(ulong vmObject, Interop.BOOL bIInspectableOnly, nint pDacItfPtrs)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetRcwCachedInterfacePointers(vmObject, bIInspectableOnly, pDacItfPtrs) : HResults.E_NOTIMPL;
#if DEBUG
[ThreadStatic]
private static List<ulong>? _debugEnumerateRcwCachedInterfacePointers;

private static List<ulong> DebugEnumerateRcwCachedInterfacePointers
=> _debugEnumerateRcwCachedInterfacePointers ??= new();

[UnmanagedCallersOnly]
private static void EnumerateRcwCachedInterfacePointersDebugCallback(ulong itfPtr, nint _)
{
DebugEnumerateRcwCachedInterfacePointers.Add(itfPtr);
}
#endif

public int EnumerateRcwCachedInterfacePointers(ulong vmObject, delegate* unmanaged<ulong, nint, void> fpCallback, nint pUserData)
Comment thread
rcj1 marked this conversation as resolved.
{
int hr = HResults.S_OK;
List<ulong> itfPtrs = new();
try
{
if (fpCallback is null)
throw new ArgumentNullException(nameof(fpCallback));

IObject obj = _target.Contracts.Object;
_ = obj.GetBuiltInComData(new TargetPointer(vmObject), out TargetPointer rcw, out _, out _);
if (rcw != TargetPointer.Null)
{
IBuiltInCOM builtInCom = _target.Contracts.BuiltInCOM;
foreach ((TargetPointer methodTable, TargetPointer unknown) in builtInCom.GetRCWInterfaces(rcw))
{
if (methodTable != TargetPointer.Null && unknown != TargetPointer.Null)
itfPtrs.Add(unknown.Value);
}
}

foreach (ulong itfPtr in itfPtrs)
fpCallback(itfPtr, pUserData);
}
catch (System.Exception ex)
{
hr = ex.HResult;
}

#if DEBUG
if (_legacy is not null)
{
DebugEnumerateRcwCachedInterfacePointers.Clear();
delegate* unmanaged<ulong, nint, void> debugCallbackPtr = &EnumerateRcwCachedInterfacePointersDebugCallback;
int hrLocal = _legacy.EnumerateRcwCachedInterfacePointers(vmObject, debugCallbackPtr, 0);
Debug.ValidateHResult(hr, hrLocal);

if (hr == HResults.S_OK)
{
List<ulong> legacyItfPtrs = DebugEnumerateRcwCachedInterfacePointers;
Debug.Assert(itfPtrs.SequenceEqual(legacyItfPtrs),
$"cDAC: [{string.Join(",", itfPtrs.Select(p => $"0x{p:x}"))}], DAC: [{string.Join(",", legacyItfPtrs.Select(p => $"0x{p:x}"))}]");
}
DebugEnumerateRcwCachedInterfacePointers.Clear();
}
#endif
return hr;
}

public int GetTypedByRefInfo(ulong pTypedByRef, nint pObjectData)
=> LegacyFallbackHelper.CanFallback() && _legacy is not null ? _legacy.GetTypedByRefInfo(pTypedByRef, pObjectData) : HResults.E_NOTIMPL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ public unsafe partial interface IDacDbiInterface
int IsRcw(ulong vmObject, Interop.BOOL* pResult);

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

[PreserveSig]
int GetTypedByRefInfo(ulong pTypedByRef, nint pObjectData);
Expand Down
Loading