trace_logging_minimal_win.h 16 KB

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