Popular sites on the NTP: Favicon improvements
[chromium-blink-merge.git] / base / trace_event / trace_event_etw_export_win.cc
blobe61a7b29645992d0e160d3d25cd91e2bc5fb58f9
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.
5 #include "base/trace_event/trace_event_etw_export_win.h"
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"
15 // The GetProcAddress technique is borrowed from
16 // https://github.com/google/UIforETW/tree/master/ETWProviders
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.
26 // In SHARED_INTERMEDIATE_DIR.
27 #include "base/trace_event/etw_manifest/chrome_events_win.h" // NOLINT
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);
41 tEventRegister EventRegisterProc = nullptr;
42 tEventWrite EventWriteProc = nullptr;
43 tEventUnregister EventUnregisterProc = nullptr;
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.
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.
60 // Examples of passing keywords to the provider using xperf:
61 // # This exports "benchmark" and "cc" events
62 // xperf -start chrome -on Chrome:0x8000000000000009
64 // # This exports "gpu", "netlog" and all other events that are not disabled by
65 // # default
66 // xperf -start chrome -on Chrome:0xA0000000000000A0
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
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;
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;
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;
127 namespace base {
128 namespace trace_event {
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"));
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();
147 UpdateEnabledCategories();
151 TraceEventETWExport::~TraceEventETWExport() {
152 EventUnregisterChrome();
155 // static
156 TraceEventETWExport* TraceEventETWExport::GetInstance() {
157 return Singleton<TraceEventETWExport,
158 StaticMemorySingletonTraits<TraceEventETWExport>>::get();
161 // static
162 void TraceEventETWExport::EnableETWExport() {
163 if (GetInstance())
164 GetInstance()->etw_export_enabled_ = true;
167 // static
168 void TraceEventETWExport::DisableETWExport() {
169 if (GetInstance())
170 GetInstance()->etw_export_enabled_ = false;
173 // static
174 bool TraceEventETWExport::IsETWExportEnabled() {
175 return (GetInstance() && GetInstance()->etw_export_enabled_);
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;
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;
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);
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());
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;
301 EventWriteChromeEvent(name, phase, arg_name_1, arg_value_1, arg_name_2,
302 arg_value_2, arg_name_3, arg_value_3);
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;
313 if (!instance->IsETWExportEnabled())
314 return false;
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;
325 return false;
328 bool TraceEventETWExport::UpdateEnabledCategories() {
329 if (etw_match_any_keyword_ == CHROME_Context.MatchAnyKeyword)
330 return false;
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;
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;
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;
357 return true;
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;
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;
379 } // namespace trace_event
380 } // namespace base