Implement ICorProfilerInfo14::GetNonGCHeapBounds (#85434)
authorEgor Bogatov <egorbo@gmail.com>
Tue, 2 May 2023 18:10:44 +0000 (20:10 +0200)
committerGitHub <noreply@github.com>
Tue, 2 May 2023 18:10:44 +0000 (20:10 +0200)
src/coreclr/inc/corprof.idl
src/coreclr/pal/prebuilt/inc/corprof.h
src/coreclr/vm/frozenobjectheap.cpp
src/coreclr/vm/frozenobjectheap.h
src/coreclr/vm/proftoeeinterfaceimpl.cpp
src/coreclr/vm/proftoeeinterfaceimpl.h
src/tests/profiler/native/nongcheap/nongcheap.cpp

index 11ce62e..1ae14ab 100644 (file)
@@ -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
@@ -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[]);
 }
 
 /*
index 7682710..8023d89 100644 (file)
@@ -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
     {
@@ -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;
+        
     };
     
     
@@ -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;
 
@@ -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 */
 
 
index 036177d..a2efc7d 100644 (file)
@@ -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);
     }
 
     // Current object is the last one in the segment
index d8cc778..d2c0bb6 100644 (file)
@@ -35,6 +35,7 @@ private:
     FrozenObjectSegment* m_CurrentSegment;
 
     friend class ProfilerObjectEnum;
+    friend class ProfToEEInterfaceImpl;
 };
 
 class FrozenObjectSegment
@@ -72,6 +73,7 @@ private:
     INDEBUG(size_t m_ObjectsCount);
 
     friend class ProfilerObjectEnum;
+    friend class ProfToEEInterfaceImpl;
 };
 
 #endif // _FROZENOBJECTHEAP_H
index e6f5cb2..f2b67a9 100644 (file)
 #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;
+        }
+    }
+    else
+    {
+        if (pcObjectRanges != nullptr)
+        {
+            *pcObjectRanges = 0;
+        }
+    }
+    return S_OK;
+}
+
 /*
  * GetStringLayout
  *
index fe0807a..a0f9ab6 100644 (file)
@@ -727,6 +727,10 @@ public:
     COM_METHOD EnumerateNonGCObjects(
         ICorProfilerObjectEnum** ppEnum);
 
+    COM_METHOD GetNonGCHeapBounds(ULONG cObjectRanges,
+                                  ULONG * pcObjectRanges,
+                                  COR_PRF_NONGC_HEAP_RANGE ranges[]);
+
     // end ICorProfilerInfo14
 
 protected:
index 167901c..eca9270 100644 (file)
@@ -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);
+            if (FAILED(hr) || !isFrozen)
+            {
+                printf("EnumerateNonGCObjects: IsFrozenObject failed\n!");
+                _failures++;
+            }
+
+            isFirstObj = false;
             nonGcObjectsEnumerated++;
         }