Skip to content

Commit e3b3aaa

Browse files
authored
[EventTrace] Update check for provider triggered GC (#109587)
* [EventTrace] Update check for provider triggered GC Re-add ControlCode check for ETW provider triggered GC Prevent disabling an EventPipe provider from triggering GC * Use SessionChange to track EventPipe vs ETW * Cleanup macros * Update NativeAOT counterpart * Fixup NativeAOT condition * Extend ControlCode keyword definition to Unix * Update NativeAOT counterpart * Update NativeAOT hardcoded ControlCode check
1 parent 6accd39 commit e3b3aaa

File tree

4 files changed

+96
-31
lines changed

4 files changed

+96
-31
lines changed

src/coreclr/inc/eventtracebase.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,15 @@ struct ProfilingScanContext;
224224
#include <evntrace.h>
225225
#include <evntprov.h>
226226
#endif //!FEATURE_NATIVEAOT
227+
#else // !defined(HOST_UNIX)
228+
229+
//
230+
// ETW and EventPipe Event Notification Callback Control Code Keywords
231+
//
232+
#define EVENT_CONTROL_CODE_DISABLE_PROVIDER 0
233+
#define EVENT_CONTROL_CODE_ENABLE_PROVIDER 1
234+
#define EVENT_CONTROL_CODE_CAPTURE_STATE 2
235+
227236
#endif //!defined(HOST_UNIX)
228237

229238

src/coreclr/nativeaot/Runtime/eventtrace.cpp

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,13 @@ enum CallbackProviderIndex
9999
DotNETRuntimePrivate = 3
100100
};
101101

102+
enum SessionChange
103+
{
104+
EventPipeSessionDisable = 0,
105+
EventPipeSessionEnable = 1,
106+
EtwSessionChangeUnknown = 2
107+
};
108+
102109
#ifdef FEATURE_ETW
103110
// EventFilterType identifies the filter type used by the PEVENT_FILTER_DESCRIPTOR
104111
enum EventFilterType
@@ -160,13 +167,15 @@ void ParseFilterDataClientSequenceNumber(
160167
}
161168
#endif // FEATURE_ETW
162169

