xref: /aosp_15_r20/external/cronet/base/trace_event/trace_logging_minimal_win.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2020 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_TRACE_EVENT_TRACE_LOGGING_MINIMAL_WIN_H_
6 #define BASE_TRACE_EVENT_TRACE_LOGGING_MINIMAL_WIN_H_
7 
8 /*
9  * TraceLogging minimal dynamic provider
10  *
11  * TlmProvider is a simple class that implements an Event Tracing for Windows
12  * (ETW) provider that generates TraceLogging events with string fields. Unlike
13  * the Windows SDK's TraceLoggingProvider.h, this provider class supports
14  * runtime-variable settings for event name, level, keyword, and field name.
15  *
16  * Note that this approach is not recommended for general use. Support for
17  * runtime-variable settings is not normally needed, and it requires extra
18  * buffering as compared to the approach used by TraceLoggingProvider.h. It is
19  * needed in this case because we're trying to feed data from the existing call
20  * sites (which use a runtime-variable function-call syntax) into ETW. If this
21  * were new code, it would be better to update each call site to use a syntax
22  * compatible with compile-time event settings compatible with structured
23  * logging like TraceLoggingProvider.h.
24  */
25 
26 #include <windows.h>
27 
28 #include <evntprov.h>
29 #include <stdint.h>
30 
31 #include <cstdint>
32 // TODO([email protected]) Update headers and use defined constants instead
33 // of magic numbers after crbug.com/1089996 is resolved.
34 
35 #include "base/functional/callback.h"
36 
37 /*
38  * An instance of TlmProvider represents a logger through which data can be
39  *  sent to Event Tracing for Windows (ETW). This logger generates
40  * TraceLogging-encoded events (compatible with the events generated by the
41  * Windows SDK's TraceLoggingProvider.h header). In most cases, a developer
42  * would prefer using TraceLoggingProvider.h over TlmProvider
43  * (TraceLoggingProvider.h is more efficient and more full-featured), but
44  * TlmProvider allows for configuring the event parameters (event name,
45  * level, keyword, field names) at runtime (TraceLoggingProvider.h requires
46  * these to be set at compile time).
47  *
48  * Note that the Register/Unregister operations are relatively expensive, so
49  * the TlmProvider instance should be a long-lived variable (i.e. global
50  * variable, static variable, or field of a long-lived object), not a local
51  * variable andnot a field of a short-lived object.
52  *
53  * Note that provider name and provider GUID are a tightly-bound pair, i.e.
54  * they should each uniquely map to each other. Once a provider name and
55  * provider GUID have been used together, no other GUID should be used with
56  * that name and no other name should be used with that GUID. Normally this
57  * goal is achieved by using a hashing algorithm to generate the GUID from
58  * a hash of the name.
59  *
60  * Note that each event should use a non-zero level and a non-zero keyword.
61  * Predefined level constants are defined in <evntrace.h>: 0=Always,
62  * 1=Critical, 2=Error, 3=Warning, 4=Info, 5=Verbose (other level values can
63  * be used but are not well-defined and are not generally useful). A keyword
64  * is a bitmask of "category" bits, where each bit indicates whether or not
65  * the event belongs in a particular category of event. The low 48 bits are
66  * user-defined and the upper 16 bits are Microsoft-defined (in <winmeta.h>).
67  *
68  * General usage:
69  *
70  *     // During component initialization (main or DllMain), call Register().
71  *     // Note that there is an overload of the TlmProvider constructor that
72  *     // calls Register(), but it's often convenient to do this manually
73  *     // (i.e. to control the timing of the call to Register).
74  *     my_provider.Register(
75  *         "MyCompany.MyComponentName",
76  *         MyComponentGuid);
77  *
78  *     // To log an event with minimal code:
79  *     my_provider.WriteEvent("MyEventName",
80  *         TlmEventDescriptor(
81  *             TRACE_LEVEL_VERBOSE, // Level defined in <evntrace.h>
82  *             0x20),               // Keyword bits are user-defined.
83  *         // Value must not be null for the string fields.
84  *         TlmUtf8StringField("MyUtf8Field", GetValue1()),
85  *         TlmMbcsStringField("MyAsciiField", GetValue2()));
86  *
87  *     // Note that the minimal-code example has a bit of overhead, as it
88  *     // will make the calls to GetValue1(), GetValue2(), and WriteEvent()
89  *     // even if nobody is listening to the event. WriteEvent() will return
90        // immediately if nobody is listening, but there is still some
91  *     // overhead. To minimize the overhead when nobody is listening,
92  *     // add an extra IF condition:
93  *     static const auto MyEventDescriptor = TlmEventDescriptor(
94  *         TRACE_LEVEL_VERBOSE, // Level defined in <evntrace.h>
95  *         0x20);               // Keyword bits are user-defined.
96  *     if (my_provider.IsEnabled(MyEventDescriptor))
97  *     {
98  *         // The IF condition is primarily to prevent unnecessary
99  *         // calls to GetValue1() and GetValue2().
100  *         my_provider.WriteEvent("MyEventName",
101  *             MyEventDescriptor,
102  *             // Value must not be null for the string fields.
103  *             TlmUtf8StringField("MyUtf8Field", GetValue1()),
104  *             TlmMbcsStringField("MyAsciiField", GetValue2()));
105  *     }
106  *
107  *     // During component shutdown (main or DllMain), call Unregister().
108  *     // Note that the TlmProvider destructor will also call
109  *     // Unregister(), butit's often convenient to do this manually
110  *     // (i.e. to control the timingof the call to Unregister).
111  *     my_provider.Unregister();
112  */
113 
114 #include "base/base_export.h"
115 #include "base/functional/callback.h"
116 #include "base/memory/raw_ptr.h"
117 
118 class BASE_EXPORT TlmProvider {
119  public:
120   enum class EventControlCode {
121     kDisableProvider = 0,
122     kEnableProvider = 1,
123     kCaptureState = 2,
124     kHighest = kCaptureState
125   };
126 
127   // Initialize a provider in the unregistered state.
128   // Note that WriteEvent and Unregister operations on an unregistered
129   // provider are safe no-ops.
130   TlmProvider() noexcept;
131 
132   // Initializes a provider and attempts to register it.
133   // If there is an error, provider will be left unregistered.
134   // Note that WriteEvent and Unregister operations on an unregistered
135   // provider are safe no-ops.
136   TlmProvider(
137       const char* provider_name,
138       const GUID& provider_guid,
139       base::RepeatingCallback<void(EventControlCode)> on_updated) noexcept;
140 
141   // If provider is registered, unregisters provider.
142   ~TlmProvider();
143 
144   // Disable copy operations.
145   TlmProvider(const TlmProvider&) = delete;
146   TlmProvider& operator=(const TlmProvider&) = delete;
147 
148   // Unregisters this provider.
149   // Calling Unregister on an unregistered provider is a safe no-op.
150   // Not thread safe - caller must ensure serialization between calls to
151   // Register() and calls to Unregister().
152   void Unregister() noexcept;
153 
154   // Registers this provider. Returns Win32 error code or 0 for success.
155   // Error code is primarily for debugging and should generally be ignored
156   // in production (failure to register means Unregister and WriteEvent are
157   // safe no-ops.)
158   // Calling Register on an already-registered provider is a fatal error.
159   // Not thread safe - caller must ensure serialization between calls to
160   // Register() and calls to Unregister().
161   ULONG Register(
162       const char* provider_name,
163       const GUID& provider_guid,
164       base::RepeatingCallback<void(EventControlCode)> on_updated) noexcept;
165 
166   // Returns true if any active trace listeners are interested in any events
167   // from this provider.
168   // Equivalent to IsEnabled(0, 0).
169   bool IsEnabled() const noexcept;
170 
171   // Returns true if any active trace listeners are interested in events
172   // from this provider with the specified level.
173   // Equivalent to IsEnabled(level, 0).
174   bool IsEnabled(uint8_t level) const noexcept;
175 
176   // Returns true if any active trace listeners are interested in events
177   // from this provider with the specified level and keyword.
178   bool IsEnabled(uint8_t level, uint64_t keyword) const noexcept;
179 
180   // Returns true if any active trace listeners are interested in events
181   // from this provider with the specified level and keyword.
182   // Equivalent to IsEnabled(event_descriptor.level, event_descriptor.keyword).
183   bool IsEnabled(const EVENT_DESCRIPTOR& event_descriptor) const noexcept;
184 
keyword_any()185   uint64_t keyword_any() const { return keyword_any_; }
186 
187   // If any active trace listeners are interested in events from this provider
188   // with the specified level and keyword, packs the data into an event and
189   // sends it to ETW. Returns Win32 error code or 0 for success.
190   template <class... FieldTys>
WriteEvent(std::string_view event_name,const EVENT_DESCRIPTOR & event_descriptor,const FieldTys &...event_fields)191   ULONG WriteEvent(std::string_view event_name,
192                    const EVENT_DESCRIPTOR& event_descriptor,
193                    const FieldTys&... event_fields) const noexcept {
194     if (!IsEnabled(event_descriptor)) {
195       // If nobody is listening, report success.
196       return 0;
197     }
198     // Pack the event metadata.
199     char metadata[kMaxEventMetadataSize];
200     uint16_t metadata_index;
201     metadata_index = EventBegin(metadata, event_name);
202     {  // scope for dummy array (simulates a C++17 comma-fold expression)
203       char dummy[sizeof...(FieldTys) == 0 ? 1 : sizeof...(FieldTys)] = {
204           EventAddField(metadata, &metadata_index, event_fields.in_type_,
205                         event_fields.out_type_, event_fields.Name())...};
206       DCHECK(dummy);
207     }
208 
209     // Pack the event data.
210     constexpr uint8_t kDescriptorsCount =
211         2 + DataDescCountSum<FieldTys...>::value;
212     EVENT_DATA_DESCRIPTOR descriptors[kDescriptorsCount];
213     uint8_t descriptors_index = 2;
214     {  // scope for dummy array (simulates a C++17 comma-fold expression)
215       char dummy[sizeof...(FieldTys) == 0 ? 1 : sizeof...(FieldTys)] = {
216           EventDescriptorFill(descriptors, &descriptors_index,
217                               event_fields)...};
218       DCHECK(dummy);
219     }
220 
221     // Finalize event and call EventWrite.
222     return EventEnd(metadata, metadata_index, descriptors, descriptors_index,
223                     event_descriptor);
224   }
225 
226  private:
227   // Size of the buffer used for provider metadata (field within the
228   // TlmProvider object). Provider metadata consists of the nul-terminated
229   // provider name plus a few sizes and flags, so this buffer needs to be
230   // just a few bytes larger than the largest expected provider name.
231   static constexpr uint16_t kMaxProviderMetadataSize = 128;
232 
233   // Size of the buffer used for event metadata (stack-allocated in the
234   // WriteEvent method). Event metadata consists of nul-terminated event
235   // name, nul-terminated field names, field types (1 or 2 bytes per field),
236   // and a few bytes for sizes and flags.
237   static constexpr uint16_t kMaxEventMetadataSize = 256;
238 
239   template <class... FieldTys>
240   struct DataDescCountSum;  // undefined
241 
242   template <>
243   struct DataDescCountSum<> {
244     static constexpr uint8_t value = 0;
245   };
246 
247   template <class FieldTy1, class... FieldTyRest>
248   struct DataDescCountSum<FieldTy1, FieldTyRest...> {
249     static constexpr uint8_t value =
250         FieldTy1::data_desc_count_ + DataDescCountSum<FieldTyRest...>::value;
251   };
252 
253   template <class FieldTy>
254   static char EventDescriptorFill(EVENT_DATA_DESCRIPTOR* descriptors,
255                                   uint8_t* pdescriptors_index,
256                                   const FieldTy& event_field) noexcept {
257     event_field.FillEventDescriptor(&descriptors[*pdescriptors_index]);
258     *pdescriptors_index += FieldTy::data_desc_count_;
259     return 0;
260   }
261 
262   // This is called from the OS, so use the required call type.
263   static void __stdcall StaticEnableCallback(
264       const GUID* source_id,
265       ULONG is_enabled,
266       UCHAR level,
267       ULONGLONG match_any_keyword,
268       ULONGLONG match_all_keyword,
269       PEVENT_FILTER_DESCRIPTOR FilterData,
270       PVOID callback_context);
271 
272   // Returns initial value of metadata_index.
273   uint16_t EventBegin(char* metadata,
274                       std::string_view event_name) const noexcept;
275 
276   char EventAddField(char* metadata,
277                      uint16_t* metadata_index,
278                      uint8_t in_type,
279                      uint8_t out_type,
280                      const char* field_name) const noexcept;
281 
282   // Returns Win32 error code, or 0 for success.
283   ULONG EventEnd(char* metadata,
284                  uint16_t metadata_index,
285                  EVENT_DATA_DESCRIPTOR* descriptors,
286                  uint32_t descriptors_index,
287                  const EVENT_DESCRIPTOR& event_descriptor) const noexcept;
288 
289   bool KeywordEnabled(uint64_t keyword) const noexcept;
290 
291   uint16_t AppendNameToMetadata(char* metadata,
292                                 uint16_t metadata_size,
293                                 uint16_t metadata_index,
294                                 std::string_view name) const noexcept;
295 
296   uint32_t level_plus1_ = 0;
297   uint16_t provider_metadata_size_ = 0;
298   uint64_t keyword_any_ = 0;
299   uint64_t keyword_all_ = 0;
300   uint64_t reg_handle_ = 0;
301   base::RepeatingCallback<void(EventControlCode)> on_updated_callback_;
302   char provider_metadata_[kMaxProviderMetadataSize] = {};
303 };
304 
305 // Base class for field types.
306 template <uint8_t data_desc_count,
307           uint8_t in_type,
308           uint8_t out_type = 0>  // Default out_type is TlgOutNULL
309 class TlmFieldBase {
310  public:
311   constexpr const char* Name() const noexcept { return name_; }
312 
313  protected:
314   explicit constexpr TlmFieldBase(const char* name) noexcept : name_(name) {}
315 
316  private:
317   friend class TlmProvider;
318 
319   static constexpr uint8_t data_desc_count_ = data_desc_count;
320   static constexpr uint8_t in_type_ = in_type;
321   static constexpr uint8_t out_type_ = out_type;
322 
323   const char* name_;
324 };
325 
326 // Class that represents an event field containing nul-terminated MBCS data.
327 class TlmMbcsStringField
328     : public TlmFieldBase<1, 2>  // 1 data descriptor, Type = TlgInANSISTRING
329 {
330  public:
331   // name is a utf-8 nul-terminated string.
332   // value is MBCS nul-terminated string (assumed to be in system's default code
333   // page).
334   TlmMbcsStringField(const char* name, const char* value) noexcept;
335 
336   const char* Value() const noexcept;
337 
338   void FillEventDescriptor(EVENT_DATA_DESCRIPTOR* descriptors) const noexcept;
339 
340  private:
341   const char* value_;
342 };
343 
344 // Class that represents an event field containing nul-terminated UTF-8 data.
345 class TlmUtf8StringField
346     : public TlmFieldBase<1, 2, 35>  // 1 data descriptor, Type =
347                                      // TlgInANSISTRING + TlgOutUTF8
348 {
349  public:
350   // name and value are utf-8 nul-terminated strings.
351   TlmUtf8StringField(const char* name, const char* value) noexcept;
352 
353   const char* Value() const noexcept;
354 
355   void FillEventDescriptor(EVENT_DATA_DESCRIPTOR* descriptors) const noexcept;
356 
357  private:
358   const char* value_;
359 };
360 
361 // Class that represents an event field containing a 64 bit signed integer.
362 class TlmInt64Field
363     : public TlmFieldBase<1, 9>  // 1 data descriptor, Type = _TlgInINT64
364 {
365  public:
366   // name is a utf-8 nul-terminated string.
367   // value is 64 bit signed integer
368   TlmInt64Field(const char* name, const int64_t value) noexcept;
369   int64_t Value() const noexcept;
370   void FillEventDescriptor(EVENT_DATA_DESCRIPTOR* descriptors) const noexcept;
371 
372  private:
373   const int64_t value_;
374 };
375 
376 class TlmUInt64Field
377     : public TlmFieldBase<1, 10>  // 1 data descriptor, Type = _TlgInUINT64
378 {
379  public:
380   // name is a utf-8 nul-terminated string.
381   // value is 64 bit signed integer
382   TlmUInt64Field(const char* name, const uint64_t value) noexcept;
383   uint64_t Value() const noexcept;
384   void FillEventDescriptor(EVENT_DATA_DESCRIPTOR* descriptors) const noexcept;
385 
386  private:
387   const uint64_t value_;
388 };
389 
390 // Helper for creating event descriptors for use with WriteEvent.
391 constexpr EVENT_DESCRIPTOR TlmEventDescriptor(uint8_t level,
392                                               uint64_t keyword) noexcept {
393   return {// Id
394           // TraceLogging generally uses the event's Name instead of Id+Version,
395           // so Id is normally set to 0 for TraceLogging events.
396           0,
397 
398           // Version
399           // TraceLogging generally uses the event's Name instead of Id+Version,
400           // so Version is normally set to 0 for TraceLogging events.
401           0,
402 
403           // Channel (WINEVENT_CHANNEL_*)
404           // TraceLogging-based events normally use channel 11.
405           11,  // = WINEVENT_CHANNEL_TRACELOGGING
406 
407           // Level (WINEVENT_LEVEL_*)
408           // 0=always, 1=fatal, 2=error, 3=warning, 4=info, 5=verbose.
409           // Levels higher than 5 are for user-defined debug levels.
410           level,
411 
412           // Opcode (WINEVENT_OPCODE_*)
413           // Set Opcode for special semantics such as starting/ending an
414           // activity.
415           0,  // = WINEVENT_OPCODE_INFO
416 
417           // Task
418           // Set Task for user-defined semantics.
419           0,  // = WINEVENT_TASK_NONE
420 
421           // Keyword
422           // A keyword is a 64-bit value used for filtering events. Each bit of
423           // the keyword indicates whether the event belongs to a particular
424           // category of events. The top 16 bits of keyword have
425           // Microsoft-defined semantics and should be set to 0. The low 48 bits
426           // of keyword have user-defined semantics. All events should use a
427           // nonzero keyword to support effective event filtering (events with
428           // keyword set to 0 always pass keyword filtering).
429           keyword};
430 }
431 
432 #endif  // BASE_TRACE_EVENT_TRACE_LOGGING_MINIMAL_WIN_H_
433