Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add loader heap enumeration api's (ICorDebugProcess11) #39124

Merged
merged 5 commits into from
Jul 15, 2020
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
134 changes: 134 additions & 0 deletions src/coreclr/src/debug/daccess/dacdbiimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "switches.h"
#include "generics.h"
#include "stackwalk.h"
#include "virtualcallstub.h"

#include "dacdbiimpl.h"

Expand Down Expand Up @@ -3570,6 +3571,139 @@ HRESULT DacDbiInterfaceImpl::GetDelegateTargetObject(
return hr;
}

static bool TrackMemoryRangeHelper(PTR_VOID pvArgs, PTR_VOID pvAllocationBase, SIZE_T cbReserved)
{
// The pvArgs is really pointing to a debugger-side container. Sadly the callback only takes a PTR_VOID.
CQuickArrayList<COR_MEMORY_RANGE> *rangeCollection =
(CQuickArrayList<COR_MEMORY_RANGE>*)(dac_cast<TADDR>(pvArgs));
TADDR rangeStart = dac_cast<TADDR>(pvAllocationBase);
TADDR rangeEnd = rangeStart + cbReserved;
rangeCollection->Push({rangeStart, rangeEnd});

// This is a tracking function, not a search callback. Pretend we never found what we were looking for
// to get all possible ranges.
return false;
}

void DacDbiInterfaceImpl::EnumerateMemRangesForLoaderAllocator(PTR_LoaderAllocator pLoaderAllocator, CQuickArrayList<COR_MEMORY_RANGE> *rangeAcummulator)
{
CQuickArrayList<PTR_LoaderHeap> heapsToEnumerate;

// We always expect to see these three heaps
_ASSERTE(pLoaderAllocator->GetLowFrequencyHeap() != NULL);
heapsToEnumerate.Push(pLoaderAllocator->GetLowFrequencyHeap());

_ASSERTE(pLoaderAllocator->GetHighFrequencyHeap() != NULL);
heapsToEnumerate.Push(pLoaderAllocator->GetHighFrequencyHeap());

_ASSERTE(pLoaderAllocator->GetStubHeap() != NULL);
heapsToEnumerate.Push(pLoaderAllocator->GetStubHeap());

// GetVirtualCallStubManager returns VirtualCallStubManager*, but it's really an address to target as
// pLoaderAllocator is DACized. Cast it so we don't try to to a Host to Target translation.
VirtualCallStubManager *pVcsMgr = PTR_VirtualCallStubManager(TO_TADDR(pLoaderAllocator->GetVirtualCallStubManager()));
LOG((LF_CORDB, LL_INFO10000, "DDBII::EMRFLA: VirtualCallStubManager 0x%x\n", PTR_HOST_TO_TADDR(pVcsMgr)));
if (pVcsMgr)
{
if (pVcsMgr->indcell_heap != NULL) heapsToEnumerate.Push(pVcsMgr->indcell_heap);
if (pVcsMgr->lookup_heap != NULL) heapsToEnumerate.Push(pVcsMgr->lookup_heap);
if (pVcsMgr->resolve_heap != NULL) heapsToEnumerate.Push(pVcsMgr->resolve_heap);
if (pVcsMgr->dispatch_heap != NULL) heapsToEnumerate.Push(pVcsMgr->dispatch_heap);
if (pVcsMgr->cache_entry_heap != NULL) heapsToEnumerate.Push(pVcsMgr->cache_entry_heap);
}

TADDR rangeAccumAsTaddr = TO_TADDR(rangeAcummulator);
for (uint32_t i = 0; i < (uint32_t)heapsToEnumerate.Size(); i++)
{
LOG((LF_CORDB, LL_INFO10000, "DDBII::EMRFLA: LoaderHeap 0x%x\n", heapsToEnumerate[i].GetAddr()));
heapsToEnumerate[i]->EnumPageRegions(TrackMemoryRangeHelper, rangeAccumAsTaddr);
}
}

