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