blob: 609fde219e55804a91c3db4367c1883a894a8565 [file] [log] [blame]
// Copyright 2015 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/background_tracing_manager_impl.h"
#include <optional>
#include <utility>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/location.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/path_service.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/sequence_bound.h"
#include "base/time/time.h"
#include "base/uuid.h"
#include "base/values.h"
#include "build/build_config.h"
#include "components/tracing/common/trace_startup_config.h"
#include "components/variations/hashing.h"
#include "content/browser/tracing/background_startup_tracing_observer.h"
#include "content/browser/tracing/background_tracing_active_scenario.h"
#include "content/browser/tracing/background_tracing_agent_client_impl.h"
#include "content/browser/tracing/background_tracing_rule.h"
#include "content/browser/tracing/trace_report/trace_report_database.h"
#include "content/browser/tracing/trace_report/trace_upload_list.h"
#include "content/browser/tracing/tracing_controller_impl.h"
#include "content/common/child_process.mojom.h"
#include "content/public/browser/browser_child_process_host.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_data.h"
#include "content/public/browser/child_process_host.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/tracing_delegate.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "net/base/network_change_notifier.h"
#include "services/tracing/public/cpp/perfetto/trace_event_data_source.h"
#include "services/tracing/public/cpp/trace_event_agent.h"
#include "services/tracing/public/cpp/tracing_features.h"
#include "third_party/zlib/google/compression_utils.h"
namespace content {
namespace {
// The time to live of a trace is currently 14 days.
const base::TimeDelta kTraceTimeToLive = base::Days(14);
// We limit uploads of 1 trace per scenario over a period of 7 days. Since
// traces live in the database for longer than 7 days, their TTL doesn't affect
// this unless the database is manually cleared.
const base::TimeDelta kMinTimeUntilNextUpload = base::Days(7);
// We limit the overall number of traces per scenario saved to the database at
// 20. When traces are deleted after their TTL, it leaves more capacity for new
// traces.
const size_t kMaxTracesPerScenario = 20;
const char kBackgroundTracingConfig[] = "config";
// |g_background_tracing_manager| is intentionally leaked on shutdown.
BackgroundTracingManager* g_background_tracing_manager = nullptr;
BackgroundTracingManagerImpl* g_background_tracing_manager_impl = nullptr;
void OpenDatabaseOnDatabaseTaskRunner(
TraceReportDatabase* database,
std::optional<base::FilePath> database_dir,
base::OnceCallback<void(BackgroundTracingManagerImpl::ScenarioCountMap,
std::optional<BaseTraceReport>,
bool)> on_database_created) {
if (database->is_initialized()) {
return;
}
bool success;
if (!database_dir) {
success = database->OpenDatabaseInMemoryForTesting(); // IN-TEST
} else {
success = database->OpenDatabase(*database_dir);
}
std::optional<NewTraceReport> report_to_upload;
if (base::FeatureList::IsEnabled(kBackgroundTracingDatabase)) {
report_to_upload = database->GetNextReportPendingUpload();
} else {
// Traces pending upload from previous sessions have timed out.
database->AllPendingUploadSkipped(SkipUploadReason::kUploadTimedOut);
}
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(std::move(on_database_created),
database->GetScenarioCounts(),
std::move(report_to_upload), success));
}
void AddTraceOnDatabaseTaskRunner(
TraceReportDatabase* database,
std::string&& serialized_trace,
std::string&& serialized_system_profile,
BaseTraceReport base_report,
bool should_save_trace,
bool is_crash_scenario,
base::OnceCallback<void(std::optional<NewTraceReport>, bool)>
on_trace_saved) {
if (!database->is_initialized()) {
return;
}
base::Time since = base::Time::Now() - kMinTimeUntilNextUpload;
auto upload_count =
database->UploadCountSince(base_report.scenario_name, since);
if (base_report.skip_reason == SkipUploadReason::kNoSkip &&
!is_crash_scenario && upload_count && *upload_count > 0) {
base_report.skip_reason = SkipUploadReason::kScenarioQuotaExceeded;
if (!should_save_trace) {
return;
}
}
std::string compressed_trace;
bool success = compression::GzipCompress(serialized_trace, &compressed_trace);
std::optional<NewTraceReport> report_to_upload;
if (success) {
UMA_HISTOGRAM_COUNTS_100000("Tracing.Background.CompressedTraceSizeInKB",
compressed_trace.size() / 1024);
if (base::FeatureList::IsEnabled(kBackgroundTracingDatabase)) {
NewTraceReport trace_report = base_report;
trace_report.trace_content = std::move(compressed_trace);
trace_report.system_profile = std::move(serialized_system_profile);
success = database->AddTrace(trace_report);
} else {
// When the database is disabled, we still store the base report without
// the trace content proto to enable tracking of trace upload limits.
success = database->AddTrace(base_report);
if (success && base_report.skip_reason == SkipUploadReason::kNoSkip) {
report_to_upload = std::move(base_report);
report_to_upload->trace_content = std::move(compressed_trace);
report_to_upload->system_profile = std::move(serialized_system_profile);
}
}
}
if (base::FeatureList::IsEnabled(kBackgroundTracingDatabase)) {
report_to_upload = database->GetNextReportPendingUpload();
}
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(std::move(on_trace_saved),
std::move(report_to_upload), success));
}
void GetProtoValueOnDatabaseTaskRunner(
TraceReportDatabase* database,
base::Token uuid,
base::OnceCallback<void(std::optional<std::string>,
std::optional<std::string>)> receive_callback,
base::OnceCallback<void(std::optional<BaseTraceReport>, bool)>
on_finalize_complete) {
DCHECK(base::FeatureList::IsEnabled(kBackgroundTracingDatabase));
auto trace_content = database->GetTraceContent(uuid);
auto serialized_system_profile = database->GetSystemProfile(uuid);
std::optional<ClientTraceReport> next_report;
if (trace_content) {
if (database->UploadComplete(uuid, base::Time::Now())) {
next_report = database->GetNextReportPendingUpload();
}
}
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(std::move(on_finalize_complete), std::move(next_report),
trace_content.has_value()));
std::move(receive_callback)
.Run(std::move(trace_content), std::move(serialized_system_profile));
}
} // namespace
BASE_FEATURE(kBackgroundTracingDatabase,
"BackgroundTracingDatabase",
base::FEATURE_DISABLED_BY_DEFAULT);
// static
const char BackgroundTracingManager::kContentTriggerConfig[] =
"content-trigger-config";
// static
std::unique_ptr<BackgroundTracingManager>
BackgroundTracingManager::CreateInstance() {
return std::make_unique<BackgroundTracingManagerImpl>();
}
// static
BackgroundTracingManager& BackgroundTracingManager::GetInstance() {
CHECK_NE(nullptr, g_background_tracing_manager);
return *g_background_tracing_manager;
}
// static
void BackgroundTracingManager::SetInstance(
BackgroundTracingManager* tracing_manager) {
DCHECK(g_background_tracing_manager == nullptr || tracing_manager == nullptr);
g_background_tracing_manager = tracing_manager;
}
// static
void BackgroundTracingManagerImpl::RecordMetric(Metrics metric) {
UMA_HISTOGRAM_ENUMERATION("Tracing.Background.ScenarioState", metric,
Metrics::NUMBER_OF_BACKGROUND_TRACING_METRICS);
}
// static
BackgroundTracingManagerImpl& BackgroundTracingManagerImpl::GetInstance() {
CHECK_NE(nullptr, g_background_tracing_manager_impl);
return *g_background_tracing_manager_impl;
}
// static
void BackgroundTracingManagerImpl::ActivateForProcess(
int child_process_id,
mojom::ChildProcess* child_process) {
// NOTE: Called from any thread.
mojo::PendingRemote<tracing::mojom::BackgroundTracingAgentProvider>
pending_provider;
child_process->GetBackgroundTracingAgentProvider(
pending_provider.InitWithNewPipeAndPassReceiver());
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(&BackgroundTracingManagerImpl::AddPendingAgent,
child_process_id, std::move(pending_provider)));
}
BackgroundTracingManagerImpl::BackgroundTracingManagerImpl()
: delegate_(GetContentClient()->browser()->GetTracingDelegate()),
database_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN})),
trace_database_(nullptr,
base::OnTaskRunnerDeleter(database_task_runner_)) {
BackgroundTracingManager::SetInstance(this);
NamedTriggerManager::SetInstance(this);
g_background_tracing_manager_impl = this;
BackgroundStartupTracingObserver::GetInstance();
}
BackgroundTracingManagerImpl::~BackgroundTracingManagerImpl() {
DCHECK_EQ(this, g_background_tracing_manager_impl);
if (active_scenario_) {
active_scenario_->Abort();
} else {
for (auto& scenario : scenarios_) {
scenario->Disable();
}
}
if (legacy_active_scenario_) {
legacy_active_scenario_->AbortScenario();
}
BackgroundTracingManager::SetInstance(nullptr);
NamedTriggerManager::SetInstance(nullptr);
g_background_tracing_manager_impl = nullptr;
}
void BackgroundTracingManagerImpl::OpenDatabaseIfExists() {
if (trace_database_) {
return;
}
std::optional<base::FilePath> database_dir =
GetContentClient()->browser()->GetLocalTracesDirectory();
if (!database_dir.has_value()) {
return;
}
trace_database_ = {new TraceReportDatabase,
base::OnTaskRunnerDeleter(database_task_runner_)};
database_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
[](TraceReportDatabase* trace_database, base::FilePath path) {
trace_database->OpenDatabaseIfExists(path);
},
base::Unretained(trace_database_.get()), database_dir.value()));
}
void BackgroundTracingManagerImpl::GetAllTraceReports(
GetReportsCallback callback) {
if (!trace_database_) {
std::move(callback).Run({});
return;
}
database_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&TraceReportDatabase::GetAllReports,
base::Unretained(trace_database_.get())),
std::move(callback));
}
void BackgroundTracingManagerImpl::DeleteSingleTrace(
const base::Token& trace_uuid,
FinishedProcessingCallback callback) {
if (!trace_database_) {
std::move(callback).Run(false);
return;
}
database_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&TraceReportDatabase::DeleteTrace,
base::Unretained(trace_database_.get()), trace_uuid),
std::move(callback));
}
void BackgroundTracingManagerImpl::DeleteAllTraces(
TraceUploadList::FinishedProcessingCallback callback) {
if (!trace_database_) {
std::move(callback).Run(false);
return;
}
database_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&TraceReportDatabase::DeleteAllTraces,
base::Unretained(trace_database_.get())),
std::move(callback));
}
void BackgroundTracingManagerImpl::UserUploadSingleTrace(
const base::Token& trace_uuid,
TraceUploadList::FinishedProcessingCallback callback) {
if (!trace_database_) {
std::move(callback).Run(false);
return;
}
database_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&TraceReportDatabase::UserRequestedUpload,
base::Unretained(trace_database_.get()), trace_uuid),
std::move(callback));
}
void BackgroundTracingManagerImpl::DownloadTrace(const base::Token& trace_uuid,
GetProtoCallback callback) {
if (!trace_database_) {
std::move(callback).Run(std::nullopt);
return;
}
database_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&TraceReportDatabase::GetTraceContent,
base::Unretained(trace_database_.get()), trace_uuid),
base::BindOnce(
[](GetProtoCallback callback,
const std::optional<std::string>& result) {
if (result) {
std::move(callback).Run(base::span<const char>(*result));
} else {
std::move(callback).Run(std::nullopt);
}
},
std::move(callback)));
}
void BackgroundTracingManagerImpl::OnTraceDatabaseCreated(
ScenarioCountMap scenario_saved_counts,
std::optional<BaseTraceReport> trace_to_upload,
bool creation_result) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
scenario_saved_counts_ = std::move(scenario_saved_counts);
trace_report_to_upload_ = std::move(trace_to_upload);
if (!creation_result) {
RecordMetric(Metrics::DATABASE_INITIALIZATION_FAILED);
return;
}
clean_database_timer_.Start(
FROM_HERE, base::Days(1),
base::BindRepeating(&BackgroundTracingManagerImpl::CleanDatabase,
weak_factory_.GetWeakPtr()));
}
void BackgroundTracingManagerImpl::OnTraceDatabaseUpdated(
ScenarioCountMap scenario_saved_counts) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
scenario_saved_counts_ = std::move(scenario_saved_counts);
}
void BackgroundTracingManagerImpl::OnTraceSaved(
const std::string& scenario_name,
std::optional<NewTraceReport> trace_to_upload,
bool success) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RecordMetric(success ? Metrics::SAVE_TRACE_SUCCEEDED
: Metrics::SAVE_TRACE_FAILED);
trace_report_to_upload_ = std::move(trace_to_upload);
if (success) {
++scenario_saved_counts_[scenario_name];
}
for (EnabledStateTestObserver* observer : background_tracing_observers_) {
observer->OnTraceSaved();
}
}
void BackgroundTracingManagerImpl::AddMetadataGeneratorFunction() {
auto* metadata_source = tracing::TraceEventMetadataSource::GetInstance();
metadata_source->AddGeneratorFunction(
base::BindRepeating(&BackgroundTracingManagerImpl::GenerateMetadataProto,
base::Unretained(this)));
}
bool BackgroundTracingManagerImpl::RequestActivateScenario() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RecordMetric(Metrics::SCENARIO_ACTIVATION_REQUESTED);
// Multi-scenarios sessions can't be initialized twice.
DCHECK(scenarios_.empty());
if (legacy_active_scenario_ &&
(legacy_active_scenario_->state() !=
BackgroundTracingActiveScenario::State::kIdle)) {
return false;
}
// If we don't have a high resolution timer available, traces will be
// too inaccurate to be useful.
if (!base::TimeTicks::IsHighResolution()) {
RecordMetric(Metrics::SCENARIO_ACTION_FAILED_LOWRES_CLOCK);
return false;
}
return true;
}
void BackgroundTracingManagerImpl::SetReceiveCallback(
ReceiveCallback receive_callback) {
receive_callback_ = std::move(receive_callback);
}
bool BackgroundTracingManagerImpl::InitializeScenarios(
const perfetto::protos::gen::ChromeFieldTracingConfig& config,
DataFiltering data_filtering) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!RequestActivateScenario()) {
return false;
}
requires_anonymized_data_ = (data_filtering != NO_DATA_FILTERING);
bool enable_package_name_filter =
(data_filtering == ANONYMIZE_DATA_AND_FILTER_PACKAGE_NAME);
InitializeTraceReportDatabase();
for (const auto& scenario_config : config.scenarios()) {
auto scenario =
TracingScenario::Create(scenario_config, requires_anonymized_data_,
enable_package_name_filter, this);
if (!scenario) {
return false;
}
scenarios_.push_back(std::move(scenario));
scenarios_.back()->Enable();
}
RecordMetric(Metrics::SCENARIO_ACTIVATED_SUCCESSFULLY);
return true;
}
bool BackgroundTracingManagerImpl::SetActiveScenario(
std::unique_ptr<BackgroundTracingConfig> config,
DataFiltering data_filtering) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
std::unique_ptr<BackgroundTracingConfigImpl> config_impl(
static_cast<BackgroundTracingConfigImpl*>(config.release()));
config_impl = BackgroundStartupTracingObserver::GetInstance()
.IncludeStartupConfigIfNeeded(std::move(config_impl));
bool startup_tracing_enabled = BackgroundStartupTracingObserver::GetInstance()
.enabled_in_current_session();
if (startup_tracing_enabled) {
// Anonymize data for startup tracing by default. We currently do not
// support storing the config in preferences for next session.
data_filtering = DataFiltering::ANONYMIZE_DATA;
}
if (!config_impl) {
return false;
}
if (!RequestActivateScenario()) {
return false;
}
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// If startup config was not set and we're not a SYSTEM scenario (system
// might already have started a trace in the background) but tracing was
// enabled, then do not set any scenario.
if (base::trace_event::TraceLog::GetInstance()->IsEnabled() &&
!startup_tracing_enabled &&
config_impl->tracing_mode() != BackgroundTracingConfigImpl::SYSTEM) {
return false;
}
#endif
if (config_impl->upload_limit_kb()) {
upload_limit_kb_ = *config_impl->upload_limit_kb();
}
if (config_impl->upload_limit_network_kb()) {
upload_limit_network_kb_ = *config_impl->upload_limit_network_kb();
}
requires_anonymized_data_ = (data_filtering != NO_DATA_FILTERING);
config_impl->set_requires_anonymized_data(requires_anonymized_data_);
bool enable_package_name_filter =
(data_filtering == ANONYMIZE_DATA_AND_FILTER_PACKAGE_NAME);
config_impl->SetPackageNameFilteringEnabled(enable_package_name_filter);
// TODO(oysteine): Retry when time_until_allowed has elapsed.
if (delegate_ &&
!delegate_->OnBackgroundTracingActive(requires_anonymized_data_)) {
return false;
}
legacy_active_scenario_ = std::make_unique<BackgroundTracingActiveScenario>(
std::move(config_impl), delegate_.get(),
base::BindOnce(&BackgroundTracingManagerImpl::OnScenarioAborted,
base::Unretained(this)));
for (EnabledStateTestObserver* observer : background_tracing_observers_) {
observer->OnScenarioActive(
legacy_active_scenario_->GetConfig()->scenario_name());
}
InitializeTraceReportDatabase();
if (startup_tracing_enabled) {
RecordMetric(Metrics::STARTUP_SCENARIO_TRIGGERED);
DoEmitNamedTrigger(kStartupTracingTriggerName);
}
legacy_active_scenario_->StartTracingIfConfigNeedsIt();
RecordMetric(Metrics::SCENARIO_ACTIVATED_SUCCESSFULLY);
return true;
}
void BackgroundTracingManagerImpl::InitializeTraceReportDatabase(
bool open_in_memory) {
std::optional<base::FilePath> database_dir;
if (!trace_database_) {
trace_database_ = {new TraceReportDatabase,
base::OnTaskRunnerDeleter(database_task_runner_)};
if (!open_in_memory) {
database_dir = GetContentClient()->browser()->GetLocalTracesDirectory();
if (!database_dir.has_value()) {
OnTraceDatabaseCreated({}, std::nullopt, false);
return;
}
}
}
database_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
OpenDatabaseOnDatabaseTaskRunner,
base::Unretained(trace_database_.get()), std::move(database_dir),
base::BindOnce(&BackgroundTracingManagerImpl::OnTraceDatabaseCreated,
weak_factory_.GetWeakPtr())));
}
bool BackgroundTracingManagerImpl::OnScenarioActive(
TracingScenario* active_scenario) {
DCHECK_EQ(active_scenario_, nullptr);
if (GetScenarioSavedCount(active_scenario->scenario_name()) >=
kMaxTracesPerScenario) {
return false;
}
if (delegate_ &&
!delegate_->OnBackgroundTracingActive(requires_anonymized_data_)) {
return false;
}
active_scenario_ = active_scenario;
UMA_HISTOGRAM_SPARSE("Tracing.Background.Scenario.Active",
variations::HashName(active_scenario->scenario_name()));
for (EnabledStateTestObserver* observer : background_tracing_observers_) {
observer->OnScenarioActive(active_scenario_->scenario_name());
}
for (auto& scenario : scenarios_) {
if (scenario.get() == active_scenario) {
continue;
}
scenario->Disable();
}
return true;
}
bool BackgroundTracingManagerImpl::OnScenarioIdle(
TracingScenario* idle_scenario) {
DCHECK_EQ(active_scenario_, idle_scenario);
active_scenario_ = nullptr;
UMA_HISTOGRAM_SPARSE("Tracing.Background.Scenario.Idle",
variations::HashName(idle_scenario->scenario_name()));
for (EnabledStateTestObserver* observer : background_tracing_observers_) {
observer->OnScenarioIdle(idle_scenario->scenario_name());
}
bool is_allowed_finalization =
!delegate_ ||
delegate_->OnBackgroundTracingIdle(requires_anonymized_data_);
for (auto& scenario : scenarios_) {
scenario->Enable();
}
return is_allowed_finalization;
}
void BackgroundTracingManagerImpl::OnScenarioRecording(
TracingScenario* scenario) {
DCHECK_EQ(active_scenario_, scenario);
UMA_HISTOGRAM_SPARSE("Tracing.Background.Scenario.Recording",
variations::HashName(scenario->scenario_name()));
OnStartTracingDone();
}
void BackgroundTracingManagerImpl::SaveTrace(
TracingScenario* scenario,
base::Token trace_uuid,
const BackgroundTracingRule* triggered_rule,
std::string&& trace_data) {
OnProtoDataComplete(std::move(trace_data), scenario->scenario_name(),
triggered_rule->rule_id(), /*is_crash_scenario=*/false,
trace_uuid);
}
bool BackgroundTracingManagerImpl::HasActiveScenario() {
return legacy_active_scenario_ != nullptr || active_scenario_ != nullptr;
}
bool BackgroundTracingManagerImpl::HasTraceToUpload() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Send the logs only when the trace size is within limits. If the connection
// type changes and we have a bigger than expected trace, then the next time
// service asks us when wifi is available, the trace will be sent. If we did
// collect a trace that is bigger than expected, then we will end up never
// uploading, and drop the trace. This should never happen because the trace
// buffer limits are set appropriately.
if (!trace_report_to_upload_) {
return false;
}
if (trace_report_to_upload_->total_size <= GetTraceUploadLimitKb() * 1024) {
return true;
}
RecordMetric(Metrics::LARGE_UPLOAD_WAITING_TO_RETRY);
return false;
}
void BackgroundTracingManagerImpl::GetTraceToUpload(
base::OnceCallback<void(std::optional<std::string>,
std::optional<std::string>)> receive_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!trace_report_to_upload_) {
std::move(receive_callback).Run(std::nullopt, std::nullopt);
return;
}
// Trace content was kept in memory.
if (!trace_report_to_upload_->trace_content.empty()) {
std::move(receive_callback)
.Run(std::move(trace_report_to_upload_->trace_content),
std::move(trace_report_to_upload_->system_profile));
database_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
[](TraceReportDatabase* trace_database, const base::Token& uuid) {
trace_database->UploadComplete(uuid, base::Time::Now());
},
base::Unretained(trace_database_.get()),
trace_report_to_upload_->uuid));
OnFinalizeComplete(std::nullopt, true);
return;
}
DCHECK(trace_database_);
BaseTraceReport trace_report = *std::move(trace_report_to_upload_);
database_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
GetProtoValueOnDatabaseTaskRunner,
base::Unretained(trace_database_.get()), trace_report.uuid,
std::move(receive_callback),
base::BindOnce(&BackgroundTracingManagerImpl::OnFinalizeComplete,
weak_factory_.GetWeakPtr())));
}
void BackgroundTracingManagerImpl::OnFinalizeComplete(
std::optional<BaseTraceReport> trace_to_upload,
bool success) {
trace_report_to_upload_ = std::move(trace_to_upload);
if (success) {
BackgroundTracingManagerImpl::RecordMetric(Metrics::UPLOAD_SUCCEEDED);
} else {
BackgroundTracingManagerImpl::RecordMetric(Metrics::UPLOAD_FAILED);
}
}
void BackgroundTracingManagerImpl::AddEnabledStateObserverForTesting(
BackgroundTracingManager::EnabledStateTestObserver* observer) {
// Ensure that this code is called on the UI thread, except for
// tests where a UI thread might not have been initialized at this point.
DCHECK(
content::BrowserThread::CurrentlyOn(content::BrowserThread::UI) ||
!content::BrowserThread::IsThreadInitialized(content::BrowserThread::UI));
background_tracing_observers_.insert(observer);
}
void BackgroundTracingManagerImpl::RemoveEnabledStateObserverForTesting(
BackgroundTracingManager::EnabledStateTestObserver* observer) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
background_tracing_observers_.erase(observer);
}
void BackgroundTracingManagerImpl::AddAgent(
tracing::mojom::BackgroundTracingAgent* agent) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
agents_.insert(agent);
for (AgentObserver* observer : agent_observers_) {
observer->OnAgentAdded(agent);
}
}
void BackgroundTracingManagerImpl::RemoveAgent(
tracing::mojom::BackgroundTracingAgent* agent) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (AgentObserver* observer : agent_observers_) {
observer->OnAgentRemoved(agent);
}
agents_.erase(agent);
}
void BackgroundTracingManagerImpl::AddAgentObserver(AgentObserver* observer) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
agent_observers_.insert(observer);
MaybeConstructPendingAgents();
for (tracing::mojom::BackgroundTracingAgent* agent : agents_) {
observer->OnAgentAdded(agent);
}
}
void BackgroundTracingManagerImpl::RemoveAgentObserver(
AgentObserver* observer) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
agent_observers_.erase(observer);
for (tracing::mojom::BackgroundTracingAgent* agent : agents_) {
observer->OnAgentRemoved(agent);
}
}
BackgroundTracingActiveScenario*
BackgroundTracingManagerImpl::GetActiveScenarioForTesting() {
DCHECK(legacy_active_scenario_);
return legacy_active_scenario_.get();
}
bool BackgroundTracingManagerImpl::IsTracingForTesting() {
if (legacy_active_scenario_) {
return legacy_active_scenario_->state() ==
BackgroundTracingActiveScenario::State::kTracing;
} else if (active_scenario_) {
return active_scenario_->current_state() ==
TracingScenario::State::kRecording;
}
return false;
}
void BackgroundTracingManagerImpl::SaveTraceForTesting(
std::string&& serialized_trace,
const std::string& scenario_name,
const std::string& rule_name,
const base::Token& uuid) {
InitializeTraceReportDatabase(true);
OnProtoDataComplete(std::move(serialized_trace), scenario_name, rule_name,
/*is_crash_scenario=*/false, uuid);
}
size_t BackgroundTracingManagerImpl::GetScenarioSavedCount(
const std::string& scenario_name) {
auto it = scenario_saved_counts_.find(scenario_name);
if (it != scenario_saved_counts_.end()) {
return it->second;
}
return 0;
}
void BackgroundTracingManagerImpl::OnProtoDataComplete(
std::string&& serialized_trace,
const std::string& scenario_name,
const std::string& rule_name,
bool is_crash_scenario,
const base::Token& uuid) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (EnabledStateTestObserver* observer : background_tracing_observers_) {
observer->OnTraceReceived(serialized_trace);
}
if (!receive_callback_) {
DCHECK(trace_database_);
UMA_HISTOGRAM_SPARSE("Tracing.Background.Scenario.SaveTrace",
variations::HashName(scenario_name));
SkipUploadReason skip_reason = SkipUploadReason::kNoSkip;
if (!requires_anonymized_data_) {
skip_reason = SkipUploadReason::kNotAnonymized;
} else if (serialized_trace.size() > upload_limit_kb_ * 1024) {
skip_reason = SkipUploadReason::kSizeLimitExceeded;
}
bool should_save_trace =
!delegate_ || delegate_->ShouldSaveUnuploadedTrace();
if (skip_reason != SkipUploadReason::kNoSkip && !should_save_trace) {
return;
}
BackgroundTracingManagerImpl::RecordMetric(Metrics::FINALIZATION_STARTED);
UMA_HISTOGRAM_COUNTS_100000("Tracing.Background.FinalizingTraceSizeInKB2",
serialized_trace.size() / 1024);
BaseTraceReport base_report;
base_report.uuid = uuid;
base_report.creation_time = base::Time::Now();
base_report.scenario_name = scenario_name;
base_report.upload_rule_name = rule_name;
base_report.total_size = serialized_trace.size();
base_report.skip_reason = skip_reason;
std::string serialized_system_profile;
if (system_profile_recorder_) {
serialized_system_profile = system_profile_recorder_.Run();
}
database_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
AddTraceOnDatabaseTaskRunner,
base::Unretained(trace_database_.get()),
std::move(serialized_trace), std::move(serialized_system_profile),
std::move(base_report), should_save_trace, is_crash_scenario,
base::BindOnce(&BackgroundTracingManagerImpl::OnTraceSaved,
weak_factory_.GetWeakPtr(), scenario_name)));
} else {
BackgroundTracingManagerImpl::RecordMetric(
Metrics::FINALIZATION_STARTED_WITH_LOCAL_OUTPUT);
receive_callback_.Run(
std::move(serialized_trace),
base::BindOnce(&BackgroundTracingManagerImpl::OnFinalizeComplete,
weak_factory_.GetWeakPtr(), std::nullopt));
}
}
std::unique_ptr<content::BackgroundTracingConfig>
BackgroundTracingManagerImpl::GetBackgroundTracingConfig(
const std::string& trial_name) {
std::string config_text =
base::GetFieldTrialParamValue(trial_name, kBackgroundTracingConfig);
if (config_text.empty())
return nullptr;
auto value = base::JSONReader::Read(config_text);
if (!value)
return nullptr;
if (!value->is_dict())
return nullptr;
return BackgroundTracingConfig::FromDict(std::move(*value).TakeDict());
}
void BackgroundTracingManagerImpl::SetSystemProfileRecorder(
base::RepeatingCallback<std::string()> recorder) {
system_profile_recorder_ = std::move(recorder);
}
void BackgroundTracingManagerImpl::SetNamedTriggerCallback(
const std::string& trigger_name,
base::RepeatingCallback<bool()> callback) {
if (!callback) {
named_trigger_callbacks_.erase(trigger_name);
} else {
named_trigger_callbacks_.emplace(trigger_name, std::move(callback));
}
}
bool BackgroundTracingManagerImpl::DoEmitNamedTrigger(
const std::string& trigger_name) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto it = named_trigger_callbacks_.find(trigger_name);
if (it == named_trigger_callbacks_.end()) {
return false;
}
return it->second.Run();
}
void BackgroundTracingManagerImpl::InvalidateTriggersCallbackForTesting() {
named_trigger_callbacks_.clear();
}
void BackgroundTracingManagerImpl::OnStartTracingDone() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (EnabledStateTestObserver* observer : background_tracing_observers_) {
observer->OnTraceStarted();
}
}
void BackgroundTracingManagerImpl::GenerateMetadataProto(
perfetto::protos::pbzero::ChromeMetadataPacket* metadata,
bool privacy_filtering_enabled) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (legacy_active_scenario_) {
legacy_active_scenario_->GenerateMetadataProto(metadata);
} else if (active_scenario_) {
active_scenario_->GenerateMetadataProto(metadata);
}
}
void BackgroundTracingManagerImpl::AbortScenarioForTesting() {
if (legacy_active_scenario_) {
legacy_active_scenario_->AbortScenario();
} else if (active_scenario_) {
active_scenario_->Abort();
}
}
void BackgroundTracingManagerImpl::OnScenarioAborted() {
DCHECK(legacy_active_scenario_);
for (EnabledStateTestObserver* observer : background_tracing_observers_) {
observer->OnScenarioIdle(
legacy_active_scenario_->GetConfig()->scenario_name());
}
legacy_active_scenario_.reset();
}
void BackgroundTracingManagerImpl::CleanDatabase() {
DCHECK(trace_database_);
database_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(
[](TraceReportDatabase* trace_database) {
trace_database->DeleteTracesOlderThan(kTraceTimeToLive);
return trace_database->GetScenarioCounts();
},
base::Unretained(trace_database_.get())),
base::BindOnce(&BackgroundTracingManagerImpl::OnTraceDatabaseUpdated,
weak_factory_.GetWeakPtr()));
}
void BackgroundTracingManagerImpl::DeleteTracesInDateRange(base::Time start,
base::Time end) {
// The trace report database needs to exist for clean up. Avoid creating or
// initializing the trace report database to perform a database clean up.
std::optional<base::FilePath> database_dir;
if (!trace_database_) {
database_dir = GetContentClient()->browser()->GetLocalTracesDirectory();
if (database_dir.has_value()) {
return;
}
trace_database_ = {new TraceReportDatabase,
base::OnTaskRunnerDeleter(database_task_runner_)};
}
auto on_database_updated =
base::BindOnce(&BackgroundTracingManagerImpl::OnTraceDatabaseUpdated,
weak_factory_.GetWeakPtr());
database_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
[](TraceReportDatabase* trace_database,
std::optional<base::FilePath> database_dir, base::Time start,
base::Time end,
base::OnceCallback<void(ScenarioCountMap)> on_database_updated) {
if (database_dir.has_value() &&
!trace_database->OpenDatabaseIfExists(database_dir.value())) {
return;
}
if (!trace_database->is_initialized()) {
return;
}
if (trace_database->DeleteTracesInDateRange(start, end)) {
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(std::move(on_database_updated),
trace_database->GetScenarioCounts()));
} else {
RecordMetric(Metrics::DATABASE_CLEANUP_FAILED);
}
},
base::Unretained(trace_database_.get()), database_dir, start, end,
std::move(on_database_updated)));
}
// static
void BackgroundTracingManagerImpl::AddPendingAgent(
int child_process_id,
mojo::PendingRemote<tracing::mojom::BackgroundTracingAgentProvider>
pending_provider) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Delay agent initialization until we have an interested AgentObserver.
// We set disconnect handler for cleanup when the tracing target is closed.
mojo::Remote<tracing::mojom::BackgroundTracingAgentProvider> provider(
std::move(pending_provider));
provider.set_disconnect_handler(base::BindOnce(
&BackgroundTracingManagerImpl::ClearPendingAgent, child_process_id));
GetInstance().pending_agents_[child_process_id] = std::move(provider);
GetInstance().MaybeConstructPendingAgents();
}
// static
void BackgroundTracingManagerImpl::ClearPendingAgent(int child_process_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
GetInstance().pending_agents_.erase(child_process_id);
}
void BackgroundTracingManagerImpl::MaybeConstructPendingAgents() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (agent_observers_.empty())
return;
for (auto& pending_agent : pending_agents_) {
pending_agent.second.set_disconnect_handler(base::OnceClosure());
BackgroundTracingAgentClientImpl::Create(pending_agent.first,
std::move(pending_agent.second));
}
pending_agents_.clear();
}
size_t BackgroundTracingManagerImpl::GetTraceUploadLimitKb() const {
#if BUILDFLAG(IS_ANDROID)
auto type = net::NetworkChangeNotifier::GetConnectionType();
if (net::NetworkChangeNotifier::IsConnectionCellular(type)) {
return upload_limit_network_kb_;
}
#endif
return upload_limit_kb_;
}
} // namespace content