#include "services/tracing/public/cpp/trace_startup.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/trace_event/trace_log.h"
#include "components/tracing/common/trace_startup_config.h"
#include "components/tracing/common/trace_to_console.h"
#include "components/tracing/common/tracing_switches.h"
#include "services/tracing/public/cpp/perfetto/perfetto_config.h"
#include "services/tracing/public/cpp/perfetto/producer_client.h"
#include "services/tracing/public/cpp/perfetto/system_producer.h"
#include "services/tracing/public/cpp/perfetto/trace_event_data_source.h"
#include "services/tracing/public/cpp/perfetto/traced_value_proto_writer.h"
#include "services/tracing/public/cpp/trace_event_agent.h"
#include "services/tracing/public/cpp/trace_event_args_allowlist.h"
#include "services/tracing/public/cpp/tracing_features.h"
#include "third_party/perfetto/include/perfetto/tracing/track.h"
namespace tracing {
namespace {
constexpr char kJsonFormat[] = "json";
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
constexpr uint32_t kStartupTracingTimeoutMs = 30 * 1000;
#endif
using base::trace_event::TraceConfig;
using base::trace_event::TraceLog;
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
bool CanBePropagatedViaCommandLine(
const base::trace_event::TraceConfig& trace_config) {
base::trace_event::TraceConfig reconstructed_config(
trace_config.ToCategoryFilterString(),
trace_config.ToTraceOptionsString());
return reconstructed_config.ToString() == trace_config.ToString();
}
#endif
}
bool g_tracing_initialized_after_threadpool_and_featurelist = false;
bool IsTracingInitialized() {
return g_tracing_initialized_after_threadpool_and_featurelist;
}
void EnableStartupTracingIfNeeded() {
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
RegisterTracedValueProtoWriter();
TraceEventDataSource::GetInstance()->RegisterStartupHooks();
PerfettoTracedProcess::Get();
perfetto::internal::TrackRegistry::InitializeInstance();
TraceLog::GetInstance();
auto* startup_config = TraceStartupConfig::GetInstance();
if (startup_config->IsEnabled()) {
TraceEventAgent::GetInstance();
TraceConfig trace_config = startup_config->GetTraceConfig();
bool privacy_filtering_enabled =
startup_config->GetSessionOwner() ==
TraceStartupConfig::SessionOwner::kBackgroundTracing ||
command_line.HasSwitch(switches::kTraceStartupEnablePrivacyFiltering);
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
bool convert_to_legacy_json = startup_config->GetOutputFormat() ==
TraceStartupConfig::OutputFormat::kLegacyJSON;
perfetto::TraceConfig perfetto_config = tracing::GetDefaultPerfettoConfig(
trace_config, privacy_filtering_enabled, convert_to_legacy_json);
int duration_in_seconds =
tracing::TraceStartupConfig::GetInstance()->GetStartupDuration();
if (duration_in_seconds > 0)
perfetto_config.set_duration_ms(duration_in_seconds * 1000);
perfetto::Tracing::SetupStartupTracingOpts opts;
opts.timeout_ms = kStartupTracingTimeoutMs;
opts.backend = perfetto::kCustomBackend;
PerfettoTracedProcess::Get()->RequestStartupTracing(perfetto_config, opts);
#else
PerfettoProducer* producer =
PerfettoTracedProcess::Get()->producer_client();
if (startup_config->GetSessionOwner() ==
TraceStartupConfig::SessionOwner::kSystemTracing) {
PerfettoTracedProcess::Get()->SetupSystemTracing();
producer = PerfettoTracedProcess::Get()->system_producer();
}
if (!PerfettoTracedProcess::Get()->SetupStartupTracing(
producer, trace_config, privacy_filtering_enabled)) {
startup_config->SetDisabled();
}
#endif
}
}
bool EnableStartupTracingForProcess(
const base::trace_event::TraceConfig& trace_config,
bool privacy_filtering_enabled) {
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
perfetto::TraceConfig perfetto_config =
tracing::GetDefaultPerfettoConfig(trace_config, privacy_filtering_enabled,
false);
perfetto::Tracing::SetupStartupTracingOpts opts;
opts.timeout_ms = kStartupTracingTimeoutMs;
opts.backend = perfetto::kCustomBackend;
PerfettoTracedProcess::Get()->RequestStartupTracing(perfetto_config, opts);
return true;
#else
return PerfettoTracedProcess::Get()->SetupStartupTracing(
PerfettoTracedProcess::Get()->producer_client(), trace_config,
privacy_filtering_enabled);
#endif
}
void InitTracingPostThreadPoolStartAndFeatureList(bool enable_consumer) {
if (g_tracing_initialized_after_threadpool_and_featurelist)
return;
g_tracing_initialized_after_threadpool_and_featurelist = true;
DCHECK(base::ThreadPoolInstance::Get());
DCHECK(base::FeatureList::GetInstance());
PerfettoTracedProcess::Get()->OnThreadPoolAvailable(enable_consumer);
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
if (ShouldSetupSystemTracing()) {
TraceEventAgent::GetInstance();
if (!PerfettoTracedProcess::Get()->system_producer())
PerfettoTracedProcess::Get()->SetupSystemTracing();
PerfettoTracedProcess::Get()
->GetTaskRunner()
->GetOrCreateTaskRunner()
->PostTask(FROM_HERE, base::BindOnce([]() {
PerfettoTracedProcess::Get()
->system_producer()
->ConnectToSystemService();
}));
}
#endif
}
void PropagateTracingFlagsToChildProcessCmdLine(base::CommandLine* cmd_line) {
base::trace_event::TraceLog* trace_log =
base::trace_event::TraceLog::GetInstance();
base::trace_event::TraceConfig trace_config;
bool privacy_filtering_enabled = false;
bool convert_to_legacy_json = false;
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
const auto* startup_config = TraceStartupConfig::GetInstance();
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (startup_config->IsEnabled()) {
trace_config = startup_config->GetTraceConfig();
privacy_filtering_enabled =
startup_config->GetSessionOwner() ==
TraceStartupConfig::SessionOwner::kBackgroundTracing ||
command_line.HasSwitch(switches::kTraceStartupEnablePrivacyFiltering);
convert_to_legacy_json = startup_config->GetOutputFormat() ==
TraceStartupConfig::OutputFormat::kLegacyJSON;
} else if (trace_log->IsEnabled()) {
const auto chrome_config =
trace_log->GetCurrentTrackEventDataSourceConfig().chrome_config();
trace_config = base::trace_event::TraceConfig(chrome_config.trace_config());
privacy_filtering_enabled = chrome_config.privacy_filtering_enabled();
convert_to_legacy_json = chrome_config.convert_to_legacy_json();
} else {
return;
}
#else
if (!trace_log->IsEnabled())
return;
if (!TraceEventDataSource::GetInstance()->IsEnabled())
return;
if (PerfettoTracedProcess::Get()->system_producer() &&
PerfettoTracedProcess::Get()->system_producer()->IsTracingActive()) {
return;
}
trace_config = trace_log->GetCurrentTraceConfig();
privacy_filtering_enabled =
TraceEventDataSource::GetInstance()->IsPrivacyFilteringEnabled();
#endif
if (!trace_config.event_filters().empty())
return;
if (!trace_config.histogram_names().empty())
return;
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
if (!CanBePropagatedViaCommandLine(trace_config))
return;
#endif
if (privacy_filtering_enabled)
cmd_line->AppendSwitch(switches::kTraceStartupEnablePrivacyFiltering);
if (convert_to_legacy_json)
cmd_line->AppendSwitchASCII(switches::kTraceStartupFormat, kJsonFormat);
cmd_line->AppendSwitchASCII(switches::kTraceStartup,
trace_config.ToCategoryFilterString());
cmd_line->AppendSwitchASCII(switches::kTraceStartupRecordMode,
trace_config.ToTraceOptionsString());
}
}