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

Implement ICorProfilerInfo14::GetNonGCHeapBounds #85434

Merged
merged 6 commits into from
May 2, 2023
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
16 changes: 16 additions & 0 deletions src/coreclr/inc/corprof.idl
Original file line number Diff line number Diff line change
Expand Up @@ -2035,6 +2035,17 @@ typedef struct COR_PRF_GC_GENERATION_RANGE
} COR_PRF_GC_GENERATION_RANGE;


/*
* COR_PRF_NONGC_GENERATION_RANGE describes a range of memory in the GetNonGCHeapBounds function.
*/
typedef struct COR_PRF_NONGC_HEAP_RANGE
{
ObjectID rangeStart; // the start of the range
UINT_PTR rangeLength; // the used length of the range
UINT_PTR rangeLengthReserved; // the amount of memory reserved for the range (including rangeLength)

} COR_PRF_NONGC_HEAP_RANGE;


/*
* COR_PRF_CLAUSE_TYPE defines the various clause codes for the EX clauses
Expand Down Expand Up @@ -4254,6 +4265,11 @@ interface ICorProfilerInfo13 : ICorProfilerInfo12
interface ICorProfilerInfo14 : ICorProfilerInfo13
{
HRESULT EnumerateNonGCObjects([out] ICorProfilerObjectEnum** ppEnum);

HRESULT GetNonGCHeapBounds(
[in] ULONG cObjectRanges,
[out] ULONG *pcObjectRanges,
[out, size_is(cObjectRanges), length_is(*pcObjectRanges)] COR_PRF_NONGC_HEAP_RANGE ranges[]);
}

/*
Expand Down
22 changes: 22 additions & 0 deletions src/coreclr/pal/prebuilt/inc/corprof.h
Original file line number Diff line number Diff line change
Expand Up @@ -1666,6 +1666,13 @@ typedef struct COR_PRF_GC_GENERATION_RANGE
UINT_PTR rangeLengthReserved;
} COR_PRF_GC_GENERATION_RANGE;

typedef struct COR_PRF_NONGC_HEAP_RANGE
{
ObjectID rangeStart;
UINT_PTR rangeLength;
UINT_PTR rangeLengthReserved;
} COR_PRF_NONGC_HEAP_RANGE;

typedef /* [public][public][public] */
enum __MIDL___MIDL_itf_corprof_0000_0001_0005
{
Expand Down Expand Up @@ -23231,6 +23238,11 @@ EXTERN_C const IID IID_ICorProfilerInfo14;
virtual HRESULT STDMETHODCALLTYPE EnumerateNonGCObjects(
/* [out] */ ICorProfilerObjectEnum **ppEnum) = 0;

virtual HRESULT STDMETHODCALLTYPE GetNonGCHeapBounds(
/* [in] */ ULONG cObjectRanges,
/* [out] */ ULONG *pcObjectRanges,
/* [length_is][size_is][out] */ COR_PRF_NONGC_HEAP_RANGE ranges[ ]) = 0;

};


Expand Down Expand Up @@ -24039,6 +24051,13 @@ EXTERN_C const IID IID_ICorProfilerInfo14;
ICorProfilerInfo14 * This,
/* [out] */ ICorProfilerObjectEnum **ppEnum);

DECLSPEC_XFGVIRT(ICorProfilerInfo14, GetNonGCHeapBounds)
HRESULT ( STDMETHODCALLTYPE *GetNonGCHeapBounds )(
ICorProfilerInfo14 * This,
/* [in] */ ULONG cObjectRanges,
/* [out] */ ULONG *pcObjectRanges,
/* [length_is][size_is][out] */ COR_PRF_NONGC_HEAP_RANGE ranges[ ]);

END_INTERFACE
} ICorProfilerInfo14Vtbl;

Expand Down Expand Up @@ -24402,6 +24421,9 @@ EXTERN_C const IID IID_ICorProfilerInfo14;
#define ICorProfilerInfo14_EnumerateNonGCObjects(This,ppEnum) \
( (This)->lpVtbl -> EnumerateNonGCObjects(This,ppEnum) )

#define ICorProfilerInfo14_GetNonGCHeapBounds(This,cObjectRanges,pcObjectRanges,ranges) \
( (This)->lpVtbl -> GetNonGCHeapBounds(This,cObjectRanges,pcObjectRanges,ranges) )

