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