OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/trace_event/trace_event_etw_export_win.h" | 5 #include "base/trace_event/trace_event_etw_export_win.h" |
6 | 6 |
7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/memory/singleton.h" | 9 #include "base/memory/singleton.h" |
10 #include "base/strings/string_tokenizer.h" | 10 #include "base/strings/string_tokenizer.h" |
(...skipping 25 matching lines...) Expand all Loading... |
36 typedef ULONG(__stdcall* tEventWrite)(REGHANDLE RegHandle, | 36 typedef ULONG(__stdcall* tEventWrite)(REGHANDLE RegHandle, |
37 PCEVENT_DESCRIPTOR EventDescriptor, | 37 PCEVENT_DESCRIPTOR EventDescriptor, |
38 ULONG UserDataCount, | 38 ULONG UserDataCount, |
39 PEVENT_DATA_DESCRIPTOR UserData); | 39 PEVENT_DATA_DESCRIPTOR UserData); |
40 typedef ULONG(__stdcall* tEventUnregister)(REGHANDLE RegHandle); | 40 typedef ULONG(__stdcall* tEventUnregister)(REGHANDLE RegHandle); |
41 | 41 |
42 tEventRegister EventRegisterProc = nullptr; | 42 tEventRegister EventRegisterProc = nullptr; |
43 tEventWrite EventWriteProc = nullptr; | 43 tEventWrite EventWriteProc = nullptr; |
44 tEventUnregister EventUnregisterProc = nullptr; | 44 tEventUnregister EventUnregisterProc = nullptr; |
45 | 45 |
46 // |filtered_event_group_names| contains the event categories that can be | 46 // |kFilteredEventGroupNames| contains the event categories that can be |
47 // exported individually. These categories can be enabled by passing the correct | 47 // exported individually. These categories can be enabled by passing the correct |
48 // keyword when starting the trace. A keyword is a 64-bit flag and we attribute | 48 // keyword when starting the trace. A keyword is a 64-bit flag and we attribute |
49 // one bit per category. We can therefore enable a particular category by | 49 // one bit per category. We can therefore enable a particular category by |
50 // setting its corresponding bit in the keyword. For events that are not present | 50 // setting its corresponding bit in the keyword. For events that are not present |
51 // in |filtered_event_group_names|, we have two bits that control their | 51 // in |kFilteredEventGroupNames|, we have two bits that control their |
52 // behaviour. When bit 61 is enabled, any event that is not disabled by default | 52 // behaviour. When bit 61 is enabled, any event that is not disabled by default |
53 // (ie. doesn't start with disabled-by-default-) will be exported. Likewise, | 53 // (ie. doesn't start with disabled-by-default-) will be exported. Likewise, |
54 // when bit 62 is enabled, any event that is disabled by default will be | 54 // when bit 62 is enabled, any event that is disabled by default will be |
55 // exported. | 55 // exported. |
56 // | 56 // |
57 // Note that bit 63 (MSB) must always be set, otherwise tracing will be disabled | 57 // Note that bit 63 (MSB) must always be set, otherwise tracing will be disabled |
58 // by ETW. Therefore, the keyword will always be greater than | 58 // by ETW. Therefore, the keyword will always be greater than |
59 // 0x8000000000000000. | 59 // 0x8000000000000000. |
60 // | 60 // |
61 // Examples of passing keywords to the provider using xperf: | 61 // Examples of passing keywords to the provider using xperf: |
62 // # This exports "benchmark" and "cc" events | 62 // # This exports "benchmark" and "cc" events |
63 // xperf -start chrome -on Chrome:0x8000000000000009 | 63 // xperf -start chrome -on Chrome:0x8000000000000009 |
64 // | 64 // |
65 // # This exports "gpu", "netlog" and all other events that are not disabled by | 65 // # This exports "gpu", "netlog" and all other events that are not disabled by |
66 // # default | 66 // # default |
67 // xperf -start chrome -on Chrome:0xA0000000000000A0 | 67 // xperf -start chrome -on Chrome:0xA0000000000000A0 |
68 // | 68 // |
69 // More info about starting a trace and keyword can be obtained by using the | 69 // More info about starting a trace and keyword can be obtained by using the |
70 // help section of xperf (xperf -help start). Note that xperf documentation | 70 // help section of xperf (xperf -help start). Note that xperf documentation |
71 // refers to keywords as flags and there are two ways to enable them, using | 71 // refers to keywords as flags and there are two ways to enable them, using |
72 // group names or the hex representation. We only support the latter. Also, we | 72 // group names or the hex representation. We only support the latter. Also, we |
73 // ignore the level. | 73 // ignore the level. |
74 const char* const filtered_event_group_names[] = { | 74 const char* const kFilteredEventGroupNames[] = { |
75 "benchmark", // 0x1 | 75 "benchmark", // 0x1 |
76 "blink", // 0x2 | 76 "blink", // 0x2 |
77 "browser", // 0x4 | 77 "browser", // 0x4 |
78 "cc", // 0x8 | 78 "cc", // 0x8 |
79 "evdev", // 0x10 | 79 "evdev", // 0x10 |
80 "gpu", // 0x20 | 80 "gpu", // 0x20 |
81 "input", // 0x40 | 81 "input", // 0x40 |
82 "netlog", // 0x80 | 82 "netlog", // 0x80 |
83 "renderer.scheduler", // 0x100 | 83 "renderer.scheduler", // 0x100 |
84 "toplevel", // 0x200 | 84 "toplevel", // 0x200 |
85 "v8", // 0x400 | 85 "v8", // 0x400 |
86 "disabled-by-default-cc.debug", // 0x800 | 86 "disabled-by-default-cc.debug", // 0x800 |
87 "disabled-by-default-cc.debug.picture", // 0x1000 | 87 "disabled-by-default-cc.debug.picture", // 0x1000 |
88 "disabled-by-default-toplevel.flow"}; // 0x2000 | 88 "disabled-by-default-toplevel.flow"}; // 0x2000 |
89 const char* other_events_group_name = "__OTHER_EVENTS"; // 0x2000000000000000 | 89 const char kOtherEventsGroupName[] = "__OTHER_EVENTS"; // 0x2000000000000000 |
90 const char* disabled_other_events_group_name = | 90 const char kDisabledOtherEventsGroupName[] = |
91 "__DISABLED_OTHER_EVENTS"; // 0x4000000000000000 | 91 "__DISABLED_OTHER_EVENTS"; // 0x4000000000000000 |
92 uint64 other_events_keyword_bit = 1ULL << 61; | 92 const uint64 kOtherEventsKeywordBit = 1ULL << 61; |
93 uint64 disabled_other_events_keyword_bit = 1ULL << 62; | 93 const uint64 kDisabledOtherEventsKeywordBit = 1ULL << 62; |
94 | 94 const size_t kNumberOfCategories = ARRAYSIZE(kFilteredEventGroupNames) + 2U; |
95 // This object will be created by each process. It's a background (low-priority) | |
96 // thread that will monitor the ETW keyword for any changes. | |
97 class ETWKeywordUpdateThread : public base::PlatformThread::Delegate { | |
98 public: | |
99 ETWKeywordUpdateThread() {} | |
100 ~ETWKeywordUpdateThread() override {} | |
101 | |
102 // Implementation of PlatformThread::Delegate: | |
103 void ThreadMain() override { | |
104 base::PlatformThread::SetName("ETW Keyword Update Thread"); | |
105 base::TimeDelta sleep_time = | |
106 base::TimeDelta::FromMilliseconds(kUpdateTimerDelayMs); | |
107 while (1) { | |
108 base::PlatformThread::Sleep(sleep_time); | |
109 base::trace_event::TraceEventETWExport::UpdateETWKeyword(); | |
110 } | |
111 } | |
112 | |
113 private: | |
114 // Time between checks for ETW keyword changes (in milliseconds). | |
115 unsigned int kUpdateTimerDelayMs = 1000; | |
116 }; | |
117 | 95 |
118 } // namespace | 96 } // namespace |
119 | 97 |
120 // Redirector function for EventRegister. Called by macros in | 98 // Redirector function for EventRegister. Called by macros in |
121 // chrome_events_win.h | 99 // chrome_events_win.h |
122 ULONG EVNTAPI EventRegister(LPCGUID ProviderId, | 100 ULONG EVNTAPI EventRegister(LPCGUID ProviderId, |
123 PENABLECALLBACK EnableCallback, | 101 PENABLECALLBACK EnableCallback, |
124 PVOID CallbackContext, | 102 PVOID CallbackContext, |
125 PREGHANDLE RegHandle) { | 103 PREGHANDLE RegHandle) { |
126 if (EventRegisterProc) | 104 if (EventRegisterProc) |
(...skipping 18 matching lines...) Expand all Loading... |
145 // chrome_events_win.h | 123 // chrome_events_win.h |
146 ULONG EVNTAPI EventUnregister(REGHANDLE RegHandle) { | 124 ULONG EVNTAPI EventUnregister(REGHANDLE RegHandle) { |
147 if (EventUnregisterProc) | 125 if (EventUnregisterProc) |
148 return EventUnregisterProc(RegHandle); | 126 return EventUnregisterProc(RegHandle); |
149 return 0; | 127 return 0; |
150 } | 128 } |
151 | 129 |
152 namespace base { | 130 namespace base { |
153 namespace trace_event { | 131 namespace trace_event { |
154 | 132 |
| 133 // This object will be created by each process. It's a background (low-priority) |
| 134 // thread that will monitor the ETW keyword for any changes. |
| 135 class TraceEventETWExport::ETWKeywordUpdateThread |
| 136 : public base::PlatformThread::Delegate { |
| 137 public: |
| 138 ETWKeywordUpdateThread() {} |
| 139 ~ETWKeywordUpdateThread() override {} |
| 140 |
| 141 // Implementation of PlatformThread::Delegate: |
| 142 void ThreadMain() override { |
| 143 base::PlatformThread::SetName("ETW Keyword Update Thread"); |
| 144 base::TimeDelta sleep_time = |
| 145 base::TimeDelta::FromMilliseconds(kUpdateTimerDelayMs); |
| 146 while (1) { |
| 147 base::PlatformThread::Sleep(sleep_time); |
| 148 base::trace_event::TraceEventETWExport::UpdateETWKeyword(); |
| 149 } |
| 150 } |
| 151 |
| 152 private: |
| 153 // Time between checks for ETW keyword changes (in milliseconds). |
| 154 unsigned int kUpdateTimerDelayMs = 1000; |
| 155 }; |
| 156 |
155 TraceEventETWExport::TraceEventETWExport() | 157 TraceEventETWExport::TraceEventETWExport() |
156 : etw_export_enabled_(false), etw_match_any_keyword_(0ULL) { | 158 : etw_export_enabled_(false), etw_match_any_keyword_(0ULL) { |
157 // Find Advapi32.dll. This should always succeed. | 159 // Find Advapi32.dll. This should always succeed. |
158 HMODULE AdvapiDLL = ::LoadLibraryW(L"Advapi32.dll"); | 160 HMODULE AdvapiDLL = ::LoadLibraryW(L"Advapi32.dll"); |
159 if (AdvapiDLL) { | 161 if (AdvapiDLL) { |
160 // Try to find the ETW functions. This will fail on XP. | 162 // Try to find the ETW functions. This will fail on XP. |
161 EventRegisterProc = reinterpret_cast<tEventRegister>( | 163 EventRegisterProc = reinterpret_cast<tEventRegister>( |
162 ::GetProcAddress(AdvapiDLL, "EventRegister")); | 164 ::GetProcAddress(AdvapiDLL, "EventRegister")); |
163 EventWriteProc = reinterpret_cast<tEventWrite>( | 165 EventWriteProc = reinterpret_cast<tEventWrite>( |
164 ::GetProcAddress(AdvapiDLL, "EventWrite")); | 166 ::GetProcAddress(AdvapiDLL, "EventWrite")); |
165 EventUnregisterProc = reinterpret_cast<tEventUnregister>( | 167 EventUnregisterProc = reinterpret_cast<tEventUnregister>( |
166 ::GetProcAddress(AdvapiDLL, "EventUnregister")); | 168 ::GetProcAddress(AdvapiDLL, "EventUnregister")); |
167 | 169 |
168 // Register the ETW provider. If registration fails then the event logging | 170 // Register the ETW provider. If registration fails then the event logging |
169 // calls will fail (on XP this call will do nothing). | 171 // calls will fail (on XP this call will do nothing). |
170 EventRegisterChrome(); | 172 EventRegisterChrome(); |
171 } | 173 } |
| 174 |
| 175 // Make sure to initialize the map with all the group names. Subsequent |
| 176 // modifications will be made by the background thread and only affect the |
| 177 // values of the keys (no key addition/deletion). Therefore, the map does not |
| 178 // require a lock for access. |
| 179 for (int i = 0; i < ARRAYSIZE(kFilteredEventGroupNames); i++) |
| 180 categories_status_[kFilteredEventGroupNames[i]] = false; |
| 181 categories_status_[kOtherEventsGroupName] = false; |
| 182 categories_status_[kDisabledOtherEventsGroupName] = false; |
| 183 DCHECK_EQ(kNumberOfCategories, categories_status_.size()); |
172 } | 184 } |
173 | 185 |
174 TraceEventETWExport::~TraceEventETWExport() { | 186 TraceEventETWExport::~TraceEventETWExport() { |
175 EventUnregisterChrome(); | 187 EventUnregisterChrome(); |
176 } | 188 } |
177 | 189 |
178 // static | 190 // static |
179 TraceEventETWExport* TraceEventETWExport::GetInstance() { | 191 TraceEventETWExport* TraceEventETWExport::GetInstance() { |
180 return Singleton<TraceEventETWExport, | 192 return Singleton<TraceEventETWExport, |
181 StaticMemorySingletonTraits<TraceEventETWExport>>::get(); | 193 StaticMemorySingletonTraits<TraceEventETWExport>>::get(); |
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
364 | 376 |
365 bool TraceEventETWExport::UpdateEnabledCategories() { | 377 bool TraceEventETWExport::UpdateEnabledCategories() { |
366 if (etw_match_any_keyword_ == CHROME_Context.MatchAnyKeyword) | 378 if (etw_match_any_keyword_ == CHROME_Context.MatchAnyKeyword) |
367 return false; | 379 return false; |
368 | 380 |
369 // If the keyword has changed, update each category. | 381 // If the keyword has changed, update each category. |
370 // Chrome_Context.MatchAnyKeyword is set by UIforETW (or other ETW trace | 382 // Chrome_Context.MatchAnyKeyword is set by UIforETW (or other ETW trace |
371 // recording tools) using the ETW infrastructure. This value will be set in | 383 // recording tools) using the ETW infrastructure. This value will be set in |
372 // all Chrome processes that have registered their ETW provider. | 384 // all Chrome processes that have registered their ETW provider. |
373 etw_match_any_keyword_ = CHROME_Context.MatchAnyKeyword; | 385 etw_match_any_keyword_ = CHROME_Context.MatchAnyKeyword; |
374 for (int i = 0; i < ARRAYSIZE(filtered_event_group_names); i++) { | 386 for (int i = 0; i < ARRAYSIZE(kFilteredEventGroupNames); i++) { |
375 if (etw_match_any_keyword_ & (1ULL << i)) { | 387 if (etw_match_any_keyword_ & (1ULL << i)) { |
376 categories_status_[filtered_event_group_names[i]] = true; | 388 categories_status_[kFilteredEventGroupNames[i]] = true; |
377 } else { | 389 } else { |
378 categories_status_[filtered_event_group_names[i]] = false; | 390 categories_status_[kFilteredEventGroupNames[i]] = false; |
379 } | 391 } |
380 } | 392 } |
381 | 393 |
382 // Also update the two default categories. | 394 // Also update the two default categories. |
383 if (etw_match_any_keyword_ & other_events_keyword_bit) { | 395 if (etw_match_any_keyword_ & kOtherEventsKeywordBit) { |
384 categories_status_[other_events_group_name] = true; | 396 categories_status_[kOtherEventsGroupName] = true; |
385 } else { | 397 } else { |
386 categories_status_[other_events_group_name] = false; | 398 categories_status_[kOtherEventsGroupName] = false; |
387 } | 399 } |
388 if (etw_match_any_keyword_ & disabled_other_events_keyword_bit) { | 400 if (etw_match_any_keyword_ & kDisabledOtherEventsKeywordBit) { |
389 categories_status_[disabled_other_events_group_name] = true; | 401 categories_status_[kDisabledOtherEventsGroupName] = true; |
390 } else { | 402 } else { |
391 categories_status_[disabled_other_events_group_name] = false; | 403 categories_status_[kDisabledOtherEventsGroupName] = false; |
392 } | 404 } |
393 | 405 |
| 406 DCHECK_EQ(kNumberOfCategories, categories_status_.size()); |
| 407 |
394 // Update the categories in TraceLog. | 408 // Update the categories in TraceLog. |
395 TraceLog::GetInstance()->UpdateETWCategoryGroupEnabledFlags(); | 409 TraceLog::GetInstance()->UpdateETWCategoryGroupEnabledFlags(); |
396 | 410 |
397 return true; | 411 return true; |
398 } | 412 } |
399 | 413 |
400 bool TraceEventETWExport::IsCategoryEnabled(const char* category_name) const { | 414 bool TraceEventETWExport::IsCategoryEnabled(const char* category_name) const { |
| 415 DCHECK_EQ(kNumberOfCategories, categories_status_.size()); |
401 // Try to find the category and return its status if found | 416 // Try to find the category and return its status if found |
402 auto it = categories_status_.find(category_name); | 417 auto it = categories_status_.find(category_name); |
403 if (it != categories_status_.end()) | 418 if (it != categories_status_.end()) |
404 return it->second; | 419 return it->second; |
405 | 420 |
406 // Otherwise return the corresponding default status by first checking if the | 421 // Otherwise return the corresponding default status by first checking if the |
407 // category is disabled by default. | 422 // category is disabled by default. |
408 if (StringPiece(category_name).starts_with("disabled-by-default")) { | 423 if (StringPiece(category_name).starts_with("disabled-by-default")) { |
409 DCHECK(categories_status_.find(disabled_other_events_group_name) != | 424 DCHECK(categories_status_.find(kDisabledOtherEventsGroupName) != |
410 categories_status_.end()); | 425 categories_status_.end()); |
411 return categories_status_.find(disabled_other_events_group_name)->second; | 426 return categories_status_.find(kDisabledOtherEventsGroupName)->second; |
412 } else { | 427 } else { |
413 DCHECK(categories_status_.find(other_events_group_name) != | 428 DCHECK(categories_status_.find(kOtherEventsGroupName) != |
414 categories_status_.end()); | 429 categories_status_.end()); |
415 return categories_status_.find(other_events_group_name)->second; | 430 return categories_status_.find(kOtherEventsGroupName)->second; |
416 } | 431 } |
417 } | 432 } |
418 | 433 |
419 // static | 434 // static |
420 void TraceEventETWExport::UpdateETWKeyword() { | 435 void TraceEventETWExport::UpdateETWKeyword() { |
421 if (!IsETWExportEnabled()) | 436 if (!IsETWExportEnabled()) |
422 return; | 437 return; |
423 auto* instance = GetInstance(); | 438 auto* instance = GetInstance(); |
424 DCHECK(instance); | 439 DCHECK(instance); |
425 instance->UpdateEnabledCategories(); | 440 instance->UpdateEnabledCategories(); |
426 } | 441 } |
427 } // namespace trace_event | 442 } // namespace trace_event |
428 } // namespace base | 443 } // namespace base |
OLD | NEW |