void DacDbiInterfaceImpl::EnumerateMemRangesForJitCodeHeaps(CQuickArrayList<COR_MEMORY_RANGE> *rangeAcummulator)
{
// We should always have a valid EEJitManager with at least one code heap.
EEJitManager *pEM = ExecutionManager::GetEEJitManager();
_ASSERTE(pEM != NULL && pEM->m_pCodeHeap.IsValid());

PTR_HeapList pHeapList = pEM->m_pCodeHeap;
while (pHeapList != NULL)
{
CodeHeap *pHeap = pHeapList->pHeap;
DacpJitCodeHeapInfo jitCodeHeapInfo = DACGetHeapInfoForCodeHeap(pHeap);

switch (jitCodeHeapInfo.codeHeapType)
{
case CODEHEAP_LOADER:
{
TADDR targetLoaderHeap = CLRDATA_ADDRESS_TO_TADDR(jitCodeHeapInfo.LoaderHeap);
LOG((LF_CORDB, LL_INFO10000,
"DDBII::EMRFJCH: LoaderCodeHeap 0x%x with LoaderHeap at 0x%x\n",
PTR_HOST_TO_TADDR(pHeap), targetLoaderHeap));
PTR_ExplicitControlLoaderHeap pLoaderHeap = PTR_ExplicitControlLoaderHeap(targetLoaderHeap);
pLoaderHeap->EnumPageRegions(TrackMemoryRangeHelper, TO_TADDR(rangeAcummulator));
break;
}

case CODEHEAP_HOST:
{
LOG((LF_CORDB, LL_INFO10000,
"DDBII::EMRFJCH: HostCodeHeap 0x%x\n",
PTR_HOST_TO_TADDR(pHeap)));
rangeAcummulator->Push({
CLRDATA_ADDRESS_TO_TADDR(jitCodeHeapInfo.HostData.baseAddr),
CLRDATA_ADDRESS_TO_TADDR(jitCodeHeapInfo.HostData.currentAddr)
});
break;
}

default:
{
LOG((LF_CORDB, LL_INFO10000, "DDBII::EMRFJCH: unknown heap type at 0x%x\n\n", pHeap));
hoyosjs marked this conversation as resolved.
Show resolved Hide resolved
_ASSERTE("Unknown heap type enumerating code ranges.");
break;
}
}

pHeapList = pHeapList->GetNext();
}
}

HRESULT DacDbiInterfaceImpl::GetLoaderHeapMemoryRanges(DacDbiArrayList<COR_MEMORY_RANGE> *pRanges)
{
LOG((LF_CORDB, LL_INFO10000, "DDBII::GLHMR\n"));
DD_ENTER_MAY_THROW;

HRESULT hr = S_OK;

EX_TRY
{
CQuickArrayList<COR_MEMORY_RANGE> memoryRanges;

// Anything that's loaded in the SystemDomain or into the main AppDomain's default context in .NET Core
// and after uses only one global allocator. Enumerating that one is enough for most purposes.
// This doesn't consider any uses of AssemblyLoadingContexts (Unloadable or not). Each context has
// it's own LoaderAllocator, but there's no easy way of getting a hand at them other than going through
// the heap, getting a managed LoaderAllocators, from there getting a Scout, and from there getting a native
// pointer to the LoaderAllocator tos enumerate.
PTR_LoaderAllocator pGlobalAllocator = SystemDomain::System()->GetLoaderAllocator();
_ASSERTE(pGlobalAllocator);
EnumerateMemRangesForLoaderAllocator(pGlobalAllocator, &memoryRanges);

EnumerateMemRangesForJitCodeHeaps(&memoryRanges);

// This code doesn't enumerate module thunk heaps to support IJW.
// It's a fairly rare scenario and requires to enumerate all modules.
// The return for such added time is minimal.

_ASSERTE(memoryRanges.Size() < INT_MAX);
pRanges->Init(memoryRanges.Ptr(), (UINT) memoryRanges.Size());
}
EX_CATCH_HRESULT(hr);

return hr;
}

void DacDbiInterfaceImpl::GetStackFramesFromException(VMPTR_Object vmObject, DacDbiArrayList<DacExceptionCallStackData>& dacStackFrames)
{
DD_ENTER_MAY_THROW;
Expand Down
10 changes: 10 additions & 0 deletions src/coreclr/src/debug/daccess/dacdbiimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,8 @@ class DacDbiInterfaceImpl :
OUT VMPTR_Object *ppTargetObj,
OUT VMPTR_AppDomain *ppTargetAppDomain);