#endif /* COBJMACROS */


Expand Down
4 changes: 1 addition & 3 deletions src/coreclr/vm/frozenobjectheap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,7 @@ Object* FrozenObjectSegment::GetNextObject(Object* obj) const
uint8_t* nextObj = (reinterpret_cast<uint8_t*>(obj) + ALIGN_UP(obj->GetSize(), DATA_ALIGNMENT));
if (nextObj < m_pCurrent)
{
Object* result = reinterpret_cast<Object*>(nextObj);
INDEBUG(result->Validate());
return result;
return reinterpret_cast<Object*>(nextObj);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we agreed not to use COOP (Validate() needs it)

}

// Current object is the last one in the segment
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/vm/frozenobjectheap.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class FrozenObjectHeapManager
FrozenObjectSegment* m_CurrentSegment;

friend class ProfilerObjectEnum;
friend class ProfToEEInterfaceImpl;
};

class FrozenObjectSegment
Expand Down Expand Up @@ -72,6 +73,7 @@ class FrozenObjectSegment
INDEBUG(size_t m_ObjectsCount);

friend class ProfilerObjectEnum;
friend class ProfToEEInterfaceImpl;
};

#endif // _FROZENOBJECTHEAP_H
Expand Down
64 changes: 64 additions & 0 deletions src/coreclr/vm/proftoeeinterfaceimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@
#include "safemath.h"
#include "threadsuspend.h"
#include "inlinetracking.h"
#include "frozenobjectheap.h"

#ifdef PROFILING_SUPPORTED
#include "profilinghelper.h"
Expand Down Expand Up @@ -7598,6 +7599,9 @@ HRESULT ProfToEEInterfaceImpl::EnumerateNonGCObjects(ICorProfilerObjectEnum** pp
GC_NOTRIGGER;
MODE_ANY;
EE_THREAD_NOT_REQUIRED;

// FrozenObjectHeapManager takes a lock
CAN_TAKE_LOCK;
}
CONTRACTL_END;

Expand All @@ -7624,6 +7628,66 @@ HRESULT ProfToEEInterfaceImpl::EnumerateNonGCObjects(ICorProfilerObjectEnum** pp
return hr;
}

HRESULT ProfToEEInterfaceImpl::GetNonGCHeapBounds(ULONG cObjectRanges,
ULONG *pcObjectRanges,
COR_PRF_NONGC_HEAP_RANGE ranges[])
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
EE_THREAD_NOT_REQUIRED;

// FrozenObjectHeapManager takes a lock
CAN_TAKE_LOCK;
}
CONTRACTL_END;

if ((cObjectRanges > 0) && (ranges == nullptr))
{
// Copy GetGenerationBounds's behavior for consistency
return E_INVALIDARG;
}

FrozenObjectHeapManager* foh = SystemDomain::GetFrozenObjectHeapManager();
CrstHolder ch(&foh->m_Crst);

const unsigned segmentsCount = foh->m_FrozenSegments.GetCount();
FrozenObjectSegment** segments = foh->m_FrozenSegments.GetElements();
if (segments != nullptr && segmentsCount > 0)
{
const ULONG segmentsToInspect = min(cObjectRanges, (ULONG)segmentsCount);

for (unsigned segIdx = 0; segIdx < segmentsToInspect; segIdx++)
{
uint8_t* firstObj = segments[segIdx]->m_pStart + sizeof(ObjHeader);

// Start of the segment (first object)
ranges[segIdx].rangeStart = (ObjectID)firstObj;

// Total size reserved for a segment
ranges[segIdx].rangeLengthReserved = (UINT_PTR)segments[segIdx]->m_Size;

// Size of the segment that is currently in use
ranges[segIdx].rangeLength = (UINT_PTR)(segments[segIdx]->m_pCurrent - firstObj);
}

if (pcObjectRanges != nullptr)
{
*pcObjectRanges = segmentsToInspect;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@EgorBo - I think we want *pcObjectRanges = segmentsCount

I'd expect the current behavior will make a test case like this fail:

ULONG count
GetNonGCHeapBounds(0, &count, nullptr);
ranges = new ranges[count];
GetNonGCHeapBounds(count, nullptr, ranges);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@EgorBo - I think we want *pcObjectRanges = segmentsCount

I'd expect the current behavior will make a test case like this fail:

ULONG count
GetNonGCHeapBounds(0, &count, nullptr);
ranges = new ranges[count];
GetNonGCHeapBounds(count, nullptr, ranges);

Ah, makes sense, I thought it was "how many entries were written to ranges". Will fix

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I've seen it used that way too in other interfaces, but I checked GetGenerationBounds() for the GC case and it appears to have the segmentsCount semantics there so presumably we want to match the behavior.

}
}
else
{
if (pcObjectRanges != nullptr)
{
*pcObjectRanges = 0;
}
}
return S_OK;
}

