1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 #ifndef __GCEVENTSTATUS_H__
6 #define __GCEVENTSTATUS_H__
10 * gceventstatus.h - Eventing status for a standalone GC
12 * In order for a local GC to determine what events are enabled
13 * in an efficient manner, the GC maintains some local state about
14 * keywords and levels that are enabled for each eventing provider.
16 * The GC fires events from two providers: the "main" provider
17 * and the "private" provider. This file tracks keyword and level
18 * information for each provider separately.
20 * It is the responsibility of the EE to inform the GC of changes
21 * to eventing state. This is accomplished by invoking the
22 * `IGCHeap::ControlEvents` and `IGCHeap::ControlPrivateEvents` callbacks
23 * on the EE's heap instance, which ultimately will enable and disable keywords
24 * and levels within this file.
30 #include "gcevent_serializers.h"
32 // Uncomment this define to print out event state changes to standard error.
33 // #define TRACE_GC_EVENT_STATE 1
36 * GCEventProvider represents one of the two providers that the GC can
37 * fire events from: the default and private providers.
41 GCEventProvider_Default = 0,
42 GCEventProvider_Private = 1
46 * GCEventStatus maintains all eventing state for the GC. It consists
47 * of a keyword bitmask and level for each provider that the GC can use
50 * A level and event pair are considered to be "enabled" on a given provider
51 * if the given level is less than or equal to the current enabled level
52 * and if the keyword is present in the enabled keyword bitmask for that
59 * The enabled level for each provider.
61 static Volatile<GCEventLevel> enabledLevels[2];
64 * The bitmap of enabled keywords for each provider.
66 static Volatile<GCEventKeyword> enabledKeywords[2];
70 * IsEnabled queries whether or not the given level and keyword are
71 * enabled on the given provider, returning true if they are.
73 __forceinline static bool IsEnabled(GCEventProvider provider, GCEventKeyword keyword, GCEventLevel level)
75 assert(level >= GCEventLevel_None && level < GCEventLevel_Max);
77 size_t index = static_cast<size_t>(provider);
78 return (enabledLevels[index].LoadWithoutBarrier() >= level)
79 && (enabledKeywords[index].LoadWithoutBarrier() & keyword);
83 * Set sets the eventing state (level and keyword bitmap) for a given
84 * provider to the provided values.
86 static void Set(GCEventProvider provider, GCEventKeyword keywords, GCEventLevel level)
88 assert((level >= GCEventLevel_None && level < GCEventLevel_Max) || level == GCEventLevel_LogAlways);
90 size_t index = static_cast<size_t>(provider);
92 enabledLevels[index] = level;
93 enabledKeywords[index] = keywords;
95 #if TRACE_GC_EVENT_STATE
96 fprintf(stderr, "event state change:\n");
97 DebugDumpState(provider);
98 #endif // TRACE_GC_EVENT_STATE
101 #if TRACE_GC_EVENT_STATE
103 static void DebugDumpState(GCEventProvider provider)
105 size_t index = static_cast<size_t>(provider);
106 GCEventLevel level = enabledLevels[index];
107 GCEventKeyword keyword = enabledKeywords[index];
108 if (provider == GCEventProvider_Default)
110 fprintf(stderr, "provider: default\n");
114 fprintf(stderr, "provider: private\n");
119 case GCEventLevel_None:
120 fprintf(stderr, " level: None\n");
122 case GCEventLevel_Fatal:
123 fprintf(stderr, " level: Fatal\n");
125 case GCEventLevel_Error:
126 fprintf(stderr, " level: Error\n");
128 case GCEventLevel_Warning:
129 fprintf(stderr, " level: Warning\n");
131 case GCEventLevel_Information:
132 fprintf(stderr, " level: Information\n");
134 case GCEventLevel_Verbose:
135 fprintf(stderr, " level: Verbose\n");
137 case GCEventLevel_LogAlways:
138 fprintf(stderr, " level: LogAlways");
141 fprintf(stderr, " level: %d?\n", level);
145 fprintf(stderr, " keywords: ");
146 if (keyword & GCEventKeyword_GC)
148 fprintf(stderr, "GC ");
151 if (keyword & GCEventKeyword_GCHandle)
153 fprintf(stderr, "GCHandle ");
156 if (keyword & GCEventKeyword_GCHeapDump)
158 fprintf(stderr, "GCHeapDump ");
161 if (keyword & GCEventKeyword_GCSampledObjectAllocationHigh)
163 fprintf(stderr, "GCSampledObjectAllocationHigh ");
166 if (keyword & GCEventKeyword_GCHeapSurvivalAndMovement)
168 fprintf(stderr, "GCHeapSurvivalAndMovement ");
171 if (keyword & GCEventKeyword_GCHeapCollect)
173 fprintf(stderr, "GCHeapCollect ");
176 if (keyword & GCEventKeyword_GCHeapAndTypeNames)
178 fprintf(stderr, "GCHeapAndTypeNames ");
181 if (keyword & GCEventKeyword_GCSampledObjectAllocationLow)
183 fprintf(stderr, "GCSampledObjectAllocationLow ");
186 fprintf(stderr, "\n");
188 #endif // TRACE_GC_EVENT_STATUS
190 // This class is a singleton and can't be instantiated.
191 GCEventStatus() = delete;
195 * FireDynamicEvent is a variadic function that fires a dynamic event with the
196 * given name and event payload. This function serializes the arguments into
197 * a binary payload that is then passed to IGCToCLREventSink::FireDynamicEvent.
199 template<typename... EventArgument>
200 void FireDynamicEvent(const char* name, EventArgument... arguments)
202 size_t size = gc_event::SerializedSize(arguments...);
203 if (size > UINT32_MAX)
205 // ETW can't handle anything this big.
206 // we shouldn't be firing events that big anyway.
210 uint8_t* buf = new (nothrow) uint8_t[size];
213 // best effort - if we're OOM, don't bother with the event.
217 memset(buf, 0, size);
218 uint8_t* cursor = buf;
219 gc_event::Serialize(&cursor, arguments...);
220 IGCToCLREventSink* sink = GCToEEInterface::EventSink();
221 assert(sink != nullptr);
222 sink->FireDynamicEvent(name, buf, static_cast<uint32_t>(size));
227 * In order to provide a consistent interface between known and dynamic events,
228 * two wrapper functions are generated for each known and dynamic event:
229 * GCEventEnabled##name() - Returns true if the event is enabled, false otherwise.
230 * GCEventFire##name(...) - Fires the event, with the event payload consisting of
231 * the arguments to the function.
233 * Because the schema of dynamic events comes from the DYNAMIC_EVENT xmacro, we use
234 * the arguments vector as the argument list to `FireDynamicEvent`, which will traverse
235 * the list of arguments and call `IGCToCLREventSink::FireDynamicEvent` with a serialized
236 * payload. Known events will delegate to IGCToCLREventSink::Fire##name.
238 #if FEATURE_EVENT_TRACE
239 #define KNOWN_EVENT(name, provider, level, keyword) \
240 inline bool GCEventEnabled##name() { return GCEventStatus::IsEnabled(provider, keyword, level); } \
241 template<typename... EventActualArgument> \
242 inline void GCEventFire##name(EventActualArgument... arguments) \
244 IGCToCLREventSink* sink = GCToEEInterface::EventSink(); \
245 assert(sink != nullptr); \
246 sink->Fire##name(arguments...); \
249 #define DYNAMIC_EVENT(name, level, keyword, ...) \
250 inline bool GCEventEnabled##name() { return GCEventStatus::IsEnabled(GCEventProvider_Default, keyword, level); } \
251 template<typename... EventActualArgument> \
252 inline void GCEventFire##name(EventActualArgument... arguments) { FireDynamicEvent<__VA_ARGS__>(#name, arguments...); }
254 #include "gcevents.h"
256 #define EVENT_ENABLED(name) GCEventEnabled##name()
257 #define FIRE_EVENT(name, ...) GCEventFire##name(__VA_ARGS__)
259 #define EVENT_ENABLED(name) false
260 #define FIRE_EVENT(name, ...) 0
261 #endif // FEATURE_EVENT_TRACE
263 #endif // __GCEVENTSTATUS_H__