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