blob: d95f847cec7389a1db576a650a3533d4229752c2 [file] [log] [blame]
Etienne Pierre-doraybb577032023-06-24 04:44:281// 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 "content/browser/tracing/tracing_scenario.h"
Etienne Pierre-doray89055052024-04-02 19:51:396
Etienne Pierre-doray8a78cda2023-10-06 14:50:177#include <memory>
Tom Sepez8f3e25a2024-10-05 00:28:228#include <utility>
Etienne Pierre-doraybb577032023-06-24 04:44:289
Etienne Pierre-doray89055052024-04-02 19:51:3910#include "base/hash/md5.h"
Etienne Pierre-doray825d20a12023-07-10 18:24:4411#include "base/memory/ptr_util.h"
12#include "base/memory/ref_counted_memory.h"
Etienne Pierre-doray97f2a832024-04-05 15:55:5513#include "base/metrics/histogram_functions.h"
Etienne Pierre-doray57db71a2024-12-10 23:50:4314#include "base/strings/strcat.h"
Etienne Pierre-doraybb577032023-06-24 04:44:2815#include "base/strings/stringprintf.h"
Alex Attarf62d4e22023-09-26 16:49:4316#include "base/token.h"
Etienne Pierre-doraybb577032023-06-24 04:44:2817#include "base/tracing/trace_time.h"
Etienne Pierre-dorayb4d088dd2023-09-27 12:40:5118#include "components/variations/hashing.h"
Etienne Pierre-doray825d20a12023-07-10 18:24:4419#include "content/browser/tracing/background_tracing_manager_impl.h"
Etienne Pierre-doray23fe82c52024-11-25 17:57:4620#include "content/browser/tracing/triggers_data_source.h"
Etienne Pierre-doraybb577032023-06-24 04:44:2821#include "content/public/browser/browser_task_traits.h"
22#include "content/public/browser/browser_thread.h"
Etienne Pierre-doray825d20a12023-07-10 18:24:4423#include "services/tracing/public/cpp/perfetto/perfetto_config.h"
Etienne Pierre-doray801a50c2024-03-19 02:23:5824#include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
Chinglin Yu6e022962025-01-30 10:23:2625#include "services/tracing/public/cpp/tracing_features.h"
Etienne Pierre-doraybb577032023-06-24 04:44:2826#include "third_party/perfetto/protos/perfetto/config/track_event/track_event_config.gen.h"
27
28namespace content {
29
Etienne Pierre-doray3e01b522023-10-10 15:38:3830namespace {
31
Etienne Pierre-doray801a50c2024-03-19 02:23:5832constexpr uint32_t kStartupTracingTimeoutMs = 30 * 1000; // 30 sec
Etienne Pierre-doray801a50c2024-03-19 02:23:5833
Etienne Pierre-doray3e01b522023-10-10 15:38:3834} // namespace
35
Etienne Pierre-doray78ffab22023-08-07 18:00:0036void TracingScenario::TracingSessionDeleter::operator()(
37 perfetto::TracingSession* ptr) const {
38 ptr->SetOnErrorCallback({});
39 delete ptr;
40}
41
42class TracingScenario::TraceReader
43 : public base::RefCountedThreadSafe<TraceReader> {
44 public:
Etienne Pierre-doray14445052023-12-08 17:46:2245 explicit TraceReader(TracingSession tracing_session, base::Token trace_uuid)
46 : tracing_session(std::move(tracing_session)), trace_uuid(trace_uuid) {}
Etienne Pierre-doray78ffab22023-08-07 18:00:0047
48 TracingSession tracing_session;
Etienne Pierre-doray14445052023-12-08 17:46:2249 base::Token trace_uuid;
Etienne Pierre-doray78ffab22023-08-07 18:00:0050 std::string serialized_trace;
51
Etienne Pierre-doray9c0b4c62024-11-08 14:00:1652 static void ReadTrace(scoped_refptr<TracingScenario::TraceReader> reader,
53 base::WeakPtr<TracingScenario> scenario,
54 scoped_refptr<base::SequencedTaskRunner> task_runner,
55 const BackgroundTracingRule* triggered_rule) {
Etienne Pierre-dorayb5e78be2025-08-18 18:30:1356 base::TimeTicks read_start_time = base::TimeTicks::Now();
57 TRACE_EVENT_BEGIN(
58 "tracing.background", "ReadTrace",
59 perfetto::NamedTrack::FromPointer("Scenario.ReadTrace", reader.get()));
Etienne Pierre-doray9c0b4c62024-11-08 14:00:1660 reader->tracing_session->ReadTrace(
Etienne Pierre-dorayb5e78be2025-08-18 18:30:1361 [task_runner, scenario, reader, triggered_rule, read_start_time](
Etienne Pierre-doray9c0b4c62024-11-08 14:00:1662 perfetto::TracingSession::ReadTraceCallbackArgs args) mutable {
63 if (args.size) {
64 reader->serialized_trace.append(args.data, args.size);
65 }
66 if (!args.has_more) {
Etienne Pierre-dorayb5e78be2025-08-18 18:30:1367 TRACE_EVENT_END("tracing.background",
68 perfetto::NamedTrack::FromPointer(
69 "Scenario.ReadTrace", reader.get()));
70 base::UmaHistogramMediumTimes(
71 "Tracing.Background.ReadTraceDuration",
72 base::TimeTicks::Now() - read_start_time);
Etienne Pierre-doray9c0b4c62024-11-08 14:00:1673 task_runner->PostTask(
74 FROM_HERE, base::BindOnce(&TracingScenario::OnFinalizingDone,
75 scenario, reader->trace_uuid,
76 std::move(reader->serialized_trace),
77 std::move(reader->tracing_session),
78 triggered_rule));
79 }
80 });
81 }
82
Etienne Pierre-doray78ffab22023-08-07 18:00:0083 private:
84 friend class base::RefCountedThreadSafe<TraceReader>;
85
86 ~TraceReader() = default;
87};
88
Etienne Pierre-doray825d20a12023-07-10 18:24:4489using Metrics = BackgroundTracingManagerImpl::Metrics;
90
Etienne Pierre-doray8a78cda2023-10-06 14:50:1791TracingScenarioBase::~TracingScenarioBase() = default;
92
93void TracingScenarioBase::Disable() {
94 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
95 for (auto& rule : start_rules_) {
96 rule->Uninstall();
97 }
98 for (auto& rule : stop_rules_) {
99 rule->Uninstall();
100 }
101 for (auto& rule : upload_rules_) {
102 rule->Uninstall();
103 }
104}
105
106void TracingScenarioBase::Enable() {
107 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
108 for (auto& rule : start_rules_) {
109 rule->Install(base::BindRepeating(&TracingScenarioBase::OnStartTrigger,
110 base::Unretained(this)));
111 }
112}
113
Etienne Pierre-doray97f2a832024-04-05 15:55:55114uint32_t TracingScenarioBase::TriggerNameHash(
115 const BackgroundTracingRule* triggered_rule) const {
116 return variations::HashName(
Etienne Pierre-doraycf6f2442024-12-03 18:23:47117 base::StrCat({scenario_name(), ".", triggered_rule->rule_name()}));
Etienne Pierre-doray97f2a832024-04-05 15:55:55118}
119
Tom Sepez8f3e25a2024-10-05 00:28:22120TracingScenarioBase::TracingScenarioBase(std::string scenario_name)
121 : scenario_name_(std::move(scenario_name)),
Etienne Pierre-doray8a78cda2023-10-06 14:50:17122 task_runner_(base::SequencedTaskRunner::GetCurrentDefault()) {}
123
Etienne Pierre-doray3e01b522023-10-10 15:38:38124// static
125std::unique_ptr<NestedTracingScenario> NestedTracingScenario::Create(
126 const perfetto::protos::gen::NestedScenarioConfig& config,
127 Delegate* scenario_delegate) {
128 auto scenario =
129 base::WrapUnique(new NestedTracingScenario(config, scenario_delegate));
130 if (!scenario->Initialize(config)) {
131 return nullptr;
132 }
133 return scenario;
134}
135
Etienne Pierre-doray8a78cda2023-10-06 14:50:17136NestedTracingScenario::NestedTracingScenario(
137 const perfetto::protos::gen::NestedScenarioConfig& config,
138 Delegate* scenario_delegate)
139 : TracingScenarioBase(config.scenario_name()),
Etienne Pierre-doray3e01b522023-10-10 15:38:38140 scenario_delegate_(scenario_delegate) {}
Etienne Pierre-doray8a78cda2023-10-06 14:50:17141
142NestedTracingScenario::~NestedTracingScenario() = default;
143
144void NestedTracingScenario::Disable() {
145 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
146 SetState(State::kDisabled);
147 TracingScenarioBase::Disable();
148}
149
150void NestedTracingScenario::Enable() {
151 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
152 CHECK_EQ(current_state_, State::kDisabled);
153 SetState(State::kEnabled);
154 TracingScenarioBase::Enable();
155}
156
Etienne Pierre-doray3e01b522023-10-10 15:38:38157bool NestedTracingScenario::Initialize(
158 const perfetto::protos::gen::NestedScenarioConfig& config) {
Etienne Pierre-doraya83afef2024-04-17 16:49:38159 return BackgroundTracingRule::Append(config.start_rules(), start_rules_) &&
160 BackgroundTracingRule::Append(config.stop_rules(), stop_rules_) &&
161 BackgroundTracingRule::Append(config.upload_rules(), upload_rules_);
Etienne Pierre-doray3e01b522023-10-10 15:38:38162}
163
Etienne Pierre-doray8a78cda2023-10-06 14:50:17164bool NestedTracingScenario::OnStartTrigger(
165 const BackgroundTracingRule* triggered_rule) {
166 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
167 if (current_state() != State::kEnabled) {
168 return false;
169 }
Etienne Pierre-doraycf6f2442024-12-03 18:23:47170 TriggersDataSource::EmitTrigger(triggered_rule);
Etienne Pierre-doray97f2a832024-04-05 15:55:55171 base::UmaHistogramSparse("Tracing.Background.Scenario.Trigger.Start",
172 TriggerNameHash(triggered_rule));
Etienne Pierre-doray8a78cda2023-10-06 14:50:17173 for (auto& rule : start_rules_) {
174 rule->Uninstall();
175 }
176 for (auto& rule : stop_rules_) {
177 rule->Install(base::BindRepeating(&NestedTracingScenario::OnStopTrigger,
178 base::Unretained(this)));
179 }
180 for (auto& rule : upload_rules_) {
181 rule->Install(base::BindRepeating(&NestedTracingScenario::OnUploadTrigger,
182 base::Unretained(this)));
183 }
184 scenario_delegate_->OnNestedScenarioStart(this);
185 SetState(State::kActive);
186 return true;
187}
188
189bool NestedTracingScenario::OnStopTrigger(
190 const BackgroundTracingRule* triggered_rule) {
191 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Etienne Pierre-doraycf6f2442024-12-03 18:23:47192 TriggersDataSource::EmitTrigger(triggered_rule);
Etienne Pierre-doray97f2a832024-04-05 15:55:55193 base::UmaHistogramSparse("Tracing.Background.Scenario.Trigger.Stop",
194 TriggerNameHash(triggered_rule));
Etienne Pierre-doray8a78cda2023-10-06 14:50:17195 for (auto& rule : stop_rules_) {
196 rule->Uninstall();
197 }
198 SetState(State::kStopping);
199 scenario_delegate_->OnNestedScenarioStop(this);
200 return true;
201}
202
203bool NestedTracingScenario::OnUploadTrigger(
204 const BackgroundTracingRule* triggered_rule) {
205 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Etienne Pierre-dorayf6576d942025-04-22 17:42:13206 TriggersDataSource::EmitTrigger(triggered_rule);
207 base::UmaHistogramSparse("Tracing.Background.Scenario.Trigger.Upload",
208 TriggerNameHash(triggered_rule));
Etienne Pierre-doray8a78cda2023-10-06 14:50:17209
210 for (auto& rule : stop_rules_) {
211 rule->Uninstall();
212 }
213 for (auto& rule : upload_rules_) {
214 rule->Uninstall();
215 }
216 SetState(State::kDisabled);
217 scenario_delegate_->OnNestedScenarioUpload(this, triggered_rule);
218 return true;
219}
220
221void NestedTracingScenario::SetState(State new_state) {
222 current_state_ = new_state;
223}
224
Etienne Pierre-doray825d20a12023-07-10 18:24:44225// static
226std::unique_ptr<TracingScenario> TracingScenario::Create(
227 const perfetto::protos::gen::ScenarioConfig& config,
Etienne Pierre-doray89055052024-04-02 19:51:39228 bool enable_privacy_filter,
Etienne Pierre-dorayffeda342024-11-05 21:47:47229 bool is_local_scenario,
Etienne Pierre-doray72caf352023-09-28 04:05:06230 bool enable_package_name_filter,
Etienne Pierre-doray0af80e72024-06-28 18:25:29231 bool request_startup_tracing,
Etienne Pierre-doray98ecab42023-09-25 17:35:58232 Delegate* scenario_delegate) {
Etienne Pierre-doray89055052024-04-02 19:51:39233 auto scenario = base::WrapUnique(
Etienne Pierre-doray0af80e72024-06-28 18:25:29234 new TracingScenario(config, scenario_delegate, enable_privacy_filter,
Etienne Pierre-dorayffeda342024-11-05 21:47:47235 is_local_scenario, request_startup_tracing));
Etienne Pierre-doray89055052024-04-02 19:51:39236 if (!scenario->Initialize(config, enable_package_name_filter)) {
Etienne Pierre-doray825d20a12023-07-10 18:24:44237 return nullptr;
238 }
239 return scenario;
240}
241
Etienne Pierre-doraybb577032023-06-24 04:44:28242TracingScenario::TracingScenario(
243 const perfetto::protos::gen::ScenarioConfig& config,
Etienne Pierre-doray89055052024-04-02 19:51:39244 Delegate* scenario_delegate,
Etienne Pierre-doray0af80e72024-06-28 18:25:29245 bool enable_privacy_filter,
Etienne Pierre-dorayffeda342024-11-05 21:47:47246 bool is_local_scenario,
Etienne Pierre-doray0af80e72024-06-28 18:25:29247 bool request_startup_tracing)
Etienne Pierre-doray8a78cda2023-10-06 14:50:17248 : TracingScenarioBase(config.scenario_name()),
Etienne Pierre-dorayc5029de2025-05-27 15:48:54249 description_(config.scenario_description()),
Etienne Pierre-doray89055052024-04-02 19:51:39250 privacy_filtering_enabled_(enable_privacy_filter),
Etienne Pierre-dorayffeda342024-11-05 21:47:47251 is_local_scenario_(is_local_scenario),
Etienne Pierre-doray0af80e72024-06-28 18:25:29252 request_startup_tracing_(request_startup_tracing),
Chinglin Yu6e022962025-01-30 10:23:26253 use_system_backend_(config.use_system_backend()),
Etienne Pierre-doraybb577032023-06-24 04:44:28254 trace_config_(config.trace_config()),
Etienne Pierre-doray3e01b522023-10-10 15:38:38255 scenario_delegate_(scenario_delegate) {}
Etienne Pierre-doraybb577032023-06-24 04:44:28256
257TracingScenario::~TracingScenario() = default;
258
Etienne Pierre-doray3e01b522023-10-10 15:38:38259bool TracingScenario::Initialize(
260 const perfetto::protos::gen::ScenarioConfig& config,
Etienne Pierre-doray3e01b522023-10-10 15:38:38261 bool enable_package_name_filter) {
Chinglin Yu6e022962025-01-30 10:23:26262 bool enable_system_backend =
263 use_system_backend_ && tracing::SystemBackgroundTracingEnabled();
Etienne Pierre-doray3e01b522023-10-10 15:38:38264 if (!tracing::AdaptPerfettoConfigForChrome(
Etienne Pierre-doray89055052024-04-02 19:51:39265 &trace_config_, privacy_filtering_enabled_,
266 enable_package_name_filter,
Chinglin Yu6e022962025-01-30 10:23:26267 enable_system_backend)) {
Etienne Pierre-doray3e01b522023-10-10 15:38:38268 return false;
269 }
270 for (const auto& nested_config : config.nested_scenarios()) {
271 auto nested_scenario = NestedTracingScenario::Create(nested_config, this);
272 if (!nested_scenario) {
273 return false;
274 }
275 nested_scenarios_.push_back(std::move(nested_scenario));
276 }
Etienne Pierre-doraya83afef2024-04-17 16:49:38277 return BackgroundTracingRule::Append(config.start_rules(), start_rules_) &&
278 BackgroundTracingRule::Append(config.stop_rules(), stop_rules_) &&
279 BackgroundTracingRule::Append(config.upload_rules(), upload_rules_) &&
280 BackgroundTracingRule::Append(config.setup_rules(), setup_rules_);
Etienne Pierre-doray825d20a12023-07-10 18:24:44281}
282
Etienne Pierre-doraybb577032023-06-24 04:44:28283void TracingScenario::Disable() {
284 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Etienne Pierre-doray64542e642023-07-24 17:00:52285 CHECK_EQ(current_state_, State::kEnabled);
Etienne Pierre-doraybb577032023-06-24 04:44:28286 SetState(State::kDisabled);
Etienne Pierre-doraybb577032023-06-24 04:44:28287 for (auto& rule : setup_rules_) {
288 rule->Uninstall();
289 }
Etienne Pierre-doray8a78cda2023-10-06 14:50:17290 TracingScenarioBase::Disable();
Etienne Pierre-doraybb577032023-06-24 04:44:28291}
292
293void TracingScenario::Enable() {
294 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Etienne Pierre-doray64542e642023-07-24 17:00:52295 CHECK_EQ(current_state_, State::kDisabled);
Etienne Pierre-doraybb577032023-06-24 04:44:28296 SetState(State::kEnabled);
Etienne Pierre-doraybb577032023-06-24 04:44:28297 for (auto& rule : setup_rules_) {
298 rule->Install(base::BindRepeating(&TracingScenario::OnSetupTrigger,
299 base::Unretained(this)));
300 }
Etienne Pierre-doray8a78cda2023-10-06 14:50:17301 TracingScenarioBase::Enable();
Etienne Pierre-doraybb577032023-06-24 04:44:28302}
303
304void TracingScenario::Abort() {
305 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
306
Etienne Pierre-doray8a78cda2023-10-06 14:50:17307 TracingScenarioBase::Disable();
308 DisableNestedScenarios();
Etienne Pierre-doraybb577032023-06-24 04:44:28309 SetState(State::kStopping);
310 tracing_session_->Stop();
311}
312
Etienne Pierre-dorayb4d088dd2023-09-27 12:40:51313void TracingScenario::GenerateMetadataProto(
314 perfetto::protos::pbzero::ChromeMetadataPacket* metadata) {
315 auto* background_tracing_metadata =
316 metadata->set_background_tracing_metadata();
317
318 uint32_t scenario_name_hash = variations::HashName(scenario_name());
319 background_tracing_metadata->set_scenario_name_hash(scenario_name_hash);
320
321 if (triggered_rule_) {
322 auto* triggered_rule = background_tracing_metadata->set_triggered_rule();
323 triggered_rule_->GenerateMetadataProto(triggered_rule);
324 }
325}
326
Etienne Pierre-doray825d20a12023-07-10 18:24:44327std::unique_ptr<perfetto::TracingSession>
328TracingScenario::CreateTracingSession() {
Chinglin Yu6e022962025-01-30 10:23:26329 // If the scenario is configured to use the system backend, the platform
330 // should support it (see OnSetupTrigger()). Otherwise this is a configuration
331 // error.
332 DCHECK(!use_system_backend_ || tracing::SystemBackgroundTracingEnabled());
333 auto backend_type =
334 (use_system_backend_ && tracing::SystemBackgroundTracingEnabled())
335 ? perfetto::BackendType::kSystemBackend
336 : perfetto::BackendType::kCustomBackend;
337 return perfetto::Tracing::NewTrace(backend_type);
Etienne Pierre-doray825d20a12023-07-10 18:24:44338}
339
Etienne Pierre-doraybb577032023-06-24 04:44:28340void TracingScenario::SetupTracingSession() {
341 DCHECK(!tracing_session_);
342 tracing_session_ = CreateTracingSession();
Alex Attarf62d4e22023-09-26 16:49:43343 session_id_ = base::Token::CreateRandom();
Etienne Pierre-doray9c0b4c62024-11-08 14:00:16344 session_unguessable_name_ = base::UnguessableToken::Create();
345 trace_config_.set_trace_uuid_lsb(session_id_.high());
346 trace_config_.set_trace_uuid_msb(session_id_.low());
347 trace_config_.set_unique_session_name(session_unguessable_name_.ToString());
Etienne Pierre-doraybb577032023-06-24 04:44:28348 tracing_session_->Setup(trace_config_);
349 tracing_session_->SetOnStartCallback([task_runner = task_runner_,
350 weak_ptr = GetWeakPtr()]() {
351 task_runner->PostTask(
352 FROM_HERE, base::BindOnce(&TracingScenario::OnTracingStart, weak_ptr));
353 });
354 tracing_session_->SetOnErrorCallback(
355 [task_runner = task_runner_,
356 weak_ptr = GetWeakPtr()](perfetto::TracingError error) {
357 task_runner->PostTask(
358 FROM_HERE,
359 base::BindOnce(&TracingScenario::OnTracingError, weak_ptr, error));
360 });
361}
362
Etienne Pierre-doray8a78cda2023-10-06 14:50:17363void TracingScenario::OnNestedScenarioStart(
364 NestedTracingScenario* active_scenario) {
365 CHECK_EQ(active_scenario_, nullptr);
366 active_scenario_ = active_scenario;
Etienne Pierre-dorayf704b172025-06-17 23:49:44367 // Other nested scenarios are disabled.
Etienne Pierre-doray8a78cda2023-10-06 14:50:17368 for (auto& scenario : nested_scenarios_) {
369 if (scenario.get() == active_scenario_) {
370 continue;
371 }
372 scenario->Disable();
373 }
Etienne Pierre-doray8a78cda2023-10-06 14:50:17374 // If in `kSetup`, the tracing session is started.
375 if (current_state() == State::kSetup) {
376 OnStartTrigger(nullptr);
377 }
378}
379
380void TracingScenario::OnNestedScenarioStop(
381 NestedTracingScenario* nested_scenario) {
382 CHECK_EQ(active_scenario_, nested_scenario);
Etienne Pierre-doray8a78cda2023-10-06 14:50:17383 // Stop the scenario asynchronously in case an upload trigger is triggered in
384 // the same task.
385 on_nested_stopped_.Reset(base::BindOnce(
386 [](TracingScenario* self, NestedTracingScenario* nested_scenario) {
387 CHECK_EQ(nested_scenario->current_state(),
388 NestedTracingScenario::State::kStopping);
389 CHECK_EQ(self->current_state_, State::kRecording);
390 CHECK_EQ(self->active_scenario_, nested_scenario);
391 nested_scenario->Disable();
392 self->active_scenario_ = nullptr;
393 // All nested scenarios are re-enabled.
394 for (auto& scenario : self->nested_scenarios_) {
395 scenario->Enable();
396 }
397 },
398 base::Unretained(this), nested_scenario));
399 task_runner_->PostTask(FROM_HERE, on_nested_stopped_.callback());
400}
401
402void TracingScenario::OnNestedScenarioUpload(
Etienne Pierre-doray9c0b4c62024-11-08 14:00:16403 NestedTracingScenario* nested_scenario,
Etienne Pierre-doray8a78cda2023-10-06 14:50:17404 const BackgroundTracingRule* triggered_rule) {
Etienne Pierre-doray9c0b4c62024-11-08 14:00:16405 DCHECK_EQ(active_scenario_, nested_scenario);
406 CHECK_EQ(nested_scenario->current_state(),
407 NestedTracingScenario::State::kDisabled);
Etienne Pierre-dorayd8efb742025-02-26 15:13:30408 CHECK(current_state_ == State::kStarting ||
409 current_state_ == State::kRecording)
410 << static_cast<int>(current_state_);
Etienne Pierre-doray9c0b4c62024-11-08 14:00:16411
Etienne Pierre-dorayf6576d942025-04-22 17:42:13412 on_nested_stopped_.Cancel();
Etienne Pierre-doray8a78cda2023-10-06 14:50:17413 active_scenario_ = nullptr;
Etienne Pierre-doray9c0b4c62024-11-08 14:00:16414 SetState(State::kCloning);
Etienne Pierre-dorayd8efb742025-02-26 15:13:30415 // Skip cloning if the trace isn't allowed to save or is still starting.
Etienne Pierre-doraya64d2b12025-06-13 19:12:03416 if (current_state_ == State::kStarting ||
417 !scenario_delegate_->OnScenarioCloned(this)) {
Etienne Pierre-doray9c0b4c62024-11-08 14:00:16418 OnTracingCloned();
419 return;
420 }
421 TracingSession cloned_session = CreateTracingSession();
422 auto reader = base::MakeRefCounted<TraceReader>(std::move(cloned_session),
423 base::Token());
424 perfetto::TracingSession::CloneTraceArgs args{
425 .unique_session_name = session_unguessable_name_.ToString()};
426 reader->tracing_session->CloneTrace(
427 args,
428 [task_runner = task_runner_, weak_ptr = GetWeakPtr(), reader,
429 triggered_rule](perfetto::TracingSession::CloneTraceCallbackArgs args) {
430 task_runner->PostTask(
431 FROM_HERE,
432 base::BindOnce(&TracingScenario::OnTracingCloned, weak_ptr));
433 if (!args.success) {
434 return;
435 }
436 reader->trace_uuid = base::Token(args.uuid_lsb, args.uuid_msb);
437 TraceReader::ReadTrace(reader, weak_ptr, task_runner, triggered_rule);
438 });
Etienne Pierre-doray8a78cda2023-10-06 14:50:17439}
440
Etienne Pierre-doraybb577032023-06-24 04:44:28441bool TracingScenario::OnSetupTrigger(
442 const BackgroundTracingRule* triggered_rule) {
443 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
444
Chinglin Yu6e022962025-01-30 10:23:26445 // Skip this scenario if it requires the system backend, but the system
446 // backend is unavailable (a configuration error).
447 if (use_system_backend_ && !tracing::SystemBackgroundTracingEnabled()) {
448 return false;
449 }
450
Etienne Pierre-doray98ecab42023-09-25 17:35:58451 if (!scenario_delegate_->OnScenarioActive(this)) {
Etienne Pierre-doray825d20a12023-07-10 18:24:44452 return false;
453 }
454
Etienne Pierre-doraybb577032023-06-24 04:44:28455 for (auto& rule : setup_rules_) {
456 rule->Uninstall();
457 }
Etienne Pierre-doraybb577032023-06-24 04:44:28458 for (auto& rule : stop_rules_) {
459 rule->Install(base::BindRepeating(&TracingScenario::OnStopTrigger,
460 base::Unretained(this)));
461 }
462 for (auto& rule : upload_rules_) {
463 rule->Install(base::BindRepeating(&TracingScenario::OnUploadTrigger,
464 base::Unretained(this)));
465 }
Etienne Pierre-doray8a78cda2023-10-06 14:50:17466 for (auto& scenario : nested_scenarios_) {
467 scenario->Enable();
468 }
Etienne Pierre-doraybb577032023-06-24 04:44:28469 SetState(State::kSetup);
470 SetupTracingSession();
471 return true;
472}
473
474bool TracingScenario::OnStartTrigger(
475 const BackgroundTracingRule* triggered_rule) {
476 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
477
Etienne Pierre-doraya64d2b12025-06-13 19:12:03478 if (triggered_rule) {
479 base::UmaHistogramSparse("Tracing.Background.Scenario.Trigger.Start",
480 TriggerNameHash(triggered_rule));
481 }
482
Etienne Pierre-doraybb577032023-06-24 04:44:28483 if (current_state() == State::kEnabled) {
484 // Move to setup before starting the session below.
Etienne Pierre-doray825d20a12023-07-10 18:24:44485 if (!OnSetupTrigger(triggered_rule)) {
486 return false;
487 }
Etienne Pierre-doraybb577032023-06-24 04:44:28488 } else if (current_state() != State::kSetup) {
489 return false;
490 }
491
492 for (auto& rule : start_rules_) {
493 rule->Uninstall();
494 }
495
Etienne Pierre-dorayd8efb742025-02-26 15:13:30496 SetState(State::kStarting);
Etienne Pierre-doray801a50c2024-03-19 02:23:58497
Etienne Pierre-doray0af80e72024-06-28 18:25:29498 if (request_startup_tracing_) {
499 perfetto::Tracing::SetupStartupTracingOpts opts;
500 opts.timeout_ms = kStartupTracingTimeoutMs;
501 opts.backend = perfetto::kCustomBackend;
Etienne Pierre-doray06b3b7842025-07-04 17:01:39502 perfetto::Tracing::SetupStartupTracingBlocking(trace_config_, opts);
Etienne Pierre-doray0af80e72024-06-28 18:25:29503 }
Etienne Pierre-doray801a50c2024-03-19 02:23:58504
Etienne Pierre-doraybb577032023-06-24 04:44:28505 tracing_session_->SetOnStopCallback([task_runner = task_runner_,
506 weak_ptr = GetWeakPtr()]() {
507 task_runner->PostTask(
508 FROM_HERE, base::BindOnce(&TracingScenario::OnTracingStop, weak_ptr));
509 });
510 tracing_session_->Start();
Etienne Pierre-dorayd9a94672024-03-18 23:02:28511 if (triggered_rule) {
Etienne Pierre-doraycf6f2442024-12-03 18:23:47512 TriggersDataSource::EmitTrigger(triggered_rule);
Etienne Pierre-dorayd9a94672024-03-18 23:02:28513 }
Etienne Pierre-doraybb577032023-06-24 04:44:28514 return true;
515}
516
517bool TracingScenario::OnStopTrigger(
518 const BackgroundTracingRule* triggered_rule) {
519 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
520
Etienne Pierre-doraycf6f2442024-12-03 18:23:47521 TriggersDataSource::EmitTrigger(triggered_rule);
Etienne Pierre-doray97f2a832024-04-05 15:55:55522 base::UmaHistogramSparse("Tracing.Background.Scenario.Trigger.Stop",
523 TriggerNameHash(triggered_rule));
Etienne Pierre-doraybb577032023-06-24 04:44:28524 for (auto& rule : stop_rules_) {
525 rule->Uninstall();
526 }
Etienne Pierre-doray8a78cda2023-10-06 14:50:17527 if (active_scenario_) {
528 on_nested_stopped_.Cancel();
Etienne Pierre-dorayd645dd472025-07-22 14:35:04529 active_scenario_->Disable();
530 active_scenario_ = nullptr;
Etienne Pierre-doray8a78cda2023-10-06 14:50:17531 } else {
532 for (auto& nested_scenario : nested_scenarios_) {
533 nested_scenario->Disable();
534 }
535 }
Etienne Pierre-doraybb577032023-06-24 04:44:28536 if (current_state_ == State::kSetup) {
Etienne Pierre-doray8a78cda2023-10-06 14:50:17537 CHECK_EQ(nullptr, active_scenario_);
Etienne Pierre-doraybb577032023-06-24 04:44:28538 // Tear down the session since we haven't been tracing yet.
539 for (auto& rule : upload_rules_) {
540 rule->Uninstall();
541 }
542 for (auto& rule : start_rules_) {
543 rule->Uninstall();
544 }
545 tracing_session_.reset();
546 SetState(State::kDisabled);
Etienne Pierre-doray825d20a12023-07-10 18:24:44547 scenario_delegate_->OnScenarioIdle(this);
Etienne Pierre-doraybb577032023-06-24 04:44:28548 return true;
Etienne Pierre-dorayd8efb742025-02-26 15:13:30549 } else if (current_state_ == State::kStarting) {
550 for (auto& rule : upload_rules_) {
551 rule->Uninstall();
552 }
Etienne Pierre-doraybb577032023-06-24 04:44:28553 }
Etienne Pierre-doraybb577032023-06-24 04:44:28554 tracing_session_->Stop();
Etienne Pierre-doray78ffab22023-08-07 18:00:00555 SetState(State::kStopping);
Etienne Pierre-doraybb577032023-06-24 04:44:28556 return true;
557}
558
559bool TracingScenario::OnUploadTrigger(
560 const BackgroundTracingRule* triggered_rule) {
561 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
562
Etienne Pierre-doraycf6f2442024-12-03 18:23:47563 TriggersDataSource::EmitTrigger(triggered_rule);
Etienne Pierre-doray97f2a832024-04-05 15:55:55564 base::UmaHistogramSparse("Tracing.Background.Scenario.Trigger.Upload",
565 TriggerNameHash(triggered_rule));
Etienne Pierre-doraybb577032023-06-24 04:44:28566 for (auto& rule : stop_rules_) {
567 rule->Uninstall();
568 }
569 for (auto& rule : upload_rules_) {
570 rule->Uninstall();
571 }
Etienne Pierre-doray8a78cda2023-10-06 14:50:17572 DisableNestedScenarios();
Etienne Pierre-doraybb577032023-06-24 04:44:28573 // Setup is ignored.
574 if (current_state_ == State::kSetup) {
575 for (auto& rule : start_rules_) {
576 rule->Uninstall();
577 }
578 tracing_session_.reset();
579 SetState(State::kDisabled);
Etienne Pierre-doray825d20a12023-07-10 18:24:44580 scenario_delegate_->OnScenarioIdle(this);
Etienne Pierre-doraybb577032023-06-24 04:44:28581 return true;
Etienne Pierre-dorayd8efb742025-02-26 15:13:30582 } else if (current_state_ == State::kStarting) {
583 SetState(State::kStopping);
584 tracing_session_->Stop();
585 return true;
Etienne Pierre-doraybb577032023-06-24 04:44:28586 }
Etienne Pierre-doray64542e642023-07-24 17:00:52587 CHECK(current_state_ == State::kRecording ||
Etienne Pierre-doray9c0b4c62024-11-08 14:00:16588 current_state_ == State::kStopping || current_state_ == State::kCloning)
Etienne Pierre-doray64542e642023-07-24 17:00:52589 << static_cast<int>(current_state_);
Etienne Pierre-doraybd38b0752023-09-05 22:42:46590 triggered_rule_ = triggered_rule;
Etienne Pierre-doray78ffab22023-08-07 18:00:00591 if (current_state_ != State::kStopping) {
592 tracing_session_->Stop();
593 }
Etienne Pierre-doraybb577032023-06-24 04:44:28594 SetState(State::kFinalizing);
Etienne Pierre-doraybb577032023-06-24 04:44:28595 return true;
596}
597
598void TracingScenario::OnTracingError(perfetto::TracingError error) {
599 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Etienne Pierre-doray825d20a12023-07-10 18:24:44600 if (!tracing_session_) {
Etienne Pierre-doray64542e642023-07-24 17:00:52601 CHECK(current_state_ == State::kDisabled ||
602 current_state_ == State::kEnabled)
603 << static_cast<int>(current_state_);
Etienne Pierre-doray825d20a12023-07-10 18:24:44604 return;
605 }
Etienne Pierre-doraybb577032023-06-24 04:44:28606 for (auto& rule : start_rules_) {
607 rule->Uninstall();
608 }
609 for (auto& rule : stop_rules_) {
610 rule->Uninstall();
611 }
612 for (auto& rule : upload_rules_) {
613 rule->Uninstall();
614 }
Etienne Pierre-doray8a78cda2023-10-06 14:50:17615 DisableNestedScenarios();
Etienne Pierre-doray825d20a12023-07-10 18:24:44616 SetState(State::kStopping);
617 tracing_session_->Stop();
Etienne Pierre-doray9653cf82025-02-07 17:08:50618 scenario_delegate_->OnScenarioError(this, error);
Etienne Pierre-doraybb577032023-06-24 04:44:28619}
620
621void TracingScenario::OnTracingStart() {
622 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Etienne Pierre-dorayd8efb742025-02-26 15:13:30623 SetState(State::kRecording);
Etienne Pierre-doray825d20a12023-07-10 18:24:44624 scenario_delegate_->OnScenarioRecording(this);
Etienne Pierre-doraybb577032023-06-24 04:44:28625}
626
627void TracingScenario::OnTracingStop() {
628 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
629
Etienne Pierre-doray64542e642023-07-24 17:00:52630 if (current_state_ != State::kStopping &&
631 current_state_ != State::kFinalizing) {
632 // Tracing was stopped internally.
633 CHECK(current_state_ == State::kSetup ||
Etienne Pierre-dorayd8efb742025-02-26 15:13:30634 current_state_ == State::kStarting ||
Etienne Pierre-doray9c0b4c62024-11-08 14:00:16635 current_state_ == State::kRecording ||
636 current_state_ == State::kCloning)
Etienne Pierre-doray64542e642023-07-24 17:00:52637 << static_cast<int>(current_state_);
638 for (auto& rule : start_rules_) {
639 rule->Uninstall();
640 }
641 for (auto& rule : stop_rules_) {
642 rule->Uninstall();
643 }
644 }
Etienne Pierre-doray98ecab42023-09-25 17:35:58645 for (auto& rule : upload_rules_) {
646 rule->Uninstall();
Etienne Pierre-doray825d20a12023-07-10 18:24:44647 }
Etienne Pierre-doray8a78cda2023-10-06 14:50:17648 DisableNestedScenarios();
Etienne Pierre-doray98ecab42023-09-25 17:35:58649 bool should_upload = (current_state_ == State::kFinalizing);
650 auto tracing_session = std::move(tracing_session_);
651 SetState(State::kDisabled);
652 if (!scenario_delegate_->OnScenarioIdle(this)) {
653 should_upload = false;
654 }
655 if (!should_upload) {
656 tracing_session.reset();
Etienne Pierre-doraybb577032023-06-24 04:44:28657 return;
658 }
Etienne Pierre-doray98ecab42023-09-25 17:35:58659 DCHECK(triggered_rule_);
Etienne Pierre-doray14445052023-12-08 17:46:22660 auto reader = base::MakeRefCounted<TraceReader>(std::move(tracing_session),
661 session_id_);
Etienne Pierre-doray9c0b4c62024-11-08 14:00:16662 TraceReader::ReadTrace(reader, GetWeakPtr(), task_runner_,
663 triggered_rule_.get());
664}
665
666void TracingScenario::OnTracingCloned() {
667 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Etienne Pierre-doray85a31fbf2025-03-19 17:13:18668 if (current_state_ == State::kCloning) {
669 SetState(State::kRecording);
670 }
671 if (current_state_ != State::kStarting &&
672 current_state_ != State::kRecording) {
Etienne Pierre-doray9c0b4c62024-11-08 14:00:16673 // Tracing was stopped.
674 return;
675 }
Etienne Pierre-doray9c0b4c62024-11-08 14:00:16676 // All nested scenarios are re-enabled.
677 for (auto& scenario : nested_scenarios_) {
678 scenario->Enable();
679 }
Etienne Pierre-doraybb577032023-06-24 04:44:28680}
681
Etienne Pierre-doray98ecab42023-09-25 17:35:58682void TracingScenario::OnFinalizingDone(
Etienne Pierre-doray14445052023-12-08 17:46:22683 base::Token trace_uuid,
Etienne Pierre-doray98ecab42023-09-25 17:35:58684 std::string&& serialized_trace,
685 TracingSession tracing_session,
686 const BackgroundTracingRule* triggered_rule) {
Etienne Pierre-doray78ffab22023-08-07 18:00:00687 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
688
689 tracing_session.reset();
Etienne Pierre-doray14445052023-12-08 17:46:22690 scenario_delegate_->SaveTrace(this, trace_uuid, triggered_rule,
Etienne Pierre-doraybd38b0752023-09-05 22:42:46691 std::move(serialized_trace));
Etienne Pierre-doray78ffab22023-08-07 18:00:00692}
693
Etienne Pierre-doray8a78cda2023-10-06 14:50:17694void TracingScenario::DisableNestedScenarios() {
695 if (active_scenario_) {
696 CHECK(current_state_ == State::kRecording ||
Mikhail Khokhlova46ddf42025-03-07 17:17:44697 current_state_ == State::kStarting ||
Etienne Pierre-doray8a78cda2023-10-06 14:50:17698 current_state_ == State::kStopping)
699 << static_cast<int>(current_state_);
700 on_nested_stopped_.Cancel();
701 active_scenario_->Disable();
702 active_scenario_ = nullptr;
703 } else if (current_state_ == State::kRecording ||
Etienne Pierre-dorayd8efb742025-02-26 15:13:30704 current_state_ == State::kSetup ||
705 current_state_ == State::kStarting) {
Etienne Pierre-doray8a78cda2023-10-06 14:50:17706 for (auto& nested_scenario : nested_scenarios_) {
707 nested_scenario->Disable();
708 }
709 }
710}
711
Etienne Pierre-doraybb577032023-06-24 04:44:28712void TracingScenario::SetState(State new_state) {
Etienne Pierre-doray9c0b4c62024-11-08 14:00:16713 if (new_state == State::kEnabled || new_state == State::kDisabled ||
714 new_state == State::kCloning) {
715 if (new_state == State::kEnabled || new_state == State::kDisabled) {
716 CHECK_EQ(nullptr, tracing_session_);
717 }
Etienne Pierre-doray8a78cda2023-10-06 14:50:17718 CHECK_EQ(nullptr, active_scenario_);
719 for (auto& scenario : nested_scenarios_) {
720 CHECK_EQ(NestedTracingScenario::State::kDisabled,
721 scenario->current_state());
722 }
Etienne Pierre-doraybb577032023-06-24 04:44:28723 }
724 current_state_ = new_state;
725}
726
727base::WeakPtr<TracingScenario> TracingScenario::GetWeakPtr() {
728 return weak_ptr_factory_.GetWeakPtr();
729}
730
731} // namespace content