0

[tracing] Modernize TracingSamplerProfiler

This CL gets rid of DataSourceProxy for TracingSamplerProfiler.
It also removes dead code around legacy StartupTracing.

Change-Id: I9d687d681d26cd092e3afed263edf2c7543a9a51
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6203461
Reviewed-by: Mikhail Khokhlov <khokhlov@google.com>
Commit-Queue: Etienne Pierre-Doray <etiennep@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1416309}
This commit is contained in:
Etienne Pierre-doray
2025-02-05 10:47:26 -08:00
committed by Chromium LUCI CQ
parent 31a4514e78
commit 7638e654a0
4 changed files with 209 additions and 383 deletions

@@ -18,6 +18,7 @@
#include "content/public/test/content_browser_test_utils.h" #include "content/public/test/content_browser_test_utils.h"
#include "services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer_proto.h" #include "services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer_proto.h"
#include "services/tracing/public/cpp/perfetto/metadata_data_source.h" #include "services/tracing/public/cpp/perfetto/metadata_data_source.h"
#include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "third_party/perfetto/protos/perfetto/config/chrome/chrome_config.gen.h" #include "third_party/perfetto/protos/perfetto/config/chrome/chrome_config.gen.h"
@@ -92,6 +93,17 @@ perfetto::protos::gen::TraceConfig TraceConfigWithMetadata(
return perfetto_config; return perfetto_config;
} }
perfetto::protos::gen::TraceConfig TraceConfigWithSamplerProfiler() {
auto perfetto_config = base::test::DefaultTraceConfig(
"-*,disabled-by-default-cpu_profiler", false);
auto* data_source = perfetto_config.add_data_sources();
auto* source_config = data_source->mutable_config();
source_config->set_name("org.chromium.sampler_profiler");
return perfetto_config;
}
perfetto::protos::gen::TraceConfig TraceConfigWithMetadataMultisession( perfetto::protos::gen::TraceConfig TraceConfigWithMetadataMultisession(
const std::string& category_filter_string) { const std::string& category_filter_string) {
auto perfetto_config = auto perfetto_config =
@@ -336,6 +348,69 @@ IN_PROC_BROWSER_TEST_F(TracingEndToEndBrowserTest,
std::vector<std::string>{"1"})); std::vector<std::string>{"1"}));
} }
namespace {
class FakeUnwinder : public base::Unwinder {
public:
bool CanUnwindFrom(const base::Frame& current_frame) const override {
return true;
}
base::UnwindResult TryUnwind(base::UnwinderStateCapture* capture_state,
base::RegisterContext* thread_context,
uintptr_t stack_top,
std::vector<base::Frame>* stack) override {
return base::UnwindResult::kCompleted;
}
};
// Note that this is relevant only for Android, since TracingSamplingProfiler
// ignores any provided unwinder factory for non-Android platforms:
// https://source.chromium.org/chromium/chromium/src/+/main:services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.cc;l=905-908;drc=70d839a3b8bcf1ef43c42a54a4b27f14ee149750
base::StackSamplingProfiler::UnwindersFactory MakeFakeUnwinder() {
return base::BindOnce([] {
auto fake_unwinder = std::make_unique<FakeUnwinder>();
std::vector<std::unique_ptr<base::Unwinder>> unwinders;
unwinders.push_back(std::move(fake_unwinder));
return unwinders;
});
}
} // namespace
IN_PROC_BROWSER_TEST_F(TracingEndToEndBrowserTest, CpuProfiler) {
// In the browser process, the tracing sampler profiler gets constructed by
// the chrome/ layer, so we need to do the same manually for testing purposes.
std::unique_ptr<tracing::TracingSamplerProfiler> tracing_sampler_profiler;
{
base::ScopedAllowBlockingForTesting allow_blocking;
tracing_sampler_profiler =
tracing::TracingSamplerProfiler::CreateOnMainThread(
base::BindRepeating(&MakeFakeUnwinder));
}
// There won't be any samples if stack unwinding isn't supported.
if (!tracing::TracingSamplerProfiler::IsStackUnwindingSupportedForTesting()) {
GTEST_SKIP() << "Stack unwinding not supported on this platform";
}
base::RunLoop wait_for_sample;
tracing_sampler_profiler->SetSampleCallbackForTesting(
wait_for_sample.QuitClosure());
base::test::TestTraceProcessor ttp;
ttp.StartTrace(TraceConfigWithSamplerProfiler());
wait_for_sample.Run();
absl::Status status = ttp.StopAndParseTrace();
ASSERT_TRUE(status.ok()) << status.message();
auto result = ttp.RunQuery("SELECT * FROM cpu_profile_stack_sample");
ASSERT_TRUE(result.has_value()) << result.error();
EXPECT_GT(result.value().size(), 1U);
}
IN_PROC_BROWSER_TEST_F(TracingEndToEndBrowserTest, IN_PROC_BROWSER_TEST_F(TracingEndToEndBrowserTest,
MemoryInstrumentationDetailed) { MemoryInstrumentationDetailed) {
base::WaitableEvent dump_completed; base::WaitableEvent dump_completed;

@@ -60,7 +60,6 @@
using StreamingProfilePacketHandle = using StreamingProfilePacketHandle =
protozero::MessageHandle<perfetto::protos::pbzero::StreamingProfilePacket>; protozero::MessageHandle<perfetto::protos::pbzero::StreamingProfilePacket>;
using TracePacketHandle = perfetto::TraceWriter::TracePacketHandle;
namespace tracing { namespace tracing {
@@ -88,172 +87,75 @@ uintptr_t executable_start_addr() {
// Pointer to the main thread instance, if any. // Pointer to the main thread instance, if any.
TracingSamplerProfiler* g_main_thread_instance = nullptr; TracingSamplerProfiler* g_main_thread_instance = nullptr;
class TracingSamplerProfilerDataSource; class TracingSamplerProfilerManager {
TracingSamplerProfilerDataSource* g_sampler_profiler_ds_for_test = nullptr;
class TracingSamplerProfilerDataSource
: public PerfettoTracedProcess::DataSourceBase {
public: public:
static TracingSamplerProfilerDataSource* Get() { static TracingSamplerProfilerManager* Get() {
static base::NoDestructor<TracingSamplerProfilerDataSource> instance; static base::NoDestructor<TracingSamplerProfilerManager> instance;
return instance.get(); return instance.get();
} }
void OnDataSourceStart(TracingSamplerProfiler::DataSource* data_source) {
base::AutoLock lock(lock_);
DCHECK_EQ(data_source_, nullptr);
data_source_ = data_source;
for (TracingSamplerProfiler* profiler : profilers_) {
profiler->StartTracing(data_source_->CreateTraceWriter(),
data_source_->privacy_filtering_enabled());
}
}
void OnDataSourceStop(TracingSamplerProfiler::DataSource* data_source) {
base::AutoLock lock(lock_);
DCHECK_EQ(data_source_, data_source);
data_source_ = nullptr;
for (TracingSamplerProfiler* profiler : profilers_) {
profiler->StopTracing();
}
}
void WillClearIncrementalState() {
incremental_state_reset_id_.fetch_add(1u, std::memory_order_relaxed);
}
uint32_t GetIncrementalStateResetID() {
return incremental_state_reset_id_.load(std::memory_order_relaxed);
}
void RegisterProfiler(TracingSamplerProfiler* profiler) { void RegisterProfiler(TracingSamplerProfiler* profiler) {
base::AutoLock lock(lock_); base::AutoLock lock(lock_);
if (!profilers_.insert(profiler).second) { if (!profilers_.insert(profiler).second) {
return; return;
} }
if (is_started_) { if (data_source_) {
profiler->StartTracing( profiler->StartTracing(data_source_->CreateTraceWriter(),
CreateTraceWriter(), data_source_->privacy_filtering_enabled());
data_source_config_.chrome_config().privacy_filtering_enabled());
} else if (is_startup_tracing_) {
profiler->StartTracing(
nullptr,
/*should_enable_filtering=*/true);
} }
} }
void UnregisterProfiler(TracingSamplerProfiler* profiler) { void UnregisterProfiler(TracingSamplerProfiler* profiler) {
base::AutoLock lock(lock_); base::AutoLock lock(lock_);
if (!profilers_.erase(profiler) || !(is_started_ || is_startup_tracing_)) { if (!profilers_.erase(profiler)) {
return; return;
} }
profiler->StopTracing(); if (data_source_) {
}
// PerfettoTracedProcess::DataSourceBase implementation, called by
// ProducerClient.
void StartTracingImpl(
const perfetto::DataSourceConfig& data_source_config) override {
base::AutoLock lock(lock_);
DCHECK(!is_started_);
is_started_ = true;
is_startup_tracing_ = false;
data_source_config_ = data_source_config;
bool should_enable_filtering =
data_source_config.chrome_config().privacy_filtering_enabled();
for (TracingSamplerProfiler* profiler : profilers_) {
profiler->StartTracing(CreateTraceWriter(), should_enable_filtering);
}
}
void StopTracingImpl(base::OnceClosure stop_complete_callback) override {
base::AutoLock lock(lock_);
DCHECK(is_started_);
is_started_ = false;
is_startup_tracing_ = false;
for (TracingSamplerProfiler* profiler : profilers_) {
profiler->StopTracing(); profiler->StopTracing();
} }
std::move(stop_complete_callback).Run();
}
void Flush(base::RepeatingClosure flush_complete_callback) override {
flush_complete_callback.Run();
}
void SetupStartupTracing(const base::trace_event::TraceConfig& trace_config,
bool privacy_filtering_enabled) override {
bool enable_sampler_profiler = trace_config.IsCategoryGroupEnabled(
TRACE_DISABLED_BY_DEFAULT("cpu_profiler"));
if (!enable_sampler_profiler)
return;
base::AutoLock lock(lock_);
if (is_started_) {
return;
}
is_startup_tracing_ = true;
for (TracingSamplerProfiler* profiler : profilers_) {
// Enable filtering for startup tracing always to be safe.
profiler->StartTracing(
nullptr,
/*should_enable_filtering=*/true);
}
}
void AbortStartupTracing() override {
base::AutoLock lock(lock_);
if (!is_startup_tracing_) {
return;
}
for (TracingSamplerProfiler* profiler : profilers_) {
// Enable filtering for startup tracing always to be safe.
profiler->StartTracing(
nullptr,
/*should_enable_filtering=*/true);
}
is_startup_tracing_ = false;
}
void ClearIncrementalState() override {
incremental_state_reset_id_.fetch_add(1u, std::memory_order_relaxed);
}
static uint32_t GetIncrementalStateResetID() {
return incremental_state_reset_id_.load(std::memory_order_relaxed);
}
using DataSourceProxy =
PerfettoTracedProcess::DataSourceProxy<TracingSamplerProfilerDataSource>;
static void ResetForTesting() {
if (!g_sampler_profiler_ds_for_test)
return;
g_sampler_profiler_ds_for_test->~TracingSamplerProfilerDataSource();
new (g_sampler_profiler_ds_for_test) TracingSamplerProfilerDataSource;
}
void RegisterDataSource() {
perfetto::DataSourceDescriptor dsd;
dsd.set_name(mojom::kSamplerProfilerSourceName);
DataSourceProxy::Register(dsd, this);
} }
private: private:
friend class base::NoDestructor<TracingSamplerProfilerDataSource>; friend class base::NoDestructor<TracingSamplerProfilerManager>;
TracingSamplerProfilerDataSource() TracingSamplerProfilerManager() = default;
: DataSourceBase(mojom::kSamplerProfilerSourceName) { ~TracingSamplerProfilerManager() = default;
g_sampler_profiler_ds_for_test = this;
}
~TracingSamplerProfilerDataSource() override {
// Unreachable because of static instance of type `base::NoDestructor<>`
// and private ctr.
// Reachable only in case of test mode. See `ResetForTesting()`.
}
// We create one trace writer per profiled thread both in SDK and non-SDK
// build. This is necessary because each profiler keeps its own interned data
// index, so to avoid collisions interned data should go into different
// writer sequences.
std::unique_ptr<perfetto::TraceWriterBase> CreateTraceWriter();
// TODO(eseckler): Use GUARDED_BY annotations for all members below.
base::Lock lock_; // Protects subsequent members. base::Lock lock_; // Protects subsequent members.
std::set<raw_ptr<TracingSamplerProfiler, SetExperimental>> profilers_; std::set<raw_ptr<TracingSamplerProfiler, SetExperimental>> profilers_
bool is_startup_tracing_ = false; GUARDED_BY(lock_);
bool is_started_ = false; raw_ptr<TracingSamplerProfiler::DataSource> data_source_ GUARDED_BY(lock_);
perfetto::DataSourceConfig data_source_config_; std::atomic<uint32_t> incremental_state_reset_id_{1};
static std::atomic<uint32_t> incremental_state_reset_id_;
}; };
using DataSourceProxy = TracingSamplerProfilerDataSource::DataSourceProxy;
// static
std::atomic<uint32_t>
TracingSamplerProfilerDataSource::incremental_state_reset_id_{0};
base::SequenceLocalStorageSlot<TracingSamplerProfiler>& base::SequenceLocalStorageSlot<TracingSamplerProfiler>&
GetSequenceLocalStorageProfilerSlot() { GetSequenceLocalStorageProfilerSlot() {
static base::SequenceLocalStorageSlot<TracingSamplerProfiler> storage; static base::SequenceLocalStorageSlot<TracingSamplerProfiler> storage;
@@ -376,17 +278,25 @@ perfetto::StaticString UnwinderTypeToString(
} // namespace } // namespace
TracingSamplerProfiler::TracingProfileBuilder::BufferedSample::BufferedSample( TracingSamplerProfiler::DataSource::~DataSource() = default;
base::TimeTicks ts,
std::vector<base::Frame>&& s)
: timestamp(ts), sample(std::move(s)) {}
TracingSamplerProfiler::TracingProfileBuilder::BufferedSample:: void TracingSamplerProfiler::DataSource::OnSetup(const SetupArgs& args) {
~BufferedSample() = default; privacy_filtering_enabled_ =
args.config->chrome_config().privacy_filtering_enabled();
}
TracingSamplerProfiler::TracingProfileBuilder::BufferedSample::BufferedSample( void TracingSamplerProfiler::DataSource::OnStart(const StartArgs&) {
TracingSamplerProfiler::TracingProfileBuilder::BufferedSample&& other) TracingSamplerProfilerManager::Get()->OnDataSourceStart(this);
: BufferedSample(other.timestamp, std::move(other.sample)) {} }
void TracingSamplerProfiler::DataSource::OnStop(const StopArgs&) {
TracingSamplerProfilerManager::Get()->OnDataSourceStop(this);
}
void TracingSamplerProfiler::DataSource::WillClearIncrementalState(
const ClearIncrementalStateArgs& args) {
TracingSamplerProfilerManager::Get()->WillClearIncrementalState();
}
TracingSamplerProfiler::TracingProfileBuilder::TracingProfileBuilder( TracingSamplerProfiler::TracingProfileBuilder::TracingProfileBuilder(
base::PlatformThreadId sampled_thread_id, base::PlatformThreadId sampled_thread_id,
@@ -425,41 +335,22 @@ using SampleDebugProto =
void TracingSamplerProfiler::TracingProfileBuilder::OnSampleCompleted( void TracingSamplerProfiler::TracingProfileBuilder::OnSampleCompleted(
std::vector<base::Frame> frames, std::vector<base::Frame> frames,
base::TimeTicks sample_timestamp) { base::TimeTicks sample_timestamp) {
base::AutoLock l(trace_writer_lock_); WriteSampleToTrace(std::move(frames), sample_timestamp);
bool is_startup_tracing = (trace_writer_ == nullptr);
if (is_startup_tracing) {
if (buffered_samples_.size() < kMaxBufferedSamples) {
buffered_samples_.emplace_back(
BufferedSample(sample_timestamp, std::move(frames)));
}
return;
}
if (!buffered_samples_.empty()) {
for (auto& sample : buffered_samples_) {
WriteSampleToTrace(std::move(sample));
}
buffered_samples_.clear();
}
BufferedSample sample(sample_timestamp, std::move(frames));
WriteSampleToTrace(std::move(sample));
if (sample_callback_for_testing_) { if (sample_callback_for_testing_) {
sample_callback_for_testing_.Run(); sample_callback_for_testing_.Run();
} }
} }
void TracingSamplerProfiler::TracingProfileBuilder::WriteSampleToTrace( void TracingSamplerProfiler::TracingProfileBuilder::WriteSampleToTrace(
TracingSamplerProfiler::TracingProfileBuilder::BufferedSample sample) { std::vector<base::Frame> frames,
auto& frames = sample.sample; base::TimeTicks sample_timestamp) {
auto reset_id = uint32_t previous_incremental_state_reset_id = std::exchange(
TracingSamplerProfilerDataSource::GetIncrementalStateResetID(); last_incremental_state_reset_id_,
if (reset_id != last_incremental_state_reset_id_) { TracingSamplerProfilerManager::Get()->GetIncrementalStateResetID());
reset_incremental_state_ = true; bool reset_incremental_state =
last_incremental_state_reset_id_ = reset_id; (previous_incremental_state_reset_id != last_incremental_state_reset_id_);
}
if (reset_incremental_state_) { if (reset_incremental_state) {
stack_profile_writer_.ResetEmittedState(); stack_profile_writer_.ResetEmittedState();
TracePacketHandle trace_packet = trace_writer_->NewTracePacket(); TracePacketHandle trace_packet = trace_writer_->NewTracePacket();
@@ -473,13 +364,12 @@ void TracingSamplerProfiler::TracingProfileBuilder::WriteSampleToTrace(
thread_descriptor->set_pid( thread_descriptor->set_pid(
base::trace_event::TraceLog::GetInstance()->process_id()); base::trace_event::TraceLog::GetInstance()->process_id());
thread_descriptor->set_tid(sampled_thread_id_); thread_descriptor->set_tid(sampled_thread_id_);
last_timestamp_ = sample.timestamp; last_timestamp_ = sample_timestamp;
thread_descriptor->set_reference_timestamp_us( thread_descriptor->set_reference_timestamp_us(
last_timestamp_.since_origin().InMicroseconds()); last_timestamp_.since_origin().InMicroseconds());
TRACE_EVENT_INSTANT(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"), TRACE_EVENT_INSTANT(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
UnwinderTypeToString(unwinder_type_)); UnwinderTypeToString(unwinder_type_));
reset_incremental_state_ = false;
} }
TracePacketHandle trace_packet = trace_writer_->NewTracePacket(); TracePacketHandle trace_packet = trace_writer_->NewTracePacket();
@@ -497,14 +387,8 @@ void TracingSamplerProfiler::TracingProfileBuilder::WriteSampleToTrace(
} }
streaming_profile_packet->add_timestamp_delta_us( streaming_profile_packet->add_timestamp_delta_us(
(sample.timestamp - last_timestamp_).InMicroseconds()); (sample_timestamp - last_timestamp_).InMicroseconds());
last_timestamp_ = sample.timestamp; last_timestamp_ = sample_timestamp;
}
void TracingSamplerProfiler::TracingProfileBuilder::SetTraceWriter(
std::unique_ptr<perfetto::TraceWriterBase> writer) {
base::AutoLock l(trace_writer_lock_);
trace_writer_ = std::move(writer);
} }
void TracingSamplerProfiler::TracingProfileBuilder::SetUnwinderType( void TracingSamplerProfiler::TracingProfileBuilder::SetUnwinderType(
@@ -520,7 +404,7 @@ TracingSamplerProfiler::StackProfileWriter::~StackProfileWriter() = default;
InterningID InterningID
TracingSamplerProfiler::StackProfileWriter::GetCallstackIDAndMaybeEmit( TracingSamplerProfiler::StackProfileWriter::GetCallstackIDAndMaybeEmit(
std::vector<base::Frame>& frames, std::vector<base::Frame>& frames,
perfetto::TraceWriter::TracePacketHandle* trace_packet) { TracePacketHandle* trace_packet) {
size_t ip_hash = 0; size_t ip_hash = 0;
for (const auto& frame : frames) { for (const auto& frame : frames) {
ip_hash = base::HashInts(ip_hash, frame.instruction_pointer); ip_hash = base::HashInts(ip_hash, frame.instruction_pointer);
@@ -733,15 +617,11 @@ void TracingSamplerProfiler::DeleteOnChildThreadForTesting() {
GetSequenceLocalStorageProfilerSlot().reset(); GetSequenceLocalStorageProfilerSlot().reset();
} }
// static
void TracingSamplerProfiler::ResetDataSourceForTesting() {
TracingSamplerProfilerDataSource::Get()->ResetForTesting();
RegisterDataSource();
}
// static // static
void TracingSamplerProfiler::RegisterDataSource() { void TracingSamplerProfiler::RegisterDataSource() {
TracingSamplerProfilerDataSource::Get()->RegisterDataSource(); perfetto::DataSourceDescriptor dsd;
dsd.set_name(mojom::kSamplerProfilerSourceName);
perfetto::DataSource<TracingSamplerProfiler::DataSource>::Register(dsd);
} }
// static // static
@@ -763,26 +643,10 @@ void TracingSamplerProfiler::SetAuxUnwinderFactoryOnMainThread(
g_main_thread_instance->SetAuxUnwinderFactory(factory); g_main_thread_instance->SetAuxUnwinderFactory(factory);
} }
// TODO(b/336718643): Remove unused code after removing use_perfetto_client_library build void TracingSamplerProfiler::SetSampleCallbackForTesting(
// flag. const base::RepeatingClosure& sample_callback_for_testing) {
// static base::AutoLock lock(lock_);
void TracingSamplerProfiler::StartTracingForTesting() { sample_callback_for_testing_ = sample_callback_for_testing;
TracingSamplerProfilerDataSource::Get()->StartTracing(
1, perfetto::DataSourceConfig());
}
// static
void TracingSamplerProfiler::SetupStartupTracingForTesting() {
base::trace_event::TraceConfig config(
TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
base::trace_event::TraceRecordMode::RECORD_UNTIL_FULL);
TracingSamplerProfilerDataSource::Get()->SetupStartupTracing(
config, /*privacy_filtering_enabled=*/false);
}
// static
void TracingSamplerProfiler::StopTracingForTesting() {
TracingSamplerProfilerDataSource::Get()->StopTracing(base::DoNothing());
} }
TracingSamplerProfiler::TracingSamplerProfiler( TracingSamplerProfiler::TracingSamplerProfiler(
@@ -801,11 +665,11 @@ TracingSamplerProfiler::TracingSamplerProfiler(
// created on the profiled thread. See crbug.com/1392158#c26 for details. // created on the profiled thread. See crbug.com/1392158#c26 for details.
base::ThreadDelegatePosix::Create(sampled_thread_token_); base::ThreadDelegatePosix::Create(sampled_thread_token_);
#endif // INITIALIZE_THREAD_DELEGATE_POSIX #endif // INITIALIZE_THREAD_DELEGATE_POSIX
TracingSamplerProfilerDataSource::Get()->RegisterProfiler(this); TracingSamplerProfilerManager::Get()->RegisterProfiler(this);
} }
TracingSamplerProfiler::~TracingSamplerProfiler() { TracingSamplerProfiler::~TracingSamplerProfiler() {
TracingSamplerProfilerDataSource::Get()->UnregisterProfiler(this); TracingSamplerProfilerManager::Get()->UnregisterProfiler(this);
if (g_main_thread_instance == this) if (g_main_thread_instance == this)
g_main_thread_instance = nullptr; g_main_thread_instance = nullptr;
} }
@@ -818,22 +682,11 @@ void TracingSamplerProfiler::SetAuxUnwinderFactory(
profiler_->AddAuxUnwinder(aux_unwinder_factory_.Run()); profiler_->AddAuxUnwinder(aux_unwinder_factory_.Run());
} }
void TracingSamplerProfiler::SetSampleCallbackForTesting(
const base::RepeatingClosure& sample_callback_for_testing) {
base::AutoLock lock(lock_);
sample_callback_for_testing_ = sample_callback_for_testing;
}
void TracingSamplerProfiler::StartTracing( void TracingSamplerProfiler::StartTracing(
std::unique_ptr<perfetto::TraceWriterBase> trace_writer, std::unique_ptr<perfetto::TraceWriterBase> trace_writer,
bool should_enable_filtering) { bool should_enable_filtering) {
base::AutoLock lock(lock_); base::AutoLock lock(lock_);
if (profiler_) { DCHECK_EQ(profiler_, nullptr);
if (trace_writer) {
profile_builder_->SetTraceWriter(std::move(trace_writer));
}
return;
}
if (!base::StackSamplingProfiler::IsSupportedForCurrentPlatform()) { if (!base::StackSamplingProfiler::IsSupportedForCurrentPlatform()) {
return; return;
@@ -844,8 +697,7 @@ void TracingSamplerProfiler::StartTracing(
params.sampling_interval = base::Milliseconds(50); params.sampling_interval = base::Milliseconds(50);
auto profile_builder = std::make_unique<TracingProfileBuilder>( auto profile_builder = std::make_unique<TracingProfileBuilder>(
sampled_thread_token_.id, sampled_thread_token_.id, std::move(trace_writer),
std::move(trace_writer),
should_enable_filtering, sample_callback_for_testing_); should_enable_filtering, sample_callback_for_testing_);
profile_builder_ = profile_builder.get(); profile_builder_ = profile_builder.get();
@@ -910,19 +762,19 @@ void TracingSamplerProfiler::StopTracing() {
PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS( PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS(
COMPONENT_EXPORT(TRACING_CPP), COMPONENT_EXPORT(TRACING_CPP),
tracing::TracingSamplerProfilerDataSource::DataSourceProxy); tracing::TracingSamplerProfiler::DataSource);
// This should go after PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS // This should go after PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS
// to avoid instantiation of type() template method before specialization. // to avoid instantiation of type() template method before specialization.
std::unique_ptr<perfetto::TraceWriterBase> std::unique_ptr<perfetto::TraceWriterBase>
tracing::TracingSamplerProfilerDataSource::CreateTraceWriter() { tracing::TracingSamplerProfiler::DataSource::CreateTraceWriter() {
perfetto::internal::DataSourceStaticState* static_state = perfetto::internal::DataSourceStaticState* static_state =
perfetto::DataSourceHelper<DataSourceProxy>::type().static_state(); perfetto::DataSourceHelper<TracingSamplerProfiler::DataSource>::type()
.static_state();
// DataSourceProxy disallows multiple instances, so our instance will always // DataSourceProxy disallows multiple instances, so our instance will always
// have index 0. // have index 0.
perfetto::internal::DataSourceState* instance_state = static_state->TryGet(0); perfetto::internal::DataSourceState* instance_state = static_state->TryGet(0);
CHECK(instance_state); CHECK(instance_state);
return perfetto::internal::TracingMuxer::Get()->CreateTraceWriter( return perfetto::internal::TracingMuxer::Get()->CreateTraceWriter(
static_state, data_source_config_.target_buffer(), instance_state, static_state, 0, instance_state, perfetto::BufferExhaustedPolicy::kDrop);
perfetto::BufferExhaustedPolicy::kDrop);
} }

@@ -25,6 +25,7 @@
#include "services/tracing/public/cpp/buildflags.h" #include "services/tracing/public/cpp/buildflags.h"
#include "services/tracing/public/cpp/perfetto/interning_index.h" #include "services/tracing/public/cpp/perfetto/interning_index.h"
#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_writer.h" #include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_writer.h"
#include "third_party/perfetto/include/perfetto/tracing/data_source.h"
#if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_ARM64) && \ #if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_ARM64) && \
BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS) BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
@@ -57,6 +58,35 @@ class LoaderLockSamplingThread;
// field |profiler_| to be thread-safe. // field |profiler_| to be thread-safe.
class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler { class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler {
public: public:
class COMPONENT_EXPORT(TRACING_CPP) DataSource
: public perfetto::DataSource<DataSource> {
public:
static constexpr bool kSupportsMultipleInstances = false;
using TraceContext = perfetto::DataSource<DataSource>::TraceContext;
DataSource() = default;
~DataSource() override;
void OnSetup(const SetupArgs& args) override;
void OnStart(const StartArgs&) override;
void OnStop(const StopArgs&) override;
void WillClearIncrementalState(
const ClearIncrementalStateArgs& args) override;
// We create one trace writer per profiled thread. This is necessary because
// each profiler keeps its own interned data index, so to avoid collisions
// interned data should go into different writer sequences.
std::unique_ptr<perfetto::TraceWriterBase> CreateTraceWriter();
bool privacy_filtering_enabled() const {
return privacy_filtering_enabled_;
}
private:
bool privacy_filtering_enabled_ = false;
};
using TracePacketHandle = DataSource::TraceContext::TracePacketHandle;
class COMPONENT_EXPORT(TRACING_CPP) StackProfileWriter { class COMPONENT_EXPORT(TRACING_CPP) StackProfileWriter {
public: public:
explicit StackProfileWriter(bool should_enable_filtering); explicit StackProfileWriter(bool should_enable_filtering);
@@ -69,9 +99,8 @@ class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler {
// corresponding to the callstack. Meanwhile it could emit extra entries // corresponding to the callstack. Meanwhile it could emit extra entries
// to intern data. |function_name| member in Frame could be std::move(ed) by // to intern data. |function_name| member in Frame could be std::move(ed) by
// this method to reduce number of copies we have for function names. // this method to reduce number of copies we have for function names.
InterningID GetCallstackIDAndMaybeEmit( InterningID GetCallstackIDAndMaybeEmit(std::vector<base::Frame>& frames,
std::vector<base::Frame>& frames, TracePacketHandle* trace_packet);
perfetto::TraceWriter::TracePacketHandle* trace_packet);
void ResetEmittedState(); void ResetEmittedState();
@@ -119,48 +148,16 @@ class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler {
void OnProfileCompleted(base::TimeDelta profile_duration, void OnProfileCompleted(base::TimeDelta profile_duration,
base::TimeDelta sampling_period) override {} base::TimeDelta sampling_period) override {}
void SetTraceWriter(
std::unique_ptr<perfetto::TraceWriterBase> trace_writer);
void SetUnwinderType(TracingSamplerProfiler::UnwinderType unwinder_type); void SetUnwinderType(TracingSamplerProfiler::UnwinderType unwinder_type);
private: private:
struct BufferedSample { void WriteSampleToTrace(std::vector<base::Frame> frames,
BufferedSample(base::TimeTicks, std::vector<base::Frame>&&); base::TimeTicks sample_timestamp);
BufferedSample(const BufferedSample&) = delete;
BufferedSample& operator=(const BufferedSample&) = delete;
BufferedSample(BufferedSample&& other);
~BufferedSample();
base::TimeTicks timestamp;
std::vector<base::Frame> sample;
};
void WriteSampleToTrace(BufferedSample sample);
// TODO(ssid): Consider using an interning scheme to reduce memory usage
// and increase the sample size.
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
// We usually sample at 50ms, and expect that tracing should have started in
// 10s (5s for 2 threads). Approximately 100 frames and 200 samples would use
// 300KiB.
constexpr static size_t kMaxBufferedSamples = 200;
#else
// 2000 samples are enough to store samples for 100 seconds (50s for 2
// threads), and consumes about 3MiB of memory.
constexpr static size_t kMaxBufferedSamples = 2000;
#endif
std::vector<BufferedSample> buffered_samples_;
base::ModuleCache module_cache_; base::ModuleCache module_cache_;
const base::PlatformThreadId sampled_thread_id_; const base::PlatformThreadId sampled_thread_id_;
base::Lock trace_writer_lock_;
std::unique_ptr<perfetto::TraceWriterBase> trace_writer_; std::unique_ptr<perfetto::TraceWriterBase> trace_writer_;
StackProfileWriter stack_profile_writer_; StackProfileWriter stack_profile_writer_;
bool reset_incremental_state_ = true;
uint32_t last_incremental_state_reset_id_ = 0; uint32_t last_incremental_state_reset_id_ = 0;
base::TimeTicks last_timestamp_; base::TimeTicks last_timestamp_;
base::RepeatingClosure sample_callback_for_testing_; base::RepeatingClosure sample_callback_for_testing_;
@@ -202,11 +199,7 @@ class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler {
factory); factory);
// For tests. // For tests.
static void SetupStartupTracingForTesting();
static void DeleteOnChildThreadForTesting(); static void DeleteOnChildThreadForTesting();
static void StartTracingForTesting();
static void StopTracingForTesting();
static void ResetDataSourceForTesting();
// Returns whether of not the sampler profiling is able to unwind the stack // Returns whether of not the sampler profiling is able to unwind the stack
// on this platform, ignoring any CoreUnwindersCallback provided. // on this platform, ignoring any CoreUnwindersCallback provided.
static bool IsStackUnwindingSupportedForTesting(); static bool IsStackUnwindingSupportedForTesting();
@@ -247,7 +240,7 @@ class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler {
UnwinderType unwinder_type_; UnwinderType unwinder_type_;
base::Lock lock_; base::Lock lock_;
std::unique_ptr<base::StackSamplingProfiler> profiler_; // under |lock_| std::unique_ptr<base::StackSamplingProfiler> profiler_ GUARDED_BY(lock_);
// This dangling raw_ptr occurred in: // This dangling raw_ptr occurred in:
// services_unittests: TracingSampleProfilerTest.SamplingChildThread // services_unittests: TracingSampleProfilerTest.SamplingChildThread
// https://ci.chromium.org/ui/p/chromium/builders/try/win-rel/237204/test-results?q=ExactID%3Aninja%3A%2F%2Fservices%3Aservices_unittests%2FTracingSampleProfilerTest.SamplingChildThread+VHash%3A83af393c6a76b581 // https://ci.chromium.org/ui/p/chromium/builders/try/win-rel/237204/test-results?q=ExactID%3Aninja%3A%2F%2Fservices%3Aservices_unittests%2FTracingSampleProfilerTest.SamplingChildThread+VHash%3A83af393c6a76b581

@@ -66,8 +66,6 @@ using ::testing::Return;
using PacketVector = using PacketVector =
std::vector<std::unique_ptr<perfetto::protos::TracePacket>>; std::vector<std::unique_ptr<perfetto::protos::TracePacket>>;
std::unique_ptr<perfetto::TracingSession> g_tracing_session;
#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING) #if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
class MockLoaderLockSampler : public LoaderLockSampler { class MockLoaderLockSampler : public LoaderLockSampler {
@@ -122,9 +120,7 @@ class TracingSampleProfilerTest : public testing::Test {
events_stack_received_count_ = 0u; events_stack_received_count_ = 0u;
tracing::PerfettoTracedProcess::DataSourceBase::ResetTaskRunnerForTesting( TracingSamplerProfiler::RegisterDataSource();
base::SingleThreadTaskRunner::GetCurrentDefault());
TracingSamplerProfiler::ResetDataSourceForTesting();
} }
void TearDown() override { void TearDown() override {
@@ -141,29 +137,27 @@ class TracingSampleProfilerTest : public testing::Test {
ds_cfg = trace_config.add_data_sources()->mutable_config(); ds_cfg = trace_config.add_data_sources()->mutable_config();
ds_cfg->set_name("track_event"); ds_cfg->set_name("track_event");
g_tracing_session = perfetto::Tracing::NewTrace(); tracing_session_ = perfetto::Tracing::NewTrace();
g_tracing_session->Setup(trace_config); tracing_session_->Setup(trace_config);
g_tracing_session->StartBlocking(); tracing_session_->StartBlocking();
// Make sure TraceEventMetadataSource::StartTracingImpl gets run.
base::RunLoop().RunUntilIdle();
} }
void WaitForEvents() { base::PlatformThread::Sleep(base::Milliseconds(200)); } void WaitForEvents() { base::PlatformThread::Sleep(base::Milliseconds(200)); }
void EnsureTraceStopped() { void EnsureTraceStopped() {
if (!g_tracing_session) if (!tracing_session_) {
return; return;
}
base::TrackEvent::Flush(); base::TrackEvent::Flush();
base::RunLoop wait_for_stop; base::RunLoop wait_for_stop;
g_tracing_session->SetOnStopCallback( tracing_session_->SetOnStopCallback(
[&wait_for_stop] { wait_for_stop.Quit(); }); [&wait_for_stop] { wait_for_stop.Quit(); });
g_tracing_session->Stop(); tracing_session_->Stop();
wait_for_stop.Run(); wait_for_stop.Run();
std::vector<char> serialized_data = tracing_session_->ReadTraceBlocking();
std::vector<char> serialized_data = g_tracing_session->ReadTraceBlocking(); tracing_session_.reset();
g_tracing_session.reset();
perfetto::protos::Trace trace; perfetto::protos::Trace trace;
EXPECT_TRUE( EXPECT_TRUE(
@@ -212,8 +206,6 @@ class TracingSampleProfilerTest : public testing::Test {
} }
protected: protected:
// We want our singleton torn down after each test.
base::ShadowingAtExitManager at_exit_manager_;
base::trace_event::TraceResultBuffer trace_buffer_; base::trace_event::TraceResultBuffer trace_buffer_;
base::test::TaskEnvironment task_environment_; base::test::TaskEnvironment task_environment_;
@@ -221,6 +213,8 @@ class TracingSampleProfilerTest : public testing::Test {
std::vector<std::unique_ptr<perfetto::protos::TracePacket>> std::vector<std::unique_ptr<perfetto::protos::TracePacket>>
finalized_packets_; finalized_packets_;
std::unique_ptr<perfetto::TracingSession> tracing_session_;
// Number of stack sampling events received. // Number of stack sampling events received.
size_t events_stack_received_count_ = 0; size_t events_stack_received_count_ = 0;
@@ -273,10 +267,8 @@ TEST_F(TracingSampleProfilerTest, OnSampleCompleted) {
TracingSamplerProfiler::CreateOnMainThread(base::BindRepeating( TracingSamplerProfiler::CreateOnMainThread(base::BindRepeating(
[] { return MakeMockUnwinderFactoryWithExpectations(); })); [] { return MakeMockUnwinderFactoryWithExpectations(); }));
BeginTrace(); BeginTrace();
base::RunLoop().RunUntilIdle();
WaitForEvents(); WaitForEvents();
EndTracing(); EndTracing();
base::RunLoop().RunUntilIdle();
ValidateReceivedEvents(); ValidateReceivedEvents();
} }
@@ -296,85 +288,11 @@ TEST_F(TracingSampleProfilerTest, MAYBE_JoinRunningTracing) {
auto profiler = auto profiler =
TracingSamplerProfiler::CreateOnMainThread(base::BindRepeating( TracingSamplerProfiler::CreateOnMainThread(base::BindRepeating(
[] { return MakeMockUnwinderFactoryWithExpectations(); })); [] { return MakeMockUnwinderFactoryWithExpectations(); }));
base::RunLoop().RunUntilIdle();
WaitForEvents(); WaitForEvents();
EndTracing(); EndTracing();
base::RunLoop().RunUntilIdle();
ValidateReceivedEvents(); ValidateReceivedEvents();
} }
TEST_F(TracingSampleProfilerTest, TestStartupTracing) {
auto profiler =
TracingSamplerProfiler::CreateOnMainThread(base::BindRepeating(
[] { return MakeMockUnwinderFactoryWithExpectations(); }));
TracingSamplerProfiler::SetupStartupTracingForTesting();
base::RunLoop().RunUntilIdle();
WaitForEvents();
auto start_tracing_ts = TRACE_TIME_TICKS_NOW();
BeginTrace();
base::RunLoop().RunUntilIdle();
WaitForEvents();
EndTracing();
base::RunLoop().RunUntilIdle();
if (TracingSamplerProfiler::IsStackUnwindingSupportedForTesting()) {
uint32_t seq_id = FindProfilerSequenceId();
auto& packets = GetFinalizedPackets();
int64_t reference_ts = 0;
int64_t first_profile_ts = 0;
for (auto& packet : packets) {
if (packet->trusted_packet_sequence_id() == seq_id) {
if (packet->has_thread_descriptor()) {
reference_ts = packet->thread_descriptor().reference_timestamp_us();
} else if (packet->has_streaming_profile_packet()) {
first_profile_ts =
reference_ts +
packet->streaming_profile_packet().timestamp_delta_us(0);
break;
}
}
}
// Expect first sample before tracing started.
EXPECT_LT(first_profile_ts,
start_tracing_ts.since_origin().InMicroseconds());
}
}
TEST_F(TracingSampleProfilerTest, JoinStartupTracing) {
TracingSamplerProfiler::SetupStartupTracingForTesting();
base::RunLoop().RunUntilIdle();
auto profiler =
TracingSamplerProfiler::CreateOnMainThread(base::BindRepeating(
[] { return MakeMockUnwinderFactoryWithExpectations(); }));
WaitForEvents();
auto start_tracing_ts = TRACE_TIME_TICKS_NOW();
BeginTrace();
base::RunLoop().RunUntilIdle();
WaitForEvents();
EndTracing();
base::RunLoop().RunUntilIdle();
if (TracingSamplerProfiler::IsStackUnwindingSupportedForTesting()) {
uint32_t seq_id = FindProfilerSequenceId();
auto& packets = GetFinalizedPackets();
int64_t reference_ts = 0;
int64_t first_profile_ts = 0;
for (auto& packet : packets) {
if (packet->trusted_packet_sequence_id() == seq_id) {
if (packet->has_thread_descriptor()) {
reference_ts = packet->thread_descriptor().reference_timestamp_us();
} else if (packet->has_streaming_profile_packet()) {
first_profile_ts =
reference_ts +
packet->streaming_profile_packet().timestamp_delta_us(0);
break;
}
}
}
// Expect first sample before tracing started.
EXPECT_LT(first_profile_ts,
start_tracing_ts.since_origin().InMicroseconds());
}
}
// This is needed because this code is racy (example: // This is needed because this code is racy (example:
// https://crbug.com/338398659#comment1) by design: tracing needs to have // https://crbug.com/338398659#comment1) by design: tracing needs to have
// minimal runtime overhead, so tracing code assumes certain things are already // minimal runtime overhead, so tracing code assumes certain things are already
@@ -396,14 +314,12 @@ TEST_F(TracingSampleProfilerTest, MAYBE_SamplingChildThread) {
base::BindRepeating( base::BindRepeating(
[] { return MakeMockUnwinderFactoryWithExpectations(); }))); [] { return MakeMockUnwinderFactoryWithExpectations(); })));
BeginTrace(); BeginTrace();
base::RunLoop().RunUntilIdle();
WaitForEvents(); WaitForEvents();
EndTracing(); EndTracing();
ValidateReceivedEvents(); ValidateReceivedEvents();
sampled_thread.task_runner()->PostTask( sampled_thread.task_runner()->PostTask(
FROM_HERE, FROM_HERE,
base::BindOnce(&TracingSamplerProfiler::DeleteOnChildThreadForTesting)); base::BindOnce(&TracingSamplerProfiler::DeleteOnChildThreadForTesting));
base::RunLoop().RunUntilIdle();
} }
#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING) #if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
@@ -422,10 +338,8 @@ TEST_F(TracingSampleProfilerTest, SampleLoaderLockOnMainThread) {
auto profiler = TracingSamplerProfiler::CreateOnMainThread(); auto profiler = TracingSamplerProfiler::CreateOnMainThread();
BeginTrace(); BeginTrace();
base::RunLoop().RunUntilIdle();
WaitForEvents(); WaitForEvents();
EndTracing(); EndTracing();
base::RunLoop().RunUntilIdle();
// Since the loader lock state changed each time it was sampled an event // Since the loader lock state changed each time it was sampled an event
// should be emitted each time. // should be emitted each time.
@@ -441,10 +355,8 @@ TEST_F(TracingSampleProfilerTest, SampleLoaderLockAlwaysHeld) {
auto profiler = TracingSamplerProfiler::CreateOnMainThread(); auto profiler = TracingSamplerProfiler::CreateOnMainThread();
BeginTrace(); BeginTrace();
base::RunLoop().RunUntilIdle();
WaitForEvents(); WaitForEvents();
EndTracing(); EndTracing();
base::RunLoop().RunUntilIdle();
// An event should be emitted at the first sample when the loader lock was // An event should be emitted at the first sample when the loader lock was
// held, and then not again since the state never changed. // held, and then not again since the state never changed.
@@ -459,10 +371,8 @@ TEST_F(TracingSampleProfilerTest, SampleLoaderLockNeverHeld) {
auto profiler = TracingSamplerProfiler::CreateOnMainThread(); auto profiler = TracingSamplerProfiler::CreateOnMainThread();
BeginTrace(); BeginTrace();
base::RunLoop().RunUntilIdle();
WaitForEvents(); WaitForEvents();
EndTracing(); EndTracing();
base::RunLoop().RunUntilIdle();
// No events should be emitted since the lock is never held. // No events should be emitted since the lock is never held.
EXPECT_EQ(event_analyzer.CountEvents(), 0U); EXPECT_EQ(event_analyzer.CountEvents(), 0U);
@@ -479,13 +389,11 @@ TEST_F(TracingSampleProfilerTest, SampleLoaderLockOnChildThread) {
sampled_thread.task_runner()->PostTask( sampled_thread.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&TracingSamplerProfiler::CreateOnChildThread)); FROM_HERE, base::BindOnce(&TracingSamplerProfiler::CreateOnChildThread));
BeginTrace(); BeginTrace();
base::RunLoop().RunUntilIdle();
WaitForEvents(); WaitForEvents();
EndTracing(); EndTracing();
sampled_thread.task_runner()->PostTask( sampled_thread.task_runner()->PostTask(
FROM_HERE, FROM_HERE,
base::BindOnce(&TracingSamplerProfiler::DeleteOnChildThreadForTesting)); base::BindOnce(&TracingSamplerProfiler::DeleteOnChildThreadForTesting));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(event_analyzer.CountEvents(), 0U); EXPECT_EQ(event_analyzer.CountEvents(), 0U);
} }
@@ -499,10 +407,8 @@ TEST_F(TracingSampleProfilerTest, SampleLoaderLockWithoutMock) {
// test process. // test process.
auto profiler = TracingSamplerProfiler::CreateOnMainThread(); auto profiler = TracingSamplerProfiler::CreateOnMainThread();
BeginTrace(); BeginTrace();
base::RunLoop().RunUntilIdle();
WaitForEvents(); WaitForEvents();
EndTracing(); EndTracing();
base::RunLoop().RunUntilIdle();
// The loader lock may or may not be held during the test, so there's no // The loader lock may or may not be held during the test, so there's no
// output to test. The test passes if it reaches the end without crashing. // output to test. The test passes if it reaches the end without crashing.