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