/*
* GetStringLayout
*
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/vm/proftoeeinterfaceimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,10 @@ class ProfToEEInterfaceImpl : public ICorProfilerInfo14
COM_METHOD EnumerateNonGCObjects(
ICorProfilerObjectEnum** ppEnum);

COM_METHOD GetNonGCHeapBounds(ULONG cObjectRanges,
ULONG * pcObjectRanges,
COR_PRF_NONGC_HEAP_RANGE ranges[]);

// end ICorProfilerInfo14

protected:
Expand Down
64 changes: 63 additions & 1 deletion src/tests/profiler/native/nongcheap/nongcheap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,51 @@ HRESULT NonGcHeapProfiler::GarbageCollectionFinished()

_garbageCollections++;

const int MAX_NON_GC_HEAP_SEGMENTS = 16;
COR_PRF_NONGC_HEAP_RANGE segments[MAX_NON_GC_HEAP_SEGMENTS];
ULONG segCount;
ObjectID firstObj = 0;
HRESULT hr = pCorProfilerInfo->GetNonGCHeapBounds(MAX_NON_GC_HEAP_SEGMENTS, &segCount, segments);
if (FAILED(hr))
{
printf("GetNonGCHeapBounds returned an error\n!");
_failures++;
}
else if (segCount == 0 || segCount > MAX_NON_GC_HEAP_SEGMENTS)
{
printf("GetNonGCHeapBounds: invalid segCount (%u)\n!", segCount);
_failures++;
}
else
{
// Save very first object ID to compare with EnumerateNonGCObjects
firstObj = segments[0].rangeStart;

printf("\nGetNonGCHeapBounds (segCount = %u):\n", segCount);
for (ULONG i = 0; i < segCount; i++)
{
printf("\tseg#%u, rangeStart=%p, rangeLength=%u, rangeLengthReserved=%u\n",
i, (void*)segments[i].rangeStart, (ULONG)segments[i].rangeLength, (ULONG)segments[i].rangeLengthReserved);

if ((ULONG)segments[i].rangeLength > (ULONG)segments[i].rangeLengthReserved)
{
printf("GetNonGCHeapBounds: rangeLength > rangeLengthReserved");
_failures++;
}

if (!segments[i].rangeStart)
{
printf("GetNonGCHeapBounds: rangeStart is null");
_failures++;
}
}
printf("\n");
}

// Let's make sure we got the same number of objects as we got from the callback
// by testing the EnumerateNonGCObjects API.
ICorProfilerObjectEnum* pEnum = NULL;
HRESULT hr = pCorProfilerInfo->EnumerateNonGCObjects(&pEnum);
hr = pCorProfilerInfo->EnumerateNonGCObjects(&pEnum);
if (FAILED(hr))
{
printf("EnumerateNonGCObjects returned an error\n!");
Expand All @@ -72,8 +113,29 @@ HRESULT NonGcHeapProfiler::GarbageCollectionFinished()
{
int nonGcObjectsEnumerated = 0;
ObjectID obj;
bool isFirstObj = true;
while (pEnum->Next(1, &obj, NULL) == S_OK)
{
if (isFirstObj)
{
if (firstObj != obj)
{
printf("EnumerateNonGCObjects: firstObj != obj\n!");
_failures++;
}
}

// Add test coverage for IsFrozenObject API, currently, it is expected to return true
// for objects from non-GC heap (it might also return true for frozen segments we don't track)
BOOL isFrozen;
hr = pCorProfilerInfo->IsFrozenObject(obj, &isFrozen);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cshung added test coverage for IsFrozenObject on your request

if (FAILED(hr) || !isFrozen)
{
printf("EnumerateNonGCObjects: IsFrozenObject failed\n!");
_failures++;
}

isFirstObj = false;
nonGcObjectsEnumerated++;
}

Expand Down