Skip to content

Commit

Permalink
[ICorProfiler] Add new API to enumerate GC Heap objects (#103735)
Browse files Browse the repository at this point in the history
* [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
  • Loading branch information
mdh1418 committed Jul 11, 2024
1 parent 29fe6a8 commit 39b6512
Show file tree
Hide file tree
Showing 20 changed files with 4,540 additions and 2,577 deletions.
32 changes: 32 additions & 0 deletions src/coreclr/gc/gc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52138,6 +52138,38 @@ void GCHeap::DiagWalkHeap (walk_fn fn, void* context, int gen_number, bool walk_
gc_heap::walk_heap (fn, context, gen_number, walk_large_object_heap_p);
}

// Walking the GC Heap requires that the EE is suspended and all heap allocation contexts are fixed.
// DiagWalkHeap is invoked only during a GC, where both requirements are met.
// So DiagWalkHeapWithACHandling facilitates a GC Heap walk outside of a GC by handling allocation contexts logic,
// and it leaves the responsibility of suspending and resuming EE to the callers.
void GCHeap::DiagWalkHeapWithACHandling (walk_fn fn, void* context, int gen_number, bool walk_large_object_heap_p)
{
#ifdef MULTIPLE_HEAPS
for (int hn = 0; hn < gc_heap::n_heaps; hn++)
{
gc_heap* hp = gc_heap::g_heaps [hn];
#else
{
gc_heap* hp = pGenGCHeap;
#endif //MULTIPLE_HEAPS
hp->fix_allocation_contexts (FALSE);
}

DiagWalkHeap (fn, context, gen_number, walk_large_object_heap_p);


#ifdef MULTIPLE_HEAPS
for (int hn = 0; hn < gc_heap::n_heaps; hn++)
{
gc_heap* hp = gc_heap::g_heaps [hn];
#else
{
gc_heap* hp = pGenGCHeap;
#endif //MULTIPLE_HEAPS
hp->repair_allocation_contexts (TRUE);
}
}

void GCHeap::DiagWalkFinalizeQueue (void* gc_context, fq_walk_fn fn)
{
gc_heap* hp = (gc_heap*)gc_context;
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/gc/gcimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,8 @@ class GCHeap : public IGCHeapInternal
virtual void DiagGetGCSettings(EtwGCSettingsInfo* etw_settings);

virtual unsigned int GetGenerationWithRange(Object* object, uint8_t** ppStart, uint8_t** ppAllocated, uint8_t** ppReserved);

virtual void DiagWalkHeapWithACHandling(walk_fn fn, void* context, int gen_number, bool walk_large_object_heap_p);
public:
Object * NextObj (Object * object);

Expand Down
5 changes: 4 additions & 1 deletion src/coreclr/gc/gcinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// The minor version of the IGCHeap interface. Non-breaking changes are required
// to bump the minor version number. GCs and EEs with minor version number
// mismatches can still interoperate correctly, with some care.
#define GC_INTERFACE_MINOR_VERSION 2
#define GC_INTERFACE_MINOR_VERSION 3

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

virtual size_t GetLOHThreshold() PURE_VIRTUAL

// Walk the heap object by object outside of a GC.
virtual void DiagWalkHeapWithACHandling(walk_fn fn, void* context, int gen_number, bool walk_large_object_heap_p) PURE_VIRTUAL
};

#ifdef WRITE_BARRIER_CHECK
Expand Down
47 changes: 46 additions & 1 deletion src/coreclr/inc/corprof.idl
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,11 @@ typedef enum _COR_PRF_HANDLE_TYPE

typedef void** ObjectHandleID;

/*
* Callback for each object in the GC Heap
*/
typedef BOOL STDMETHODCALLTYPE (* ObjectCallback)(ObjectID object, void* callbackState);

/* -------------------------------------------------------------------------- *
* Forward declarations
* -------------------------------------------------------------------------- */
Expand Down Expand Up @@ -2651,7 +2656,6 @@ typedef enum
COR_PRF_CODEGEN_DEBUG_INFO = 0x0003,
} COR_PRF_CODEGEN_FLAGS;


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

[
object,
uuid(B446462D-BD22-41DD-872D-DC714C49EB56),
pointer_default(unique),
local
]
interface ICorProfilerInfo15 : ICorProfilerInfo14
{
/*
* EnumerateGCHeapObjects is a method that iterates over each object in the GC heap.
* For each object, it invokes the provided callback function which should return a bool
* indicating whether or not enumeration should continue.
* Enumerating the GC heap requires suspending the runtime. The profiler may accomplish this
* by starting from a state where the runtime is not suspended and by doing one of:
*
* From the same thread,
* Invoking ICorProfilerInfo10::SuspendRuntime()
* ...
* Invoking ICorProfilerInfo15::EnumerateGCHeapObjects()
* ...
* Invoking ICorProfilerInfo10::ResumeRuntime()
*
* or
*
* Invoke ICorProfilerInfo15::EnumerateGCHeapObjects() on its own, and leverage its
* built-in runtime suspension logic.
*
* Parameters:
* - callback: A function pointer to the callback function that will be invoked for each object in the GC heap.
* The callback function should accept an ObjectID and a void pointer as parameters and return a BOOL.
* - callbackState: A void pointer that can be used to pass state information to the callback function.
*
* Returns:
* - HRESULT: A code indicating the result of the operation. If the method succeeds,
* it returns S_OK. If it fails, it returns an error code.
*/
HRESULT EnumerateGCHeapObjects(
[in] ObjectCallback callback,
[in] void* callbackState);
}

/*
* This interface lets you iterate over methods in the runtime.
*/
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/pal/prebuilt/idl/corprof_i.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ MIDL_DEFINE_GUID(IID, IID_ICorProfilerInfo13,0x6E6C7EE2,0x0701,0x4EC2,0x9D,0x29,
MIDL_DEFINE_GUID(IID, IID_ICorProfilerInfo14,0xF460E352,0xD76D,0x4FE9,0x83,0x5F,0xF6,0xAF,0x9D,0x6E,0x86,0x2D);


MIDL_DEFINE_GUID(IID, IID_ICorProfilerInfo15,0xB446462D,0xBD22,0x41DD,0x87,0x2D,0xDC,0x71,0x4C,0x49,0xEB,0x56);


MIDL_DEFINE_GUID(IID, IID_ICorProfilerMethodEnum,0xFCCEE788,0x0088,0x454B,0xA8,0x11,0xC9,0x9F,0x29,0x8D,0x19,0x42);


Expand Down
Loading

0 comments on commit 39b6512

Please sign in to comment.