| 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. | 
|---|
| 4 |  | 
|---|
| 5 | #ifndef __GCEVENTSTATUS_H__ | 
|---|
| 6 | #define __GCEVENTSTATUS_H__ | 
|---|
| 7 |  | 
|---|
| 8 |  | 
|---|
| 9 | /* | 
|---|
| 10 | * gceventstatus.h - Eventing status for a standalone GC | 
|---|
| 11 | * | 
|---|
| 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. | 
|---|
| 15 | * | 
|---|
| 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. | 
|---|
| 19 | * | 
|---|
| 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. | 
|---|
| 25 | */ | 
|---|
| 26 |  | 
|---|
| 27 | #include "common.h" | 
|---|
| 28 | #include "gcenv.h" | 
|---|
| 29 | #include "gc.h" | 
|---|
| 30 | #include "gcevent_serializers.h" | 
|---|
| 31 |  | 
|---|
| 32 | // Uncomment this define to print out event state changes to standard error. | 
|---|
| 33 | // #define TRACE_GC_EVENT_STATE 1 | 
|---|
| 34 |  | 
|---|
| 35 | /* | 
|---|
| 36 | * GCEventProvider represents one of the two providers that the GC can | 
|---|
| 37 | * fire events from: the default and private providers. | 
|---|
| 38 | */ | 
|---|
| 39 | enum GCEventProvider | 
|---|
| 40 | { | 
|---|
| 41 | GCEventProvider_Default = 0, | 
|---|
| 42 | GCEventProvider_Private = 1 | 
|---|
| 43 | }; | 
|---|
| 44 |  | 
|---|
| 45 | /* | 
|---|
| 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 | 
|---|
| 48 | * to fire events. | 
|---|
| 49 | * | 
|---|
| 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 | 
|---|
| 53 | * provider. | 
|---|
| 54 | */ | 
|---|
| 55 | class GCEventStatus | 
|---|
| 56 | { | 
|---|
| 57 | private: | 
|---|
| 58 | /* | 
|---|
| 59 | * The enabled level for each provider. | 
|---|
| 60 | */ | 
|---|
| 61 | static Volatile<GCEventLevel> enabledLevels[2]; | 
|---|
| 62 |  | 
|---|
| 63 | /* | 
|---|
| 64 | * The bitmap of enabled keywords for each provider. | 
|---|
| 65 | */ | 
|---|
| 66 | static Volatile<GCEventKeyword> enabledKeywords[2]; | 
|---|
| 67 |  | 
|---|
| 68 | public: | 
|---|
| 69 | /* | 
|---|
| 70 | * IsEnabled queries whether or not the given level and keyword are | 
|---|
| 71 | * enabled on the given provider, returning true if they are. | 
|---|
| 72 | */ | 
|---|
| 73 | __forceinline static bool IsEnabled(GCEventProvider provider, GCEventKeyword keyword, GCEventLevel level) | 
|---|
| 74 | { | 
|---|
| 75 | assert(level >= GCEventLevel_None && level < GCEventLevel_Max); | 
|---|
| 76 |  | 
|---|
| 77 | size_t index = static_cast<size_t>(provider); | 
|---|
| 78 | return (enabledLevels[index].LoadWithoutBarrier() >= level) | 
|---|
| 79 | && (enabledKeywords[index].LoadWithoutBarrier() & keyword); | 
|---|
| 80 | } | 
|---|
| 81 |  | 
|---|
| 82 | /* | 
|---|
| 83 | * Set sets the eventing state (level and keyword bitmap) for a given | 
|---|
| 84 | * provider to the provided values. | 
|---|
| 85 | */ | 
|---|
| 86 | static void Set(GCEventProvider provider, GCEventKeyword keywords, GCEventLevel level) | 
|---|
| 87 | { | 
|---|
| 88 | assert((level >= GCEventLevel_None && level < GCEventLevel_Max) || level == GCEventLevel_LogAlways); | 
|---|
| 89 |  | 
|---|
| 90 | size_t index = static_cast<size_t>(provider); | 
|---|
| 91 |  | 
|---|
| 92 | enabledLevels[index] = level; | 
|---|
| 93 | enabledKeywords[index] = keywords; | 
|---|
| 94 |  | 
|---|
| 95 | #if TRACE_GC_EVENT_STATE | 
|---|
| 96 | fprintf(stderr, "event state change:\n"); | 
|---|
| 97 | DebugDumpState(provider); | 
|---|
| 98 | #endif // TRACE_GC_EVENT_STATE | 
|---|
| 99 | } | 
|---|
| 100 |  | 
|---|
| 101 | #if TRACE_GC_EVENT_STATE | 
|---|
| 102 | private: | 
|---|
| 103 | static void DebugDumpState(GCEventProvider provider) | 
|---|
| 104 | { | 
|---|
| 105 | size_t index = static_cast<size_t>(provider); | 
|---|
| 106 | GCEventLevel level = enabledLevels[index]; | 
|---|
| 107 | GCEventKeyword keyword = enabledKeywords[index]; | 
|---|
| 108 | if (provider == GCEventProvider_Default) | 
|---|
| 109 | { | 
|---|
| 110 | fprintf(stderr, "provider: default\n"); | 
|---|
| 111 | } | 
|---|
| 112 | else | 
|---|
| 113 | { | 
|---|
| 114 | fprintf(stderr, "provider: private\n"); | 
|---|
| 115 | } | 
|---|
| 116 |  | 
|---|
| 117 | switch (level) | 
|---|
| 118 | { | 
|---|
| 119 | case GCEventLevel_None: | 
|---|
| 120 | fprintf(stderr, "  level: None\n"); | 
|---|
| 121 | break; | 
|---|
| 122 | case GCEventLevel_Fatal: | 
|---|
| 123 | fprintf(stderr, "  level: Fatal\n"); | 
|---|
| 124 | break; | 
|---|
| 125 | case GCEventLevel_Error: | 
|---|
| 126 | fprintf(stderr, "  level: Error\n"); | 
|---|
| 127 | break; | 
|---|
| 128 | case GCEventLevel_Warning: | 
|---|
| 129 | fprintf(stderr, "  level: Warning\n"); | 
|---|
| 130 | break; | 
|---|
| 131 | case GCEventLevel_Information: | 
|---|
| 132 | fprintf(stderr, "  level: Information\n"); | 
|---|
| 133 | break; | 
|---|
| 134 | case GCEventLevel_Verbose: | 
|---|
| 135 | fprintf(stderr, "  level: Verbose\n"); | 
|---|
| 136 | break; | 
|---|
| 137 | case GCEventLevel_LogAlways: | 
|---|
| 138 | fprintf(stderr, "  level: LogAlways"); | 
|---|
| 139 | break; | 
|---|
| 140 | default: | 
|---|
| 141 | fprintf(stderr, "  level: %d?\n", level); | 
|---|
| 142 | break; | 
|---|
| 143 | } | 
|---|
| 144 |  | 
|---|
| 145 | fprintf(stderr, "  keywords: "); | 
|---|
| 146 | if (keyword & GCEventKeyword_GC) | 
|---|
| 147 | { | 
|---|
| 148 | fprintf(stderr, "GC "); | 
|---|
| 149 | } | 
|---|
| 150 |  | 
|---|
| 151 | if (keyword & GCEventKeyword_GCHandle) | 
|---|
| 152 | { | 
|---|
| 153 | fprintf(stderr, "GCHandle "); | 
|---|
| 154 | } | 
|---|
| 155 |  | 
|---|
| 156 | if (keyword & GCEventKeyword_GCHeapDump) | 
|---|
| 157 | { | 
|---|
| 158 | fprintf(stderr, "GCHeapDump "); | 
|---|
| 159 | } | 
|---|
| 160 |  | 
|---|
| 161 | if (keyword & GCEventKeyword_GCSampledObjectAllocationHigh) | 
|---|
| 162 | { | 
|---|
| 163 | fprintf(stderr, "GCSampledObjectAllocationHigh "); | 
|---|
| 164 | } | 
|---|
| 165 |  | 
|---|
| 166 | if (keyword & GCEventKeyword_GCHeapSurvivalAndMovement) | 
|---|
| 167 | { | 
|---|
| 168 | fprintf(stderr, "GCHeapSurvivalAndMovement "); | 
|---|
| 169 | } | 
|---|
| 170 |  | 
|---|
| 171 | if (keyword & GCEventKeyword_GCHeapCollect) | 
|---|
| 172 | { | 
|---|
| 173 | fprintf(stderr, "GCHeapCollect "); | 
|---|
| 174 | } | 
|---|
| 175 |  | 
|---|
| 176 | if (keyword & GCEventKeyword_GCHeapAndTypeNames) | 
|---|
| 177 | { | 
|---|
| 178 | fprintf(stderr, "GCHeapAndTypeNames "); | 
|---|
| 179 | } | 
|---|
| 180 |  | 
|---|
| 181 | if (keyword & GCEventKeyword_GCSampledObjectAllocationLow) | 
|---|
| 182 | { | 
|---|
| 183 | fprintf(stderr, "GCSampledObjectAllocationLow "); | 
|---|
| 184 | } | 
|---|
| 185 |  | 
|---|
| 186 | fprintf(stderr, "\n"); | 
|---|
| 187 | } | 
|---|
| 188 | #endif // TRACE_GC_EVENT_STATUS | 
|---|
| 189 |  | 
|---|
| 190 | // This class is a singleton and can't be instantiated. | 
|---|
| 191 | GCEventStatus() = delete; | 
|---|
| 192 | }; | 
|---|
| 193 |  | 
|---|
| 194 | /* | 
|---|
| 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. | 
|---|
| 198 | */ | 
|---|
| 199 | template<typename... EventArgument> | 
|---|
| 200 | void FireDynamicEvent(const char* name, EventArgument... arguments) | 
|---|
| 201 | { | 
|---|
| 202 | size_t size = gc_event::SerializedSize(arguments...); | 
|---|
| 203 | if (size > UINT32_MAX) | 
|---|
| 204 | { | 
|---|
| 205 | // ETW can't handle anything this big. | 
|---|
| 206 | // we shouldn't be firing events that big anyway. | 
|---|
| 207 | return; | 
|---|
| 208 | } | 
|---|
| 209 |  | 
|---|
| 210 | uint8_t* buf = new (nothrow) uint8_t[size]; | 
|---|
| 211 | if (!buf) | 
|---|
| 212 | { | 
|---|
| 213 | // best effort - if we're OOM, don't bother with the event. | 
|---|
| 214 | return; | 
|---|
| 215 | } | 
|---|
| 216 |  | 
|---|
| 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)); | 
|---|
| 223 | delete[] buf; | 
|---|
| 224 | }; | 
|---|
| 225 |  | 
|---|
| 226 | /* | 
|---|
| 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. | 
|---|
| 232 | * | 
|---|
| 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. | 
|---|
| 237 | */ | 
|---|
| 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) \ | 
|---|
| 243 | {                                                               \ | 
|---|
| 244 | if (GCEventEnabled##name())                                 \ | 
|---|
| 245 | {                                                           \ | 
|---|
| 246 | IGCToCLREventSink* sink = GCToEEInterface::EventSink(); \ | 
|---|
| 247 | assert(sink != nullptr);                                \ | 
|---|
| 248 | sink->Fire##name(arguments...);                         \ | 
|---|
| 249 | }                                                           \ | 
|---|
| 250 | } | 
|---|
| 251 |  | 
|---|
| 252 | #define DYNAMIC_EVENT(name, level, keyword, ...)                                                                   \ | 
|---|
| 253 | inline bool GCEventEnabled##name() { return GCEventStatus::IsEnabled(GCEventProvider_Default, keyword, level); } \ | 
|---|
| 254 | template<typename... EventActualArgument>                                                                        \ | 
|---|
| 255 | inline void GCEventFire##name(EventActualArgument... arguments) { FireDynamicEvent<__VA_ARGS__>(#name, arguments...); } | 
|---|
| 256 |  | 
|---|
| 257 | #include "gcevents.h" | 
|---|
| 258 |  | 
|---|
| 259 | #define EVENT_ENABLED(name) GCEventEnabled##name() | 
|---|
| 260 | #define FIRE_EVENT(name, ...) GCEventFire##name(__VA_ARGS__) | 
|---|
| 261 | #else | 
|---|
| 262 | #define EVENT_ENABLED(name) false | 
|---|
| 263 | #define FIRE_EVENT(name, ...) 0 | 
|---|
| 264 | #endif // FEATURE_EVENT_TRACE | 
|---|
| 265 |  | 
|---|
| 266 | #endif // __GCEVENTSTATUS_H__ | 
|---|
| 267 |  | 
|---|