-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Changes from all commits
24293e7
4af8097
4efbcd2
3516ffe
bb04e60
71163ca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -125,6 +125,7 @@ | |
#include "safemath.h" | ||
#include "threadsuspend.h" | ||
#include "inlinetracking.h" | ||
#include "frozenobjectheap.h" | ||
|
||
#ifdef PROFILING_SUPPORTED | ||
#include "profilinghelper.h" | ||
|
@@ -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; | ||
|
||
|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @EgorBo - I think we want I'd expect the current behavior will make a test case like this fail:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Ah, makes sense, I thought it was "how many entries were written to ranges". Will fix There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
* | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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!"); | ||
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @cshung added test coverage for |
||
if (FAILED(hr) || !isFrozen) | ||
{ | ||
printf("EnumerateNonGCObjects: IsFrozenObject failed\n!"); | ||
_failures++; | ||
} | ||
|
||
isFirstObj = false; | ||
nonGcObjectsEnumerated++; | ||
} | ||
|
||
|
There was a problem hiding this comment.
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)