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
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
,
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
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
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
73 const char* const filtered_event_group_names
[] = {
82 "renderer.scheduler", // 0x100
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;
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
,
108 // Redirector function for EventWrite. Called by macros in
109 // chrome_events_win.h
110 ULONG EVNTAPI
EventWrite(REGHANDLE RegHandle
,
111 PCEVENT_DESCRIPTOR EventDescriptor
,
113 PEVENT_DATA_DESCRIPTOR UserData
) {
115 return EventWriteProc(RegHandle
, EventDescriptor
, UserDataCount
, UserData
);
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
);
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");
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();
156 TraceEventETWExport
* TraceEventETWExport::GetInstance() {
157 return Singleton
<TraceEventETWExport
,
158 StaticMemorySingletonTraits
<TraceEventETWExport
>>::get();
162 void TraceEventETWExport::EnableETWExport() {
164 GetInstance()->etw_export_enabled_
= true;
168 void TraceEventETWExport::DisableETWExport() {
170 GetInstance()->etw_export_enabled_
= false;
174 bool TraceEventETWExport::IsETWExportEnabled() {
175 return (GetInstance() && GetInstance()->etw_export_enabled_
);
179 void TraceEventETWExport::AddEvent(
181 const unsigned char* category_group_enabled
,
183 unsigned long long id
,
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())
194 const char* phase_string
= nullptr;
195 // Space to store the phase identifier and null-terminator, when needed.
196 char phase_buffer
[2];
198 case TRACE_EVENT_PHASE_BEGIN
:
199 phase_string
= "Begin";
201 case TRACE_EVENT_PHASE_END
:
202 phase_string
= "End";
204 case TRACE_EVENT_PHASE_COMPLETE
:
205 phase_string
= "Complete";
207 case TRACE_EVENT_PHASE_INSTANT
:
208 phase_string
= "Instant";
210 case TRACE_EVENT_PHASE_ASYNC_BEGIN
:
211 phase_string
= "Async Begin";
213 case TRACE_EVENT_PHASE_ASYNC_STEP_INTO
:
214 phase_string
= "Async Step Into";
216 case TRACE_EVENT_PHASE_ASYNC_STEP_PAST
:
217 phase_string
= "Async Step Past";
219 case TRACE_EVENT_PHASE_ASYNC_END
:
220 phase_string
= "Async End";
222 case TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN
:
223 phase_string
= "Nestable Async Begin";
225 case TRACE_EVENT_PHASE_NESTABLE_ASYNC_END
:
226 phase_string
= "Nestable Async End";
228 case TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT
:
229 phase_string
= "Nestable Async Instant";
231 case TRACE_EVENT_PHASE_FLOW_BEGIN
:
232 phase_string
= "Phase Flow Begin";
234 case TRACE_EVENT_PHASE_FLOW_STEP
:
235 phase_string
= "Phase Flow Step";
237 case TRACE_EVENT_PHASE_FLOW_END
:
238 phase_string
= "Phase Flow End";
240 case TRACE_EVENT_PHASE_METADATA
:
241 phase_string
= "Phase Metadata";
243 case TRACE_EVENT_PHASE_COUNTER
:
244 phase_string
= "Phase Counter";
246 case TRACE_EVENT_PHASE_SAMPLE
:
247 phase_string
= "Phase Sample";
249 case TRACE_EVENT_PHASE_CREATE_OBJECT
:
250 phase_string
= "Phase Create Object";
252 case TRACE_EVENT_PHASE_SNAPSHOT_OBJECT
:
253 phase_string
= "Phase Snapshot Object";
255 case TRACE_EVENT_PHASE_DELETE_OBJECT
:
256 phase_string
= "Phase Delete Object";
259 phase_buffer
[0] = phase
;
261 phase_string
= phase_buffer
;
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);
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());
289 void TraceEventETWExport::AddCustomEvent(const char* name
,
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())
301 EventWriteChromeEvent(name
, phase
, arg_name_1
, arg_value_1
, arg_name_2
,
302 arg_value_2
, arg_name_3
, arg_value_3
);
306 bool TraceEventETWExport::IsCategoryGroupEnabled(
307 const char* category_group_name
) {
308 DCHECK(category_group_name
);
309 auto instance
= GetInstance();
310 if (instance
== nullptr)
313 if (!instance
->IsETWExportEnabled())
316 CStringTokenizer
category_group_tokens(
317 category_group_name
, category_group_name
+ strlen(category_group_name
),
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())) {
328 bool TraceEventETWExport::UpdateEnabledCategories() {
329 if (etw_match_any_keyword_
== CHROME_Context
.MatchAnyKeyword
)
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;
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;
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;
354 categories_status_
[disabled_other_events_group_name
] = false;
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())
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
;
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