Index: base/trace_event/trace_event_etw_export_win.cc |
diff --git a/base/trace_event/trace_event_etw_export_win.cc b/base/trace_event/trace_event_etw_export_win.cc |
index d199bf5c8b2e4f6f359335b55e045d393adcb0b5..98e45539c84e1bcd595ea3248a50d9116716d61f 100644 |
--- a/base/trace_event/trace_event_etw_export_win.cc |
+++ b/base/trace_event/trace_event_etw_export_win.cc |
@@ -7,6 +7,7 @@ |
#include "base/command_line.h" |
#include "base/logging.h" |
#include "base/memory/singleton.h" |
+#include "base/strings/string_tokenizer.h" |
#include "base/strings/utf_string_conversions.h" |
#include "base/trace_event/trace_event.h" |
#include "base/trace_event/trace_event_impl.h" |
@@ -40,6 +41,55 @@ typedef ULONG(__stdcall* tEventUnregister)(REGHANDLE RegHandle); |
tEventRegister EventRegisterProc = nullptr; |
tEventWrite EventWriteProc = nullptr; |
tEventUnregister EventUnregisterProc = nullptr; |
+ |
+// |filtered_event_group_names| contains the event categories that can be |
+// exported individually. These categories can be enabled by passing the correct |
+// keyword when starting the trace. A keyword is a 64-bit flag and we attribute |
+// one bit per category. We can therefore enable a particular category by |
+// setting its corresponding bit in the keyword. For events that are not present |
+// in |filtered_event_group_names|, we have two bits that control their |
+// behaviour. When bit 61 is enabled, any event that is not disabled by default |
+// (ie. doesn't start with disabled-by-default-) will be exported. Likewise, |
+// when bit 62 is enabled, any event that is disabled by default will be |
+// exported. |
+// |
+// Note that bit 63 (MSB) must always be set, otherwise tracing will be disabled |
+// by ETW. Therefore, the keyword will always be greater than |
+// 0x8000000000000000. |
+// |
+// Examples of passing keywords to the provider using xperf: |
+// # This exports "benchmark" and "cc" events |
+// xperf -start chrome -on Chrome:0x8000000000000009 |
+// |
+// # This exports "gpu", "netlog" and all other events that are not disabled by |
+// # default |
+// xperf -start chrome -on Chrome:0xA0000000000000A0 |
+// |
+// More info about starting a trace and keyword can be obtained by using the |
+// help section of xperf (xperf -help start). Note that xperf documentation |
+// refers to keywords as flags and there are two ways to enable them, using |
+// group names or the hex representation. We only support the latter. Also, we |
+// ignore the level. |
+const char* const filtered_event_group_names[] = { |
+ "benchmark", // 0x1 |
+ "blink", // 0x2 |
+ "browser", // 0x4 |
+ "cc", // 0x8 |
+ "evdev", // 0x10 |
+ "gpu", // 0x20 |
+ "input", // 0x40 |
+ "netlog", // 0x80 |
+ "renderer.scheduler", // 0x100 |
+ "toplevel", // 0x200 |
+ "v8", // 0x400 |
+ "disabled-by-default-cc.debug", // 0x800 |
+ "disabled-by-default-cc.debug.picture", // 0x1000 |
+ "disabled-by-default-toplevel.flow"}; // 0x2000 |
+const char* other_events_group_name = "__OTHER_EVENTS"; // 0x2000000000000000 |
+const char* disabled_other_events_group_name = |
+ "__DISABLED_OTHER_EVENTS"; // 0x4000000000000000 |
+uint64 other_events_keyword_bit = 1ULL << 61; |
+uint64 disabled_other_events_keyword_bit = 1ULL << 62; |
} // namespace |
// Redirector function for EventRegister. Called by macros in |
@@ -77,7 +127,8 @@ ULONG EVNTAPI EventUnregister(REGHANDLE RegHandle) { |
namespace base { |
namespace trace_event { |
-TraceEventETWExport::TraceEventETWExport() : ETWExportEnabled_(false) { |
+TraceEventETWExport::TraceEventETWExport() |
+ : etw_export_enabled_(false), etw_match_any_keyword_(0ULL) { |
// Find Advapi32.dll. This should always succeed. |
HMODULE AdvapiDLL = ::LoadLibraryW(L"Advapi32.dll"); |
if (AdvapiDLL) { |
@@ -92,6 +143,8 @@ TraceEventETWExport::TraceEventETWExport() : ETWExportEnabled_(false) { |
// Register the ETW provider. If registration fails then the event logging |
// calls will fail (on XP this call will do nothing). |
EventRegisterChrome(); |
+ |
+ UpdateEnabledCategories(); |
} |
} |
@@ -108,13 +161,18 @@ TraceEventETWExport* TraceEventETWExport::GetInstance() { |
// static |
void TraceEventETWExport::EnableETWExport() { |
if (GetInstance()) |
- GetInstance()->ETWExportEnabled_ = true; |
+ GetInstance()->etw_export_enabled_ = true; |
} |
// static |
void TraceEventETWExport::DisableETWExport() { |
if (GetInstance()) |
- GetInstance()->ETWExportEnabled_ = false; |
+ GetInstance()->etw_export_enabled_ = false; |
+} |
+ |
+// static |
+bool TraceEventETWExport::IsETWExportEnabled() { |
+ return (GetInstance() && GetInstance()->etw_export_enabled_); |
} |
// static |
@@ -129,7 +187,7 @@ void TraceEventETWExport::AddEvent( |
const unsigned long long* arg_values, |
const scoped_refptr<ConvertableToTraceFormat>* convertable_values) { |
// We bail early in case exporting is disabled or no consumer is listening. |
- if (!GetInstance() || !GetInstance()->ETWExportEnabled_ || |
+ if (!GetInstance() || !GetInstance()->etw_export_enabled_ || |
!EventEnabledChromeEvent()) |
return; |
@@ -211,7 +269,7 @@ void TraceEventETWExport::AddEvent( |
// *total* process CPU time when ETW tracing, and many of the strings |
// created exceed WPA's 4094 byte limit and are shown as: |
// "Unable to parse data". See crbug.com/488257 |
- //convertable_values[i]->AppendAsTraceFormat(arg_values_string + i); |
+ // convertable_values[i]->AppendAsTraceFormat(arg_values_string + i); |
} else { |
TraceEvent::TraceValue trace_event; |
trace_event.as_uint = arg_values[i]; |
@@ -236,7 +294,7 @@ void TraceEventETWExport::AddCustomEvent(const char* name, |
const char* arg_value_2, |
const char* arg_name_3, |
const char* arg_value_3) { |
- if (!GetInstance() || !GetInstance()->ETWExportEnabled_ || |
+ if (!GetInstance() || !GetInstance()->etw_export_enabled_ || |
!EventEnabledChromeEvent()) |
return; |
@@ -244,8 +302,78 @@ void TraceEventETWExport::AddCustomEvent(const char* name, |
arg_value_2, arg_name_3, arg_value_3); |
} |
-void TraceEventETWExport::Resurrect() { |
- StaticMemorySingletonTraits<TraceEventETWExport>::Resurrect(); |
+// static |
+bool TraceEventETWExport::IsCategoryGroupEnabled( |
+ const char* category_group_name) { |
+ DCHECK(category_group_name); |
+ auto instance = GetInstance(); |
+ if (instance == nullptr) |
+ return false; |
+ |
+ if (!instance->IsETWExportEnabled()) |
+ return false; |
+ |
+ CStringTokenizer category_group_tokens( |
+ category_group_name, category_group_name + strlen(category_group_name), |
+ ","); |
+ while (category_group_tokens.GetNext()) { |
+ std::string category_group_token = category_group_tokens.token(); |
+ if (instance->IsCategoryEnabled(category_group_token.c_str())) { |
+ return true; |
+ } |
+ } |
+ return false; |
+} |
+ |
+bool TraceEventETWExport::UpdateEnabledCategories() { |
+ if (etw_match_any_keyword_ == CHROME_Context.MatchAnyKeyword) |
+ return false; |
+ |
+ // If the keyword has changed, update each category. |
+ // Chrome_Context.MatchAnyKeyword is set by UIforETW (or other ETW trace |
+ // recording tools) using the ETW infrastructure. This value will be set in |
+ // all Chrome processes that have registered their ETW provider. |
+ etw_match_any_keyword_ = CHROME_Context.MatchAnyKeyword; |
+ for (int i = 0; i < ARRAYSIZE(filtered_event_group_names); i++) { |
+ if (etw_match_any_keyword_ & (1ULL << i)) { |
+ categories_status_[filtered_event_group_names[i]] = true; |
+ } else { |
+ categories_status_[filtered_event_group_names[i]] = false; |
+ } |
+ } |
+ |
+ // Also update the two default categories. |
+ if (etw_match_any_keyword_ & other_events_keyword_bit) { |
+ categories_status_[other_events_group_name] = true; |
+ } else { |
+ categories_status_[other_events_group_name] = false; |
+ } |
+ if (etw_match_any_keyword_ & disabled_other_events_keyword_bit) { |
+ categories_status_[disabled_other_events_group_name] = true; |
+ } else { |
+ categories_status_[disabled_other_events_group_name] = false; |
+ } |
+ |
+ return true; |
+} |
+ |
+bool TraceEventETWExport::IsCategoryEnabled(const char* category_name) const { |
+ // Try to find the category and return its status if found |
+ auto it = categories_status_.find(category_name); |
+ if (it != categories_status_.end()) |
+ return it->second; |
+ |
+ // Otherwise return the corresponding default status by first checking if the |
+ // category is disabled by default. |
+ if (StringPiece(category_name).starts_with("disabled-by-default")) { |
+ DCHECK(categories_status_.find(disabled_other_events_group_name) != |
+ categories_status_.end()); |
+ return categories_status_.find(disabled_other_events_group_name)->second; |
+ } else { |
+ DCHECK(categories_status_.find(other_events_group_name) != |
+ categories_status_.end()); |
+ return categories_status_.find(other_events_group_name)->second; |
+ } |
} |
} // namespace trace_event |