| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "base/trace_event/trace_event_etw_export_win.h" | |
| 6 | |
| 7 #include "base/command_line.h" | |
| 8 #include "base/logging.h" | |
| 9 #include "base/memory/singleton.h" | |
| 10 #include "base/strings/string_tokenizer.h" | |
| 11 #include "base/strings/utf_string_conversions.h" | |
| 12 #include "base/trace_event/trace_event.h" | |
| 13 #include "base/trace_event/trace_event_impl.h" | |
| 14 | |
| 15 // The GetProcAddress technique is borrowed from | |
| 16 // https://github.com/google/UIforETW/tree/master/ETWProviders | |
| 17 // | |
| 18 // EVNTAPI is used in evntprov.h which is included by chrome_events_win.h. | |
| 19 // We define EVNTAPI without the DECLSPEC_IMPORT specifier so that we can | |
| 20 // implement these functions locally instead of using the import library, and | |
| 21 // can therefore still run on Windows XP. | |
| 22 #define EVNTAPI __stdcall | |
| 23 // Include the event register/write/unregister macros compiled from the manifest | |
| 24 // file. Note that this includes evntprov.h which requires a Vista+ Windows SDK. | |
| 25 // | |
| 26 // In SHARED_INTERMEDIATE_DIR. | |
| 27 #include "base/trace_event/etw_manifest/chrome_events_win.h" // NOLINT | |
| 28 | |
| 29 namespace { | |
| 30 // Typedefs for use with GetProcAddress | |
| 31 typedef ULONG(__stdcall* tEventRegister)(LPCGUID ProviderId, | |
| 32 PENABLECALLBACK EnableCallback, | |
| 33 PVOID CallbackContext, | |
| 34 PREGHANDLE RegHandle); | |
| 35 typedef ULONG(__stdcall* tEventWrite)(REGHANDLE RegHandle, | |
| 36 PCEVENT_DESCRIPTOR EventDescriptor, | |
| 37 ULONG UserDataCount, | |
| 38 PEVENT_DATA_DESCRIPTOR UserData); | |
| 39 typedef ULONG(__stdcall* tEventUnregister)(REGHANDLE RegHandle); | |
| 40 | |
| 41 tEventRegister EventRegisterProc = nullptr; | |
| 42 tEventWrite EventWriteProc = 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; | |
| 93 } // namespace | |
| 94 | |
| 95 // Redirector function for EventRegister. Called by macros in | |
| 96 // chrome_events_win.h | |
| 97 ULONG EVNTAPI EventRegister(LPCGUID ProviderId, | |
| 98 PENABLECALLBACK EnableCallback, | |
| 99 PVOID CallbackContext, | |
| 100 PREGHANDLE RegHandle) { | |
| 101 if (EventRegisterProc) | |
| 102 return EventRegisterProc(ProviderId, EnableCallback, CallbackContext, | |
| 103 RegHandle); | |
| 104 *RegHandle = 0; | |
| 105 return 0; | |
| 106 } | |
| 107 | |
| 108 // Redirector function for EventWrite. Called by macros in | |
| 109 // chrome_events_win.h | |
| 110 ULONG EVNTAPI EventWrite(REGHANDLE RegHandle, | |
| 111 PCEVENT_DESCRIPTOR EventDescriptor, | |
| 112 ULONG UserDataCount, | |
| 113 PEVENT_DATA_DESCRIPTOR UserData) { | |
| 114 if (EventWriteProc) | |
| 115 return EventWriteProc(RegHandle, EventDescriptor, UserDataCount, UserData); | |
| 116 return 0; | |
| 117 } | |
| 118 | |
| 119 // Redirector function for EventUnregister. Called by macros in | |
| 120 // chrome_events_win.h | |
| 121 ULONG EVNTAPI EventUnregister(REGHANDLE RegHandle) { | |
| 122 if (EventUnregisterProc) | |
| 123 return EventUnregisterProc(RegHandle); | |
| 124 return 0; | |
| 125 } | |
| 126 | |
| 127 namespace base { | |
| 128 namespace trace_event { | |
| 129 | |
| 130 TraceEventETWExport::TraceEventETWExport() | |
| 131 : etw_export_enabled_(false), etw_match_any_keyword_(0ULL) { | |
| 132 // Find Advapi32.dll. This should always succeed. | |
| 133 HMODULE AdvapiDLL = ::LoadLibraryW(L"Advapi32.dll"); | |
| 134 if (AdvapiDLL) { | |
| 135 // Try to find the ETW functions. This will fail on XP. | |
| 136 EventRegisterProc = reinterpret_cast<tEventRegister>( | |
| 137 ::GetProcAddress(AdvapiDLL, "EventRegister")); | |
| 138 EventWriteProc = reinterpret_cast<tEventWrite>( | |
| 139 ::GetProcAddress(AdvapiDLL, "EventWrite")); | |
| 140 EventUnregisterProc = reinterpret_cast<tEventUnregister>( | |
| 141 ::GetProcAddress(AdvapiDLL, "EventUnregister")); | |
| 142 | |
| 143 // Register the ETW provider. If registration fails then the event logging | |
| 144 // calls will fail (on XP this call will do nothing). | |
| 145 EventRegisterChrome(); | |
| 146 | |
| 147 UpdateEnabledCategories(); | |
| 148 } | |
| 149 } | |
| 150 | |
| 151 TraceEventETWExport::~TraceEventETWExport() { | |
| 152 EventUnregisterChrome(); | |
| 153 } | |
| 154 | |
| 155 // static | |
| 156 TraceEventETWExport* TraceEventETWExport::GetInstance() { | |
| 157 return Singleton<TraceEventETWExport, | |
| 158 StaticMemorySingletonTraits<TraceEventETWExport>>::get(); | |
| 159 } | |
| 160 | |
| 161 // static | |
| 162 void TraceEventETWExport::EnableETWExport() { | |
| 163 if (GetInstance()) | |
| 164 GetInstance()->etw_export_enabled_ = true; | |
| 165 } | |
| 166 | |
| 167 // static | |
| 168 void TraceEventETWExport::DisableETWExport() { | |
| 169 if (GetInstance()) | |
| 170 GetInstance()->etw_export_enabled_ = false; | |
| 171 } | |
| 172 | |
| 173 // static | |
| 174 bool TraceEventETWExport::IsETWExportEnabled() { | |
| 175 return (GetInstance() && GetInstance()->etw_export_enabled_); | |
| 176 } | |
| 177 | |
| 178 // static | |
| 179 void TraceEventETWExport::AddEvent( | |
| 180 char phase, | |
| 181 const unsigned char* category_group_enabled, | |
| 182 const char* name, | |
| 183 unsigned long long id, | |
| 184 int num_args, | |
| 185 const char** arg_names, | |
| 186 const unsigned char* arg_types, | |
| 187 const unsigned long long* arg_values, | |
| 188 const scoped_refptr<ConvertableToTraceFormat>* convertable_values) { | |
| 189 // We bail early in case exporting is disabled or no consumer is listening. | |
| 190 if (!GetInstance() || !GetInstance()->etw_export_enabled_ || | |
| 191 !EventEnabledChromeEvent()) | |
| 192 return; | |
| 193 | |
| 194 const char* phase_string = nullptr; | |
| 195 // Space to store the phase identifier and null-terminator, when needed. | |
| 196 char phase_buffer[2]; | |
| 197 switch (phase) { | |
| 198 case TRACE_EVENT_PHASE_BEGIN: | |
| 199 phase_string = "Begin"; | |
| 200 break; | |
| 201 case TRACE_EVENT_PHASE_END: | |
| 202 phase_string = "End"; | |
| 203 break; | |
| 204 case TRACE_EVENT_PHASE_COMPLETE: | |
| 205 phase_string = "Complete"; | |
| 206 break; | |
| 207 case TRACE_EVENT_PHASE_INSTANT: | |
| 208 phase_string = "Instant"; | |
| 209 break; | |
| 210 case TRACE_EVENT_PHASE_ASYNC_BEGIN: | |
| 211 phase_string = "Async Begin"; | |
| 212 break; | |
| 213 case TRACE_EVENT_PHASE_ASYNC_STEP_INTO: | |
| 214 phase_string = "Async Step Into"; | |
| 215 break; | |
| 216 case TRACE_EVENT_PHASE_ASYNC_STEP_PAST: | |
| 217 phase_string = "Async Step Past"; | |
| 218 break; | |
| 219 case TRACE_EVENT_PHASE_ASYNC_END: | |
| 220 phase_string = "Async End"; | |
| 221 break; | |
| 222 case TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN: | |
| 223 phase_string = "Nestable Async Begin"; | |
| 224 break; | |
| 225 case TRACE_EVENT_PHASE_NESTABLE_ASYNC_END: | |
| 226 phase_string = "Nestable Async End"; | |
| 227 break; | |
| 228 case TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT: | |
| 229 phase_string = "Nestable Async Instant"; | |
| 230 break; | |
| 231 case TRACE_EVENT_PHASE_FLOW_BEGIN: | |
| 232 phase_string = "Phase Flow Begin"; | |
| 233 break; | |
| 234 case TRACE_EVENT_PHASE_FLOW_STEP: | |
| 235 phase_string = "Phase Flow Step"; | |
| 236 break; | |
| 237 case TRACE_EVENT_PHASE_FLOW_END: | |
| 238 phase_string = "Phase Flow End"; | |
| 239 break; | |
| 240 case TRACE_EVENT_PHASE_METADATA: | |
| 241 phase_string = "Phase Metadata"; | |
| 242 break; | |
| 243 case TRACE_EVENT_PHASE_COUNTER: | |
| 244 phase_string = "Phase Counter"; | |
| 245 break; | |
| 246 case TRACE_EVENT_PHASE_SAMPLE: | |
| 247 phase_string = "Phase Sample"; | |
| 248 break; | |
| 249 case TRACE_EVENT_PHASE_CREATE_OBJECT: | |
| 250 phase_string = "Phase Create Object"; | |
| 251 break; | |
| 252 case TRACE_EVENT_PHASE_SNAPSHOT_OBJECT: | |
| 253 phase_string = "Phase Snapshot Object"; | |
| 254 break; | |
| 255 case TRACE_EVENT_PHASE_DELETE_OBJECT: | |
| 256 phase_string = "Phase Delete Object"; | |
| 257 break; | |
| 258 default: | |
| 259 phase_buffer[0] = phase; | |
| 260 phase_buffer[1] = 0; | |
| 261 phase_string = phase_buffer; | |
| 262 break; | |
| 263 } | |
| 264 | |
| 265 std::string arg_values_string[3]; | |
| 266 for (int i = 0; i < num_args; i++) { | |
| 267 if (arg_types[i] == TRACE_VALUE_TYPE_CONVERTABLE) { | |
| 268 // Temporarily do nothing here. This function consumes 1/3 to 1/2 of | |
| 269 // *total* process CPU time when ETW tracing, and many of the strings | |
| 270 // created exceed WPA's 4094 byte limit and are shown as: | |
| 271 // "Unable to parse data". See crbug.com/488257 | |
| 272 // convertable_values[i]->AppendAsTraceFormat(arg_values_string + i); | |
| 273 } else { | |
| 274 TraceEvent::TraceValue trace_event; | |
| 275 trace_event.as_uint = arg_values[i]; | |
| 276 TraceEvent::AppendValueAsJSON(arg_types[i], trace_event, | |
| 277 arg_values_string + i); | |
| 278 } | |
| 279 } | |
| 280 | |
| 281 EventWriteChromeEvent( | |
| 282 name, phase_string, num_args > 0 ? arg_names[0] : "", | |
| 283 arg_values_string[0].c_str(), num_args > 1 ? arg_names[1] : "", | |
| 284 arg_values_string[1].c_str(), num_args > 2 ? arg_names[2] : "", | |
| 285 arg_values_string[2].c_str()); | |
| 286 } | |
| 287 | |
| 288 // static | |
| 289 void TraceEventETWExport::AddCustomEvent(const char* name, | |
| 290 char const* phase, | |
| 291 const char* arg_name_1, | |
| 292 const char* arg_value_1, | |
| 293 const char* arg_name_2, | |
| 294 const char* arg_value_2, | |
| 295 const char* arg_name_3, | |
| 296 const char* arg_value_3) { | |
| 297 if (!GetInstance() || !GetInstance()->etw_export_enabled_ || | |
| 298 !EventEnabledChromeEvent()) | |
| 299 return; | |
| 300 | |
| 301 EventWriteChromeEvent(name, phase, arg_name_1, arg_value_1, arg_name_2, | |
| 302 arg_value_2, arg_name_3, arg_value_3); | |
| 303 } | |
| 304 | |
| 305 // static | |
| 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 } | |
| 377 } | |
| 378 | |
| 379 } // namespace trace_event | |
| 380 } // namespace base | |
| OLD | NEW |