blob: 39ffdbd5a53f0fe0b6451e7ff23a2f5c14ff5b8e [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#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/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; // 30 sec
#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 // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
} // namespace
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();
TraceEventDataSource::GetInstance()->RegisterStartupHooks();
// Create the PerfettoTracedProcess.
PerfettoTracedProcess::Get();
// Initialize the client library's TrackRegistry to support trace points
// during startup tracing. We don't setup the client library completely here
// yet, because we don't have field trials loaded yet (which influence which
// backends we enable).
// TODO(eseckler): Make it possible to initialize client lib backends after
// setting up the client library?
perfetto::internal::TrackRegistry::InitializeInstance();
// Ensure TraceLog is initialized first.
// https://crbug.com/764357
TraceLog::GetInstance();
auto* startup_config = TraceStartupConfig::GetInstance();
if (startup_config->IsEnabled()) {
// Ensure that data sources are created and registered.
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;
// TODO(khokhlov): Support startup tracing with the system backend in the
// SDK build.
opts.backend = perfetto::kCustomBackend;
// TODO(khokhlov): After client library is moved onto a separate thread
// and it's possible to start startup tracing early, replace this call with
// perfetto::Tracing::SetupStartupTracing(perfetto_config, args).
PerfettoTracedProcess::Get()->RequestStartupTracing(perfetto_config, opts);
#else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
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 // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
}
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,
/*convert_to_legacy_json=*/false);
perfetto::Tracing::SetupStartupTracingOpts opts;
opts.timeout_ms = kStartupTracingTimeoutMs;
opts.backend = perfetto::kCustomBackend;
// TODO(khokhlov): After client library is moved onto a separate thread
// and it's possible to start startup tracing early, replace this call with
// perfetto::Tracing::SetupStartupTracing(perfetto_config, args).
PerfettoTracedProcess::Get()->RequestStartupTracing(perfetto_config, opts);
return true;
#else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
return PerfettoTracedProcess::Get()->SetupStartupTracing(
PerfettoTracedProcess::Get()->producer_client(), trace_config,
privacy_filtering_enabled);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
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()) {
// Ensure that data sources are created and registered.
TraceEventAgent::GetInstance();
// Connect to system service if available (currently a no-op except on
// Posix). Has to happen on the producer's sequence, as all communication
// with the system Perfetto service should occur on a single sequence.
if (!PerfettoTracedProcess::Get()->system_producer())
PerfettoTracedProcess::Get()->SetupSystemTracing();
PerfettoTracedProcess::Get()
->GetTaskRunner()
->GetOrCreateTaskRunner()
->PostTask(FROM_HERE, base::BindOnce([]() {
PerfettoTracedProcess::Get()
->system_producer()
->ConnectToSystemService();
}));
}
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
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)
// TODO(khokhlov): Figure out if we are using custom or system backend and
// propagate this info to the child process (after startup tracing w/system
// backend is supported in the SDK build).
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 // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
if (!trace_log->IsEnabled())
return;
// It's possible that tracing is enabled only for atrace, in which case the
// TraceEventDataSource isn't registered. In that case, there's no reason to
// enable startup tracing in the child process (and we wouldn't know the
// correct value for privacy_filtering_enabled below).
if (!TraceEventDataSource::GetInstance()->IsEnabled())
return;
// (Posix)SystemProducer doesn't currently support startup tracing, so don't
// attempt to enable startup tracing in child processes if system tracing is
// active.
if (PerfettoTracedProcess::Get()->system_producer() &&
PerfettoTracedProcess::Get()->system_producer()->IsTracingActive()) {
return;
}
// The child process startup may race with a concurrent disabling of the
// tracing session by the tracing service. To avoid being stuck in startup
// tracing mode forever, the child process will discard the startup session
// after a timeout (|startup_tracing_timer_| in TraceEventDataSource).
//
// Note that we disregard the config's process filter, since it's possible
// that the trace consumer will update the config to include the process
// shortly. Otherwise, the startup tracing timeout in the child will
// eventually disable tracing for the process.
trace_config = trace_log->GetCurrentTraceConfig();
privacy_filtering_enabled =
TraceEventDataSource::GetInstance()->IsPrivacyFilteringEnabled();
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// We can't currently propagate event filter options, histogram names, memory
// dump configs, or trace buffer sizes via command line flags (they only
// support categories, trace options, record mode). If event filters or
// histogram names are set, we bail out here to avoid recording events that we
// shouldn't in the child process. Even if memory dump config is set, it's OK
// to propagate the remaining config, because the child won't record events it
// shouldn't without it and will adopt the memory dump config once it connects
// to the tracing service. Buffer sizes configure the tracing service's
// central buffer, so also don't affect local tracing.
//
// TODO(eseckler): Support propagating the full config via command line flags
// somehow (--trace-config?). This will also need some rethinking to support
// multiple concurrent tracing sessions in the future.
if (!trace_config.event_filters().empty())
return;
if (!trace_config.histogram_names().empty())
return;
// In SDK build, any difference between startup config and the config
// supplied to the tracing service will prevent the service from adopting
// the startup session. So if the config contains any field that can't be
// propagated via command line, we bail out here.
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
if (!CanBePropagatedViaCommandLine(trace_config))
return;
#endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// Make sure that the startup session uses privacy filtering mode if it's
// enabled for the browser's session.
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());
// The argument filtering setting is passed via trace options as part of
// --trace-startup-record-mode.
cmd_line->AppendSwitchASCII(switches::kTraceStartupRecordMode,
trace_config.ToTraceOptionsString());
}
} // namespace tracing