HRESULT GetLoaderHeapMemoryRanges(OUT DacDbiArrayList<COR_MEMORY_RANGE> * pRanges);

// retrieves the list of COM interfaces 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)
Expand Down Expand Up @@ -394,6 +396,14 @@ class DacDbiInterfaceImpl :
OUT DacDbiArrayList<DebuggerIPCE_ExpandedTypeData> * pTypes);

private:
// Helper to enumerate all possible memory ranges help by a loader allocator.
void EnumerateMemRangesForLoaderAllocator(
PTR_LoaderAllocator pLoaderAllocator,
CQuickArrayList<COR_MEMORY_RANGE> *rangeAcummulator);

void EnumerateMemRangesForJitCodeHeaps(
CQuickArrayList<COR_MEMORY_RANGE> *rangeAcummulator);

// Given a pointer to a managed function, obtain the method desc for it.
// Equivalent to GetMethodDescPtrFromIp, except if the method isn't jitted
// it will look for it in code stubs.
Expand Down
6 changes: 5 additions & 1 deletion src/coreclr/src/debug/daccess/dacimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1455,8 +1455,12 @@ class ClrDataAccess
private:
#endif

#ifdef FEATURE_COMINTEROP
protected:
// Populates a DacpJitCodeHeapInfo with proper information about the
// code heap type and the information needed to locate it.
DacpJitCodeHeapInfo DACGetHeapInfoForCodeHeap(CodeHeap *heapAddr);

#ifdef FEATURE_COMINTEROP
// Returns CCW pointer based on a target address.
PTR_ComCallWrapper DACGetCCWFromAddress(CLRDATA_ADDRESS addr);

