blob: 1425a7580149ced191ac2167538dadf226fb9129 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// 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/perfetto/trace_event_data_source.h"
#include <atomic>
#include <map>
#include <memory>
#include <utility>
#include <vector>
#include "base/base64.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/debug/crash_logging.h"
#include "base/debug/leak_annotations.h"
#include "base/json/json_writer.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted_memory.h"
#include "base/metrics/histogram_samples.h"
#include "base/metrics/metrics_hashes.h"
#include "base/metrics/statistics_recorder.h"
#include "base/metrics/user_metrics.h"
#include "base/no_destructor.h"
#include "base/pickle.h"
#include "base/rand_util.h"
#include "base/sequence_checker.h"
#include "base/strings/pattern.h"
#include "base/strings/string_number_conversions.h"
#include "base/synchronization/lock.h"
#include "base/task/common/scoped_defer_task_posting.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/time.h"
#include "base/trace_event/trace_config.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_log.h"
#include "base/tracing/trace_time.h"
#include "base/tracing/tracing_tls.h"
#include "build/build_config.h"
#include "components/tracing/common/tracing_switches.h"
#include "services/tracing/public/cpp/perfetto/macros.h"
#include "services/tracing/public/cpp/perfetto/perfetto_producer.h"
#include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
#include "services/tracing/public/cpp/perfetto/system_producer.h"
#include "services/tracing/public/cpp/perfetto/trace_string_lookup.h"
#include "services/tracing/public/cpp/perfetto/traced_value_proto_writer.h"
#include "services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.h"
#include "services/tracing/public/cpp/trace_event_args_allowlist.h"
#include "services/tracing/public/cpp/trace_startup.h"
#include "services/tracing/public/mojom/constants.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/perfetto/include/perfetto/ext/tracing/core/shared_memory_arbiter.h"
#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_writer.h"
#include "third_party/perfetto/include/perfetto/protozero/message.h"
#include "third_party/perfetto/include/perfetto/tracing/track.h"
#include "third_party/perfetto/include/perfetto/tracing/track_event_interned_data_index.h"
#include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_metadata.pbzero.h"
#include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h"
#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_histogram_sample.pbzero.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_process_descriptor.pbzero.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_user_event.pbzero.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
#if defined(OS_ANDROID)
#include "base/android/application_status_listener.h"
#include "base/android/build_info.h"
#include "base/trace_event/application_state_proto_android.h"
#endif
using TraceLog = base::trace_event::TraceLog;
using TraceEvent = base::trace_event::TraceEvent;
using TraceConfig = base::trace_event::TraceConfig;
using TracePacketHandle = perfetto::TraceWriter::TracePacketHandle;
using TraceRecordMode = base::trace_event::TraceRecordMode;
using perfetto::protos::pbzero::ChromeMetadataPacket;
using perfetto::protos::pbzero::ChromeProcessDescriptor;
using perfetto::protos::pbzero::ProcessDescriptor;
using perfetto::protos::pbzero::TrackDescriptor;
namespace tracing {
namespace {
TraceEventMetadataSource* g_trace_event_metadata_source_for_testing = nullptr;
void EmitRecurringUpdates() {
#if defined(OS_ANDROID)
static const ChromeProcessDescriptor::ProcessType process_type =
GetProcessType(
base::trace_event::TraceLog::GetInstance()->process_name());
if (process_type == ChromeProcessDescriptor::PROCESS_BROWSER) {
auto state = base::android::ApplicationStatusListener::GetState();
TRACE_APPLICATION_STATE(state);
}
#endif
}
static_assert(
sizeof(TraceEventDataSource::SessionFlags) <= sizeof(uint64_t),
"SessionFlags should remain small to ensure lock-free atomic operations");
// Helper class used to ensure no tasks are posted while
// TraceEventDataSource::lock_ is held.
class SCOPED_LOCKABLE AutoLockWithDeferredTaskPosting {
public:
explicit AutoLockWithDeferredTaskPosting(base::Lock& lock)
EXCLUSIVE_LOCK_FUNCTION(lock)
: autolock_(lock) {}
~AutoLockWithDeferredTaskPosting() UNLOCK_FUNCTION() = default;
private:
// The ordering is important: |defer_task_posting_| must be destroyed
// after |autolock_| to ensure the lock is not held when any deferred
// tasks are posted..
base::ScopedDeferTaskPosting defer_task_posting_;
base::AutoLock autolock_;
};
absl::optional<uint64_t> GetTraceCrashId() {
static base::debug::CrashKeyString* key = base::debug::AllocateCrashKeyString(
"chrome-trace-id", base::debug::CrashKeySize::Size32);
if (!key) {
return absl::nullopt;
}
uint64_t id = base::RandUint64();
base::debug::SetCrashKeyString(key, base::NumberToString(id));
return id;
}
} // namespace
using perfetto::protos::pbzero::ChromeEventBundle;
using ChromeEventBundleHandle = protozero::MessageHandle<ChromeEventBundle>;
// static
TraceEventMetadataSource* TraceEventMetadataSource::GetInstance() {
static base::NoDestructor<TraceEventMetadataSource> instance;
return instance.get();
}
TraceEventMetadataSource::TraceEventMetadataSource()
: DataSourceBase(mojom::kMetaDataSourceName),
origin_task_runner_(base::SequencedTaskRunnerHandle::Get()) {
g_trace_event_metadata_source_for_testing = this;
PerfettoTracedProcess::Get()->AddDataSource(this);
AddGeneratorFunction(base::BindRepeating(
&TraceEventMetadataSource::WriteMetadataPacket, base::Unretained(this)));
AddGeneratorFunction(base::BindRepeating(
&TraceEventMetadataSource::GenerateTraceConfigMetadataDict,
base::Unretained(this)));
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
perfetto::DataSourceDescriptor dsd;
dsd.set_name(mojom::kMetaDataSourceName);
DataSourceProxy::Register(dsd, this);
#endif
}
TraceEventMetadataSource::~TraceEventMetadataSource() = default;
void TraceEventMetadataSource::AddGeneratorFunction(
JsonMetadataGeneratorFunction generator) {
DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
{
base::AutoLock lock(lock_);
json_generator_functions_.push_back(generator);
}
// An EventBundle is created when nullptr is passed.
GenerateJsonMetadataFromGenerator(generator, nullptr);
}
void TraceEventMetadataSource::AddGeneratorFunction(
MetadataGeneratorFunction generator) {
DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
{
base::AutoLock lock(lock_);
generator_functions_.push_back(generator);
}
GenerateMetadataFromGenerator(generator);
}
void TraceEventMetadataSource::AddGeneratorFunction(
PacketGeneratorFunction generator) {
DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
{
base::AutoLock lock(lock_);
packet_generator_functions_.push_back(generator);
}
GenerateMetadataPacket(generator);
}
void TraceEventMetadataSource::WriteMetadataPacket(
perfetto::protos::pbzero::ChromeMetadataPacket* metadata_proto,
bool privacy_filtering_enabled) {
#if defined(OS_ANDROID) && defined(OFFICIAL_BUILD)
// Version code is only set for official builds on Android.
const char* version_code_str =
base::android::BuildInfo::GetInstance()->package_version_code();
if (version_code_str) {
int version_code = 0;
bool res = base::StringToInt(version_code_str, &version_code);
DCHECK(res);
metadata_proto->set_chrome_version_code(version_code);
}
#endif // defined(OS_ANDROID) && defined(OFFICIAL_BUILD)
AutoLockWithDeferredTaskPosting lock(lock_);
if (parsed_chrome_config_) {
metadata_proto->set_enabled_categories(
parsed_chrome_config_->ToCategoryFilterString());
}
}
absl::optional<base::Value>
TraceEventMetadataSource::GenerateTraceConfigMetadataDict() {
AutoLockWithDeferredTaskPosting lock(lock_);
if (chrome_config_.empty()) {
return absl::nullopt;
}
base::Value metadata_dict(base::Value::Type::DICTIONARY);
// If argument filtering is enabled, we need to check if the trace config is
// allowlisted before emitting it.
// TODO(eseckler): Figure out a way to solve this without calling directly
// into IsMetadataAllowlisted().
if (!parsed_chrome_config_->IsArgumentFilterEnabled() ||
IsMetadataAllowlisted("trace-config")) {
metadata_dict.SetStringKey("trace-config", chrome_config_);
} else {
metadata_dict.SetStringKey("trace-config", "__stripped__");
}
chrome_config_ = std::string();
return metadata_dict;
}
void TraceEventMetadataSource::GenerateMetadataFromGenerator(
const TraceEventMetadataSource::MetadataGeneratorFunction& generator) {
DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
{
AutoLockWithDeferredTaskPosting lock(lock_);
if (!emit_metadata_at_start_)
return;
}
DataSourceProxy::Trace([&](DataSourceProxy::TraceContext ctx) {
auto packet = ctx.NewTracePacket();
packet->set_timestamp(perfetto::TrackEvent::GetTraceTimeNs());
packet->set_timestamp_clock_id(perfetto::TrackEvent::GetTraceClockId());
auto* chrome_metadata = packet->set_chrome_metadata();
generator.Run(chrome_metadata, privacy_filtering_enabled_);
});
#else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
perfetto::TraceWriter::TracePacketHandle trace_packet;
{
AutoLockWithDeferredTaskPosting lock(lock_);
if (!emit_metadata_at_start_ || !trace_writer_) {
return;
}
trace_packet = trace_writer_->NewTracePacket();
}
trace_packet->set_timestamp(
TRACE_TIME_TICKS_NOW().since_origin().InNanoseconds());
trace_packet->set_timestamp_clock_id(base::tracing::kTraceClockId);
auto* chrome_metadata = trace_packet->set_chrome_metadata();
generator.Run(chrome_metadata, privacy_filtering_enabled_);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
void TraceEventMetadataSource::GenerateMetadataPacket(
const TraceEventMetadataSource::PacketGeneratorFunction& generator) {
DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
{
AutoLockWithDeferredTaskPosting lock(lock_);
if (!emit_metadata_at_start_)
return;
}
DataSourceProxy::Trace([&](DataSourceProxy::TraceContext ctx) {
auto packet = ctx.NewTracePacket();
packet->set_timestamp(perfetto::TrackEvent::GetTraceTimeNs());
packet->set_timestamp_clock_id(perfetto::TrackEvent::GetTraceClockId());
generator.Run(packet.get(), privacy_filtering_enabled_);
});
#else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
perfetto::TraceWriter::TracePacketHandle trace_packet;
{
AutoLockWithDeferredTaskPosting lock(lock_);
if (!emit_metadata_at_start_ || !trace_writer_)
return;
trace_packet = trace_writer_->NewTracePacket();
}
trace_packet->set_timestamp(
TRACE_TIME_TICKS_NOW().since_origin().InNanoseconds());
trace_packet->set_timestamp_clock_id(base::tracing::kTraceClockId);
generator.Run(trace_packet.get(), privacy_filtering_enabled_);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
void TraceEventMetadataSource::GenerateJsonMetadataFromGenerator(
const TraceEventMetadataSource::JsonMetadataGeneratorFunction& generator,
ChromeEventBundle* event_bundle) {
DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
auto write_to_bundle = [&generator](ChromeEventBundle* bundle) {
absl::optional<base::Value> metadata_dict = generator.Run();
if (!metadata_dict)
return;
for (auto it : metadata_dict->DictItems()) {
auto* new_metadata = bundle->add_metadata();
new_metadata->set_name(it.first.c_str());
if (it.second.is_int()) {
new_metadata->set_int_value(it.second.GetInt());
} else if (it.second.is_bool()) {
new_metadata->set_bool_value(it.second.GetBool());
} else if (it.second.is_string()) {
new_metadata->set_string_value(it.second.GetString().c_str());
} else {
std::string json_value;
base::JSONWriter::Write(it.second, &json_value);
new_metadata->set_json_value(json_value.c_str());
}
}
};
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
{
AutoLockWithDeferredTaskPosting lock(lock_);
if (!emit_metadata_at_start_)
return;
}
if (event_bundle) {
write_to_bundle(event_bundle);
return;
}
DataSourceProxy::Trace([&](DataSourceProxy::TraceContext ctx) {
auto packet = ctx.NewTracePacket();
packet->set_timestamp(perfetto::TrackEvent::GetTraceTimeNs());
packet->set_timestamp_clock_id(perfetto::TrackEvent::GetTraceClockId());
write_to_bundle(packet->set_chrome_events());
});
#else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
perfetto::TraceWriter::TracePacketHandle trace_packet;
if (!event_bundle) {
{
AutoLockWithDeferredTaskPosting lock(lock_);
if (!emit_metadata_at_start_ || !trace_writer_) {
return;
}
trace_packet = trace_writer_->NewTracePacket();
}
trace_packet->set_timestamp(
TRACE_TIME_TICKS_NOW().since_origin().InNanoseconds());
trace_packet->set_timestamp_clock_id(base::tracing::kTraceClockId);
event_bundle = trace_packet->set_chrome_events();
}
write_to_bundle(event_bundle);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
base::Value TraceEventMetadataSource::GenerateLegacyMetadataDict() {
DCHECK(!privacy_filtering_enabled_);
base::Value merged_metadata(base::Value::Type::DICTIONARY);
std::vector<JsonMetadataGeneratorFunction> json_generators;
{
base::AutoLock lock(lock_);
json_generators = json_generator_functions_;
}
for (auto& generator : json_generators) {
absl::optional<base::Value> metadata_dict = generator.Run();
if (!metadata_dict) {
continue;
}
merged_metadata.MergeDictionary(&(*metadata_dict));
}
base::trace_event::MetadataFilterPredicate metadata_filter =
base::trace_event::TraceLog::GetInstance()->GetMetadataFilterPredicate();
// This out-of-band generation of the global metadata is only used by the
// crash service uploader path, which always requires privacy filtering.
CHECK(metadata_filter);
for (auto it : merged_metadata.DictItems()) {
if (!metadata_filter.Run(it.first)) {
it.second = base::Value("__stripped__");
}
}
return merged_metadata;
}
void TraceEventMetadataSource::GenerateMetadata(
std::unique_ptr<
std::vector<TraceEventMetadataSource::JsonMetadataGeneratorFunction>>
json_generators,
std::unique_ptr<
std::vector<TraceEventMetadataSource::MetadataGeneratorFunction>>
proto_generators,
std::unique_ptr<
std::vector<TraceEventMetadataSource::PacketGeneratorFunction>>
packet_generators) {
DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
perfetto::TraceWriter* trace_writer;
#endif
bool privacy_filtering_enabled;
{
AutoLockWithDeferredTaskPosting lock(lock_);
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
trace_writer = trace_writer_.get();
#endif
privacy_filtering_enabled = privacy_filtering_enabled_;
}
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
DataSourceProxy::Trace([&](DataSourceProxy::TraceContext ctx) {
for (auto& generator : *packet_generators) {
auto packet = ctx.NewTracePacket();
packet->set_timestamp(perfetto::TrackEvent::GetTraceTimeNs());
packet->set_timestamp_clock_id(perfetto::TrackEvent::GetTraceClockId());
generator.Run(packet.get(), privacy_filtering_enabled);
}
{
auto trace_packet = ctx.NewTracePacket();
trace_packet->set_timestamp(perfetto::TrackEvent::GetTraceTimeNs());
trace_packet->set_timestamp_clock_id(
perfetto::TrackEvent::GetTraceClockId());
auto* chrome_metadata = trace_packet->set_chrome_metadata();
for (auto& generator : *proto_generators) {
generator.Run(chrome_metadata, privacy_filtering_enabled);
}
}
if (!privacy_filtering_enabled) {
auto trace_packet = ctx.NewTracePacket();
trace_packet->set_timestamp(perfetto::TrackEvent::GetTraceTimeNs());
trace_packet->set_timestamp_clock_id(
perfetto::TrackEvent::GetTraceClockId());
ChromeEventBundle* event_bundle = trace_packet->set_chrome_events();
for (auto& generator : *json_generators) {
GenerateJsonMetadataFromGenerator(generator, event_bundle);
}
}
// Force flush the packets since the default flush happens at end of trace,
// and the trace writer's chunk could then be discarded (in kDiscard mode).
ctx.Flush();
});
#else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
for (auto& generator : *packet_generators) {
TracePacketHandle generator_trace_packet = trace_writer->NewTracePacket();
generator_trace_packet->set_timestamp(
TRACE_TIME_TICKS_NOW().since_origin().InNanoseconds());
generator_trace_packet->set_timestamp_clock_id(
base::tracing::kTraceClockId);
generator.Run(generator_trace_packet.get(), privacy_filtering_enabled);
}
TracePacketHandle trace_packet = trace_writer_->NewTracePacket();
trace_packet->set_timestamp(
TRACE_TIME_TICKS_NOW().since_origin().InNanoseconds());
trace_packet->set_timestamp_clock_id(base::tracing::kTraceClockId);
auto* chrome_metadata = trace_packet->set_chrome_metadata();
for (auto& generator : *proto_generators) {
generator.Run(chrome_metadata, privacy_filtering_enabled);
}
trace_packet->Finalize();
if (!privacy_filtering_enabled) {
trace_packet = trace_writer_->NewTracePacket();
trace_packet->set_timestamp(
TRACE_TIME_TICKS_NOW().since_origin().InNanoseconds());
trace_packet->set_timestamp_clock_id(base::tracing::kTraceClockId);
ChromeEventBundle* event_bundle = trace_packet->set_chrome_events();
for (auto& generator : *json_generators) {
GenerateJsonMetadataFromGenerator(generator, event_bundle);
}
trace_packet->Finalize();
}
// Force flush the packets since the default flush happens at end of trace,
// and the trace writer's chunk could then be discarded (in kDiscard mode).
trace_writer->Flush();
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
void TraceEventMetadataSource::StartTracingImpl(
PerfettoProducer* producer,
const perfetto::DataSourceConfig& data_source_config) {
auto json_generators =
std::make_unique<std::vector<JsonMetadataGeneratorFunction>>();
auto proto_generators =
std::make_unique<std::vector<MetadataGeneratorFunction>>();
auto packet_generators =
std::make_unique<std::vector<PacketGeneratorFunction>>();
{
AutoLockWithDeferredTaskPosting lock(lock_);
privacy_filtering_enabled_ =
data_source_config.chrome_config().privacy_filtering_enabled();
chrome_config_ = data_source_config.chrome_config().trace_config();
parsed_chrome_config_ = std::make_unique<TraceConfig>(chrome_config_);
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
trace_writer_ =
producer->CreateTraceWriter(data_source_config.target_buffer());
#endif
switch (parsed_chrome_config_->GetTraceRecordMode()) {
case TraceRecordMode::RECORD_UNTIL_FULL:
case TraceRecordMode::RECORD_AS_MUCH_AS_POSSIBLE: {
emit_metadata_at_start_ = true;
*json_generators = json_generator_functions_;
*proto_generators = generator_functions_;
*packet_generators = packet_generator_functions_;
break;
}
case TraceRecordMode::RECORD_CONTINUOUSLY:
case TraceRecordMode::ECHO_TO_CONSOLE:
emit_metadata_at_start_ = false;
return;
}
}
// |emit_metadata_at_start_| is true if we are in discard packets mode, write
// metadata at the beginning of the trace to make it less likely to be
// dropped.
origin_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&TraceEventMetadataSource::GenerateMetadata,
base::Unretained(this), std::move(json_generators),
std::move(proto_generators),
std::move(packet_generators)));
}
void TraceEventMetadataSource::StopTracingImpl(
base::OnceClosure stop_complete_callback) {
base::OnceClosure maybe_generate_task = base::DoNothing();
{
AutoLockWithDeferredTaskPosting lock(lock_);
if (!emit_metadata_at_start_) {
// Write metadata at the end of tracing if not emitted at start (in ring
// buffer mode), to make it less likely that it is overwritten by other
// trace data in perfetto's ring buffer.
auto json_generators =
std::make_unique<std::vector<JsonMetadataGeneratorFunction>>();
*json_generators = json_generator_functions_;
auto proto_generators =
std::make_unique<std::vector<MetadataGeneratorFunction>>();
*proto_generators = generator_functions_;
auto packet_generators =
std::make_unique<std::vector<PacketGeneratorFunction>>();
*packet_generators = packet_generator_functions_;
maybe_generate_task = base::BindOnce(
&TraceEventMetadataSource::GenerateMetadata, base::Unretained(this),
std::move(json_generators), std::move(proto_generators),
std::move(packet_generators));
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
if (!trace_writer_)
maybe_generate_task = base::OnceCallback<void()>();
#endif
}
}
// Even when not generating metadata, make sure the metadata generate task
// posted at the start is finished, by posting task on origin task runner.
origin_task_runner_->PostTaskAndReply(
FROM_HERE, std::move(maybe_generate_task),
base::BindOnce(
[](TraceEventMetadataSource* ds,
base::OnceClosure stop_complete_callback) {
{
AutoLockWithDeferredTaskPosting lock(ds->lock_);
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
DataSourceProxy::Trace(
[&](DataSourceProxy::TraceContext ctx) { ctx.Flush(); });
#else
ds->trace_writer_.reset();
#endif
ds->chrome_config_ = std::string();
ds->parsed_chrome_config_.reset();
ds->emit_metadata_at_start_ = false;
}
std::move(stop_complete_callback).Run();
},
base::Unretained(this), std::move(stop_complete_callback)));
}
void TraceEventMetadataSource::Flush(
base::RepeatingClosure flush_complete_callback) {
origin_task_runner_->PostTaskAndReply(FROM_HERE, base::DoNothing(),
std::move(flush_complete_callback));
}
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
base::SequencedTaskRunner* TraceEventMetadataSource::GetTaskRunner() {
// Much like the data source, the task runner is long-lived, so returning the
// raw pointer here is safe.
base::AutoLock lock(lock_);
return origin_task_runner_.get();
}
#endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
void TraceEventMetadataSource::ResetForTesting() {
if (!g_trace_event_metadata_source_for_testing)
return;
g_trace_event_metadata_source_for_testing->~TraceEventMetadataSource();
new (g_trace_event_metadata_source_for_testing) TraceEventMetadataSource;
}
namespace {
base::ThreadLocalStorage::Slot* ThreadLocalEventSinkSlot() {
static base::NoDestructor<base::ThreadLocalStorage::Slot>
thread_local_event_sink_tls([](void* event_sink) {
base::tracing::AutoThreadLocalBoolean thread_is_in_trace_event(
base::tracing::GetThreadIsInTraceEventTLS());
delete static_cast<TrackEventThreadLocalEventSink*>(event_sink);
});
return thread_local_event_sink_tls.get();
}
TraceEventDataSource* g_trace_event_data_source_for_testing = nullptr;
} // namespace
// static
TraceEventDataSource* TraceEventDataSource::GetInstance() {
static base::NoDestructor<TraceEventDataSource> instance;
return instance.get();
}
// static
void TraceEventDataSource::ResetForTesting() {
if (!g_trace_event_data_source_for_testing)
return;
g_trace_event_data_source_for_testing->~TraceEventDataSource();
new (g_trace_event_data_source_for_testing) TraceEventDataSource;
}
TraceEventDataSource::TraceEventDataSource()
: DataSourceBase(mojom::kTraceEventDataSourceName),
disable_interning_(base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kPerfettoDisableInterning)) {
// Use an approximate creation time as this is not available as TimeTicks in
// all platforms.
process_creation_time_ticks_ = TRACE_TIME_TICKS_NOW();
DCHECK(session_flags_.is_lock_free())
<< "SessionFlags are not atomic! We rely on efficient lock-free look-up "
"of the session flags when emitting a trace event.";
g_trace_event_data_source_for_testing = this;
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
perfetto::TrackEvent::AddSessionObserver(this);
#endif
}
TraceEventDataSource::~TraceEventDataSource()
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
{
perfetto::TrackEvent::RemoveSessionObserver(this);
}
#else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
= default;
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
void TraceEventDataSource::RegisterStartupHooks() {
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
RegisterTracedValueProtoWriter();
base::trace_event::EnableTypedTraceEvents(
&TraceEventDataSource::OnAddTypedTraceEvent,
&TraceEventDataSource::OnAddTracePacket,
&TraceEventDataSource::OnAddEmptyPacket);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
}
void TraceEventDataSource::RegisterWithTraceLog(
const base::trace_event::TraceConfig& trace_config) {
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
TraceLog::GetInstance()->SetAddTraceEventOverrides(
&TraceEventDataSource::OnAddLegacyTraceEvent,
&TraceEventDataSource::FlushCurrentThread,
&TraceEventDataSource::OnUpdateDuration);
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
DCHECK(monitored_histograms_.empty());
if (trace_config.IsCategoryGroupEnabled(
TRACE_DISABLED_BY_DEFAULT("histogram_samples")) &&
trace_config.histogram_names().empty()) {
// The global callback can be added early at startup before main message
// loop is created. But histogram specific observers need task runner and
// are added when tracing service is setup in StartTracingInternal()
// instead.
base::StatisticsRecorder::SetGlobalSampleCallback(
&TraceEventDataSource::OnMetricsSampleCallback);
}
base::AutoLock l(lock_);
is_enabled_ = true;
}
void TraceEventDataSource::OnStopTracingDone() {
DCHECK_CALLED_ON_VALID_SEQUENCE(perfetto_sequence_checker_);
// WARNING: This function might never be called at the end of a tracing
// session. See comment in StartTracing() for more information.
// Unregister overrides.
TraceLog::GetInstance()->SetAddTraceEventOverrides(nullptr, nullptr, nullptr);
base::OnceClosure task;
{
base::AutoLock l(lock_);
is_enabled_ = false;
// Check for any start or stop tracing pending task.
task = std::move(flush_complete_task_);
flushing_trace_log_ = false;
IncrementSessionIdOrClearStartupFlagWhileLocked();
}
if (stop_complete_callback_) {
std::move(stop_complete_callback_).Run();
}
if (task) {
std::move(task).Run();
}
}
// static
TrackEventThreadLocalEventSink* TraceEventDataSource::GetOrPrepareEventSink(bool create_if_needed) {
// Avoid re-entrancy, which can happen during PostTasks (the taskqueue can
// emit trace events). We discard the events in this case, as any PostTasking
// to deal with these events later would break the event ordering that the
// JSON traces rely on to merge 'A'/'B' events, as well as having to deal with
// updating duration of 'X' events which haven't been added yet.
if (base::tracing::GetThreadIsInTraceEventTLS()->Get()) {
return nullptr;
}
base::tracing::AutoThreadLocalBoolean thread_is_in_trace_event(
base::tracing::GetThreadIsInTraceEventTLS());
auto* thread_local_event_sink = static_cast<TrackEventThreadLocalEventSink*>(
ThreadLocalEventSinkSlot()->Get());
// Make sure the sink was reset since the last tracing session. Normally, it
// is reset on Flush after the session is disabled. However, it may not have
// been reset if the current thread doesn't support flushing. In that case, we
// need to check here that it writes to the right buffer.
//
// Because we want to avoid locking for each event, we access |session_flags_|
// racily. It's OK if we don't see it change to the session immediately. In
// that case, the first few trace events may get lost, but we will eventually
// notice that we are writing to the wrong buffer once the change to
// |session_flags_| has propagated, and reset the sink. Note we will still
// acquire the |lock_| to safely recreate the sink in
// CreateThreadLocalEventSink().
if (thread_local_event_sink) {
SessionFlags new_session_flags =
GetInstance()->session_flags_.load(std::memory_order_relaxed);
if (new_session_flags.session_id != thread_local_event_sink->session_id()) {
delete thread_local_event_sink;
thread_local_event_sink = nullptr;
ThreadLocalEventSinkSlot()->Set(nullptr);
}
}
if (!thread_local_event_sink && create_if_needed) {
thread_local_event_sink = GetInstance()->CreateThreadLocalEventSink();
ThreadLocalEventSinkSlot()->Set(thread_local_event_sink);
}
return thread_local_event_sink;
}
bool TraceEventDataSource::IsEnabled() {
base::AutoLock l(lock_);
return is_enabled_;
}
void TraceEventDataSource::SetupStartupTracing(
PerfettoProducer* producer,
const base::trace_event::TraceConfig& trace_config,
bool privacy_filtering_enabled) {
{
AutoLockWithDeferredTaskPosting lock(lock_);
// Do not enable startup tracing if trace log is being flushed. The
// previous tracing session has not ended yet.
if (flushing_trace_log_) {
return;
}
// No need to do anything if startup tracing has already been set,
// or we know Perfetto has already been setup.
if (IsStartupTracingActive() || producer_) {
DCHECK(!privacy_filtering_enabled || privacy_filtering_enabled_);
return;
}
producer_ = producer;
privacy_filtering_enabled_ = privacy_filtering_enabled;
SetStartupTracingFlagsWhileLocked();
DCHECK(!trace_writer_);
trace_writer_ = CreateTraceWriterLocked();
}
EmitTrackDescriptor();
base::trace_event::TraceConfig config_for_trace_log(trace_config);
// Perfetto backend configures buffer sizes when tracing is started in the
// service (see perfetto_config.cc). Zero them out here for TraceLog to
// avoid DCHECKs in TraceConfig::Merge.
config_for_trace_log.SetTraceBufferSizeInKb(0);
config_for_trace_log.SetTraceBufferSizeInEvents(0);
RegisterWithTraceLog(config_for_trace_log);
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
uint8_t modes = base::trace_event::TraceLog::RECORDING_MODE;
if (!trace_config.event_filters().empty()) {
modes |= base::trace_event::TraceLog::FILTERING_MODE;
}
base::trace_event::TraceLog::GetInstance()->SetEnabled(trace_config, modes);
#endif
}
void TraceEventDataSource::AbortStartupTracing() {
DCHECK_CALLED_ON_VALID_SEQUENCE(perfetto_sequence_checker_);
std::unique_ptr<perfetto::TraceWriter> trace_writer;
PerfettoProducer* producer;
uint32_t session_id;
{
AutoLockWithDeferredTaskPosting lock(lock_);
if (!IsStartupTracingActive()) {
return;
}
producer = producer_;
// Prevent recreation of ThreadLocalEventSinks after flush.
producer_ = nullptr;
target_buffer_ = 0;
flushing_trace_log_ = true;
trace_writer = std::move(trace_writer_);
// Increment the session id to make sure that any subsequent tracing session
// uses fresh trace writers.
session_id = IncrementSessionIdOrClearStartupFlagWhileLocked();
}
if (trace_writer) {
ReturnTraceWriter(std::move(trace_writer));
}
producer->AbortStartupTracingForReservation(session_id);
auto* trace_log = base::trace_event::TraceLog::GetInstance();
trace_log->SetDisabled();
trace_log->Flush(base::BindRepeating(&TraceEventDataSource::OnFlushFinished,
base::Unretained(this)),
/*use_worker_thread=*/false);
}
uint32_t
TraceEventDataSource::IncrementSessionIdOrClearStartupFlagWhileLocked() {
// Protected by |lock_| for CreateThreadLocalEventSink() and
// SetStartupTracingFlagsWhileLocked().
lock_.AssertAcquired();
SessionFlags flags = session_flags_.load(std::memory_order_relaxed);
if (flags.is_startup_tracing) {
// Don't increment the session ID if startup tracing was active for this
// session. This way, the sinks that were created while startup tracing for
// the current session won't be cleared away (resetting such sinks could
// otherwise cause data buffered in their potentially still unbound
// StartupTraceWriters to be lost).
flags.is_startup_tracing = false;
} else {
flags.session_id++;
}
session_flags_.store(flags, std::memory_order_relaxed);
return flags.session_id;
}
void TraceEventDataSource::SetStartupTracingFlagsWhileLocked() {
// Protected by |lock_| for CreateThreadLocalEventSink() and
// IncrementSessionIdOrClearStartupFlagWhileLocked().
lock_.AssertAcquired();
SessionFlags flags = session_flags_.load(std::memory_order_relaxed);
flags.is_startup_tracing = true;
flags.session_id++;
session_flags_.store(flags, std::memory_order_relaxed);
}
bool TraceEventDataSource::IsStartupTracingActive() const {
SessionFlags flags = session_flags_.load(std::memory_order_relaxed);
return flags.is_startup_tracing;
}
void TraceEventDataSource::OnFlushFinished(
const scoped_refptr<base::RefCountedString>&,
bool has_more_events) {
if (has_more_events) {
return;
}
// Clear the pending task on the tracing service thread.
DCHECK_CALLED_ON_VALID_SEQUENCE(perfetto_sequence_checker_);
base::OnceClosure task;
{
AutoLockWithDeferredTaskPosting l(lock_);
// Run any pending start or stop tracing
// task.
task = std::move(flush_complete_task_);
flushing_trace_log_ = false;
}
if (task) {
std::move(task).Run();
}
}
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
void TraceEventDataSource::OnSetup(
const perfetto::DataSourceBase::SetupArgs& args) {
GetTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(&TraceEventDataSource::StartTracingImpl,
base::Unretained(this), nullptr, *args.config));
}
void TraceEventDataSource::OnStop(
const perfetto::DataSourceBase::StopArgs& args) {
std::function<void()> finish_async_stop = args.HandleStopAsynchronously();
base::OnceClosure stop_callback = base::BindOnce(
[](std::function<void()> callback) { callback(); }, finish_async_stop);
GetTaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(&TraceEventDataSource::StopTracingImpl,
base::Unretained(this), std::move(stop_callback)));
}
#endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
void TraceEventDataSource::StartTracingImpl(
PerfettoProducer* producer,
const perfetto::DataSourceConfig& data_source_config) {
DCHECK_CALLED_ON_VALID_SEQUENCE(perfetto_sequence_checker_);
{
AutoLockWithDeferredTaskPosting l(lock_);
if (flushing_trace_log_) {
// Delay start tracing until flush is finished. Perfetto can call start
// while flushing if startup tracing (started by ourself) is cancelled, or
// when perfetto force aborts session without waiting for stop acks.
// |flush_complete_task_| will not be null here if perfetto calls start,
// stop and start again all while flushing trace log for a previous
// session, without waiting for stop complete callback for both. In all
// these cases it is safe to just drop the |flush_complete_callback_|,
// which is supposed to run OnStopTracingDone() and send stop ack to
// Perfetto, but Perfetto already ignored the ack and continued.
// Unretained is fine here because the producer will be valid till stop
// tracing is called and at stop this task will be cleared.
flush_complete_task_ = base::BindOnce(
&TraceEventDataSource::StartTracingInternal, base::Unretained(this),
base::Unretained(producer), data_source_config);
return;
}
}
StartTracingInternal(producer, data_source_config);
}
void TraceEventDataSource::StartTracingInternal(
PerfettoProducer* producer,
const perfetto::DataSourceConfig& data_source_config) {
DCHECK_CALLED_ON_VALID_SEQUENCE(perfetto_sequence_checker_);
bool startup_tracing_active;
uint32_t session_id;
{
AutoLockWithDeferredTaskPosting lock(lock_);
bool should_enable_filtering =
data_source_config.chrome_config().privacy_filtering_enabled();
startup_tracing_active = IsStartupTracingActive();
if (startup_tracing_active) {
CHECK(!should_enable_filtering || privacy_filtering_enabled_)
<< "Startup tracing was active without privacy filtering when "
"service started tracing with privacy filtering.";
DCHECK_EQ(producer_, producer)
<< "Startup tracing was taken over by a different PerfettoProducer";
}
privacy_filtering_enabled_ = should_enable_filtering;
producer_ = producer;
target_buffer_ = data_source_config.target_buffer();
session_id = IncrementSessionIdOrClearStartupFlagWhileLocked();
if (!trace_writer_) {
trace_writer_ = CreateTraceWriterLocked();
}
}
auto trace_config =
TraceConfig(data_source_config.chrome_config().trace_config());
// SetupStartupTracing() will not setup a new startup session after we set
// |producer_| above, so accessing |startup_tracing_active| outside the lock
// is safe.
if (startup_tracing_active) {
// Binding startup buffers may cause tasks to be posted. Disable trace
// events to avoid deadlocks due to reentrancy into tracing code.
base::tracing::AutoThreadLocalBoolean thread_is_in_trace_event(
base::tracing::GetThreadIsInTraceEventTLS());
producer->BindStartupTargetBuffer(session_id,
data_source_config.target_buffer());
} else {
RegisterWithTraceLog(trace_config);
}
// We emit the track/process descriptor another time even if we were
// previously startup tracing, because the process name may have changed.
EmitTrackDescriptor();
#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
TraceLog::GetInstance()->SetEnabled(trace_config, TraceLog::RECORDING_MODE);
#endif
EmitRecurringUpdates();
ResetHistograms(trace_config);
DCHECK(monitored_histograms_.empty());
if (trace_config.IsCategoryGroupEnabled(
TRACE_DISABLED_BY_DEFAULT("histogram_samples"))) {
// Note that global callback is setup in RegisterWithTraceLog() since it can
// be done in early startup and this observer needs message loop.
for (const std::string& histogram_name : trace_config.histogram_names()) {
monitored_histograms_.emplace_back(
std::make_unique<
base::StatisticsRecorder::ScopedHistogramSampleObserver>(
histogram_name,
base::BindRepeating(
&TraceEventDataSource::OnMetricsSampleCallback)));
}
}
if (trace_config.IsCategoryGroupEnabled(
TRACE_DISABLED_BY_DEFAULT("user_action_samples"))) {
auto task_runner = base::GetRecordActionTaskRunner();
if (task_runner) {
task_runner->PostTask(
FROM_HERE, base::BindOnce([]() {
base::AddActionCallback(
TraceEventDataSource::GetInstance()->user_action_callback_);
}));
}
}
}
void TraceEventDataSource::StopTracingImpl(
base::OnceClosure stop_complete_callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(perfetto_sequence_checker_);
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
// Write metadata events etc.
LogHistograms();
std::move(stop_complete_callback).Run();
#else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
stop_complete_callback_ = std::move(stop_complete_callback);
auto on_tracing_stopped_callback =
[](TraceEventDataSource* data_source,
const scoped_refptr<base::RefCountedString>&, bool has_more_events) {
if (has_more_events) {
return;
}
data_source->OnStopTracingDone();
};
bool was_enabled = TraceLog::GetInstance()->IsEnabled();
if (was_enabled) {
// Write metadata events etc.
LogHistograms();
TraceLog::GetInstance()->SetDisabled();
}
std::unique_ptr<perfetto::TraceWriter> trace_writer;
{
AutoLockWithDeferredTaskPosting lock(lock_);
if (flush_complete_task_) {
DCHECK(!producer_);
// Skip start tracing task at this point if we still have not flushed
// trace log. We would only replace a start tracing call here since the
// current StopTracing call should have a matching start call. The service
// never calls consecutive start or stop. It is ok to ignore the start
// here since the session has already ended, before we finished flushing.
flush_complete_task_ =
base::BindOnce(std::move(on_tracing_stopped_callback), this,
scoped_refptr<base::RefCountedString>(), false);
return;
}
// Prevent recreation of ThreadLocalEventSinks after flush.
DCHECK(producer_);
producer_ = nullptr;
target_buffer_ = 0;
flushing_trace_log_ = was_enabled;
trace_writer = std::move(trace_writer_);
}
if (trace_writer) {
ReturnTraceWriter(std::move(trace_writer));
}
if (was_enabled) {
// TraceLog::SetDisabled will cause metadata events to be written; make
// sure we flush the TraceWriter for this thread (TraceLog will only call
// TraceEventDataSource::FlushCurrentThread for threads with a MessageLoop).
// TODO(eseckler): Flush all worker threads.
// TODO(oysteine): The perfetto service itself should be able to recover
// unreturned chunks so technically this can go away at some point, but
// seems needed for now.
FlushCurrentThread();
// Flush the remaining threads via TraceLog. We call CancelTracing because
// we don't want/need TraceLog to do any of its own JSON serialization.
TraceLog::GetInstance()->CancelTracing(base::BindRepeating(
on_tracing_stopped_callback, base::Unretained(this)));
} else {
on_tracing_stopped_callback(this, scoped_refptr<base::RefCountedString>(),
false);
}
#endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
base::StatisticsRecorder::SetGlobalSampleCallback(nullptr);
monitored_histograms_.clear();
auto task_runner = base::GetRecordActionTaskRunner();
if (task_runner) {
task_runner->PostTask(
FROM_HERE, base::BindOnce([]() {
base::RemoveActionCallback(
TraceEventDataSource::GetInstance()->user_action_callback_);
}));
}
}
void TraceEventDataSource::LogHistogram(base::HistogramBase* histogram) {
if (!histogram) {
return;
}
// For the purpose of calculating metrics from histograms we only want the
// delta of the events.
auto samples = histogram->SnapshotSamples();
// If there were HistogramSamples recorded during startup, then those should
// be subtracted from the overall set. This way we only report the samples
// that occured during the run.
auto it = startup_histogram_samples_.find(histogram->histogram_name());
if (it != startup_histogram_samples_.end()) {
samples->Subtract(*it->second.get());
}
base::Pickle pickle;
samples->Serialize(&pickle);
std::string buckets;
base::Base64Encode(
std::string(static_cast<const char*>(pickle.data()), pickle.size()),
&buckets);
TRACE_EVENT_INSTANT2("benchmark,uma", "UMAHistogramSamples",
TRACE_EVENT_SCOPE_PROCESS, "name",
histogram->histogram_name(), "buckets", buckets);
}
void TraceEventDataSource::ResetHistograms(const TraceConfig& trace_config) {
histograms_.clear();
startup_histogram_samples_.clear();
for (const std::string& histogram_name : trace_config.histogram_names()) {
histograms_.push_back(histogram_name);
auto* histogram = base::StatisticsRecorder::FindHistogram(histogram_name);
if (!histogram) {
continue;
}
// For the purpose of calculating metrics from histograms we only want the
// delta of the events. However we do not want to emit the results when
// resetting. This will allow LogHistogram to emit one UMAHistogramSamples
// which encompasses only the histograms recorded during the trace. We
// cache the initial HistogramSamples so that they can be subtracted from
// the full snapshot at the end.
startup_histogram_samples_.emplace(histogram_name,
histogram->SnapshotSamples());
}
}
void TraceEventDataSource::LogHistograms() {
for (const std::string& histogram_name : histograms_) {
LogHistogram(base::StatisticsRecorder::FindHistogram(histogram_name));
}
}
void TraceEventDataSource::Flush(
base::RepeatingClosure flush_complete_callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(perfetto_sequence_checker_);
DCHECK(TraceLog::GetInstance()->IsEnabled());
TraceLog::GetInstance()->Flush(base::BindRepeating(
[](base::RepeatingClosure flush_complete_callback,
const scoped_refptr<base::RefCountedString>&, bool has_more_events) {
if (has_more_events) {
return;
}
flush_complete_callback.Run();
},
std::move(flush_complete_callback)));
}
void TraceEventDataSource::ClearIncrementalState() {
TrackEventThreadLocalEventSink::ClearIncrementalState();
EmitTrackDescriptor();
base::trace_event::TraceLog::GetInstance()->OnIncrementalStateCleared();
}
std::unique_ptr<perfetto::TraceWriter>
TraceEventDataSource::CreateTraceWriterLocked() {
lock_.AssertAcquired();
if (IsStartupTracingActive()) {
DCHECK(producer_);
uint32_t session_id =
session_flags_.load(std::memory_order_relaxed).session_id;
return producer_->CreateStartupTraceWriter(session_id);
}
// |producer_| is reset when tracing is stopped, and trace writer creation can
// happen in parallel on any thread.
if (producer_) {
return producer_->CreateTraceWriter(target_buffer_);
}
return nullptr;
}
TrackEventThreadLocalEventSink*
TraceEventDataSource::CreateThreadLocalEventSink() {
AutoLockWithDeferredTaskPosting lock(lock_);
uint32_t session_id =
session_flags_.load(std::memory_order_relaxed).session_id;
auto trace_writer = CreateTraceWriterLocked();
if (!trace_writer) {
return nullptr;
}
return new TrackEventThreadLocalEventSink(std::move(trace_writer), session_id,
disable_interning_,
privacy_filtering_enabled_);
}
// static
void TraceEventDataSource::OnAddLegacyTraceEvent(
TraceEvent* trace_event,
bool thread_will_flush,
base::trace_event::TraceEventHandle* handle) {
auto* thread_local_event_sink = GetOrPrepareEventSink();
if (thread_local_event_sink) {
base::tracing::AutoThreadLocalBoolean thread_is_in_trace_event(
base::tracing::GetThreadIsInTraceEventTLS());
thread_local_event_sink->AddLegacyTraceEvent(trace_event, handle);
}
}
// static
base::trace_event::TrackEventHandle TraceEventDataSource::OnAddTypedTraceEvent(
base::trace_event::TraceEvent* trace_event) {
auto* thread_local_event_sink = GetOrPrepareEventSink();
if (thread_local_event_sink) {
// GetThreadIsInTraceEventTLS() is handled by the sink for typed events.
return thread_local_event_sink->AddTypedTraceEvent(trace_event);
}
return base::trace_event::TrackEventHandle();
}
// static
void TraceEventDataSource::OnUpdateDuration(
const unsigned char* category_group_enabled,
const char* name,
base::trace_event::TraceEventHandle handle,
int thread_id,
bool explicit_timestamps,
const base::TimeTicks& now,
const base::ThreadTicks& thread_now,
base::trace_event::ThreadInstructionCount thread_instruction_now) {
if (base::tracing::GetThreadIsInTraceEventTLS()->Get()) {
return;
}
base::tracing::AutoThreadLocalBoolean thread_is_in_trace_event(
base::tracing::GetThreadIsInTraceEventTLS());
auto* thread_local_event_sink = static_cast<TrackEventThreadLocalEventSink*>(
ThreadLocalEventSinkSlot()->Get());
if (thread_local_event_sink) {
thread_local_event_sink->UpdateDuration(
category_group_enabled, name, handle, thread_id, explicit_timestamps,
now, thread_now, thread_instruction_now);
}
}
// static
base::trace_event::TracePacketHandle TraceEventDataSource::OnAddTracePacket() {
auto* thread_local_event_sink = GetOrPrepareEventSink();
if (thread_local_event_sink) {
// GetThreadIsInTraceEventTLS() is handled by the sink for trace packets.
return thread_local_event_sink->AddTracePacket();
}
return base::trace_event::TracePacketHandle();
}
// static
void TraceEventDataSource::OnAddEmptyPacket() {
auto* thread_local_event_sink = GetOrPrepareEventSink(false);
if (thread_local_event_sink) {
// GetThreadIsInTraceEventTLS() is handled by the sink for trace packets.
thread_local_event_sink->AddEmptyPacket();
}
}
// static
void TraceEventDataSource::FlushCurrentThread() {
auto* thread_local_event_sink = static_cast<TrackEventThreadLocalEventSink*>(
ThreadLocalEventSinkSlot()->Get());
if (thread_local_event_sink) {
// Prevent any events from being emitted while we're deleting
// the sink (like from the TraceWriter being PostTask'ed for deletion).
base::tracing::AutoThreadLocalBoolean thread_is_in_trace_event(
base::tracing::GetThreadIsInTraceEventTLS());
thread_local_event_sink->Flush();
// TODO(oysteine): To support flushing while still recording, this needs to
// be changed to not destruct the TLS object as that will emit any
// uncompleted _COMPLETE events on the stack.
delete thread_local_event_sink;
ThreadLocalEventSinkSlot()->Set(nullptr);
}
}
namespace {
struct InternedHistogramName
: public perfetto::TrackEventInternedDataIndex<
InternedHistogramName,
perfetto::protos::pbzero::InternedData::kHistogramNamesFieldNumber,
std::string> {
static void Add(perfetto::protos::pbzero::InternedData* interned_data,
size_t iid,
const std::string& histogram_name) {
auto* msg = interned_data->add_histogram_names();
msg->set_iid(iid);
msg->set_name(histogram_name);
}
};
} // namespace
// static
void TraceEventDataSource::OnMetricsSampleCallback(
const char* histogram_name,
uint64_t name_hash,
base::HistogramBase::Sample sample) {
TRACE_EVENT_INSTANT(
TRACE_DISABLED_BY_DEFAULT("histogram_samples"), "HistogramSample",
[&](perfetto::EventContext ctx) {
bool privacy_filtering_enabled =
TraceEventDataSource::GetInstance()->IsPrivacyFilteringEnabled();
perfetto::protos::pbzero::ChromeHistogramSample* new_sample =
ctx.event()->set_chrome_histogram_sample();
new_sample->set_name_hash(name_hash);
new_sample->set_sample(sample);
if (!privacy_filtering_enabled) {
size_t iid = InternedHistogramName::Get(&ctx, histogram_name);
new_sample->set_name_iid(iid);
}
});
}
void TraceEventDataSource::OnUserActionSampleCallback(
const std::string& action,
base::TimeTicks action_time) {
constexpr uint64_t kGlobalInstantTrackId = 0;
TRACE_EVENT_INSTANT(
TRACE_DISABLED_BY_DEFAULT("user_action_samples"), "UserAction",
perfetto::Track::Global(kGlobalInstantTrackId),
[&](perfetto::EventContext ctx) {
bool privacy_filtering_enabled =
TraceEventDataSource::GetInstance()->IsPrivacyFilteringEnabled();
perfetto::protos::pbzero::ChromeUserEvent* new_sample =
ctx.event()->set_chrome_user_event();
if (!privacy_filtering_enabled) {
new_sample->set_action(action);
}
new_sample->set_action_hash(base::HashMetricName(action));
});
}
void TraceEventDataSource::ReturnTraceWriter(
std::unique_ptr<perfetto::TraceWriter> trace_writer) {
{
// Prevent concurrent changes to |session_flags_|.
AutoLockWithDeferredTaskPosting lock(lock_);
// If we don't have a task runner yet, we cannot create the task runner
// safely, because the thread pool may not have been brought up yet. This
// should only happen during startup tracing.
if (!PerfettoTracedProcess::GetTaskRunner()->HasTaskRunner()) {
DCHECK(IsStartupTracingActive());
// It's safe to destroy the TraceWriter on the current sequence, as the
// destruction won't post tasks or make mojo calls, because the arbiter
// wasn't bound yet.
trace_writer.reset();
return;
}
}
// Return the TraceWriter on the sequence that the PerfettoProducers run on.
// Needed as the TrackEventThreadLocalEventSink gets deleted on thread
// shutdown and we can't safely call TaskRunnerHandle::Get() at that point
// (which can happen as the TraceWriter destructor might issue a Mojo call
// synchronously, which can trigger a call to TaskRunnerHandle::Get()).
auto* trace_writer_raw = trace_writer.release();
ANNOTATE_LEAKING_OBJECT_PTR(trace_writer_raw);
// Use PostTask() on PerfettoTaskRunner to ensure we comply with
// base::ScopedDeferTaskPosting.
PerfettoTracedProcess::GetTaskRunner()->PostTask(
// Capture writer as raw pointer so that we leak it if task posting
// fails (during shutdown).
[trace_writer_raw]() { delete trace_writer_raw; });
}
void TraceEventDataSource::EmitTrackDescriptor() {
// Prevent reentrancy into tracing code (flushing the trace writer sends a
// mojo message which can result in additional trace events).
base::tracing::AutoThreadLocalBoolean thread_is_in_trace_event(
base::tracing::GetThreadIsInTraceEventTLS());
// It's safe to use this writer outside the lock because EmitTrackDescriptor()
// is either called (a) when startup tracing is set up (from the main thread)
// or (b) on the perfetto sequence. (a) is safe because the writer will not be
// destroyed before startup tracing set up is complete. (b) is safe because
// the writer is only destroyed on the perfetto sequence in this case.
perfetto::TraceWriter* writer;
bool privacy_filtering_enabled;
#if defined(OS_ANDROID)
bool is_system_producer;
#endif // defined(OS_ANDROID)
{
AutoLockWithDeferredTaskPosting lock(lock_);
writer = trace_writer_.get();
privacy_filtering_enabled = privacy_filtering_enabled_;
#if defined(OS_ANDROID)
is_system_producer =
producer_ == PerfettoTracedProcess::Get()->system_producer();
#endif // defined(OS_ANDROID)
}
if (!writer) {
return;
}
int process_id = TraceLog::GetInstance()->process_id();
if (process_id == base::kNullProcessId) {
// Do not emit descriptor without process id.
return;
}
std::string process_name = TraceLog::GetInstance()->process_name();
TracePacketHandle trace_packet = writer->NewTracePacket();
trace_packet->set_sequence_flags(
perfetto::protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);
trace_packet->set_timestamp(
TRACE_TIME_TICKS_NOW().since_origin().InNanoseconds());
trace_packet->set_timestamp_clock_id(base::tracing::kTraceClockId);
TrackDescriptor* track_descriptor = trace_packet->set_track_descriptor();
auto process_track = perfetto::ProcessTrack::Current();
// TODO(eseckler): Call process_track.Serialize() here instead once the
// client lib also fills in the ProcessDescriptor's process_name, gets the
// correct pid from Chrome, and supports privacy filtering.
track_descriptor->set_uuid(process_track.uuid);
PERFETTO_DCHECK(!process_track.parent_uuid);
ProcessDescriptor* process = track_descriptor->set_process();
process->set_pid(process_id);
process->set_start_timestamp_ns(
process_creation_time_ticks_.since_origin().InNanoseconds());
if (!privacy_filtering_enabled) {
process->set_process_name(process_name);
for (const auto& label : TraceLog::GetInstance()->process_labels()) {
process->add_process_labels(label.second);
}
}
ChromeProcessDescriptor* chrome_process =
track_descriptor->set_chrome_process();
auto process_type = GetProcessType(process_name);
if (process_type != ChromeProcessDescriptor::PROCESS_UNSPECIFIED) {
chrome_process->set_process_type(process_type);
}
// Add the crash trace ID to all the traces uploaded. If there are crashes
// during this tracing session, then the crash will contain the process's
// trace ID as "chrome-trace-id" crash key. This should be emitted
// periodically to ensure it is present in the traces when the process
// crashes. Metadata can go missing if process crashes. So, record this in
// process descriptor.
static const absl::optional<uint64_t> crash_trace_id = GetTraceCrashId();
if (crash_trace_id) {
chrome_process->set_crash_trace_id(*crash_trace_id);
}
#if defined(OS_ANDROID)
// Host app package name is only recorded if the corresponding TraceLog
// setting is set to true and privacy filtering is disabled or this is a
// system trace.
if (TraceLog::GetInstance()->ShouldRecordHostAppPackageName() &&
(!privacy_filtering_enabled || is_system_producer)) {
// Host app package name is used to group information from different
// processes that "belong" to the same WebView app.
if (process_type == ChromeProcessDescriptor::PROCESS_RENDERER ||
process_type == ChromeProcessDescriptor::PROCESS_BROWSER) {
chrome_process->set_host_app_package_name(
base::android::BuildInfo::GetInstance()->host_package_name());
}
}
#endif // defined(OS_ANDROID)
// TODO(eseckler): Set other fields on |chrome_process|.
trace_packet = TracePacketHandle();
EmitRecurringUpdates();
writer->Flush();
}
bool TraceEventDataSource::IsPrivacyFilteringEnabled() {
AutoLockWithDeferredTaskPosting lock(lock_);
return privacy_filtering_enabled_;
}
} // namespace tracing
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(
tracing::TraceEventMetadataSource::DataSourceProxy);
#endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)