• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/test/test_trace_processor.h"
6 #include "base/command_line.h"
7 #include "base/files/file_util.h"
8 #include "base/test/chrome_track_event.descriptor.h"
9 #include "base/test/perfetto_sql_stdlib.h"
10 #include "base/trace_event/trace_log.h"
11 #include "third_party/perfetto/protos/perfetto/trace/extension_descriptor.pbzero.h"
12 
13 namespace base::test {
14 
15 #if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
16 
17 namespace {
18 // Emitting the chrome_track_event.descriptor into the trace allows the trace
19 // processor to parse the arguments during ingestion of the trace events.
20 // This function emits the descriptor generated from
21 // base/tracing/protos/chrome_track_event.proto so we can use TestTraceProcessor
22 // to write tests based on new arguments/types added in the same patch.
EmitChromeTrackEventDescriptor()23 void EmitChromeTrackEventDescriptor() {
24   base::TrackEvent::Trace([&](base::TrackEvent::TraceContext ctx) {
25     protozero::MessageHandle<perfetto::protos::pbzero::TracePacket> handle =
26         ctx.NewTracePacket();
27     auto* extension_descriptor = handle->BeginNestedMessage<protozero::Message>(
28         perfetto::protos::pbzero::TracePacket::kExtensionDescriptorFieldNumber);
29     extension_descriptor->AppendBytes(
30         perfetto::protos::pbzero::ExtensionDescriptor::kExtensionSetFieldNumber,
31         perfetto::kChromeTrackEventDescriptor.data(),
32         perfetto::kChromeTrackEventDescriptor.size());
33     handle->Finalize();
34   });
35 }
36 
37 std::string kChromeSqlModuleName = "chrome";
38 // A command-line switch to save the trace test trace processor generated to
39 // make debugging complex traces.
40 constexpr char kSaveTraceSwitch[] = "ttp-save-trace";
41 
42 // Returns a vector of pairs of strings consisting of
43 // {include_key, sql_file_contents}. For example, the include key for
44 // `chrome/scroll_jank/utils.sql` is `chrome.scroll_jank.utils`.
45 // The output is used to override the Chrome SQL module in the trace processor.
GetChromeStdlib()46 TestTraceProcessorImpl::PerfettoSQLModule GetChromeStdlib() {
47   std::vector<std::pair<std::string, std::string>> stdlib;
48   for (const auto& file_to_sql :
49        perfetto::trace_processor::chrome_stdlib::kFileToSql) {
50     std::string include_key;
51     base::ReplaceChars(file_to_sql.path, "/", ".", &include_key);
52     if (include_key.ends_with(".sql")) {
53       include_key.resize(include_key.size() - 4);
54     }
55     stdlib.emplace_back(kChromeSqlModuleName + "." + include_key,
56                         file_to_sql.sql);
57   }
58   return stdlib;
59 }
60 }  // namespace
61 
DefaultTraceConfig(const StringPiece & category_filter_string,bool privacy_filtering)62 TraceConfig DefaultTraceConfig(const StringPiece& category_filter_string,
63                                bool privacy_filtering) {
64   TraceConfig trace_config;
65   auto* buffer_config = trace_config.add_buffers();
66   buffer_config->set_size_kb(4 * 1024);
67 
68   auto* data_source = trace_config.add_data_sources();
69   auto* source_config = data_source->mutable_config();
70   source_config->set_name("track_event");
71   source_config->set_target_buffer(0);
72 
73   perfetto::protos::gen::TrackEventConfig track_event_config;
74   base::trace_event::TraceConfigCategoryFilter category_filter;
75   category_filter.InitializeFromString(category_filter_string);
76 
77   // If no categories are explicitly enabled, enable the default ones.
78   // Otherwise only matching categories are enabled.
79   if (!category_filter.included_categories().empty()) {
80     track_event_config.add_disabled_categories("*");
81   }
82   for (const auto& included_category : category_filter.included_categories()) {
83     track_event_config.add_enabled_categories(included_category);
84   }
85   for (const auto& disabled_category : category_filter.disabled_categories()) {
86     track_event_config.add_enabled_categories(disabled_category);
87   }
88   for (const auto& excluded_category : category_filter.excluded_categories()) {
89     track_event_config.add_disabled_categories(excluded_category);
90   }
91 
92   // This category is added by default to tracing sessions initiated via
93   // command-line flags (see TraceConfig::ToPerfettoTrackEventConfigRaw),
94   // so to adopt startup sessions correctly, we need to specify it too.
95   track_event_config.add_enabled_categories("__metadata");
96 
97   if (privacy_filtering) {
98     track_event_config.set_filter_debug_annotations(true);
99     track_event_config.set_filter_dynamic_event_names(true);
100   }
101 
102   source_config->set_track_event_config_raw(
103       track_event_config.SerializeAsString());
104 
105   return trace_config;
106 }
107 
TestTraceProcessor()108 TestTraceProcessor::TestTraceProcessor() {
109   auto status = test_trace_processor_.OverrideSqlModule(kChromeSqlModuleName,
110                                                         GetChromeStdlib());
111   CHECK(status.ok());
112 }
113 
114 TestTraceProcessor::~TestTraceProcessor() = default;
115 
StartTrace(const StringPiece & category_filter_string,bool privacy_filtering)116 void TestTraceProcessor::StartTrace(const StringPiece& category_filter_string,
117                                     bool privacy_filtering) {
118   StartTrace(DefaultTraceConfig(category_filter_string, privacy_filtering));
119 }
120 
StartTrace(const TraceConfig & config,perfetto::BackendType backend)121 void TestTraceProcessor::StartTrace(const TraceConfig& config,
122                                     perfetto::BackendType backend) {
123   // Try to guess the correct backend if it's unspecified. In unit tests
124   // Perfetto is initialized by TraceLog, and only the in-process backend is
125   // available. In browser tests multiple backend can be available, so we
126   // explicitly specialize the custom backend to prevent tests from connecting
127   // to a system backend.
128   if (backend == perfetto::kUnspecifiedBackend) {
129     if (base::trace_event::TraceLog::GetInstance()
130             ->IsPerfettoInitializedByTraceLog()) {
131       backend = perfetto::kInProcessBackend;
132     } else {
133       backend = perfetto::kCustomBackend;
134     }
135   }
136   session_ = perfetto::Tracing::NewTrace(backend);
137   session_->Setup(config);
138   // Some tests run the tracing service on the main thread and StartBlocking()
139   // can deadlock so use a RunLoop instead.
140   base::RunLoop run_loop;
141   session_->SetOnStartCallback([&run_loop]() { run_loop.QuitWhenIdle(); });
142   session_->Start();
143   run_loop.Run();
144 }
145 
StopAndParseTrace()146 absl::Status TestTraceProcessor::StopAndParseTrace() {
147   EmitChromeTrackEventDescriptor();
148   base::TrackEvent::Flush();
149   session_->StopBlocking();
150   std::vector<char> trace = session_->ReadTraceBlocking();
151 
152   if (CommandLine::ForCurrentProcess()->HasSwitch(kSaveTraceSwitch)) {
153     ScopedAllowBlockingForTesting allow;
154     WriteFile(base::FilePath::FromASCII("test.pftrace"), trace.data(),
155               trace.size());
156   }
157 
158   return test_trace_processor_.ParseTrace(trace);
159 }
160 
161 base::expected<TestTraceProcessor::QueryResult, std::string>
RunQuery(const std::string & query)162 TestTraceProcessor::RunQuery(const std::string& query) {
163   auto result_or_error = test_trace_processor_.ExecuteQuery(query);
164   if (!result_or_error.ok()) {
165     return base::unexpected(result_or_error.error());
166   }
167   return base::ok(result_or_error.result());
168 }
169 
170 #endif  // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
171 
172 }  // namespace base::test
173