• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 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 #include "base/trace_event/trace_event_etw_export_win.h"
6 
7 #include <evntrace.h>
8 #include <guiddef.h>
9 #include <stddef.h>
10 #include <stdlib.h>
11 #include <windows.h>
12 #include <utility>
13 
14 #include "base/at_exit.h"
15 #include "base/check_op.h"
16 #include "base/command_line.h"
17 #include "base/logging.h"
18 #include "base/memory/singleton.h"
19 #include "base/no_destructor.h"
20 #include "base/strings/string_tokenizer.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/threading/platform_thread.h"
24 #include "base/trace_event/trace_event.h"
25 #include "base/trace_event/trace_event_impl.h"
26 #include "base/trace_event/trace_logging_minimal_win.h"
27 
28 namespace {
29 
30 // |kFilteredEventGroupNames| contains the event categories that can be
31 // exported individually. These categories can be enabled by passing the correct
32 // keyword when starting the trace. A keyword is a 64-bit flag and we attribute
33 // one bit per category. We can therefore enable a particular category by
34 // setting its corresponding bit in the keyword. For events that are not present
35 // in |kFilteredEventGroupNames|, we have two bits that control their
36 // behaviour. When bit 46 is enabled, any event that is not disabled by default
37 // (ie. doesn't start with disabled-by-default-) will be exported. Likewise,
38 // when bit 47 is enabled, any event that is disabled by default will be
39 // exported.
40 //
41 // Examples of passing keywords to the provider using xperf:
42 // # This exports "benchmark" and "cc" events
43 // xperf -start chrome -on Chrome:0x9
44 //
45 // # This exports "gpu", "netlog" and all other events that are not disabled by
46 // # default
47 // xperf -start chrome -on Chrome:0x4000000000A0
48 //
49 // More info about starting a trace and keyword can be obtained by using the
50 // help section of xperf (xperf -help start). Note that xperf documentation
51 // refers to keywords as flags and there are two ways to enable them, using
52 // group names or the hex representation. We only support the latter. Also, we
53 // ignore the level.
54 //
55 // To avoid continually having to bump MSEdge values to next higher bits, we
56 // are putting MSEdge values at the high end of the bit range and will grow
57 // 'down' to lower bits for future MSEdge entries.
58 //
59 // As the writing of this comment, we have 4 values:
60 //    "navigation",                                       // 0x40000000000
61 //    "ServiceWorker",                                    // 0x80000000000
62 //    "edge_webview",                                     // 0x100000000000
63 //    "diagnostic_event",                                 // 0x200000000000
64 //
65 // This means the next value added should be:
66 //    "the_next_value",                                   // 0x20000000000
67 //    "navigation",                                       // 0x40000000000
68 //    "ServiceWorker",                                    // 0x80000000000
69 //    "edge_webview",                                     // 0x100000000000
70 //    "diagnostic_event",                                 // 0x200000000000
71 //
72 // The addition of the "unused_bit_nn" entries keeps the existing code execution
73 // routines working (ex. TraceEventETWExport::UpdateEnabledCategories()) and
74 // enables others to see which bits are available.
75 //
76 // Example: For some new category group...
77 //   "latency",                                          // 0x8000
78 //   "blink.user_timing",                                // 0x10000
79 //   "unused_bit_18",                                    // 0x20000
80 //   "unused_bit_19",                                    // 0x40000
81 //   "unused_bit_20",                                    // 0x80000
82 //    ...
83 // becomes:
84 //   "latency",                                          // 0x8000
85 //   "blink.user_timing",                                // 0x10000
86 //   "new_upstream_value",                               // 0x20000
87 //   "unused_bit_19",                                    // 0x40000
88 //   "unused_bit_20",                                    // 0x80000
89 //
90 // The high 16 bits of the keyword have special semantics and should not be
91 // set for enabling individual categories as they are reserved by winmeta.xml.
92 // TODO(crbug.com/1497783): Move this to
93 // components/tracing/common/etw_export_win.cc once no longer used by
94 // TraceEventETWExport.
95 const char* const kFilteredEventGroupNames[] = {
96     "benchmark",                             // 0x1
97     "blink",                                 // 0x2
98     "browser",                               // 0x4
99     "cc",                                    // 0x8
100     "evdev",                                 // 0x10
101     "gpu",                                   // 0x20
102     "input",                                 // 0x40
103     "netlog",                                // 0x80
104     "sequence_manager",                      // 0x100
105     "toplevel",                              // 0x200
106     "v8",                                    // 0x400
107     "disabled-by-default-cc.debug",          // 0x800
108     "disabled-by-default-cc.debug.picture",  // 0x1000
109     "disabled-by-default-toplevel.flow",     // 0x2000
110     "startup",                               // 0x4000
111     "latency",                               // 0x8000
112     "blink.user_timing",                     // 0x10000
113     "media",                                 // 0x20000
114     "loading",                               // 0x40000
115     "base",                                  // 0x80000
116     "unused_bit_20",                         // 0x100000
117     "unused_bit_21",                         // 0x200000
118     "unused_bit_22",                         // 0x400000
119     "unused_bit_23",                         // 0x800000
120     "unused_bit_24",                         // 0x1000000
121     "unused_bit_25",                         // 0x2000000
122     "unused_bit_26",                         // 0x4000000
123     "unused_bit_27",                         // 0x8000000
124     "unused_bit_28",                         // 0x10000000
125     "unused_bit_29",                         // 0x20000000
126     "unused_bit_30",                         // 0x40000000
127     "unused_bit_31",                         // 0x80000000
128     "unused_bit_32",                         // 0x100000000
129     "unused_bit_33",                         // 0x200000000
130     "unused_bit_34",                         // 0x400000000
131     "unused_bit_35",                         // 0x800000000
132     "unused_bit_36",                         // 0x1000000000
133     "unused_bit_37",                         // 0x2000000000
134     "unused_bit_38",                         // 0x4000000000
135     "unused_bit_39",                         // 0x8000000000
136     "unused_bit_40",                         // 0x10000000000
137     "unused_bit_41",                         // 0x20000000000
138     "navigation",                            // 0x40000000000
139     "ServiceWorker",                         // 0x80000000000
140     "edge_webview",                          // 0x100000000000
141     "diagnostic_event",                      // 0x200000000000
142     "__OTHER_EVENTS",                        // 0x400000000000 See below
143     "__DISABLED_OTHER_EVENTS",               // 0x800000000000 See below
144 };
145 
146 // These must be kept as the last two entries in the above array.
147 constexpr uint8_t kOtherEventsGroupNameIndex = 46;
148 constexpr uint8_t kDisabledOtherEventsGroupNameIndex = 47;
149 constexpr uint64_t kCategoryKeywordMask = ~0xFFFF000000000000;
150 
151 // Max number of available keyword bits.
152 constexpr size_t kMaxNumberOfGroupNames = 48;
153 
154 }  // namespace
155 
156 namespace base {
157 namespace trace_event {
158 
TraceEventETWExport()159 TraceEventETWExport::TraceEventETWExport() {
160   // Construct the ETW provider. If construction fails then the event logging
161   // calls will fail. We're passing a callback function as part of registration.
162   // This allows us to detect changes to enable/disable/keyword changes.
163   etw_provider_ = std::make_unique<TlmProvider>(
164       "Google.Chrome", Chrome_GUID,
165       base::BindRepeating(&TraceEventETWExport::OnETWEnableUpdate,
166                           base::Unretained(this)));
167   is_registration_complete_ = true;
168 
169   // Make sure to initialize the map with all the group names. Subsequent
170   // modifications will be made by the background thread and only affect the
171   // values of the keys (no key addition/deletion). Therefore, the map does not
172   // require a lock for access.
173   // Also set up the map from category name to keyword.
174   for (size_t i = 0; i < std::size(kFilteredEventGroupNames); i++) {
175     categories_status_[kFilteredEventGroupNames[i]] = false;
176   }
177   // Make sure we stay at 48 entries, the maximum number of bits available
178   // for keyword use.
179   static_assert(std::size(kFilteredEventGroupNames) <= kMaxNumberOfGroupNames,
180                 "Exceeded max ETW keyword bits");
181 }
182 
~TraceEventETWExport()183 TraceEventETWExport::~TraceEventETWExport() {
184   is_registration_complete_ = false;
185 }
186 
187 // static
EnableETWExport()188 void TraceEventETWExport::EnableETWExport() {
189   auto* instance = GetInstance();
190   if (instance) {
191     // Sync the enabled categories with ETW by calling UpdateEnabledCategories()
192     // that checks the keyword. We'll stay in sync via the EtwEnableCallback
193     // we register in TraceEventETWExport's constructor.
194     instance->UpdateEnabledCategories();
195   }
196 }
197 
198 // static
AddEvent(char phase,const unsigned char * category_group_enabled,const char * name,unsigned long long id,TimeTicks timestamp,const TraceArguments * args)199 void TraceEventETWExport::AddEvent(char phase,
200                                    const unsigned char* category_group_enabled,
201                                    const char* name,
202                                    unsigned long long id,
203                                    TimeTicks timestamp,
204                                    const TraceArguments* args) {
205   // We bail early in case exporting is disabled or no consumer is listening.
206   auto* instance = GetInstance();
207   if (!instance) {
208     return;
209   }
210   uint64_t keyword =
211       instance->CategoryStateToETWKeyword(category_group_enabled);
212   if (!instance->etw_provider_->IsEnabled(TRACE_LEVEL_NONE, keyword)) {
213     return;
214   }
215 
216   const char* phase_string = nullptr;
217 
218   // Space to store the phase identifier and null-terminator, when needed.
219   char phase_buffer[2];
220   switch (phase) {
221     case TRACE_EVENT_PHASE_BEGIN:
222       phase_string = "Begin";
223       break;
224     case TRACE_EVENT_PHASE_END:
225       phase_string = "End";
226       break;
227     case TRACE_EVENT_PHASE_COMPLETE:
228       phase_string = "Complete";
229       break;
230     case TRACE_EVENT_PHASE_INSTANT:
231       phase_string = "Instant";
232       break;
233     case TRACE_EVENT_PHASE_ASYNC_BEGIN:
234       phase_string = "Async Begin";
235       break;
236     case TRACE_EVENT_PHASE_ASYNC_STEP_INTO:
237       phase_string = "Async Step Into";
238       break;
239     case TRACE_EVENT_PHASE_ASYNC_STEP_PAST:
240       phase_string = "Async Step Past";
241       break;
242     case TRACE_EVENT_PHASE_ASYNC_END:
243       phase_string = "Async End";
244       break;
245     case TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN:
246       phase_string = "Nestable Async Begin";
247       break;
248     case TRACE_EVENT_PHASE_NESTABLE_ASYNC_END:
249       phase_string = "Nestable Async End";
250       break;
251     case TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT:
252       phase_string = "Nestable Async Instant";
253       break;
254     case TRACE_EVENT_PHASE_FLOW_BEGIN:
255       phase_string = "Phase Flow Begin";
256       break;
257     case TRACE_EVENT_PHASE_FLOW_STEP:
258       phase_string = "Phase Flow Step";
259       break;
260     case TRACE_EVENT_PHASE_FLOW_END:
261       phase_string = "Phase Flow End";
262       break;
263     case TRACE_EVENT_PHASE_METADATA:
264       phase_string = "Phase Metadata";
265       break;
266     case TRACE_EVENT_PHASE_COUNTER:
267       phase_string = "Phase Counter";
268       break;
269     case TRACE_EVENT_PHASE_SAMPLE:
270       phase_string = "Phase Sample";
271       break;
272     case TRACE_EVENT_PHASE_CREATE_OBJECT:
273       phase_string = "Phase Create Object";
274       break;
275     case TRACE_EVENT_PHASE_SNAPSHOT_OBJECT:
276       phase_string = "Phase Snapshot Object";
277       break;
278     case TRACE_EVENT_PHASE_DELETE_OBJECT:
279       phase_string = "Phase Delete Object";
280       break;
281     default:
282       phase_buffer[0] = phase;
283       phase_buffer[1] = 0;
284       phase_string = phase_buffer;
285       break;
286   }
287 
288   std::string arg_values_string[3];
289   size_t num_args = args ? args->size() : 0;
290   for (size_t i = 0; i < num_args; i++) {
291     const auto type = args->types()[i];
292     if (type == TRACE_VALUE_TYPE_CONVERTABLE ||
293         type == TRACE_VALUE_TYPE_PROTO) {
294       // For convertable types, temporarily do nothing here. This function
295       // consumes 1/3 to 1/2 of *total* process CPU time when ETW tracing, and
296       // many of the strings created exceed WPA's 4094 byte limit and are shown
297       // as "Unable to parse data". See crbug.com/488257.
298       //
299       // For protobuf-based values, there is no string serialization so skip
300       // those as well.
301     } else {
302       args->values()[i].AppendAsString(type, arg_values_string + i);
303     }
304   }
305 
306   int64_t timestamp_ms = (timestamp - TimeTicks()).InMilliseconds();
307   // Log the event and include the info needed to decode it via TraceLogging
308   if (num_args == 0) {
309     instance->etw_provider_->WriteEvent(
310         name, TlmEventDescriptor(0, keyword),
311         TlmMbcsStringField("Phase", phase_string),
312         TlmInt64Field("Timestamp", timestamp_ms));
313   } else if (num_args == 1) {
314     instance->etw_provider_->WriteEvent(
315         name, TlmEventDescriptor(0, keyword),
316         TlmMbcsStringField("Phase", phase_string),
317         TlmInt64Field("Timestamp", timestamp_ms),
318         TlmMbcsStringField((args->names()[0]), (arg_values_string[0].c_str())));
319   } else if (num_args == 2) {
320     instance->etw_provider_->WriteEvent(
321         name, TlmEventDescriptor(0, keyword),
322         TlmMbcsStringField("Phase", phase_string),
323         TlmInt64Field("Timestamp", timestamp_ms),
324         TlmMbcsStringField((args->names()[0]), (arg_values_string[0].c_str())),
325         TlmMbcsStringField((args->names()[1]), (arg_values_string[1].c_str())));
326   } else {
327     NOTREACHED();
328   }
329 }
330 
331 // static
AddCompleteEndEvent(const unsigned char * category_group_enabled,const char * name)332 void TraceEventETWExport::AddCompleteEndEvent(
333     const unsigned char* category_group_enabled,
334     const char* name) {
335   auto* instance = GetInstance();
336   if (!instance) {
337     return;
338   }
339   uint64_t keyword =
340       instance->CategoryStateToETWKeyword(category_group_enabled);
341   if (!instance->etw_provider_->IsEnabled(TRACE_LEVEL_NONE, keyword)) {
342     return;
343   }
344 
345   // Log the event and include the info needed to decode it via TraceLogging
346   instance->etw_provider_->WriteEvent(
347       name, TlmEventDescriptor(0, keyword),
348       TlmMbcsStringField("Phase", "Complete End"));
349 }
350 
351 // static
IsCategoryGroupEnabled(StringPiece category_group_name)352 bool TraceEventETWExport::IsCategoryGroupEnabled(
353     StringPiece category_group_name) {
354   DCHECK(!category_group_name.empty());
355 
356   auto* instance = GetInstanceIfExists();
357   if (instance == nullptr)
358     return false;
359 
360   if (!instance->etw_provider_->IsEnabled())
361     return false;
362 
363   CStringTokenizer category_group_tokens(category_group_name.begin(),
364                                          category_group_name.end(), ",");
365   while (category_group_tokens.GetNext()) {
366     StringPiece category_group_token = category_group_tokens.token_piece();
367     if (instance->IsCategoryEnabled(category_group_token)) {
368       return true;
369     }
370   }
371   return false;
372 }
373 
UpdateEnabledCategories()374 bool TraceEventETWExport::UpdateEnabledCategories() {
375   if (etw_match_any_keyword_ ==
376       (etw_provider_->keyword_any() & kCategoryKeywordMask)) {
377     return false;
378   }
379 
380   // If keyword_any() has changed, update each category. The global
381   // context is set by UIforETW (or other ETW trace recording tools)
382   // using the ETW infrastructure. When the global context changes the
383   // callback will be called to set the updated keyword bits in each
384   // process that has registered their ETW provider.
385   etw_match_any_keyword_ = etw_provider_->keyword_any() & kCategoryKeywordMask;
386   for (size_t i = 0; i < std::size(kFilteredEventGroupNames); i++) {
387     if (etw_match_any_keyword_ & (1ULL << i)) {
388       categories_status_[kFilteredEventGroupNames[i]] = true;
389     } else {
390       categories_status_[kFilteredEventGroupNames[i]] = false;
391     }
392   }
393 
394   // Update the categories in TraceLog.
395   TraceLog::GetInstance()->UpdateETWCategoryGroupEnabledFlags();
396 
397   return true;
398 }
399 
IsCategoryEnabled(StringPiece category_name) const400 bool TraceEventETWExport::IsCategoryEnabled(StringPiece category_name) const {
401   // Try to find the category and return its status if found
402   auto it = categories_status_.find(category_name);
403   if (it != categories_status_.end())
404     return it->second;
405 
406   // Otherwise return the corresponding default status by first checking if the
407   // category is disabled by default.
408   if (StartsWith(category_name, "disabled-by-default")) {
409     DCHECK(categories_status_.find(
410                kFilteredEventGroupNames[kDisabledOtherEventsGroupNameIndex]) !=
411            categories_status_.end());
412     return categories_status_
413         .find(kFilteredEventGroupNames[kDisabledOtherEventsGroupNameIndex])
414         ->second;
415   } else {
416     DCHECK(categories_status_.find(
417                kFilteredEventGroupNames[kOtherEventsGroupNameIndex]) !=
418            categories_status_.end());
419     return categories_status_
420         .find(kFilteredEventGroupNames[kOtherEventsGroupNameIndex])
421         ->second;
422   }
423 }
424 
CategoryStateToETWKeyword(const uint8_t * category_state)425 uint64_t TraceEventETWExport::CategoryStateToETWKeyword(
426     const uint8_t* category_state) {
427   const TraceCategory* category = TraceCategory::FromStatePtr(category_state);
428   uint64_t keyword = CategoryGroupToETWKeyword(category->name());
429   return keyword;
430 }
431 
OnETWEnableUpdate(TlmProvider::EventControlCode enabled)432 void TraceEventETWExport::OnETWEnableUpdate(
433     TlmProvider::EventControlCode enabled) {
434   // During construction, if tracing is already enabled, we'll get
435   // a callback synchronously on the same thread. Calling GetInstance
436   // in that case will hang since we're in the process of creating the
437   // singleton.
438   if (is_registration_complete_) {
439     UpdateEnabledCategories();
440   }
441 }
442 
443 // static
GetInstance()444 TraceEventETWExport* TraceEventETWExport::GetInstance() {
445   return Singleton<TraceEventETWExport,
446                    StaticMemorySingletonTraits<TraceEventETWExport>>::get();
447 }
448 
449 // static
GetInstanceIfExists()450 TraceEventETWExport* TraceEventETWExport::GetInstanceIfExists() {
451   return Singleton<
452       TraceEventETWExport,
453       StaticMemorySingletonTraits<TraceEventETWExport>>::GetIfExists();
454 }
455 
CategoryGroupToETWKeyword(std::string_view category_group_name)456 uint64_t CategoryGroupToETWKeyword(std::string_view category_group_name) {
457   static NoDestructor<base::flat_map<std::string_view, uint64_t>>
458       categories_to_keyword([]() {
459         std::vector<std::pair<std::string_view, uint64_t>> items;
460         for (size_t i = 0; i < kOtherEventsGroupNameIndex; i++) {
461           uint64_t keyword = 1ULL << i;
462           items.emplace_back(kFilteredEventGroupNames[i], keyword);
463         }
464         std::sort(items.begin(), items.end());
465         return base::flat_map<std::string_view, uint64_t>(base::sorted_unique,
466                                                           std::move(items));
467       }());
468 
469   uint64_t keyword = 0;
470 
471   // To enable multiple sessions with this provider enabled we need to log the
472   // level and keyword with the event so that if the sessions differ in the
473   // level or keywords enabled we log the right events and allow ETW to
474   // route the data to the appropriate session.
475   // TODO(joel@microsoft.com) Explore better methods in future integration
476   // with perfetto.
477 
478   CStringTokenizer category_group_tokens(category_group_name.begin(),
479                                          category_group_name.end(), ",");
480   while (category_group_tokens.GetNext()) {
481     StringPiece category_group_token = category_group_tokens.token_piece();
482 
483     // Lookup the keyword for this part of the category_group_name
484     // and or in the keyword.
485     auto it = categories_to_keyword->find(category_group_token);
486     if (it != categories_to_keyword->end()) {
487       keyword |= it->second;
488     } else {
489       if (StartsWith(category_group_token, "disabled-by-default")) {
490         keyword |= (1ULL << kDisabledOtherEventsGroupNameIndex);
491       } else {
492         keyword |= (1ULL << kOtherEventsGroupNameIndex);
493       }
494     }
495   }
496   return keyword;
497 }
498 
499 #if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
500 
ETWKeywordToTrackEventConfig(uint64_t keyword)501 perfetto::protos::gen::TrackEventConfig ETWKeywordToTrackEventConfig(
502     uint64_t keyword) {
503   perfetto::protos::gen::TrackEventConfig track_event_config;
504   for (size_t i = 0; i < kOtherEventsGroupNameIndex; ++i) {
505     if (keyword & (1ULL << i)) {
506       track_event_config.add_enabled_categories(kFilteredEventGroupNames[i]);
507     }
508   }
509   bool other_events_enabled = (keyword & (1ULL << kOtherEventsGroupNameIndex));
510   bool disabled_other_events_enables =
511       (keyword & (1ULL << kDisabledOtherEventsGroupNameIndex));
512   if (!other_events_enabled) {
513     track_event_config.add_disabled_categories("*");
514   }
515   if (!disabled_other_events_enables) {
516     track_event_config.add_disabled_categories("disabled-by-default-*");
517   } else if (other_events_enabled) {
518     track_event_config.add_enabled_categories("disabled-by-default-*");
519   }
520   return track_event_config;
521 }
522 
523 #endif  // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
524 
525 }  // namespace trace_event
526 }  // namespace base
527