Expand Down
48 changes: 28 additions & 20 deletions src/coreclr/src/debug/daccess/request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -499,27 +499,8 @@ ClrDataAccess::GetCodeHeapList(CLRDATA_ADDRESS jitManager, unsigned int count, s
unsigned int i = 0;
while ((heapList != NULL) && (i < count))
{
// What type of CodeHeap pointer do we have?
CodeHeap *codeHeap = heapList->pHeap;
TADDR ourVTablePtr = VPTR_HOST_VTABLE_TO_TADDR(*(LPVOID*)codeHeap);
if (ourVTablePtr == LoaderCodeHeap::VPtrTargetVTable())
{
LoaderCodeHeap *loaderCodeHeap = PTR_LoaderCodeHeap(PTR_HOST_TO_TADDR(codeHeap));
codeHeaps[i].codeHeapType = CODEHEAP_LOADER;
codeHeaps[i].LoaderHeap =
TO_CDADDR(PTR_HOST_MEMBER_TADDR(LoaderCodeHeap, loaderCodeHeap, m_LoaderHeap));
}
else if (ourVTablePtr == HostCodeHeap::VPtrTargetVTable())
{
HostCodeHeap *hostCodeHeap = PTR_HostCodeHeap(PTR_HOST_TO_TADDR(codeHeap));
codeHeaps[i].codeHeapType = CODEHEAP_HOST;
codeHeaps[i].HostData.baseAddr = PTR_CDADDR(hostCodeHeap->m_pBaseAddr);
codeHeaps[i].HostData.currentAddr = PTR_CDADDR(hostCodeHeap->m_pLastAvailableCommittedAddr);
}
else
{
codeHeaps[i].codeHeapType = CODEHEAP_UNKNOWN;
}
codeHeaps[i] = DACGetHeapInfoForCodeHeap(codeHeap);
heapList = heapList->hpNext;
i++;
}
Expand Down Expand Up @@ -547,6 +528,33 @@ ClrDataAccess::GetCodeHeapList(CLRDATA_ADDRESS jitManager, unsigned int count, s
return hr;
}

DacpJitCodeHeapInfo ClrDataAccess::DACGetHeapInfoForCodeHeap(CodeHeap *heapAddr)
{
DacpJitCodeHeapInfo jitCodeHeapInfo;

TADDR targetVtblPtrForHeapType = VPTR_HOST_VTABLE_TO_TADDR(*(LPVOID*)heapAddr);
if (targetVtblPtrForHeapType == LoaderCodeHeap::VPtrTargetVTable())
{
LoaderCodeHeap *loaderCodeHeap = PTR_LoaderCodeHeap(PTR_HOST_TO_TADDR(heapAddr));
jitCodeHeapInfo.codeHeapType = CODEHEAP_LOADER;
jitCodeHeapInfo.LoaderHeap =
TO_CDADDR(PTR_HOST_MEMBER_TADDR(LoaderCodeHeap, loaderCodeHeap, m_LoaderHeap));
}
else if (targetVtblPtrForHeapType == HostCodeHeap::VPtrTargetVTable())
{
HostCodeHeap *hostCodeHeap = PTR_HostCodeHeap(PTR_HOST_TO_TADDR(heapAddr));
jitCodeHeapInfo.codeHeapType = CODEHEAP_HOST;
jitCodeHeapInfo.HostData.baseAddr = PTR_CDADDR(hostCodeHeap->m_pBaseAddr);
jitCodeHeapInfo.HostData.currentAddr = PTR_CDADDR(hostCodeHeap->m_pLastAvailableCommittedAddr);
}
else
{
jitCodeHeapInfo.codeHeapType = CODEHEAP_UNKNOWN;
}

return jitCodeHeapInfo;
}

HRESULT
ClrDataAccess::GetStackLimits(CLRDATA_ADDRESS threadPtr, CLRDATA_ADDRESS *lower,
CLRDATA_ADDRESS *upper, CLRDATA_ADDRESS *fp)
Expand Down
33 changes: 33 additions & 0 deletions src/coreclr/src/debug/di/process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2173,6 +2173,10 @@ HRESULT CordbProcess::QueryInterface(REFIID id, void **pInterface)
{
*pInterface = static_cast<ICorDebugProcess10*>(this);
}
else if (id == IID_ICorDebugProcess11)
{
*pInterface = static_cast<ICorDebugProcess11*>(this);
}
else if (id == IID_IUnknown)
{
*pInterface = static_cast<IUnknown*>(static_cast<ICorDebugProcess*>(this));
Expand Down Expand Up @@ -2531,6 +2535,35 @@ COM_METHOD CordbProcess::EnableGCNotificationEvents(BOOL fEnable)
return hr;
}

//-----------------------------------------------------------
// ICorDebugProcess11
//-----------------------------------------------------------
COM_METHOD CordbProcess::EnumerateLoaderHeapMemoryRegions(ICorDebugMemoryRangeEnum **ppRanges)
{
VALIDATE_POINTER_TO_OBJECT(ppRanges, ICorDebugMemoryRangeEnum **);
FAIL_IF_NEUTERED(this);

HRESULT hr = S_OK;

PUBLIC_API_BEGIN(this);
{
DacDbiArrayList<COR_MEMORY_RANGE> heapRanges;

hr = GetDAC()->GetLoaderHeapMemoryRanges(&heapRanges);

if (SUCCEEDED(hr))
{
RSInitHolder<CordbMemoryRangeEnumerator> heapSegmentEnumerator(
new CordbMemoryRangeEnumerator(this, &heapRanges[0], (DWORD)heapRanges.Count()));

GetContinueNeuterList()->Add(this, heapSegmentEnumerator);
heapSegmentEnumerator.TransferOwnershipExternal(ppRanges);
}
}
PUBLIC_API_END(hr);
return hr;
}

HRESULT CordbProcess::GetTypeForObject(CORDB_ADDRESS addr, CordbAppDomain* pAppDomainOverride, CordbType **ppType, CordbAppDomain **pAppDomain)
{
VMPTR_AppDomain appDomain;
Expand Down
13 changes: 11 additions & 2 deletions src/coreclr/src/debug/di/rspriv.h
Original file line number Diff line number Diff line change
Expand Up @@ -1668,6 +1668,11 @@ typedef CordbEnumerator<COR_SEGMENT,
ICorDebugHeapSegmentEnum, IID_ICorDebugHeapSegmentEnum,
IdentityConvert<COR_SEGMENT> > CordbHeapSegmentEnumerator;

typedef CordbEnumerator<COR_MEMORY_RANGE,
COR_MEMORY_RANGE,
ICorDebugMemoryRangeEnum, IID_ICorDebugMemoryRangeEnum,
IdentityConvert<COR_MEMORY_RANGE> > CordbMemoryRangeEnumerator;

typedef CordbEnumerator<CorDebugExceptionObjectStackFrame,
CorDebugExceptionObjectStackFrame,
ICorDebugExceptionObjectCallStackEnum, IID_ICorDebugExceptionObjectCallStackEnum,
Expand Down Expand Up @@ -2931,6 +2936,7 @@ class CordbProcess :
public ICorDebugProcess7,
public ICorDebugProcess8,
public ICorDebugProcess10,
public ICorDebugProcess11,
public IDacDbiInterface::IAllocator,
public IDacDbiInterface::IMetaDataLookup,
public IProcessShimHooks
Expand Down Expand Up @@ -3144,6 +3150,11 @@ class CordbProcess :
//-----------------------------------------------------------
COM_METHOD EnableGCNotificationEvents(BOOL fEnable);

//-----------------------------------------------------------
// ICorDebugProcess11
//-----------------------------------------------------------
COM_METHOD EnumerateLoaderHeapMemoryRegions(ICorDebugMemoryRangeEnum **ppRanges);

//-----------------------------------------------------------
// Methods not exposed via a COM interface.
//-----------------------------------------------------------
Expand Down Expand Up @@ -11822,5 +11833,3 @@ struct RSDebuggingInfo
#include "rspriv.inl"

#endif // #if RSPRIV_H


3 changes: 3 additions & 0 deletions src/coreclr/src/debug/inc/dacdbiinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -2730,6 +2730,9 @@ class IDacDbiInterface
OUT VMPTR_Object *ppTargetObj,
OUT VMPTR_AppDomain *ppTargetAppDomain) = 0;

virtual
HRESULT GetLoaderHeapMemoryRanges(OUT DacDbiArrayList<COR_MEMORY_RANGE> *pRanges) = 0;

// The following tag tells the DD-marshalling tool to stop scanning.
// END_MARSHAL

Expand Down
39 changes: 39 additions & 0 deletions src/coreclr/src/inc/cordebug.idl
Original file line number Diff line number Diff line change
Expand Up @@ -3313,6 +3313,45 @@ interface ICorDebugProcess10 : IUnknown
HRESULT EnableGCNotificationEvents(BOOL fEnable);
}

