Skip to content

Commit

Permalink
[EventTrace] Update check for provider triggered GC (#109587)
Browse files Browse the repository at this point in the history
* [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
  • Loading branch information
mdh1418 authored Nov 19, 2024
1 parent 6accd39 commit e3b3aaa
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 31 deletions.
9 changes: 9 additions & 0 deletions src/coreclr/inc/eventtracebase.h
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,15 @@ struct ProfilingScanContext;
#include <evntrace.h>
#include <evntprov.h>
#endif //!FEATURE_NATIVEAOT
#else // !defined(HOST_UNIX)

//
// ETW and EventPipe Event Notification Callback Control Code Keywords
//
#define EVENT_CONTROL_CODE_DISABLE_PROVIDER 0
#define EVENT_CONTROL_CODE_ENABLE_PROVIDER 1
#define EVENT_CONTROL_CODE_CAPTURE_STATE 2

#endif //!defined(HOST_UNIX)


Expand Down
47 changes: 36 additions & 11 deletions src/coreclr/nativeaot/Runtime/eventtrace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ enum CallbackProviderIndex
DotNETRuntimePrivate = 3
};

enum SessionChange
{
EventPipeSessionDisable = 0,
EventPipeSessionEnable = 1,
EtwSessionChangeUnknown = 2
};

#ifdef FEATURE_ETW
// EventFilterType identifies the filter type used by the PEVENT_FILTER_DESCRIPTOR
enum EventFilterType
Expand Down Expand Up @@ -160,13 +167,15 @@ void ParseFilterDataClientSequenceNumber(
}
#endif // FEATURE_ETW

// NOTE: When multiple ETW or EventPipe sessions are enabled, the ControlCode will be
// EVENT_CONTROL_CODE_ENABLE_PROVIDER even if the session invoking this callback is being disabled.
void EtwCallbackCommon(
CallbackProviderIndex ProviderIndex,
ULONG ControlCode,
unsigned char Level,
ULONGLONG MatchAnyKeyword,
PVOID pFilterData,
BOOL isEventPipeCallback)
SessionChange Change)
{
// LIMITED_METHOD_CONTRACT;

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

// For EventPipe, ControlCode can only be either 0 or 1.
_ASSERTE(ControlCode == 0 || ControlCode == 1);
_ASSERTE(ControlCode == EVENT_CONTROL_CODE_DISABLE_PROVIDER || ControlCode == EVENT_CONTROL_CODE_ENABLE_PROVIDER);
}

