move profiling and profiling/ETW shared diagnostics code out of gc.cpp, gcee.cpp and objecthandle.cpp#8199
Conversation
| @@ -0,0 +1,26 @@ | |||
| #ifndef __GCDIAG_H__ | |||
| @@ -0,0 +1,237 @@ | |||
| #include "gcdiag.h" | |||
There was a problem hiding this comment.
Should this file live under src/vm so that it is structured properly for the local GC?
| @@ -30623,34 +30500,21 @@ BOOL gc_heap::large_object_marked (uint8_t* o, BOOL clearp) | |||
| } | |||
|
|
|||
| #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) | |||
There was a problem hiding this comment.
Remove all GC_PROFILING and FEATURE_EVENT_TRACE ifdefs from the GC?
There was a problem hiding this comment.
I still have checks for GC_PROFILING and FEATURE_EVENT_TRACE in gc.cpp because I don't want to include the code that does stuff only with diagnostics if those are not defined. but I am not opposed to unconditionally compile this in 'cause we almost always have FEATURE_EVENT_TRACE defined.
| #ifndef __GCDIAG_H__ | ||
| #define __GCDIAG_H__ | ||
|
|
||
| typedef void (* record_surv_fn)(uint8_t*, uint8_t*, ptrdiff_t, size_t, BOOL, BOOL); |
There was a problem hiding this comment.
BOOL -> bool. (The GC interfaces should be using the native C++ primitive types, not the Windows-specific ones.)
| static void UpdateGenerationBounds(); | ||
| static void GCEnd(size_t index, int gen, int reason, bool fConcurrent); | ||
|
|
||
| static void WalkFReachableObjects(gc_heap* hp); |
There was a problem hiding this comment.
This should not be using internal GC types.
There was a problem hiding this comment.
(Maybe I do not understand what kind of separation is this change is trying to achieve.)
| void GCDiagnostics::WalkLOHSurvivors(gc_heap* hp) | ||
| { | ||
| #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) | ||
| if (ShouldTrackMovementForProfilerOrEtw()) |
There was a problem hiding this comment.
Minor comment: can we make this (and the other similar ones) be
if (!ShouldTrackMovementForProfilerOrEtw())
return;to avoid a level of nesting?
|
so as I mentioned this is an early version - it's not polished (things like license header/not exposing gc internal types will come later). I mainly wanted to get the diag specific code out of gc.cpp and a feel of what things should be on this interface. Jan and I talked, so the ETW/stress log messages will stay the way they are in gc.cpp. |
| void CFinalize::WalkFReachableObjects (gc_heap* hp) | ||
| void CFinalize::WalkFReachableObjects (fq_walk_fn fn) | ||
| { | ||
| BEGIN_PIN_PROFILER(CORProfilerPresent()); |
There was a problem hiding this comment.
BEGIN_PIN_PROFILER(CORProfilerPresent()); [](start = 4, length = 41)
I was just poking around and I didn't see any path to get here other than a call from GCDiagnostics::WalkFReachableObjects(gc_heap* hp). Assuming I didn't miss one the profiler was already pinned and doesn't need to be pinned a 2nd time.
There was a problem hiding this comment.
ahh, yes, I missed deleting the one in CFinalize::WalkFReachableObjects. thanks!
|
I chatted with Maoni offline as well, I think the overall direction is good and beyond that I didn't scrutinize too closely because the changes are still evolving. |
|
so I've moved the gcdiag.* onto the vm side and exposed the necessary things from GC on the IGCHeap interface. for the GCProfileWalkHeap code in gcee.cpp, which is shared by ETW and profiling, I am going to move the bulk of it into gcdiag.cpp so it will live in clr.dll/coreclr.dll, not gc.dll (and again expose what it needs from the GC on the IGCHeap interface). please let me know if you disagree! I'll send another commit out soon. |
|
|
||
| typedef BOOL (* walk_fn)(Object*, void*); | ||
| typedef void (* gen_walk_fn)(void* context, int generation, uint8_t* range_start, uint8_t* range_end, uint8_t* range_reserved); | ||
| typedef void (* record_surv_fn)(uint8_t* begin, uint8_t* end, ptrdiff_t reloc, size_t context, BOOL compacting_p, BOOL bgc_p); |
There was a problem hiding this comment.
use c++ bool here and below instead of BOOL?
Also - after moving things to the VM side, do you think we still need ScanContext and ProfilingScanContext to be on the GC interface anymore?
|
regarding BOOL vs bool, we are already using BOOL's all over the place in gcinterface.h so I would prefer that we do that in a separate commit. |
f3429f5 to
73c7910
Compare
|
@swgillespie @jkotas @noahfalk @adityamandaleeka @sergiy-k PTAL. This does have functional changes to make things cleaner and more efficient (mostly to make it cleaner so I don't need to expose unnecessary things on the GC interface. improving the perf of profiling was not a goal here but it ended up having that effect). Note I changed ProfScanRootsHelper to not return in this case: this really doesn't make sense - it's saying if it's an interior pointer we would only report it if it's within the condemned range but for all the other stack roots and all other kinds of roots we will report all of them without checking for this range. my change will make it report all interior pointers, which I think was the intended effect. I ended up not adding new files as I didn't want to get yet another interface so I just added stuff to gcenv.ee.cpp and GCToEEInterface. I got rid of places that checked for GC_PROFILING || FEATURE_EVENT_TRACE in gc.cpp. it's cleaner this way (yes we do compile in more code if you don't have either defined but we always have the latter defined anyway). I left ETW only stuff, perf counter and stress log stuff the way it is. In order to build a standalone gc.dll obviously that needs some work. We can do those in a separate PR. |
|
Maoni this accomplishes #7704 I imagine? I haven't looked at the code, but essentially generation info, start/stop, reason and if it concurrent or not you could supply to the profiling apis as well now without doing movement tracking? |
|
no...what kind of info is provided is no different from before. this is just to separate code out for the local GC project (where we can build GC as a standalone dll). for #7704 you need to add a new mask to the profiling api where it reads in what info you want; essentially you want to split COR_PRF_MONITOR_GC |
jkotas
left a comment
There was a problem hiding this comment.
I just added stuff to gcenv.ee.cpp and GCToEEInterface
I like it.
yes we do compile in more code
Compiling (unrechable) code is fine. Compilers are pretty good at getting rid of it from the final binary these days.
| virtual void ScanFinalizeQueue(fq_scan_fn fn, void* context) = 0; | ||
|
|
||
| // Scan handles for profiling or ETW. | ||
| virtual void ScanHandlesForProfilerAndETW(handle_scan_fn fn, int gen_number, ScanContext* context) = 0; |
There was a problem hiding this comment.
It would be nice to not have ProfilerAndETW in the names. Call it just ScanHandles or DiagScanHandles?
Maybe have Diag prefix on all the diagnostic methods - to match the other direction?
| { | ||
| #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) | ||
| Object *pObj = *ppObject; | ||
| #ifdef INTERIOR_POINTERS |
There was a problem hiding this comment.
This is internal GC define. It should not be used here - remove it here.
| { | ||
| GcScanRootsForProfilerAndETW(&ProfScanRootsHelper, max_generation, max_generation, &SC); | ||
| SC.dwEtwRootKind = kEtwGCRootKindFinalizer; | ||
| GCHeapUtilities::GetGCHeap()->ScanFinalizeQueue(&ProfScanRootsHelper, (void*)&SC); |
There was a problem hiding this comment.
Nit - (void*) should not be necessary here. (It is not in the other places in this method.)
| virtual void WalkHeap(walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p) = 0; | ||
|
|
||
| // Walks the survivors and get the relocation information if objects have moved. | ||
| virtual void WalkSurvivors(void* gc_context, record_surv_fn fn, size_t diag_context, walk_surv_type type) = 0; |
There was a problem hiding this comment.
diag_context should be void* - to match what is used for context everywhere else.
There was a problem hiding this comment.
this is because it's that way in the profiling/ETW code....they use void* sometimes and size_t other times (different kinds of contexts) and I didn't change that.
In reply to: 89092893 [](ancestors = 89092893)
There was a problem hiding this comment.
We should be creating issues for getting these things cleaned up - it would be unfortunate if we declared the GC interface to be stable with them.
There was a problem hiding this comment.
| static void DiagGCEnd(size_t index, int gen, int reason, bool fConcurrent); | ||
| static void DiagWalkFReachableObjects(void* gcContext); | ||
| static void DiagWalkSurvivors(void* gcContext); | ||
| static void DiagWalkLOHSurvivors(void* gcContext); |
| void DiagGCEnd(size_t index, int gen, int reason, bool fConcurrent); | ||
| void DiagWalkFReachableObjects(void* gcContext); | ||
| void DiagWalkSurvivors(void* gcContext); | ||
| void DiagWalkLOHSurvivors(void* gcContext); |
| if (CORProfilerTrackGC()) | ||
| { | ||
| BEGIN_PIN_PROFILER(CORProfilerPresent()); | ||
| GCHeapUtilities::GetGCHeap()->WalkFinalizeQueue(gcContext, g_FQWalkFn); |
| inline void GCToEEInterface::DiagGCStart(int gen, bool isInduced) | ||
| { | ||
| assert(g_theGCToCLR != nullptr); | ||
| return g_theGCToCLR->DiagGCStart(gen, isInduced); |
There was a problem hiding this comment.
nit - these all return void so you don't need the return
| typedef void (* record_surv_fn)(uint8_t* begin, uint8_t* end, ptrdiff_t reloc, size_t context, BOOL compacting_p, BOOL bgc_p); | ||
| typedef void (* fq_walk_fn)(BOOL, void*); | ||
| typedef void (* fq_scan_fn)(Object** ppObject, ScanContext *pSC, uint32_t dwFlags); | ||
| typedef void (* handle_scan_fn)(Object** pRef, Object* pSec, uint32_t flags, ScanContext* context, BOOL isDependent); |
There was a problem hiding this comment.
Are the contexts here derived from the void pointers passed to the interface below? If so, I'm wondering should we be enforcing that the user passes a ScanContext by making the context argument be one instead of a void pointer, or by making these function pointers accept void* as a context?
There was a problem hiding this comment.
they are different contexts. I am passing ScanContext* where it should be a ScanContext.
In reply to: 89182964 [](ancestors = 89182964)
|
@dotnet-bot test Windows_NT x64 Checked Standalone GC please |
| saved_post_plug_reloc = temp; | ||
| } | ||
|
|
||
| #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE) |
| else if (type == walk_for_loh) | ||
| walk_survivors_for_loh (context, fn); | ||
| else | ||
| assert (!"unknow type!"); |
| { | ||
| if (type == walk_for_gc) | ||
| walk_survivors_relocation (context, fn); | ||
| #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE) |
There was a problem hiding this comment.
Is this necessary to have? type won't be walk_for_bgc unless BGC is enabled anyway right?
I see walk_survivors_for_bgc is also under the BGC ifdef. If that's non-trivial to remove then feel free to ignore this comment.
| void gc_heap::walk_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p) | ||
| { | ||
| #ifdef MULTIPLE_HEAPS | ||
| int hn = 0; |
There was a problem hiding this comment.
Minor: just use for (int hn = 0;... for clarity since hn is only used in the loop?
| virtual | ||
| void DiagGCEnd(size_t index, int gen, int reason, bool fConcurrent) = 0; | ||
|
|
||
| // During a GC after we discover what objects's finalizers should run, gives the diagnostics code a chance to run. |
GC's own logging), gcee.cpp and objecthandle.cpp. The rule is GC will call the diagnostics functions at various points to communicate info and these diagnostics functions might call back into the GC if they require initimate knowledge of the GC in order to get certain info. This way gc.cpp does not need to know about details of the diagnostics components, eg, the profiling context; and the diagnostics components do not need to know details about the GC. So got rid of ProfilingScanContext in gcinterface.h and passed scanning functions to GC and handle table. Got rid of where it goes through things per heap as this is not something that diagnostics should care about, including going through stack roots per heap (which is only done in GC for scalability purposes but profiling is doing this on a single thread). This also makes it faster. Got rid of the checks for gc_low/high in ProfScanRootsHelper as this is knowledge profiling shouldn't have. And it was also incorrectly not including all interior pointer stack roots.
|
@dotnet-bot test Windows_NT x64 Checked Standalone GC please |
|
@dotnet-bot test Windows_NT Release standalone_gc |
|
BTW, I've addressed all your feedback in the latest commit. and am doing the final sanity check of the code and then will check in. |
|
@Maoni0 Is there any public information about the Local GC effort that I could read? It sounds interesting. |
|
#8268 #8267 |
|
@AlgorithmsAreCool the Local GC effort is for building the GC as its own dll so all the interaction it has with the rest of the runtime needs to communicate through interfaces. diagnostics code being a big part as we have so many different ways to do diagnostics and the diagnostics code tends to be very intertwined with the GC code. we can certainly open issues that explain what work is left that will serve as public info. I opened 2 yesterday related to diagnostics and referred to them above. we'll open more for other things that need to be done for this. please stay tuned. |
|
@dotnet-bot test Windows_NT x86 compatjit Checked Build and Test please |
|
@AlgorithmsAreCool we have started a project on this https://github.com/dotnet/coreclr/projects/3 to keep track of all the issues. |
|
Is the greater vision to allow alternative GC implementations? Or just to further modularize the coreclr? |
move profiling and profiling/ETW shared diagnostics code out of gc.cpp, gcee.cpp and objecthandle.cpp Commit migrated from dotnet/coreclr@38a0b15
This is the first step in separating the diagnostics code out from gc.cpp (except GC's own diagnostics) for the local GC project. The goal for this commit is to not have code that requires intimate knowledge about diagnostics stuff (eg, profiling, ETW, stress log, perf counters) in gc.cpp. Some of the diagnostics stuff exists in gcee.cpp today so they will need to be refactored out later.
The rule is GC will call the diagnostics functions at various points to communicate
info and these diagnostics functions might call back into the GC if they require
initimate knowledge of the GC in order to get certain info. This way gc.cpp does
not need to know about details of the diagnostics components, eg, the profiling context;
and the diagnostics components do not need to know details about the GC.
Still WIP (I haven't taken care of the gcsample part, for example) but wanted to get some early feedback. Profiling and what Profiling and ETW shared were the most problematic part so that's what I separated out. I stopped at the rest of the ETW part as they are just firing events in a straightforward way. The plan was to keep GC ETW events on the GC side so it's probably not worthwhile to have a wrapper that just calls the FireEtw* functions in another file... I am open to suggestions.
Stress log is similar to ETW - we just have some simple stress log messages sprinkled in gc.cpp. perf counter stuff is all in gcee.cpp right now.