// The memory range described by this structure is a closed-open
// interval, that is only the start is inclusive.
typedef struct _COR_MEMORY_RANGE
hoyosjs marked this conversation as resolved.
Show resolved Hide resolved
{
CORDB_ADDRESS start; // The start address of the range.
CORDB_ADDRESS end; // The end address of the range.
} COR_MEMORY_RANGE;

[
object,
local,
uuid(D1A0BCFC-5865-4437-BE3F-36F022951F8A),
pointer_default(unique)
]
interface ICorDebugMemoryRangeEnum : ICorDebugEnum
{
/*
* Gets the next "celt" number of objects in the enumeration.
* The actual number of objects retrieved is returned in "pceltFetched".
* Returns S_FALSE if the actual number of objects retrieved is smaller
* than the number of objects requested.
*/
HRESULT Next([in] ULONG celt,
[out, size_is(celt),
length_is(*pceltFetched)] COR_MEMORY_RANGE objects[],
[out] ULONG *pceltFetched);
};

[
object,
local,
uuid(344B37AA-F2C0-4D3B-9909-91CCF787DA8C),
pointer_default(unique)
]
interface ICorDebugProcess11 : IUnknown
{
HRESULT EnumerateLoaderHeapMemoryRegions([out] ICorDebugMemoryRangeEnum** ppRanges);
}

// Event types MODULE_LOADED and MODULE_UNLOADED implement this interface
[
object,
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/src/inc/dacprivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,8 @@ struct MSLAYOUT DacpJitCodeHeapInfo
CLRDATA_ADDRESS currentAddr = 0;
} HostData;
};

DacpJitCodeHeapInfo() : codeHeapType(0), LoaderHeap(0) {}
};

#include "static_assert.h"
Expand Down
Loading