Add some comments to SpanHelpers.Char IndexOf and LastIndexOf (#17447)
[platform/upstream/coreclr.git] / src / gc / gceventstatus.h
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       IGCToCLREventSink* sink = GCToEEInterface::EventSink();     \
245       assert(sink != nullptr);                                    \
246       sink->Fire##name(arguments...);                             \
247   }
248
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...); }
253
254 #include "gcevents.h"
255
256 #define EVENT_ENABLED(name) GCEventEnabled##name()
257 #define FIRE_EVENT(name, ...) GCEventFire##name(__VA_ARGS__)
258 #else
259 #define EVENT_ENABLED(name) false
260 #define FIRE_EVENT(name, ...) 0
261 #endif // FEATURE_EVENT_TRACE
262
263 #endif // __GCEVENTSTATUS_H__