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