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