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/utf_string_conversions.h" | 11 #include "base/strings/utf_string_conversions.h" |
11 #include "base/trace_event/trace_event.h" | 12 #include "base/trace_event/trace_event.h" |
12 #include "base/trace_event/trace_event_impl.h" | 13 #include "base/trace_event/trace_event_impl.h" |
13 | 14 |
14 // The GetProcAddress technique is borrowed from | 15 // The GetProcAddress technique is borrowed from |
15 // https://github.com/google/UIforETW/tree/master/ETWProviders | 16 // https://github.com/google/UIforETW/tree/master/ETWProviders |
16 // | 17 // |
17 // EVNTAPI is used in evntprov.h which is included by chrome_events_win.h. | 18 // EVNTAPI is used in evntprov.h which is included by chrome_events_win.h. |
18 // We define EVNTAPI without the DECLSPEC_IMPORT specifier so that we can | 19 // We define EVNTAPI without the DECLSPEC_IMPORT specifier so that we can |
19 // implement these functions locally instead of using the import library, and | 20 // implement these functions locally instead of using the import library, and |
(...skipping 13 matching lines...) Expand all Loading... |
33 PREGHANDLE RegHandle); | 34 PREGHANDLE RegHandle); |
34 typedef ULONG(__stdcall* tEventWrite)(REGHANDLE RegHandle, | 35 typedef ULONG(__stdcall* tEventWrite)(REGHANDLE RegHandle, |
35 PCEVENT_DESCRIPTOR EventDescriptor, | 36 PCEVENT_DESCRIPTOR EventDescriptor, |
36 ULONG UserDataCount, | 37 ULONG UserDataCount, |
37 PEVENT_DATA_DESCRIPTOR UserData); | 38 PEVENT_DATA_DESCRIPTOR UserData); |
38 typedef ULONG(__stdcall* tEventUnregister)(REGHANDLE RegHandle); | 39 typedef ULONG(__stdcall* tEventUnregister)(REGHANDLE RegHandle); |
39 | 40 |
40 tEventRegister EventRegisterProc = nullptr; | 41 tEventRegister EventRegisterProc = nullptr; |
41 tEventWrite EventWriteProc = nullptr; | 42 tEventWrite EventWriteProc = nullptr; |
42 tEventUnregister EventUnregisterProc = nullptr; | 43 tEventUnregister EventUnregisterProc = nullptr; |
| 44 |
| 45 // |filtered_event_group_names| contains the event categories that can be |
| 46 // exported individually. These categories can be enabled by passing the correct |
| 47 // keyword when starting the trace. A keyword is a 64-bit flag and we attribute |
| 48 // one bit per category. We can therefore enable a particular category by |
| 49 // setting its corresponding bit in the keyword. For events that are not present |
| 50 // in |filtered_event_group_names|, we have two bits that control their |
| 51 // behaviour. When bit 61 is enabled, any event that is not disabled by default |
| 52 // (ie. doesn't start with disabled-by-default-) will be exported. Likewise, |
| 53 // when bit 62 is enabled, any event that is disabled by default will be |
| 54 // exported. |
| 55 // |
| 56 // Note that bit 63 (MSB) must always be set, otherwise tracing will be disabled |
| 57 // by ETW. Therefore, the keyword will always be greater than |
| 58 // 0x8000000000000000. |
| 59 // |
| 60 // Examples of passing keywords to the provider using xperf: |
| 61 // # This exports "benchmark" and "cc" events |
| 62 // xperf -start chrome -on Chrome:0x8000000000000009 |
| 63 // |
| 64 // # This exports "gpu", "netlog" and all other events that are not disabled by |
| 65 // # default |
| 66 // xperf -start chrome -on Chrome:0xA0000000000000A0 |
| 67 // |
| 68 // More info about starting a trace and keyword can be obtained by using the |
| 69 // help section of xperf (xperf -help start). Note that xperf documentation |
| 70 // refers to keywords as flags and there are two ways to enable them, using |
| 71 // group names or the hex representation. We only support the latter. Also, we |
| 72 // ignore the level. |
| 73 const char* const filtered_event_group_names[] = { |
| 74 "benchmark", // 0x1 |
| 75 "blink", // 0x2 |
| 76 "browser", // 0x4 |
| 77 "cc", // 0x8 |
| 78 "evdev", // 0x10 |
| 79 "gpu", // 0x20 |
| 80 "input", // 0x40 |
| 81 "netlog", // 0x80 |
| 82 "renderer.scheduler", // 0x100 |
| 83 "toplevel", // 0x200 |
| 84 "v8", // 0x400 |
| 85 "disabled-by-default-cc.debug", // 0x800 |
| 86 "disabled-by-default-cc.debug.picture", // 0x1000 |
| 87 "disabled-by-default-toplevel.flow"}; // 0x2000 |
| 88 const char* other_events_group_name = "__OTHER_EVENTS"; // 0x2000000000000000 |
| 89 const char* disabled_other_events_group_name = |
| 90 "__DISABLED_OTHER_EVENTS"; // 0x4000000000000000 |
| 91 uint64 other_events_keyword_bit = 1ULL << 61; |
| 92 uint64 disabled_other_events_keyword_bit = 1ULL << 62; |
43 } // namespace | 93 } // namespace |
44 | 94 |
45 // Redirector function for EventRegister. Called by macros in | 95 // Redirector function for EventRegister. Called by macros in |
46 // chrome_events_win.h | 96 // chrome_events_win.h |
47 ULONG EVNTAPI EventRegister(LPCGUID ProviderId, | 97 ULONG EVNTAPI EventRegister(LPCGUID ProviderId, |
48 PENABLECALLBACK EnableCallback, | 98 PENABLECALLBACK EnableCallback, |
49 PVOID CallbackContext, | 99 PVOID CallbackContext, |
50 PREGHANDLE RegHandle) { | 100 PREGHANDLE RegHandle) { |
51 if (EventRegisterProc) | 101 if (EventRegisterProc) |
52 return EventRegisterProc(ProviderId, EnableCallback, CallbackContext, | 102 return EventRegisterProc(ProviderId, EnableCallback, CallbackContext, |
(...skipping 17 matching lines...) Expand all Loading... |
70 // chrome_events_win.h | 120 // chrome_events_win.h |
71 ULONG EVNTAPI EventUnregister(REGHANDLE RegHandle) { | 121 ULONG EVNTAPI EventUnregister(REGHANDLE RegHandle) { |
72 if (EventUnregisterProc) | 122 if (EventUnregisterProc) |
73 return EventUnregisterProc(RegHandle); | 123 return EventUnregisterProc(RegHandle); |
74 return 0; | 124 return 0; |
75 } | 125 } |
76 | 126 |
77 namespace base { | 127 namespace base { |
78 namespace trace_event { | 128 namespace trace_event { |
79 | 129 |
80 TraceEventETWExport::TraceEventETWExport() : ETWExportEnabled_(false) { | 130 TraceEventETWExport::TraceEventETWExport() |
| 131 : etw_export_enabled_(false), etw_match_any_keyword_(0ULL) { |
81 // Find Advapi32.dll. This should always succeed. | 132 // Find Advapi32.dll. This should always succeed. |
82 HMODULE AdvapiDLL = ::LoadLibraryW(L"Advapi32.dll"); | 133 HMODULE AdvapiDLL = ::LoadLibraryW(L"Advapi32.dll"); |
83 if (AdvapiDLL) { | 134 if (AdvapiDLL) { |
84 // Try to find the ETW functions. This will fail on XP. | 135 // Try to find the ETW functions. This will fail on XP. |
85 EventRegisterProc = reinterpret_cast<tEventRegister>( | 136 EventRegisterProc = reinterpret_cast<tEventRegister>( |
86 ::GetProcAddress(AdvapiDLL, "EventRegister")); | 137 ::GetProcAddress(AdvapiDLL, "EventRegister")); |
87 EventWriteProc = reinterpret_cast<tEventWrite>( | 138 EventWriteProc = reinterpret_cast<tEventWrite>( |
88 ::GetProcAddress(AdvapiDLL, "EventWrite")); | 139 ::GetProcAddress(AdvapiDLL, "EventWrite")); |
89 EventUnregisterProc = reinterpret_cast<tEventUnregister>( | 140 EventUnregisterProc = reinterpret_cast<tEventUnregister>( |
90 ::GetProcAddress(AdvapiDLL, "EventUnregister")); | 141 ::GetProcAddress(AdvapiDLL, "EventUnregister")); |
91 | 142 |
92 // Register the ETW provider. If registration fails then the event logging | 143 // Register the ETW provider. If registration fails then the event logging |
93 // calls will fail (on XP this call will do nothing). | 144 // calls will fail (on XP this call will do nothing). |
94 EventRegisterChrome(); | 145 EventRegisterChrome(); |
| 146 |
| 147 UpdateEnabledCategories(); |
95 } | 148 } |
96 } | 149 } |
97 | 150 |
98 TraceEventETWExport::~TraceEventETWExport() { | 151 TraceEventETWExport::~TraceEventETWExport() { |
99 EventUnregisterChrome(); | 152 EventUnregisterChrome(); |
100 } | 153 } |
101 | 154 |
102 // static | 155 // static |
103 TraceEventETWExport* TraceEventETWExport::GetInstance() { | 156 TraceEventETWExport* TraceEventETWExport::GetInstance() { |
104 return Singleton<TraceEventETWExport, | 157 return Singleton<TraceEventETWExport, |
105 StaticMemorySingletonTraits<TraceEventETWExport>>::get(); | 158 StaticMemorySingletonTraits<TraceEventETWExport>>::get(); |
106 } | 159 } |
107 | 160 |
108 // static | 161 // static |
109 void TraceEventETWExport::EnableETWExport() { | 162 void TraceEventETWExport::EnableETWExport() { |
110 if (GetInstance()) | 163 if (GetInstance()) |
111 GetInstance()->ETWExportEnabled_ = true; | 164 GetInstance()->etw_export_enabled_ = true; |
112 } | 165 } |
113 | 166 |
114 // static | 167 // static |
115 void TraceEventETWExport::DisableETWExport() { | 168 void TraceEventETWExport::DisableETWExport() { |
116 if (GetInstance()) | 169 if (GetInstance()) |
117 GetInstance()->ETWExportEnabled_ = false; | 170 GetInstance()->etw_export_enabled_ = false; |
118 } | 171 } |
119 | 172 |
120 // static | 173 // static |
| 174 bool TraceEventETWExport::IsETWExportEnabled() { |
| 175 return (GetInstance() && GetInstance()->etw_export_enabled_); |
| 176 } |
| 177 |
| 178 // static |
121 void TraceEventETWExport::AddEvent( | 179 void TraceEventETWExport::AddEvent( |
122 char phase, | 180 char phase, |
123 const unsigned char* category_group_enabled, | 181 const unsigned char* category_group_enabled, |
124 const char* name, | 182 const char* name, |
125 unsigned long long id, | 183 unsigned long long id, |
126 int num_args, | 184 int num_args, |
127 const char** arg_names, | 185 const char** arg_names, |
128 const unsigned char* arg_types, | 186 const unsigned char* arg_types, |
129 const unsigned long long* arg_values, | 187 const unsigned long long* arg_values, |
130 const scoped_refptr<ConvertableToTraceFormat>* convertable_values) { | 188 const scoped_refptr<ConvertableToTraceFormat>* convertable_values) { |
131 // We bail early in case exporting is disabled or no consumer is listening. | 189 // We bail early in case exporting is disabled or no consumer is listening. |
132 if (!GetInstance() || !GetInstance()->ETWExportEnabled_ || | 190 if (!GetInstance() || !GetInstance()->etw_export_enabled_ || |
133 !EventEnabledChromeEvent()) | 191 !EventEnabledChromeEvent()) |
134 return; | 192 return; |
135 | 193 |
136 const char* phase_string = nullptr; | 194 const char* phase_string = nullptr; |
137 // Space to store the phase identifier and null-terminator, when needed. | 195 // Space to store the phase identifier and null-terminator, when needed. |
138 char phase_buffer[2]; | 196 char phase_buffer[2]; |
139 switch (phase) { | 197 switch (phase) { |
140 case TRACE_EVENT_PHASE_BEGIN: | 198 case TRACE_EVENT_PHASE_BEGIN: |
141 phase_string = "Begin"; | 199 phase_string = "Begin"; |
142 break; | 200 break; |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
204 break; | 262 break; |
205 } | 263 } |
206 | 264 |
207 std::string arg_values_string[3]; | 265 std::string arg_values_string[3]; |
208 for (int i = 0; i < num_args; i++) { | 266 for (int i = 0; i < num_args; i++) { |
209 if (arg_types[i] == TRACE_VALUE_TYPE_CONVERTABLE) { | 267 if (arg_types[i] == TRACE_VALUE_TYPE_CONVERTABLE) { |
210 // Temporarily do nothing here. This function consumes 1/3 to 1/2 of | 268 // Temporarily do nothing here. This function consumes 1/3 to 1/2 of |
211 // *total* process CPU time when ETW tracing, and many of the strings | 269 // *total* process CPU time when ETW tracing, and many of the strings |
212 // created exceed WPA's 4094 byte limit and are shown as: | 270 // created exceed WPA's 4094 byte limit and are shown as: |
213 // "Unable to parse data". See crbug.com/488257 | 271 // "Unable to parse data". See crbug.com/488257 |
214 //convertable_values[i]->AppendAsTraceFormat(arg_values_string + i); | 272 // convertable_values[i]->AppendAsTraceFormat(arg_values_string + i); |
215 } else { | 273 } else { |
216 TraceEvent::TraceValue trace_event; | 274 TraceEvent::TraceValue trace_event; |
217 trace_event.as_uint = arg_values[i]; | 275 trace_event.as_uint = arg_values[i]; |
218 TraceEvent::AppendValueAsJSON(arg_types[i], trace_event, | 276 TraceEvent::AppendValueAsJSON(arg_types[i], trace_event, |
219 arg_values_string + i); | 277 arg_values_string + i); |
220 } | 278 } |
221 } | 279 } |
222 | 280 |
223 EventWriteChromeEvent( | 281 EventWriteChromeEvent( |
224 name, phase_string, num_args > 0 ? arg_names[0] : "", | 282 name, phase_string, num_args > 0 ? arg_names[0] : "", |
225 arg_values_string[0].c_str(), num_args > 1 ? arg_names[1] : "", | 283 arg_values_string[0].c_str(), num_args > 1 ? arg_names[1] : "", |
226 arg_values_string[1].c_str(), num_args > 2 ? arg_names[2] : "", | 284 arg_values_string[1].c_str(), num_args > 2 ? arg_names[2] : "", |
227 arg_values_string[2].c_str()); | 285 arg_values_string[2].c_str()); |
228 } | 286 } |
229 | 287 |
230 // static | 288 // static |
231 void TraceEventETWExport::AddCustomEvent(const char* name, | 289 void TraceEventETWExport::AddCustomEvent(const char* name, |
232 char const* phase, | 290 char const* phase, |
233 const char* arg_name_1, | 291 const char* arg_name_1, |
234 const char* arg_value_1, | 292 const char* arg_value_1, |
235 const char* arg_name_2, | 293 const char* arg_name_2, |
236 const char* arg_value_2, | 294 const char* arg_value_2, |
237 const char* arg_name_3, | 295 const char* arg_name_3, |
238 const char* arg_value_3) { | 296 const char* arg_value_3) { |
239 if (!GetInstance() || !GetInstance()->ETWExportEnabled_ || | 297 if (!GetInstance() || !GetInstance()->etw_export_enabled_ || |
240 !EventEnabledChromeEvent()) | 298 !EventEnabledChromeEvent()) |
241 return; | 299 return; |
242 | 300 |
243 EventWriteChromeEvent(name, phase, arg_name_1, arg_value_1, arg_name_2, | 301 EventWriteChromeEvent(name, phase, arg_name_1, arg_value_1, arg_name_2, |
244 arg_value_2, arg_name_3, arg_value_3); | 302 arg_value_2, arg_name_3, arg_value_3); |
245 } | 303 } |
246 | 304 |
247 void TraceEventETWExport::Resurrect() { | 305 // static |
248 StaticMemorySingletonTraits<TraceEventETWExport>::Resurrect(); | 306 bool TraceEventETWExport::IsCategoryGroupEnabled( |
| 307 const char* category_group_name) { |
| 308 DCHECK(category_group_name); |
| 309 auto instance = GetInstance(); |
| 310 if (instance == nullptr) |
| 311 return false; |
| 312 |
| 313 if (!instance->IsETWExportEnabled()) |
| 314 return false; |
| 315 |
| 316 CStringTokenizer category_group_tokens( |
| 317 category_group_name, category_group_name + strlen(category_group_name), |
| 318 ","); |
| 319 while (category_group_tokens.GetNext()) { |
| 320 std::string category_group_token = category_group_tokens.token(); |
| 321 if (instance->IsCategoryEnabled(category_group_token.c_str())) { |
| 322 return true; |
| 323 } |
| 324 } |
| 325 return false; |
| 326 } |
| 327 |
| 328 bool TraceEventETWExport::UpdateEnabledCategories() { |
| 329 if (etw_match_any_keyword_ == CHROME_Context.MatchAnyKeyword) |
| 330 return false; |
| 331 |
| 332 // If the keyword has changed, update each category. |
| 333 // Chrome_Context.MatchAnyKeyword is set by UIforETW (or other ETW trace |
| 334 // recording tools) using the ETW infrastructure. This value will be set in |
| 335 // all Chrome processes that have registered their ETW provider. |
| 336 etw_match_any_keyword_ = CHROME_Context.MatchAnyKeyword; |
| 337 for (int i = 0; i < ARRAYSIZE(filtered_event_group_names); i++) { |
| 338 if (etw_match_any_keyword_ & (1ULL << i)) { |
| 339 categories_status_[filtered_event_group_names[i]] = true; |
| 340 } else { |
| 341 categories_status_[filtered_event_group_names[i]] = false; |
| 342 } |
| 343 } |
| 344 |
| 345 // Also update the two default categories. |
| 346 if (etw_match_any_keyword_ & other_events_keyword_bit) { |
| 347 categories_status_[other_events_group_name] = true; |
| 348 } else { |
| 349 categories_status_[other_events_group_name] = false; |
| 350 } |
| 351 if (etw_match_any_keyword_ & disabled_other_events_keyword_bit) { |
| 352 categories_status_[disabled_other_events_group_name] = true; |
| 353 } else { |
| 354 categories_status_[disabled_other_events_group_name] = false; |
| 355 } |
| 356 |
| 357 return true; |
| 358 } |
| 359 |
| 360 bool TraceEventETWExport::IsCategoryEnabled(const char* category_name) const { |
| 361 // Try to find the category and return its status if found |
| 362 auto it = categories_status_.find(category_name); |
| 363 if (it != categories_status_.end()) |
| 364 return it->second; |
| 365 |
| 366 // Otherwise return the corresponding default status by first checking if the |
| 367 // category is disabled by default. |
| 368 if (StringPiece(category_name).starts_with("disabled-by-default")) { |
| 369 DCHECK(categories_status_.find(disabled_other_events_group_name) != |
| 370 categories_status_.end()); |
| 371 return categories_status_.find(disabled_other_events_group_name)->second; |
| 372 } else { |
| 373 DCHECK(categories_status_.find(other_events_group_name) != |
| 374 categories_status_.end()); |
| 375 return categories_status_.find(other_events_group_name)->second; |
| 376 } |
249 } | 377 } |
250 | 378 |
251 } // namespace trace_event | 379 } // namespace trace_event |
252 } // namespace base | 380 } // namespace base |
OLD | NEW |