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/threading/platform_thread.h"
13 #include "base/trace_event/trace_event.h"
14 #include "base/trace_event/trace_event_impl.h"
16 // The GetProcAddress technique is borrowed from
17 // https://github.com/google/UIforETW/tree/master/ETWProviders
19 // EVNTAPI is used in evntprov.h which is included by chrome_events_win.h.
20 // We define EVNTAPI without the DECLSPEC_IMPORT specifier so that we can
21 // implement these functions locally instead of using the import library, and
22 // can therefore still run on Windows XP.
23 #define EVNTAPI __stdcall
24 // Include the event register/write/unregister macros compiled from the manifest
25 // file. Note that this includes evntprov.h which requires a Vista+ Windows SDK.
27 // In SHARED_INTERMEDIATE_DIR.
28 #include "base/trace_event/etw_manifest/chrome_events_win.h" // NOLINT
31 // Typedefs for use with GetProcAddress
32 typedef ULONG(__stdcall
* tEventRegister
)(LPCGUID ProviderId
,
33 PENABLECALLBACK EnableCallback
,
34 PVOID CallbackContext
,
35 PREGHANDLE RegHandle
);
36 typedef ULONG(__stdcall
* tEventWrite
)(REGHANDLE RegHandle
,
37 PCEVENT_DESCRIPTOR EventDescriptor
,
39 PEVENT_DATA_DESCRIPTOR UserData
);
40 typedef ULONG(__stdcall
* tEventUnregister
)(REGHANDLE RegHandle
);
42 tEventRegister EventRegisterProc
= nullptr;
43 tEventWrite EventWriteProc
= nullptr;
44 tEventUnregister EventUnregisterProc
= nullptr;
46 // |kFilteredEventGroupNames| contains the event categories that can be
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
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
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
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
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
59 // 0x8000000000000000.
61 // Examples of passing keywords to the provider using xperf:
62 // # This exports "benchmark" and "cc" events
63 // xperf -start chrome -on Chrome:0x8000000000000009
65 // # This exports "gpu", "netlog" and all other events that are not disabled by
67 // xperf -start chrome -on Chrome:0xA0000000000000A0
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
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
74 const char* const kFilteredEventGroupNames
[] = {
83 "renderer.scheduler", // 0x100
86 "disabled-by-default-cc.debug", // 0x800
87 "disabled-by-default-cc.debug.picture", // 0x1000
88 "disabled-by-default-toplevel.flow"}; // 0x2000
89 const char kOtherEventsGroupName
[] = "__OTHER_EVENTS"; // 0x2000000000000000
90 const char kDisabledOtherEventsGroupName
[] =
91 "__DISABLED_OTHER_EVENTS"; // 0x4000000000000000
92 const uint64 kOtherEventsKeywordBit
= 1ULL << 61;
93 const uint64 kDisabledOtherEventsKeywordBit
= 1ULL << 62;
94 const size_t kNumberOfCategories
= ARRAYSIZE(kFilteredEventGroupNames
) + 2U;
98 // Redirector function for EventRegister. Called by macros in
99 // chrome_events_win.h
100 ULONG EVNTAPI
EventRegister(LPCGUID ProviderId
,
101 PENABLECALLBACK EnableCallback
,
102 PVOID CallbackContext
,
103 PREGHANDLE RegHandle
) {
104 if (EventRegisterProc
)
105 return EventRegisterProc(ProviderId
, EnableCallback
, CallbackContext
,
111 // Redirector function for EventWrite. Called by macros in
112 // chrome_events_win.h
113 ULONG EVNTAPI
EventWrite(REGHANDLE RegHandle
,
114 PCEVENT_DESCRIPTOR EventDescriptor
,
116 PEVENT_DATA_DESCRIPTOR UserData
) {
118 return EventWriteProc(RegHandle
, EventDescriptor
, UserDataCount
, UserData
);
122 // Redirector function for EventUnregister. Called by macros in
123 // chrome_events_win.h
124 ULONG EVNTAPI
EventUnregister(REGHANDLE RegHandle
) {
125 if (EventUnregisterProc
)
126 return EventUnregisterProc(RegHandle
);
131 namespace trace_event
{
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 PlatformThread::Delegate
{
138 ETWKeywordUpdateThread() {}
139 ~ETWKeywordUpdateThread() override
{}
141 // Implementation of PlatformThread::Delegate:
142 void ThreadMain() override
{
143 PlatformThread::SetName("ETW Keyword Update Thread");
144 TimeDelta sleep_time
= TimeDelta::FromMilliseconds(kUpdateTimerDelayMs
);
146 PlatformThread::Sleep(sleep_time
);
147 trace_event::TraceEventETWExport::UpdateETWKeyword();
152 // Time between checks for ETW keyword changes (in milliseconds).
153 unsigned int kUpdateTimerDelayMs
= 1000;
157 TraceEventETWExport::TraceEventETWExport()
158 : etw_export_enabled_(false), etw_match_any_keyword_(0ULL) {
159 // Find Advapi32.dll. This should always succeed.
160 HMODULE AdvapiDLL
= ::LoadLibraryW(L
"Advapi32.dll");
162 // Try to find the ETW functions. This will fail on XP.
163 EventRegisterProc
= reinterpret_cast<tEventRegister
>(
164 ::GetProcAddress(AdvapiDLL
, "EventRegister"));
165 EventWriteProc
= reinterpret_cast<tEventWrite
>(
166 ::GetProcAddress(AdvapiDLL
, "EventWrite"));
167 EventUnregisterProc
= reinterpret_cast<tEventUnregister
>(
168 ::GetProcAddress(AdvapiDLL
, "EventUnregister"));
170 // Register the ETW provider. If registration fails then the event logging
171 // calls will fail (on XP this call will do nothing).
172 EventRegisterChrome();
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());
186 TraceEventETWExport::~TraceEventETWExport() {
187 EventUnregisterChrome();
191 TraceEventETWExport
* TraceEventETWExport::GetInstance() {
192 return Singleton
<TraceEventETWExport
,
193 StaticMemorySingletonTraits
<TraceEventETWExport
>>::get();
197 void TraceEventETWExport::EnableETWExport() {
198 auto* instance
= GetInstance();
199 if (instance
&& !instance
->etw_export_enabled_
) {
200 instance
->etw_export_enabled_
= true;
201 // Sync the enabled categories with ETW by calling UpdateEnabledCategories()
202 // that checks the keyword. Then create a thread that will call that same
203 // function periodically, to make sure we stay in sync.
204 instance
->UpdateEnabledCategories();
205 if (instance
->keyword_update_thread_handle_
.is_null()) {
206 instance
->keyword_update_thread_
.reset(new ETWKeywordUpdateThread
);
207 PlatformThread::CreateWithPriority(
208 0, instance
->keyword_update_thread_
.get(),
209 &instance
->keyword_update_thread_handle_
, ThreadPriority::BACKGROUND
);
215 void TraceEventETWExport::DisableETWExport() {
216 auto* instance
= GetInstance();
217 if (instance
&& instance
->etw_export_enabled_
)
218 instance
->etw_export_enabled_
= false;
222 bool TraceEventETWExport::IsETWExportEnabled() {
223 auto* instance
= GetInstance();
224 return (instance
&& instance
->etw_export_enabled_
);
228 void TraceEventETWExport::AddEvent(
230 const unsigned char* category_group_enabled
,
232 unsigned long long id
,
234 const char** arg_names
,
235 const unsigned char* arg_types
,
236 const unsigned long long* arg_values
,
237 const scoped_refptr
<ConvertableToTraceFormat
>* convertable_values
) {
238 // We bail early in case exporting is disabled or no consumer is listening.
239 auto* instance
= GetInstance();
240 if (!instance
|| !instance
->etw_export_enabled_
|| !EventEnabledChromeEvent())
243 const char* phase_string
= nullptr;
244 // Space to store the phase identifier and null-terminator, when needed.
245 char phase_buffer
[2];
247 case TRACE_EVENT_PHASE_BEGIN
:
248 phase_string
= "Begin";
250 case TRACE_EVENT_PHASE_END
:
251 phase_string
= "End";
253 case TRACE_EVENT_PHASE_COMPLETE
:
254 phase_string
= "Complete";
256 case TRACE_EVENT_PHASE_INSTANT
:
257 phase_string
= "Instant";
259 case TRACE_EVENT_PHASE_ASYNC_BEGIN
:
260 phase_string
= "Async Begin";
262 case TRACE_EVENT_PHASE_ASYNC_STEP_INTO
:
263 phase_string
= "Async Step Into";
265 case TRACE_EVENT_PHASE_ASYNC_STEP_PAST
:
266 phase_string
= "Async Step Past";
268 case TRACE_EVENT_PHASE_ASYNC_END
:
269 phase_string
= "Async End";
271 case TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN
:
272 phase_string
= "Nestable Async Begin";
274 case TRACE_EVENT_PHASE_NESTABLE_ASYNC_END
:
275 phase_string
= "Nestable Async End";
277 case TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT
:
278 phase_string
= "Nestable Async Instant";
280 case TRACE_EVENT_PHASE_FLOW_BEGIN
:
281 phase_string
= "Phase Flow Begin";
283 case TRACE_EVENT_PHASE_FLOW_STEP
:
284 phase_string
= "Phase Flow Step";
286 case TRACE_EVENT_PHASE_FLOW_END
:
287 phase_string
= "Phase Flow End";
289 case TRACE_EVENT_PHASE_METADATA
:
290 phase_string
= "Phase Metadata";
292 case TRACE_EVENT_PHASE_COUNTER
:
293 phase_string
= "Phase Counter";
295 case TRACE_EVENT_PHASE_SAMPLE
:
296 phase_string
= "Phase Sample";
298 case TRACE_EVENT_PHASE_CREATE_OBJECT
:
299 phase_string
= "Phase Create Object";
301 case TRACE_EVENT_PHASE_SNAPSHOT_OBJECT
:
302 phase_string
= "Phase Snapshot Object";
304 case TRACE_EVENT_PHASE_DELETE_OBJECT
:
305 phase_string
= "Phase Delete Object";
308 phase_buffer
[0] = phase
;
310 phase_string
= phase_buffer
;
314 std::string arg_values_string
[3];
315 for (int i
= 0; i
< num_args
; i
++) {
316 if (arg_types
[i
] == TRACE_VALUE_TYPE_CONVERTABLE
) {
317 // Temporarily do nothing here. This function consumes 1/3 to 1/2 of
318 // *total* process CPU time when ETW tracing, and many of the strings
319 // created exceed WPA's 4094 byte limit and are shown as:
320 // "Unable to parse data". See crbug.com/488257
321 // convertable_values[i]->AppendAsTraceFormat(arg_values_string + i);
323 TraceEvent::TraceValue trace_event
;
324 trace_event
.as_uint
= arg_values
[i
];
325 TraceEvent::AppendValueAsJSON(arg_types
[i
], trace_event
,
326 arg_values_string
+ i
);
330 EventWriteChromeEvent(
331 name
, phase_string
, num_args
> 0 ? arg_names
[0] : "",
332 arg_values_string
[0].c_str(), num_args
> 1 ? arg_names
[1] : "",
333 arg_values_string
[1].c_str(), num_args
> 2 ? arg_names
[2] : "",
334 arg_values_string
[2].c_str());
338 void TraceEventETWExport::AddCustomEvent(const char* name
,
340 const char* arg_name_1
,
341 const char* arg_value_1
,
342 const char* arg_name_2
,
343 const char* arg_value_2
,
344 const char* arg_name_3
,
345 const char* arg_value_3
) {
346 auto* instance
= GetInstance();
347 if (!instance
|| !instance
->etw_export_enabled_
|| !EventEnabledChromeEvent())
350 EventWriteChromeEvent(name
, phase
, arg_name_1
, arg_value_1
, arg_name_2
,
351 arg_value_2
, arg_name_3
, arg_value_3
);
355 bool TraceEventETWExport::IsCategoryGroupEnabled(
356 const char* category_group_name
) {
357 DCHECK(category_group_name
);
358 auto* instance
= GetInstance();
359 if (instance
== nullptr)
362 if (!instance
->IsETWExportEnabled())
365 CStringTokenizer
category_group_tokens(
366 category_group_name
, category_group_name
+ strlen(category_group_name
),
368 while (category_group_tokens
.GetNext()) {
369 std::string category_group_token
= category_group_tokens
.token();
370 if (instance
->IsCategoryEnabled(category_group_token
.c_str())) {
377 bool TraceEventETWExport::UpdateEnabledCategories() {
378 if (etw_match_any_keyword_
== CHROME_Context
.MatchAnyKeyword
)
381 // If the keyword has changed, update each category.
382 // Chrome_Context.MatchAnyKeyword is set by UIforETW (or other ETW trace
383 // recording tools) using the ETW infrastructure. This value will be set in
384 // all Chrome processes that have registered their ETW provider.
385 etw_match_any_keyword_
= CHROME_Context
.MatchAnyKeyword
;
386 for (int i
= 0; i
< ARRAYSIZE(kFilteredEventGroupNames
); i
++) {
387 if (etw_match_any_keyword_
& (1ULL << i
)) {
388 categories_status_
[kFilteredEventGroupNames
[i
]] = true;
390 categories_status_
[kFilteredEventGroupNames
[i
]] = false;
394 // Also update the two default categories.
395 if (etw_match_any_keyword_
& kOtherEventsKeywordBit
) {
396 categories_status_
[kOtherEventsGroupName
] = true;
398 categories_status_
[kOtherEventsGroupName
] = false;
400 if (etw_match_any_keyword_
& kDisabledOtherEventsKeywordBit
) {
401 categories_status_
[kDisabledOtherEventsGroupName
] = true;
403 categories_status_
[kDisabledOtherEventsGroupName
] = false;
406 DCHECK_EQ(kNumberOfCategories
, categories_status_
.size());
408 // Update the categories in TraceLog.
409 TraceLog::GetInstance()->UpdateETWCategoryGroupEnabledFlags();
414 bool TraceEventETWExport::IsCategoryEnabled(const char* category_name
) const {
415 DCHECK_EQ(kNumberOfCategories
, categories_status_
.size());
416 // Try to find the category and return its status if found
417 auto it
= categories_status_
.find(category_name
);
418 if (it
!= categories_status_
.end())
421 // Otherwise return the corresponding default status by first checking if the
422 // category is disabled by default.
423 if (StringPiece(category_name
).starts_with("disabled-by-default")) {
424 DCHECK(categories_status_
.find(kDisabledOtherEventsGroupName
) !=
425 categories_status_
.end());
426 return categories_status_
.find(kDisabledOtherEventsGroupName
)->second
;
428 DCHECK(categories_status_
.find(kOtherEventsGroupName
) !=
429 categories_status_
.end());
430 return categories_status_
.find(kOtherEventsGroupName
)->second
;
435 void TraceEventETWExport::UpdateETWKeyword() {
436 if (!IsETWExportEnabled())
438 auto* instance
= GetInstance();
440 instance
->UpdateEnabledCategories();
442 } // namespace trace_event