1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/tracing/common/etw_export_win.h"
#include <memory>
#include "base/feature_list.h"
#include "base/no_destructor.h"
#include "base/trace_event/etw_interceptor_win.h"
#include "base/trace_event/trace_logging_minimal_win.h"
#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
#include "third_party/perfetto/include/perfetto/tracing/tracing.h"
#include "third_party/perfetto/protos/perfetto/config/interceptor_config.gen.h"
#include "third_party/perfetto/protos/perfetto/config/track_event/track_event_config.gen.h"
namespace tracing {
namespace {
BASE_FEATURE(kEnableEtwExports,
"EnableEtwExports",
base::FEATURE_ENABLED_BY_DEFAULT);
// Used to protect the upper 16 bits reserved by winmeta.xml as they
// should not be used but older logging code and tools incorrectly used
// them.
constexpr uint64_t kCategoryKeywordMask = ~0xFFFF000000000000;
perfetto::TraceConfig CreateTraceConfigForETWKeyword(uint64_t keyword) {
perfetto::TraceConfig config;
auto* data_source = config.add_data_sources();
auto* data_source_config = data_source->mutable_config();
data_source_config->set_name("track_event");
data_source_config->mutable_interceptor_config()->set_name("etwexport");
perfetto::protos::gen::TrackEventConfig track_event_config =
base::trace_event::ETWKeywordToTrackEventConfig(keyword);
data_source_config->set_track_event_config_raw(
track_event_config.SerializeAsString());
return config;
}
class ETWExportController {
public:
ETWExportController();
~ETWExportController() = delete;
// Called from the ETW EnableCallback when the state of the provider or
// keywords has changed.
void OnUpdate(TlmProvider::EventControlCode event);
private:
bool is_registration_complete_ = false;
std::unique_ptr<perfetto::StartupTracingSession> tracing_session_;
// The keywords that were enabled last time the callback was made.
uint64_t etw_match_any_keyword_ = 0;
// The provider is set based on channel for MSEdge, in other Chromium
// based browsers all channels use the same GUID/provider.
std::unique_ptr<TlmProvider> etw_provider_;
};
// This GUID is the used to identify the Chrome provider and is used whenever
// ETW is enabled via tracing tools and cannot change without updating tools
// that collect Chrome ETW data.
constexpr GUID kChromeETWGUID = {
0xD2D578D9,
0x2936,
0x45B6,
{0xA0, 0x9F, 0x30, 0xE3, 0x27, 0x15, 0xF4, 0x2D}};
ETWExportController::ETWExportController() {
// Construct the ETW provider. If construction fails then the event logging
// calls will fail. We're passing a callback function as part of registration.
// This allows us to detect changes to enable/disable/keyword changes.
etw_provider_ = std::make_unique<TlmProvider>(
"Google.Chrome", kChromeETWGUID,
base::BindRepeating(&ETWExportController::OnUpdate,
base::Unretained(this)));
base::trace_event::ETWInterceptor::Register(etw_provider_.get());
is_registration_complete_ = true;
if (etw_provider_->IsEnabled()) {
OnUpdate(TlmProvider::EventControlCode::kEnableProvider);
}
}
void ETWExportController::OnUpdate(TlmProvider::EventControlCode event) {
if (!is_registration_complete_) {
return;
}
if (event == TlmProvider::EventControlCode::kDisableProvider) {
if (tracing_session_) {
tracing_session_->Abort();
tracing_session_ = nullptr;
}
etw_match_any_keyword_ = 0;
return;
}
if (event == TlmProvider::EventControlCode::kEnableProvider) {
if (etw_match_any_keyword_ ==
(etw_provider_->keyword_any() & kCategoryKeywordMask)) {
return;
}
etw_match_any_keyword_ =
etw_provider_->keyword_any() & kCategoryKeywordMask;
if (tracing_session_) {
tracing_session_->Abort();
tracing_session_ = nullptr;
}
// ETW exporter creates a (local) startup session for the current
// process that's never adopted and doesn't timeout. Since every
// process enables export, an independent session is created in each
// process.
perfetto::Tracing::SetupStartupTracingOpts opts;
opts.timeout_ms = 0;
opts.backend = perfetto::kCustomBackend;
perfetto::TraceConfig config =
CreateTraceConfigForETWKeyword(etw_match_any_keyword_);
tracing_session_ = perfetto::Tracing::SetupStartupTracing(config, opts);
}
}
} // namespace
// static
void EnableETWExport() {
if (!base::FeatureList::IsEnabled(kEnableEtwExports)) {
return;
}
static base::NoDestructor<ETWExportController> instance{};
}
} // namespace tracing
|