Skip to content

Commit 39b6512

Browse files
authored
[ICorProfiler] Add new API to enumerate GC Heap objects (#103735)
* [ICorProfiler] Add new ICorProfilerInfo API to enumerate gc heap * Update generated corprof.h After making changes to corprof.idl and building the clr subset the generated corprof.h needs to be manually copied over from the artifacts obj directory into the prebuilt directory. * [ICorProfiler] Extend header and source files to ICorProfilerInfo15 * [ICorProfiler] Add EnumerateGCHeapObjects skeleton * [ICorProfiler] Add EnumerateGCHeapObjects implementation Add parameter check Add SuspendEE akin to SuspendRuntime Add DiagWalkHeap Add RestartEE akin to ResumeRuntime * [GC] Add IGCHeap API to enumerate GC Heap objects outside of GC * Cleanup * [GC] Update gc interface minor version * Match callback PascalCase * Address feedback - naming and description * Cleanup * Add EEToProf EnumerateGCheapObjectsCallback wrapper Profilers may want to inspect objects encountered through the callback with synchronous ICorProfilerInfo APIs. Wrapping the GC Heap walk within a EEToProfInterfaceImpl helper with a CLR_TO_PROFILER_ENTRYPOINT macro allows proper setting of callback state flags. * [Profiler][Tests] Add EnumerateGCHeapObjects unit test * Address feedback As EE may be suspended outside of ProfToEEInterfaceImpl::EnumerateGCHeapObjects, conditionally suspend/resume EE if no prior suspension is in progress. * [ICorProfiler] Mitigate runtime suspension racing with EnumerateGCHeapObjects As the runtime may be suspended in other ways, error if not a profiler requested suspension similar to ProfToEEInterfaceImpl::RuntimeSuspend * [ICorProfiler] Update Callback helper contract As EnumerateGCHeapObjectsCallback should only be invoked by ProfToEEInterfaceImpl::EnumerateGCHeapObjects which suspends EE, signal that no other EE suspension should occur with GC_NOTRIGGER and kEE2PNoTrigger. Inherit MODE_ANY from EnumerateGCHeapObjects * Update test cases * [ICorProfiler] Change Suspend/Resume Runtime to Asynchronous * [Tests] Update profiler requested runtime test * fixup * Add test for background EnumerateGCHeapObject * Set profiler requested runtime suspend flag * Fix symbol exports * Try fix compiler issues * Allow runtime to resume before returning * Fix compilation errors on non-windows platforms * Lower expected object count * Add profiler requested runtime suspension note * [Tests] Protect custom test object from GC * Fix Profiler Runtime Suspension test * Expect to resolve classes during heap walk * Address feedback * Update EnumerateGCHeapObjects usage description * Add validation to extract class fields from objects * Cleanup
1 parent 29fe6a8 commit 39b6512

20 files changed

+4540
-2577
lines changed

src/coreclr/gc/gc.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52138,6 +52138,38 @@ void GCHeap::DiagWalkHeap (walk_fn fn, void* context, int gen_number, bool walk_
5213852138
gc_heap::walk_heap (fn, context, gen_number, walk_large_object_heap_p);
5213952139
}
5214052140

52141+
// Walking the GC Heap requires that the EE is suspended and all heap allocation contexts are fixed.
52142+
// DiagWalkHeap is invoked only during a GC, where both requirements are met.
52143+
// So DiagWalkHeapWithACHandling facilitates a GC Heap walk outside of a GC by handling allocation contexts logic,
52144+
// and it leaves the responsibility of suspending and resuming EE to the callers.
52145+
void GCHeap::DiagWalkHeapWithACHandling (walk_fn fn, void* context, int gen_number, bool walk_large_object_heap_p)
52146+
{
52147+
#ifdef MULTIPLE_HEAPS
52148+
for (int hn = 0; hn < gc_heap::n_heaps; hn++)
52149+
{
52150+
gc_heap* hp = gc_heap::g_heaps [hn];
52151+
#else
52152+
{
52153+
gc_heap* hp = pGenGCHeap;
52154+
#endif //MULTIPLE_HEAPS
52155+
hp->fix_allocation_contexts (FALSE);
52156+
}
52157+
52158+
DiagWalkHeap (fn, context, gen_number, walk_large_object_heap_p);
52159+
52160+
52161+
#ifdef MULTIPLE_HEAPS
52162+
for (int hn = 0; hn < gc_heap::n_heaps; hn++)
52163+
{
52164+
gc_heap* hp = gc_heap::g_heaps [hn];
52165+
#else
52166+
{
52167+
gc_heap* hp = pGenGCHeap;
52168+
#endif //MULTIPLE_HEAPS
52169+
hp->repair_allocation_contexts (TRUE);
52170+
}
52171+
}
52172+
5214152173
void GCHeap::DiagWalkFinalizeQueue (void* gc_context, fq_walk_fn fn)
5214252174
{
5214352175
gc_heap* hp = (gc_heap*)gc_context;

src/coreclr/gc/gcimpl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,8 @@ class GCHeap : public IGCHeapInternal
316316
virtual void DiagGetGCSettings(EtwGCSettingsInfo* etw_settings);
317317

318318
virtual unsigned int GetGenerationWithRange(Object* object, uint8_t** ppStart, uint8_t** ppAllocated, uint8_t** ppReserved);
319+
320+
virtual void DiagWalkHeapWithACHandling(walk_fn fn, void* context, int gen_number, bool walk_large_object_heap_p);
319321
public:
320322
Object * NextObj (Object * object);
321323

src/coreclr/gc/gcinterface.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
// The minor version of the IGCHeap interface. Non-breaking changes are required
1212
// to bump the minor version number. GCs and EEs with minor version number
1313
// mismatches can still interoperate correctly, with some care.
14-
#define GC_INTERFACE_MINOR_VERSION 2
14+
#define GC_INTERFACE_MINOR_VERSION 3
1515

1616
// The major version of the IGCToCLR interface. Breaking changes to this interface
1717
// require bumps in the major version number.
@@ -1025,6 +1025,9 @@ class IGCHeap {
10251025
virtual uint64_t GetGenerationBudget(int generation) PURE_VIRTUAL
10261026

10271027
virtual size_t GetLOHThreshold() PURE_VIRTUAL
1028+
1029+
// Walk the heap object by object outside of a GC.
1030+
virtual void DiagWalkHeapWithACHandling(walk_fn fn, void* context, int gen_number, bool walk_large_object_heap_p) PURE_VIRTUAL
10281031
};
10291032

10301033
#ifdef WRITE_BARRIER_CHECK

src/coreclr/inc/corprof.idl

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,11 @@ typedef enum _COR_PRF_HANDLE_TYPE
861861

862862
typedef void** ObjectHandleID;
863863

864+
/*
865+
* Callback for each object in the GC Heap
866+
*/
867+
typedef BOOL STDMETHODCALLTYPE (* ObjectCallback)(ObjectID object, void* callbackState);
868+
864869
/* -------------------------------------------------------------------------- *
865870
* Forward declarations
866871
* -------------------------------------------------------------------------- */
@@ -2651,7 +2656,6 @@ typedef enum
26512656
COR_PRF_CODEGEN_DEBUG_INFO = 0x0003,
26522657
} COR_PRF_CODEGEN_FLAGS;
26532658

2654-
26552659
/*
26562660
* The CLR implements the ICorProfilerInfo interface. This interface is
26572661
* used by a code profiler to communicate with the CLR to control event
@@ -4308,6 +4312,47 @@ interface ICorProfilerInfo14 : ICorProfilerInfo13
43084312
[out] EVENTPIPE_PROVIDER *pProvider);
43094313
}
43104314

4315+
[
4316+
object,
4317+
uuid(B446462D-BD22-41DD-872D-DC714C49EB56),
4318+
pointer_default(unique),
4319+
local
4320+
]
4321+
interface ICorProfilerInfo15 : ICorProfilerInfo14
4322+
{
4323+
/*
4324+
* EnumerateGCHeapObjects is a method that iterates over each object in the GC heap.
4325+
* For each object, it invokes the provided callback function which should return a bool
4326+
* indicating whether or not enumeration should continue.
4327+
* Enumerating the GC heap requires suspending the runtime. The profiler may accomplish this
4328+
* by starting from a state where the runtime is not suspended and by doing one of:
4329+
*
4330+
* From the same thread,
4331+
* Invoking ICorProfilerInfo10::SuspendRuntime()
4332+
* ...
4333+
* Invoking ICorProfilerInfo15::EnumerateGCHeapObjects()
4334+
* ...
4335+
* Invoking ICorProfilerInfo10::ResumeRuntime()
4336+
*
4337+
* or
4338+
*
4339+
* Invoke ICorProfilerInfo15::EnumerateGCHeapObjects() on its own, and leverage its
4340+
* built-in runtime suspension logic.
4341+
*
4342+
* Parameters:
4343+
* - callback: A function pointer to the callback function that will be invoked for each object in the GC heap.
4344+
* The callback function should accept an ObjectID and a void pointer as parameters and return a BOOL.
4345+
* - callbackState: A void pointer that can be used to pass state information to the callback function.
4346+
*
4347+
* Returns:
4348+
* - HRESULT: A code indicating the result of the operation. If the method succeeds,
4349+
* it returns S_OK. If it fails, it returns an error code.
4350+
*/
4351+
HRESULT EnumerateGCHeapObjects(
4352+
[in] ObjectCallback callback,
4353+
[in] void* callbackState);
4354+
}
4355+
43114356
/*
43124357
* This interface lets you iterate over methods in the runtime.
43134358
*/

src/coreclr/pal/prebuilt/idl/corprof_i.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ MIDL_DEFINE_GUID(IID, IID_ICorProfilerInfo13,0x6E6C7EE2,0x0701,0x4EC2,0x9D,0x29,
150150
MIDL_DEFINE_GUID(IID, IID_ICorProfilerInfo14,0xF460E352,0xD76D,0x4FE9,0x83,0x5F,0xF6,0xAF,0x9D,0x6E,0x86,0x2D);
151151

152152

153+
MIDL_DEFINE_GUID(IID, IID_ICorProfilerInfo15,0xB446462D,0xBD22,0x41DD,0x87,0x2D,0xDC,0x71,0x4C,0x49,0xEB,0x56);
154+
155+
153156
MIDL_DEFINE_GUID(IID, IID_ICorProfilerMethodEnum,0xFCCEE788,0x0088,0x454B,0xA8,0x11,0xC9,0x9F,0x29,0x8D,0x19,0x42);
154157

155158

0 commit comments

Comments
 (0)