if (
Expand All @@ -221,11 +230,23 @@ void EtwCallbackCommon(

// NativeAOT currently only supports forcing a GC with ManagedHeapCollectKeyword via ETW
#ifdef FEATURE_ETW
// Special check for the runtime provider's ManagedHeapCollectKeyword. Profilers
// flick this to force a full GC.
if (ControlCode && ProviderIndex == DotNETRuntime
&& GCHeapUtilities::IsGCHeapInitialized()
&& (MatchAnyKeyword & CLR_MANAGEDHEAPCOLLECT_KEYWORD) != 0)
// Special check for a profiler requested GC.
// A full GC will be forced if:
// 1. The GC Heap is initialized.
// 2. The public provider is requesting GC.
// 3. The provider's ManagedHeapCollectKeyword is enabled.
// 4. If it is an ETW provider, the control code is to enable or capture the state of the provider.
// 5. If it is an EventPipe provider, the session is not being disabled.
bool bValidGCRequest =
GCHeapUtilities::IsGCHeapInitialized() &&
bIsPublicTraceHandle &&
((MatchAnyKeyword & CLR_MANAGEDHEAPCOLLECT_KEYWORD) != 0) &&
((ControlCode == EVENT_CONTROL_CODE_ENABLE_PROVIDER) ||
(ControlCode == EVENT_CONTROL_CODE_CAPTURE_STATE)) &&
((Change == EtwSessionChangeUnknown) ||
(Change == EventPipeSessionEnable));

if (bValidGCRequest)
{
// Profilers may (optionally) specify extra data in the filter parameter
// to log with the GCStart event.
Expand Down Expand Up @@ -269,7 +290,7 @@ void EtwCallback(
return;
}

EtwCallbackCommon(providerIndex, IsEnabled, Level, MatchAnyKeyword, FilterData, /*isEventPipeCallback*/ false);
EtwCallbackCommon(providerIndex, IsEnabled, Level, MatchAnyKeyword, FilterData, EtwSessionChangeUnknown);

if (IsEnabled &&
(context->RegistrationHandle == Microsoft_Windows_DotNETRuntimePrivateHandle) &&
Expand All @@ -292,7 +313,9 @@ void EventPipeEtwCallbackDotNETRuntime(
_In_opt_ EventFilterDescriptor* FilterData,
_Inout_opt_ PVOID CallbackContext)
{
EtwCallbackCommon(DotNETRuntime, ControlCode, Level, MatchAnyKeyword, FilterData, /*isEventPipeCallback*/ true);
SessionChange change = SourceId == NULL ? EventPipeSessionDisable : EventPipeSessionEnable;

EtwCallbackCommon(DotNETRuntime, ControlCode, Level, MatchAnyKeyword, FilterData, change);
}

void EventPipeEtwCallbackDotNETRuntimePrivate(
Expand All @@ -304,5 +327,7 @@ void EventPipeEtwCallbackDotNETRuntimePrivate(
_In_opt_ EventFilterDescriptor* FilterData,
_Inout_opt_ PVOID CallbackContext)
{
EtwCallbackCommon(DotNETRuntimePrivate, ControlCode, Level, MatchAnyKeyword, FilterData, true);
SessionChange change = SourceId == NULL ? EventPipeSessionDisable : EventPipeSessionEnable;

EtwCallbackCommon(DotNETRuntimePrivate, ControlCode, Level, MatchAnyKeyword, FilterData, change);
}
7 changes: 7 additions & 0 deletions src/coreclr/nativeaot/Runtime/eventtracebase.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,13 @@ bool DotNETRuntimeProvider_IsEnabled(unsigned char level, unsigned long long key
DotNETRuntimeProvider_IsEnabled(Level, Keyword)
#endif // FEATURE_ETW

//
// ETW and EventPipe Event Notification Callback Control Code Keywords
//
#define EVENT_CONTROL_CODE_DISABLE_PROVIDER 0
#define EVENT_CONTROL_CODE_ENABLE_PROVIDER 1
#define EVENT_CONTROL_CODE_CAPTURE_STATE 2

#else // FEATURE_EVENT_TRACE

#include "etmdummy.h"
Expand Down
64 changes: 44 additions & 20 deletions src/coreclr/vm/eventtrace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2328,6 +2328,13 @@ enum CallbackProviderIndex
DotNETRuntimePrivate = 3
};

enum SessionChange
{
EventPipeSessionDisable = 0,
EventPipeSessionEnable = 1,
EtwSessionChangeUnknown = 2
};

#if !defined(HOST_UNIX)
// EventFilterType identifies the filter type used by the PEVENT_FILTER_DESCRIPTOR
enum EventFilterType
Expand Down Expand Up @@ -2394,13 +2401,15 @@ VOID ParseFilterDataClientSequenceNumber(
// Common handler for all ETW or EventPipe event notifications. Based on the provider that
// was enabled/disabled, this implementation forwards the event state change onto GCHeapUtilities
// which will inform the GC to update its local state about what events are enabled.
// NOTE: When multiple ETW or EventPipe sessions are enabled, the ControlCode will be
// EVENT_CONTROL_CODE_ENABLE_PROVIDER even if the session invoking this callback is being disabled.
VOID EtwCallbackCommon(
CallbackProviderIndex ProviderIndex,
ULONG ControlCode,
UCHAR Level,
ULONGLONG MatchAnyKeyword,
PVOID pFilterData,
BOOL isEventPipeCallback)
SessionChange Change)
{
LIMITED_METHOD_CONTRACT;

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

// For EventPipe, ControlCode can only be either 0 or 1.
_ASSERTE(ControlCode == 0 || ControlCode == 1);
_ASSERTE(ControlCode == EVENT_CONTROL_CODE_DISABLE_PROVIDER || ControlCode == EVENT_CONTROL_CODE_ENABLE_PROVIDER);
}

if (
#if !defined(HOST_UNIX)
(ControlCode == EVENT_CONTROL_CODE_ENABLE_PROVIDER || ControlCode == EVENT_CONTROL_CODE_DISABLE_PROVIDER) &&
#endif
if ((ControlCode == EVENT_CONTROL_CODE_ENABLE_PROVIDER || ControlCode == EVENT_CONTROL_CODE_DISABLE_PROVIDER) &&
(ProviderIndex == DotNETRuntime || ProviderIndex == DotNETRuntimePrivate))
{
#if !defined(HOST_UNIX)
Expand All @@ -2466,10 +2472,23 @@ VOID EtwCallbackCommon(
GCHeapUtilities::RecordEventStateChange(bIsPublicTraceHandle, keywords, level);
}

// Special check for the runtime provider's ManagedHeapCollectKeyword. Profilers
// flick this to force a full GC.
if (g_fEEStarted && !g_fEEShutDown && bIsPublicTraceHandle &&
((MatchAnyKeyword & CLR_MANAGEDHEAPCOLLECT_KEYWORD) != 0))
// Special check for a profiler requested GC.
// A full GC will be forced if:
// 1. The runtime has started and is not shutting down.
// 2. The public provider is requesting GC.
// 3. The provider's ManagedHeapCollectKeyword is enabled.
// 4. If it is an ETW provider, the control code is to enable or capture the state of the provider.
// 5. If it is an EventPipe provider, the session is not being disabled.
bool bValidGCRequest =
g_fEEStarted && !g_fEEShutDown &&
bIsPublicTraceHandle &&
((MatchAnyKeyword & CLR_MANAGEDHEAPCOLLECT_KEYWORD) != 0) &&
((ControlCode == EVENT_CONTROL_CODE_ENABLE_PROVIDER) ||
(ControlCode == EVENT_CONTROL_CODE_CAPTURE_STATE)) &&
((Change == EtwSessionChangeUnknown) ||
(Change == EventPipeSessionEnable));

if (bValidGCRequest)
{
// Profilers may (optionally) specify extra data in the filter parameter
// to log with the GCStart event.
Expand Down Expand Up @@ -2506,7 +2525,9 @@ VOID EventPipeEtwCallbackDotNETRuntimeStress(
{
LIMITED_METHOD_CONTRACT;

EtwCallbackCommon(DotNETRuntimeStress, ControlCode, Level, MatchAnyKeyword, FilterData, true);
SessionChange change = SourceId == NULL ? EventPipeSessionDisable : EventPipeSessionEnable;

EtwCallbackCommon(DotNETRuntimeStress, ControlCode, Level, MatchAnyKeyword, FilterData, change);
}

VOID EventPipeEtwCallbackDotNETRuntime(
Expand All @@ -2520,7 +2541,9 @@ VOID EventPipeEtwCallbackDotNETRuntime(
{
LIMITED_METHOD_CONTRACT;

EtwCallbackCommon(DotNETRuntime, ControlCode, Level, MatchAnyKeyword, FilterData, true);
SessionChange change = SourceId == NULL ? EventPipeSessionDisable : EventPipeSessionEnable;

EtwCallbackCommon(DotNETRuntime, ControlCode, Level, MatchAnyKeyword, FilterData, change);
}

VOID EventPipeEtwCallbackDotNETRuntimeRundown(
Expand All @@ -2534,7 +2557,9 @@ VOID EventPipeEtwCallbackDotNETRuntimeRundown(
{
LIMITED_METHOD_CONTRACT;

EtwCallbackCommon(DotNETRuntimeRundown, ControlCode, Level, MatchAnyKeyword, FilterData, true);
SessionChange change = SourceId == NULL ? EventPipeSessionDisable : EventPipeSessionEnable;

EtwCallbackCommon(DotNETRuntimeRundown, ControlCode, Level, MatchAnyKeyword, FilterData, change);
}

VOID EventPipeEtwCallbackDotNETRuntimePrivate(
Expand All @@ -2548,7 +2573,9 @@ VOID EventPipeEtwCallbackDotNETRuntimePrivate(
{
WRAPPER_NO_CONTRACT;

EtwCallbackCommon(DotNETRuntimePrivate, ControlCode, Level, MatchAnyKeyword, FilterData, true);
SessionChange change = SourceId == NULL ? EventPipeSessionDisable : EventPipeSessionEnable;

EtwCallbackCommon(DotNETRuntimePrivate, ControlCode, Level, MatchAnyKeyword, FilterData, change);
}


Expand Down Expand Up @@ -2704,7 +2731,7 @@ extern "C"
return;
}

EtwCallbackCommon(providerIndex, ControlCode, Level, MatchAnyKeyword, FilterData, false);
EtwCallbackCommon(providerIndex, ControlCode, Level, MatchAnyKeyword, FilterData, EtwSessionChangeUnknown);

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

}
}
#endif // FEATURE_NATIVEAOT

#endif // HOST_UNIX
#ifndef FEATURE_NATIVEAOT
#endif // !defined(HOST_UNIX)

/****************************************************************************/
/* This is called by the runtime when an exception is thrown */
Expand Down

0 comments on commit e3b3aaa

Please sign in to comment.