| // 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 "content/browser/tracing/tracing_scenario.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/hash/md5.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/ref_counted_memory.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/strings/strcat.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/token.h" |
| #include "base/tracing/trace_time.h" |
| #include "components/variations/hashing.h" |
| #include "content/browser/tracing/background_tracing_manager_impl.h" |
| #include "content/browser/tracing/triggers_data_source.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "services/tracing/public/cpp/perfetto/perfetto_config.h" |
| #include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h" |
| #include "services/tracing/public/cpp/tracing_features.h" |
| #include "third_party/perfetto/protos/perfetto/config/track_event/track_event_config.gen.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| constexpr uint32_t kStartupTracingTimeoutMs = 30 * 1000; // 30 sec |
| |
| } // namespace |
| |
| void TracingScenario::TracingSessionDeleter::operator()( |
| perfetto::TracingSession* ptr) const { |
| ptr->SetOnErrorCallback({}); |
| delete ptr; |
| } |
| |
| class TracingScenario::TraceReader |
| : public base::RefCountedThreadSafe<TraceReader> { |
| public: |
| explicit TraceReader(TracingSession tracing_session, base::Token trace_uuid) |
| : tracing_session(std::move(tracing_session)), trace_uuid(trace_uuid) {} |
| |
| TracingSession tracing_session; |
| base::Token trace_uuid; |
| std::string serialized_trace; |
| |
| static void ReadTrace(scoped_refptr<TracingScenario::TraceReader> reader, |
| base::WeakPtr<TracingScenario> scenario, |
| scoped_refptr<base::SequencedTaskRunner> task_runner, |
| const BackgroundTracingRule* triggered_rule) { |
| reader->tracing_session->ReadTrace( |
| [task_runner, scenario, reader, triggered_rule]( |
| perfetto::TracingSession::ReadTraceCallbackArgs args) mutable { |
| if (args.size) { |
| reader->serialized_trace.append(args.data, args.size); |
| } |
| if (!args.has_more) { |
| task_runner->PostTask( |
| FROM_HERE, base::BindOnce(&TracingScenario::OnFinalizingDone, |
| scenario, reader->trace_uuid, |
| std::move(reader->serialized_trace), |
| std::move(reader->tracing_session), |
| triggered_rule)); |
| } |
| }); |
| } |
| |
| private: |
| friend class base::RefCountedThreadSafe<TraceReader>; |
| |
| ~TraceReader() = default; |
| }; |
| |
| using Metrics = BackgroundTracingManagerImpl::Metrics; |
| |
| TracingScenarioBase::~TracingScenarioBase() = default; |
| |
| void TracingScenarioBase::Disable() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| for (auto& rule : start_rules_) { |
| rule->Uninstall(); |
| } |
| for (auto& rule : stop_rules_) { |
| rule->Uninstall(); |
| } |
| for (auto& rule : upload_rules_) { |
| rule->Uninstall(); |
| } |
| } |
| |
| void TracingScenarioBase::Enable() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| for (auto& rule : start_rules_) { |
| rule->Install(base::BindRepeating(&TracingScenarioBase::OnStartTrigger, |
| base::Unretained(this))); |
| } |
| } |
| |
| uint32_t TracingScenarioBase::TriggerNameHash( |
| const BackgroundTracingRule* triggered_rule) const { |
| return variations::HashName( |
| base::StrCat({scenario_name(), ".", triggered_rule->rule_name()})); |
| } |
| |
| TracingScenarioBase::TracingScenarioBase(std::string scenario_name) |
| : scenario_name_(std::move(scenario_name)), |
| task_runner_(base::SequencedTaskRunner::GetCurrentDefault()) {} |
| |
| // static |
| std::unique_ptr<NestedTracingScenario> NestedTracingScenario::Create( |
| const perfetto::protos::gen::NestedScenarioConfig& config, |
| Delegate* scenario_delegate) { |
| auto scenario = |
| base::WrapUnique(new NestedTracingScenario(config, scenario_delegate)); |
| if (!scenario->Initialize(config)) { |
| return nullptr; |
| } |
| return scenario; |
| } |
| |
| NestedTracingScenario::NestedTracingScenario( |
| const perfetto::protos::gen::NestedScenarioConfig& config, |
| Delegate* scenario_delegate) |
| : TracingScenarioBase(config.scenario_name()), |
| scenario_delegate_(scenario_delegate) {} |
| |
| NestedTracingScenario::~NestedTracingScenario() = default; |
| |
| void NestedTracingScenario::Disable() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| SetState(State::kDisabled); |
| TracingScenarioBase::Disable(); |
| } |
| |
| void NestedTracingScenario::Enable() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| CHECK_EQ(current_state_, State::kDisabled); |
| SetState(State::kEnabled); |
| TracingScenarioBase::Enable(); |
| } |
| |
| bool NestedTracingScenario::Initialize( |
| const perfetto::protos::gen::NestedScenarioConfig& config) { |
| return BackgroundTracingRule::Append(config.start_rules(), start_rules_) && |
| BackgroundTracingRule::Append(config.stop_rules(), stop_rules_) && |
| BackgroundTracingRule::Append(config.upload_rules(), upload_rules_); |
| } |
| |
| bool NestedTracingScenario::OnStartTrigger( |
| const BackgroundTracingRule* triggered_rule) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (current_state() != State::kEnabled) { |
| return false; |
| } |
| TriggersDataSource::EmitTrigger(triggered_rule); |
| base::UmaHistogramSparse("Tracing.Background.Scenario.Trigger.Start", |
| TriggerNameHash(triggered_rule)); |
| for (auto& rule : start_rules_) { |
| rule->Uninstall(); |
| } |
| for (auto& rule : stop_rules_) { |
| rule->Install(base::BindRepeating(&NestedTracingScenario::OnStopTrigger, |
| base::Unretained(this))); |
| } |
| for (auto& rule : upload_rules_) { |
| rule->Install(base::BindRepeating(&NestedTracingScenario::OnUploadTrigger, |
| base::Unretained(this))); |
| } |
| scenario_delegate_->OnNestedScenarioStart(this); |
| SetState(State::kActive); |
| return true; |
| } |
| |
| bool NestedTracingScenario::OnStopTrigger( |
| const BackgroundTracingRule* triggered_rule) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| TriggersDataSource::EmitTrigger(triggered_rule); |
| base::UmaHistogramSparse("Tracing.Background.Scenario.Trigger.Stop", |
| TriggerNameHash(triggered_rule)); |
| for (auto& rule : stop_rules_) { |
| rule->Uninstall(); |
| } |
| SetState(State::kStopping); |
| scenario_delegate_->OnNestedScenarioStop(this); |
| return true; |
| } |
| |
| bool NestedTracingScenario::OnUploadTrigger( |
| const BackgroundTracingRule* triggered_rule) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| TriggersDataSource::EmitTrigger(triggered_rule); |
| base::UmaHistogramSparse("Tracing.Background.Scenario.Trigger.Upload", |
| TriggerNameHash(triggered_rule)); |
| |
| for (auto& rule : stop_rules_) { |
| rule->Uninstall(); |
| } |
| for (auto& rule : upload_rules_) { |
| rule->Uninstall(); |
| } |
| SetState(State::kDisabled); |
| scenario_delegate_->OnNestedScenarioUpload(this, triggered_rule); |
| return true; |
| } |
| |
| void NestedTracingScenario::SetState(State new_state) { |
| current_state_ = new_state; |
| } |
| |
| // static |
| std::unique_ptr<TracingScenario> TracingScenario::Create( |
| const perfetto::protos::gen::ScenarioConfig& config, |
| bool enable_privacy_filter, |
| bool is_local_scenario, |
| bool enable_package_name_filter, |
| bool request_startup_tracing, |
| Delegate* scenario_delegate) { |
| auto scenario = base::WrapUnique( |
| new TracingScenario(config, scenario_delegate, enable_privacy_filter, |
| is_local_scenario, request_startup_tracing)); |
| if (!scenario->Initialize(config, enable_package_name_filter)) { |
| return nullptr; |
| } |
| return scenario; |
| } |
| |
| TracingScenario::TracingScenario( |
| const perfetto::protos::gen::ScenarioConfig& config, |
| Delegate* scenario_delegate, |
| bool enable_privacy_filter, |
| bool is_local_scenario, |
| bool request_startup_tracing) |
| : TracingScenarioBase(config.scenario_name()), |
| description_(config.scenario_description()), |
| privacy_filtering_enabled_(enable_privacy_filter), |
| is_local_scenario_(is_local_scenario), |
| request_startup_tracing_(request_startup_tracing), |
| use_system_backend_(config.use_system_backend()), |
| trace_config_(config.trace_config()), |
| scenario_delegate_(scenario_delegate) {} |
| |
| TracingScenario::~TracingScenario() = default; |
| |
| bool TracingScenario::Initialize( |
| const perfetto::protos::gen::ScenarioConfig& config, |
| bool enable_package_name_filter) { |
| bool enable_system_backend = |
| use_system_backend_ && tracing::SystemBackgroundTracingEnabled(); |
| if (!tracing::AdaptPerfettoConfigForChrome( |
| &trace_config_, privacy_filtering_enabled_, |
| enable_package_name_filter, |
| enable_system_backend)) { |
| return false; |
| } |
| for (const auto& nested_config : config.nested_scenarios()) { |
| auto nested_scenario = NestedTracingScenario::Create(nested_config, this); |
| if (!nested_scenario) { |
| return false; |
| } |
| nested_scenarios_.push_back(std::move(nested_scenario)); |
| } |
| return BackgroundTracingRule::Append(config.start_rules(), start_rules_) && |
| BackgroundTracingRule::Append(config.stop_rules(), stop_rules_) && |
| BackgroundTracingRule::Append(config.upload_rules(), upload_rules_) && |
| BackgroundTracingRule::Append(config.setup_rules(), setup_rules_); |
| } |
| |
| void TracingScenario::Disable() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| CHECK_EQ(current_state_, State::kEnabled); |
| SetState(State::kDisabled); |
| for (auto& rule : setup_rules_) { |
| rule->Uninstall(); |
| } |
| TracingScenarioBase::Disable(); |
| } |
| |
| void TracingScenario::Enable() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| CHECK_EQ(current_state_, State::kDisabled); |
| SetState(State::kEnabled); |
| for (auto& rule : setup_rules_) { |
| rule->Install(base::BindRepeating(&TracingScenario::OnSetupTrigger, |
| base::Unretained(this))); |
| } |
| TracingScenarioBase::Enable(); |
| } |
| |
| void TracingScenario::Abort() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| TracingScenarioBase::Disable(); |
| DisableNestedScenarios(); |
| SetState(State::kStopping); |
| tracing_session_->Stop(); |
| } |
| |
| void TracingScenario::GenerateMetadataProto( |
| perfetto::protos::pbzero::ChromeMetadataPacket* metadata) { |
| auto* background_tracing_metadata = |
| metadata->set_background_tracing_metadata(); |
| |
| uint32_t scenario_name_hash = variations::HashName(scenario_name()); |
| background_tracing_metadata->set_scenario_name_hash(scenario_name_hash); |
| |
| if (triggered_rule_) { |
| auto* triggered_rule = background_tracing_metadata->set_triggered_rule(); |
| triggered_rule_->GenerateMetadataProto(triggered_rule); |
| } |
| } |
| |
| std::unique_ptr<perfetto::TracingSession> |
| TracingScenario::CreateTracingSession() { |
| // If the scenario is configured to use the system backend, the platform |
| // should support it (see OnSetupTrigger()). Otherwise this is a configuration |
| // error. |
| DCHECK(!use_system_backend_ || tracing::SystemBackgroundTracingEnabled()); |
| auto backend_type = |
| (use_system_backend_ && tracing::SystemBackgroundTracingEnabled()) |
| ? perfetto::BackendType::kSystemBackend |
| : perfetto::BackendType::kCustomBackend; |
| return perfetto::Tracing::NewTrace(backend_type); |
| } |
| |
| void TracingScenario::SetupTracingSession() { |
| DCHECK(!tracing_session_); |
| tracing_session_ = CreateTracingSession(); |
| session_id_ = base::Token::CreateRandom(); |
| session_unguessable_name_ = base::UnguessableToken::Create(); |
| trace_config_.set_trace_uuid_lsb(session_id_.high()); |
| trace_config_.set_trace_uuid_msb(session_id_.low()); |
| trace_config_.set_unique_session_name(session_unguessable_name_.ToString()); |
| tracing_session_->Setup(trace_config_); |
| tracing_session_->SetOnStartCallback([task_runner = task_runner_, |
| weak_ptr = GetWeakPtr()]() { |
| task_runner->PostTask( |
| FROM_HERE, base::BindOnce(&TracingScenario::OnTracingStart, weak_ptr)); |
| }); |
| tracing_session_->SetOnErrorCallback( |
| [task_runner = task_runner_, |
| weak_ptr = GetWeakPtr()](perfetto::TracingError error) { |
| task_runner->PostTask( |
| FROM_HERE, |
| base::BindOnce(&TracingScenario::OnTracingError, weak_ptr, error)); |
| }); |
| } |
| |
| void TracingScenario::OnNestedScenarioStart( |
| NestedTracingScenario* active_scenario) { |
| CHECK_EQ(active_scenario_, nullptr); |
| active_scenario_ = active_scenario; |
| // Other nested scenarios are disabled. |
| for (auto& scenario : nested_scenarios_) { |
| if (scenario.get() == active_scenario_) { |
| continue; |
| } |
| scenario->Disable(); |
| } |
| // If in `kSetup`, the tracing session is started. |
| if (current_state() == State::kSetup) { |
| OnStartTrigger(nullptr); |
| } |
| } |
| |
| void TracingScenario::OnNestedScenarioStop( |
| NestedTracingScenario* nested_scenario) { |
| CHECK_EQ(active_scenario_, nested_scenario); |
| // Stop the scenario asynchronously in case an upload trigger is triggered in |
| // the same task. |
| on_nested_stopped_.Reset(base::BindOnce( |
| [](TracingScenario* self, NestedTracingScenario* nested_scenario) { |
| CHECK_EQ(nested_scenario->current_state(), |
| NestedTracingScenario::State::kStopping); |
| CHECK_EQ(self->current_state_, State::kRecording); |
| CHECK_EQ(self->active_scenario_, nested_scenario); |
| nested_scenario->Disable(); |
| self->active_scenario_ = nullptr; |
| // All nested scenarios are re-enabled. |
| for (auto& scenario : self->nested_scenarios_) { |
| scenario->Enable(); |
| } |
| }, |
| base::Unretained(this), nested_scenario)); |
| task_runner_->PostTask(FROM_HERE, on_nested_stopped_.callback()); |
| } |
| |
| void TracingScenario::OnNestedScenarioUpload( |
| NestedTracingScenario* nested_scenario, |
| const BackgroundTracingRule* triggered_rule) { |
| DCHECK_EQ(active_scenario_, nested_scenario); |
| CHECK_EQ(nested_scenario->current_state(), |
| NestedTracingScenario::State::kDisabled); |
| CHECK(current_state_ == State::kStarting || |
| current_state_ == State::kRecording) |
| << static_cast<int>(current_state_); |
| |
| on_nested_stopped_.Cancel(); |
| active_scenario_ = nullptr; |
| SetState(State::kCloning); |
| // Skip cloning if the trace isn't allowed to save or is still starting. |
| if (current_state_ == State::kStarting || |
| !scenario_delegate_->OnScenarioCloned(this)) { |
| OnTracingCloned(); |
| return; |
| } |
| TracingSession cloned_session = CreateTracingSession(); |
| auto reader = base::MakeRefCounted<TraceReader>(std::move(cloned_session), |
| base::Token()); |
| perfetto::TracingSession::CloneTraceArgs args{ |
| .unique_session_name = session_unguessable_name_.ToString()}; |
| reader->tracing_session->CloneTrace( |
| args, |
| [task_runner = task_runner_, weak_ptr = GetWeakPtr(), reader, |
| triggered_rule](perfetto::TracingSession::CloneTraceCallbackArgs args) { |
| task_runner->PostTask( |
| FROM_HERE, |
| base::BindOnce(&TracingScenario::OnTracingCloned, weak_ptr)); |
| if (!args.success) { |
| return; |
| } |
| reader->trace_uuid = base::Token(args.uuid_lsb, args.uuid_msb); |
| TraceReader::ReadTrace(reader, weak_ptr, task_runner, triggered_rule); |
| }); |
| } |
| |
| bool TracingScenario::OnSetupTrigger( |
| const BackgroundTracingRule* triggered_rule) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| // Skip this scenario if it requires the system backend, but the system |
| // backend is unavailable (a configuration error). |
| if (use_system_backend_ && !tracing::SystemBackgroundTracingEnabled()) { |
| return false; |
| } |
| |
| if (!scenario_delegate_->OnScenarioActive(this)) { |
| return false; |
| } |
| |
| for (auto& rule : setup_rules_) { |
| rule->Uninstall(); |
| } |
| for (auto& rule : stop_rules_) { |
| rule->Install(base::BindRepeating(&TracingScenario::OnStopTrigger, |
| base::Unretained(this))); |
| } |
| for (auto& rule : upload_rules_) { |
| rule->Install(base::BindRepeating(&TracingScenario::OnUploadTrigger, |
| base::Unretained(this))); |
| } |
| for (auto& scenario : nested_scenarios_) { |
| scenario->Enable(); |
| } |
| SetState(State::kSetup); |
| SetupTracingSession(); |
| return true; |
| } |
| |
| bool TracingScenario::OnStartTrigger( |
| const BackgroundTracingRule* triggered_rule) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (triggered_rule) { |
| base::UmaHistogramSparse("Tracing.Background.Scenario.Trigger.Start", |
| TriggerNameHash(triggered_rule)); |
| } |
| |
| if (current_state() == State::kEnabled) { |
| // Move to setup before starting the session below. |
| if (!OnSetupTrigger(triggered_rule)) { |
| return false; |
| } |
| } else if (current_state() != State::kSetup) { |
| return false; |
| } |
| |
| for (auto& rule : start_rules_) { |
| rule->Uninstall(); |
| } |
| |
| SetState(State::kStarting); |
| |
| if (request_startup_tracing_) { |
| perfetto::Tracing::SetupStartupTracingOpts opts; |
| opts.timeout_ms = kStartupTracingTimeoutMs; |
| opts.backend = perfetto::kCustomBackend; |
| perfetto::Tracing::SetupStartupTracingBlocking(trace_config_, opts); |
| } |
| |
| tracing_session_->SetOnStopCallback([task_runner = task_runner_, |
| weak_ptr = GetWeakPtr()]() { |
| task_runner->PostTask( |
| FROM_HERE, base::BindOnce(&TracingScenario::OnTracingStop, weak_ptr)); |
| }); |
| tracing_session_->Start(); |
| if (triggered_rule) { |
| TriggersDataSource::EmitTrigger(triggered_rule); |
| } |
| return true; |
| } |
| |
| bool TracingScenario::OnStopTrigger( |
| const BackgroundTracingRule* triggered_rule) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| TriggersDataSource::EmitTrigger(triggered_rule); |
| base::UmaHistogramSparse("Tracing.Background.Scenario.Trigger.Stop", |
| TriggerNameHash(triggered_rule)); |
| for (auto& rule : stop_rules_) { |
| rule->Uninstall(); |
| } |
| if (active_scenario_) { |
| on_nested_stopped_.Cancel(); |
| active_scenario_->Disable(); |
| active_scenario_ = nullptr; |
| } else { |
| for (auto& nested_scenario : nested_scenarios_) { |
| nested_scenario->Disable(); |
| } |
| } |
| if (current_state_ == State::kSetup) { |
| CHECK_EQ(nullptr, active_scenario_); |
| // Tear down the session since we haven't been tracing yet. |
| for (auto& rule : upload_rules_) { |
| rule->Uninstall(); |
| } |
| for (auto& rule : start_rules_) { |
| rule->Uninstall(); |
| } |
| tracing_session_.reset(); |
| SetState(State::kDisabled); |
| scenario_delegate_->OnScenarioIdle(this); |
| return true; |
| } else if (current_state_ == State::kStarting) { |
| for (auto& rule : upload_rules_) { |
| rule->Uninstall(); |
| } |
| } |
| tracing_session_->Stop(); |
| SetState(State::kStopping); |
| return true; |
| } |
| |
| bool TracingScenario::OnUploadTrigger( |
| const BackgroundTracingRule* triggered_rule) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| TriggersDataSource::EmitTrigger(triggered_rule); |
| base::UmaHistogramSparse("Tracing.Background.Scenario.Trigger.Upload", |
| TriggerNameHash(triggered_rule)); |
| for (auto& rule : stop_rules_) { |
| rule->Uninstall(); |
| } |
| for (auto& rule : upload_rules_) { |
| rule->Uninstall(); |
| } |
| DisableNestedScenarios(); |
| // Setup is ignored. |
| if (current_state_ == State::kSetup) { |
| for (auto& rule : start_rules_) { |
| rule->Uninstall(); |
| } |
| tracing_session_.reset(); |
| SetState(State::kDisabled); |
| scenario_delegate_->OnScenarioIdle(this); |
| return true; |
| } else if (current_state_ == State::kStarting) { |
| SetState(State::kStopping); |
| tracing_session_->Stop(); |
| return true; |
| } |
| CHECK(current_state_ == State::kRecording || |
| current_state_ == State::kStopping || current_state_ == State::kCloning) |
| << static_cast<int>(current_state_); |
| triggered_rule_ = triggered_rule; |
| if (current_state_ != State::kStopping) { |
| tracing_session_->Stop(); |
| } |
| SetState(State::kFinalizing); |
| return true; |
| } |
| |
| void TracingScenario::OnTracingError(perfetto::TracingError error) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!tracing_session_) { |
| CHECK(current_state_ == State::kDisabled || |
| current_state_ == State::kEnabled) |
| << static_cast<int>(current_state_); |
| return; |
| } |
| for (auto& rule : start_rules_) { |
| rule->Uninstall(); |
| } |
| for (auto& rule : stop_rules_) { |
| rule->Uninstall(); |
| } |
| for (auto& rule : upload_rules_) { |
| rule->Uninstall(); |
| } |
| DisableNestedScenarios(); |
| SetState(State::kStopping); |
| tracing_session_->Stop(); |
| scenario_delegate_->OnScenarioError(this, error); |
| } |
| |
| void TracingScenario::OnTracingStart() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| SetState(State::kRecording); |
| scenario_delegate_->OnScenarioRecording(this); |
| } |
| |
| void TracingScenario::OnTracingStop() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (current_state_ != State::kStopping && |
| current_state_ != State::kFinalizing) { |
| // Tracing was stopped internally. |
| CHECK(current_state_ == State::kSetup || |
| current_state_ == State::kStarting || |
| current_state_ == State::kRecording || |
| current_state_ == State::kCloning) |
| << static_cast<int>(current_state_); |
| for (auto& rule : start_rules_) { |
| rule->Uninstall(); |
| } |
| for (auto& rule : stop_rules_) { |
| rule->Uninstall(); |
| } |
| } |
| for (auto& rule : upload_rules_) { |
| rule->Uninstall(); |
| } |
| DisableNestedScenarios(); |
| bool should_upload = (current_state_ == State::kFinalizing); |
| auto tracing_session = std::move(tracing_session_); |
| SetState(State::kDisabled); |
| if (!scenario_delegate_->OnScenarioIdle(this)) { |
| should_upload = false; |
| } |
| if (!should_upload) { |
| tracing_session.reset(); |
| return; |
| } |
| DCHECK(triggered_rule_); |
| auto reader = base::MakeRefCounted<TraceReader>(std::move(tracing_session), |
| session_id_); |
| TraceReader::ReadTrace(reader, GetWeakPtr(), task_runner_, |
| triggered_rule_.get()); |
| } |
| |
| void TracingScenario::OnTracingCloned() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (current_state_ == State::kCloning) { |
| SetState(State::kRecording); |
| } |
| if (current_state_ != State::kStarting && |
| current_state_ != State::kRecording) { |
| // Tracing was stopped. |
| return; |
| } |
| // All nested scenarios are re-enabled. |
| for (auto& scenario : nested_scenarios_) { |
| scenario->Enable(); |
| } |
| } |
| |
| void TracingScenario::OnFinalizingDone( |
| base::Token trace_uuid, |
| std::string&& serialized_trace, |
| TracingSession tracing_session, |
| const BackgroundTracingRule* triggered_rule) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| tracing_session.reset(); |
| scenario_delegate_->SaveTrace(this, trace_uuid, triggered_rule, |
| std::move(serialized_trace)); |
| } |
| |
| void TracingScenario::DisableNestedScenarios() { |
| if (active_scenario_) { |
| CHECK(current_state_ == State::kRecording || |
| current_state_ == State::kStarting || |
| current_state_ == State::kStopping) |
| << static_cast<int>(current_state_); |
| on_nested_stopped_.Cancel(); |
| active_scenario_->Disable(); |
| active_scenario_ = nullptr; |
| } else if (current_state_ == State::kRecording || |
| current_state_ == State::kSetup || |
| current_state_ == State::kStarting) { |
| for (auto& nested_scenario : nested_scenarios_) { |
| nested_scenario->Disable(); |
| } |
| } |
| } |
| |
| void TracingScenario::SetState(State new_state) { |
| if (new_state == State::kEnabled || new_state == State::kDisabled || |
| new_state == State::kCloning) { |
| if (new_state == State::kEnabled || new_state == State::kDisabled) { |
| CHECK_EQ(nullptr, tracing_session_); |
| } |
| CHECK_EQ(nullptr, active_scenario_); |
| for (auto& scenario : nested_scenarios_) { |
| CHECK_EQ(NestedTracingScenario::State::kDisabled, |
| scenario->current_state()); |
| } |
| } |
| current_state_ = new_state; |
| } |
| |
| base::WeakPtr<TracingScenario> TracingScenario::GetWeakPtr() { |
| return weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| } // namespace content |