170+
// NOTE: When multiple ETW or EventPipe sessions are enabled, the ControlCode will be
171+
// EVENT_CONTROL_CODE_ENABLE_PROVIDER even if the session invoking this callback is being disabled.
163172
void EtwCallbackCommon(
164173
CallbackProviderIndex ProviderIndex,
165174
ULONG ControlCode,
166175
unsigned char Level,
167176
ULONGLONG MatchAnyKeyword,
168177
PVOID pFilterData,
169-
BOOL isEventPipeCallback)
178+
SessionChange Change)
170179
{
171180
// LIMITED_METHOD_CONTRACT;
172181

@@ -189,14 +198,14 @@ void EtwCallbackCommon(
189198
// This callback gets called on both ETW/EventPipe session enable/disable.
190199
// We need toupdate the EventPipe provider context if we are in a callback
191200
// from EventPipe, but not from ETW.
192-
if (isEventPipeCallback)
201+
if (Change == EventPipeSessionEnable || Change == EventPipeSessionDisable)
193202
{
194203
ctxToUpdate->EventPipeProvider.Level = Level;
195204
ctxToUpdate->EventPipeProvider.EnabledKeywordsBitmask = MatchAnyKeyword;
196205
ctxToUpdate->EventPipeProvider.IsEnabled = ControlCode;
197206

198207
// For EventPipe, ControlCode can only be either 0 or 1.
199-
_ASSERTE(ControlCode == 0 || ControlCode == 1);
208+
_ASSERTE(ControlCode == EVENT_CONTROL_CODE_DISABLE_PROVIDER || ControlCode == EVENT_CONTROL_CODE_ENABLE_PROVIDER);
200209
}
201210

202211
if (
@@ -221,11 +230,23 @@ void EtwCallbackCommon(
221230

222231
// NativeAOT currently only supports forcing a GC with ManagedHeapCollectKeyword via ETW
223232
#ifdef FEATURE_ETW
224-
// Special check for the runtime provider's ManagedHeapCollectKeyword. Profilers
225-
// flick this to force a full GC.
226-
if (ControlCode && ProviderIndex == DotNETRuntime
227-
&& GCHeapUtilities::IsGCHeapInitialized()
228-
&& (MatchAnyKeyword & CLR_MANAGEDHEAPCOLLECT_KEYWORD) != 0)
233+
// Special check for a profiler requested GC.
234+
// A full GC will be forced if:
235+
// 1. The GC Heap is initialized.
236+
// 2. The public provider is requesting GC.
237+
// 3. The provider's ManagedHeapCollectKeyword is enabled.
238+
// 4. If it is an ETW provider, the control code is to enable or capture the state of the provider.
239+
// 5. If it is an EventPipe provider, the session is not being disabled.
240+
bool bValidGCRequest =
241+
GCHeapUtilities::IsGCHeapInitialized() &&
242+
bIsPublicTraceHandle &&
243+
((MatchAnyKeyword & CLR_MANAGEDHEAPCOLLECT_KEYWORD) != 0) &&
244+
((ControlCode == EVENT_CONTROL_CODE_ENABLE_PROVIDER) ||
245+
(ControlCode == EVENT_CONTROL_CODE_CAPTURE_STATE)) &&
246+
((Change == EtwSessionChangeUnknown) ||
247+
(Change == EventPipeSessionEnable));
248+
249+
if (bValidGCRequest)
229250
{
230251
// Profilers may (optionally) specify extra data in the filter parameter
231252
// to log with the GCStart event.
@@ -269,7 +290,7 @@ void EtwCallback(
269290
return;
270291
}
271292

272-
EtwCallbackCommon(providerIndex, IsEnabled, Level, MatchAnyKeyword, FilterData, /*isEventPipeCallback*/ false);
293+
EtwCallbackCommon(providerIndex, IsEnabled, Level, MatchAnyKeyword, FilterData, EtwSessionChangeUnknown);
273294

274295
if (IsEnabled &&
275296
(context->RegistrationHandle == Microsoft_Windows_DotNETRuntimePrivateHandle) &&
@@ -292,7 +313,9 @@ void EventPipeEtwCallbackDotNETRuntime(
292313
_In_opt_ EventFilterDescriptor* FilterData,
293314
_Inout_opt_ PVOID CallbackContext)
294315
{
295-
EtwCallbackCommon(DotNETRuntime, ControlCode, Level, MatchAnyKeyword, FilterData, /*isEventPipeCallback*/ true);
316+
SessionChange change = SourceId == NULL ? EventPipeSessionDisable : EventPipeSessionEnable;
317+
318+
EtwCallbackCommon(DotNETRuntime, ControlCode, Level, MatchAnyKeyword, FilterData, change);
296319
}
297320

298321
void EventPipeEtwCallbackDotNETRuntimePrivate(
@@ -304,5 +327,7 @@ void EventPipeEtwCallbackDotNETRuntimePrivate(
304327
_In_opt_ EventFilterDescriptor* FilterData,
305328
_Inout_opt_ PVOID CallbackContext)
306329
{
307-
EtwCallbackCommon(DotNETRuntimePrivate, ControlCode, Level, MatchAnyKeyword, FilterData, true);
330+
SessionChange change = SourceId == NULL ? EventPipeSessionDisable : EventPipeSessionEnable;
331+
332+
EtwCallbackCommon(DotNETRuntimePrivate, ControlCode, Level, MatchAnyKeyword, FilterData, change);
308333
}

src/coreclr/nativeaot/Runtime/eventtracebase.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,13 @@ bool DotNETRuntimeProvider_IsEnabled(unsigned char level, unsigned long long key
127127
DotNETRuntimeProvider_IsEnabled(Level, Keyword)
128128
#endif // FEATURE_ETW
129129

130+
//
131+
// ETW and EventPipe Event Notification Callback Control Code Keywords
132+
//
133+
#define EVENT_CONTROL_CODE_DISABLE_PROVIDER 0
134+
#define EVENT_CONTROL_CODE_ENABLE_PROVIDER 1
135+
#define EVENT_CONTROL_CODE_CAPTURE_STATE 2
136+
130137
#else // FEATURE_EVENT_TRACE
131138

132139
#include "etmdummy.h"

src/coreclr/vm/eventtrace.cpp

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2328,6 +2328,13 @@ enum CallbackProviderIndex
23282328
DotNETRuntimePrivate = 3
23292329
};
23302330

2331+
enum SessionChange
2332+
{
2333+
EventPipeSessionDisable = 0,
2334+
EventPipeSessionEnable = 1,
2335+
EtwSessionChangeUnknown = 2
2336+
};
2337+
23312338
#if !defined(HOST_UNIX)
23322339
// EventFilterType identifies the filter type used by the PEVENT_FILTER_DESCRIPTOR
23332340
enum EventFilterType
@@ -2394,13 +2401,15 @@ VOID ParseFilterDataClientSequenceNumber(
23942401
// Common handler for all ETW or EventPipe event notifications. Based on the provider that
23952402
// was enabled/disabled, this implementation forwards the event state change onto GCHeapUtilities
23962403
// which will inform the GC to update its local state about what events are enabled.
2404+
// NOTE: When multiple ETW or EventPipe sessions are enabled, the ControlCode will be
2405+
// EVENT_CONTROL_CODE_ENABLE_PROVIDER even if the session invoking this callback is being disabled.
23972406
VOID EtwCallbackCommon(
23982407
CallbackProviderIndex ProviderIndex,
23992408
ULONG ControlCode,
24002409
UCHAR Level,
24012410
ULONGLONG MatchAnyKeyword,
24022411
PVOID pFilterData,
2403-
BOOL isEventPipeCallback)
2412+
SessionChange Change)
24042413
{
24052414
LIMITED_METHOD_CONTRACT;
24062415

@@ -2436,20 +2445,17 @@ VOID EtwCallbackCommon(
24362445
// This callback gets called on both ETW/EventPipe session enable/disable.
24372446
// We need toupdate the EventPipe provider context if we are in a callback
24382447
// from EventPipe, but not from ETW.
2439-
if (isEventPipeCallback)
2448+
if (Change == EventPipeSessionEnable || Change == EventPipeSessionDisable)
24402449
{
24412450
ctxToUpdate->EventPipeProvider.Level = Level;
24422451
ctxToUpdate->EventPipeProvider.EnabledKeywordsBitmask = MatchAnyKeyword;
24432452
ctxToUpdate->EventPipeProvider.IsEnabled = ControlCode;
24442453

24452454
// For EventPipe, ControlCode can only be either 0 or 1.
2446-
_ASSERTE(ControlCode == 0 || ControlCode == 1);
2455+
_ASSERTE(ControlCode == EVENT_CONTROL_CODE_DISABLE_PROVIDER || ControlCode == EVENT_CONTROL_CODE_ENABLE_PROVIDER);
24472456
}
24482457

2449-
if (
2450-
#if !defined(HOST_UNIX)
2451-
(ControlCode == EVENT_CONTROL_CODE_ENABLE_PROVIDER || ControlCode == EVENT_CONTROL_CODE_DISABLE_PROVIDER) &&
2452-
#endif
2458+
if ((ControlCode == EVENT_CONTROL_CODE_ENABLE_PROVIDER || ControlCode == EVENT_CONTROL_CODE_DISABLE_PROVIDER) &&
24532459
(ProviderIndex == DotNETRuntime || ProviderIndex == DotNETRuntimePrivate))
24542460
{
24552461
#if !defined(HOST_UNIX)
@@ -2466,10 +2472,23 @@ VOID EtwCallbackCommon(
24662472
GCHeapUtilities::RecordEventStateChange(bIsPublicTraceHandle, keywords, level);
24672473
}
24682474

2469-
// Special check for the runtime provider's ManagedHeapCollectKeyword. Profilers
2470-
// flick this to force a full GC.
2471-
if (g_fEEStarted && !g_fEEShutDown && bIsPublicTraceHandle &&
2472-
((MatchAnyKeyword & CLR_MANAGEDHEAPCOLLECT_KEYWORD) != 0))
2475+
// Special check for a profiler requested GC.
2476+
// A full GC will be forced if:
2477+
// 1. The runtime has started and is not shutting down.
2478+
// 2. The public provider is requesting GC.
2479+
// 3. The provider's ManagedHeapCollectKeyword is enabled.
2480+
// 4. If it is an ETW provider, the control code is to enable or capture the state of the provider.
2481+
// 5. If it is an EventPipe provider, the session is not being disabled.
2482+
bool bValidGCRequest =
2483+
g_fEEStarted && !g_fEEShutDown &&
2484+
bIsPublicTraceHandle &&
2485+
((MatchAnyKeyword & CLR_MANAGEDHEAPCOLLECT_KEYWORD) != 0) &&
2486+
((ControlCode == EVENT_CONTROL_CODE_ENABLE_PROVIDER) ||
2487+
(ControlCode == EVENT_CONTROL_CODE_CAPTURE_STATE)) &&
2488+
((Change == EtwSessionChangeUnknown) ||
2489+
(Change == EventPipeSessionEnable));
2490+
2491+
if (bValidGCRequest)
24732492
{
24742493
// Profilers may (optionally) specify extra data in the filter parameter
24752494
// to log with the GCStart event.
@@ -2506,7 +2525,9 @@ VOID EventPipeEtwCallbackDotNETRuntimeStress(
25062525
{
25072526
LIMITED_METHOD_CONTRACT;
25082527

2509-
EtwCallbackCommon(DotNETRuntimeStress, ControlCode, Level, MatchAnyKeyword, FilterData, true);
2528+
SessionChange change = SourceId == NULL ? EventPipeSessionDisable : EventPipeSessionEnable;
2529+
2530+
EtwCallbackCommon(DotNETRuntimeStress, ControlCode, Level, MatchAnyKeyword, FilterData, change);
25102531
}
25112532

25122533
VOID EventPipeEtwCallbackDotNETRuntime(
@@ -2520,7 +2541,9 @@ VOID EventPipeEtwCallbackDotNETRuntime(
25202541
{
25212542
LIMITED_METHOD_CONTRACT;
25222543

2523-
EtwCallbackCommon(DotNETRuntime, ControlCode, Level, MatchAnyKeyword, FilterData, true);
2544+
SessionChange change = SourceId == NULL ? EventPipeSessionDisable : EventPipeSessionEnable;
2545+
2546+
EtwCallbackCommon(DotNETRuntime, ControlCode, Level, MatchAnyKeyword, FilterData, change);
25242547
}
25252548

25262549
VOID EventPipeEtwCallbackDotNETRuntimeRundown(
@@ -2534,7 +2557,9 @@ VOID EventPipeEtwCallbackDotNETRuntimeRundown(
25342557
{
25352558
LIMITED_METHOD_CONTRACT;
25362559

2537-
EtwCallbackCommon(DotNETRuntimeRundown, ControlCode, Level, MatchAnyKeyword, FilterData, true);
2560+
SessionChange change = SourceId == NULL ? EventPipeSessionDisable : EventPipeSessionEnable;
2561+
2562+
EtwCallbackCommon(DotNETRuntimeRundown, ControlCode, Level, MatchAnyKeyword, FilterData, change);
25382563
}
25392564

25402565
VOID EventPipeEtwCallbackDotNETRuntimePrivate(
@@ -2548,7 +2573,9 @@ VOID EventPipeEtwCallbackDotNETRuntimePrivate(
25482573
{
25492574
WRAPPER_NO_CONTRACT;
25502575

2551-
EtwCallbackCommon(DotNETRuntimePrivate, ControlCode, Level, MatchAnyKeyword, FilterData, true);
2576+
SessionChange change = SourceId == NULL ? EventPipeSessionDisable : EventPipeSessionEnable;
2577+
2578+
EtwCallbackCommon(DotNETRuntimePrivate, ControlCode, Level, MatchAnyKeyword, FilterData, change);
25522579
}
25532580

25542581

@@ -2704,7 +2731,7 @@ extern "C"
27042731
return;
27052732
}
27062733

2707-
EtwCallbackCommon(providerIndex, ControlCode, Level, MatchAnyKeyword, FilterData, false);
2734+
EtwCallbackCommon(providerIndex, ControlCode, Level, MatchAnyKeyword, FilterData, EtwSessionChangeUnknown);
27082735

27092736
// A manifest based provider can be enabled to multiple event tracing sessions
27102737
// As long as there is atleast 1 enabled session, IsEnabled will be TRUE
@@ -2762,10 +2789,7 @@ extern "C"
27622789

27632790
}
27642791
}
2765-
#endif // FEATURE_NATIVEAOT
2766-
2767-
#endif // HOST_UNIX
2768-
#ifndef FEATURE_NATIVEAOT
2792+
#endif // !defined(HOST_UNIX)
27692793

27702794
/****************************************************************************/
27712795
/* This is called by the runtime when an exception is thrown */

0 commit comments

Comments
 (0)