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 "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/stack_sampling/tracing_sampler_profiler.h"
#include "testing/gmock/include/gmock/gmock.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;
}
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(
const std::string& category_filter_string) {
auto perfetto_config =
@ -336,6 +348,69 @@ IN_PROC_BROWSER_TEST_F(TracingEndToEndBrowserTest,
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,
MemoryInstrumentationDetailed) {
base::WaitableEvent dump_completed;

@ -60,7 +60,6 @@
using StreamingProfilePacketHandle =
protozero::MessageHandle<perfetto::protos::pbzero::StreamingProfilePacket>;
using TracePacketHandle = perfetto::TraceWriter::TracePacketHandle;
namespace tracing {
@ -88,172 +87,75 @@ uintptr_t executable_start_addr() {
// Pointer to the main thread instance, if any.
TracingSamplerProfiler* g_main_thread_instance = nullptr;
class TracingSamplerProfilerDataSource;
TracingSamplerProfilerDataSource* g_sampler_profiler_ds_for_test = nullptr;
class TracingSamplerProfilerDataSource
: public PerfettoTracedProcess::DataSourceBase {
class TracingSamplerProfilerManager {
public:
static TracingSamplerProfilerDataSource* Get() {
static base::NoDestructor<TracingSamplerProfilerDataSource> instance;
static TracingSamplerProfilerManager* Get() {
static base::NoDestructor<TracingSamplerProfilerManager> instance;
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) {
base::AutoLock lock(lock_);
if (!profilers_.insert(profiler).second) {
return;
}
if (is_started_) {
profiler->StartTracing(
CreateTraceWriter(),
data_source_config_.chrome_config().privacy_filtering_enabled());
} else if (is_startup_tracing_) {
profiler->StartTracing(
nullptr,
/*should_enable_filtering=*/true);
if (data_source_) {
profiler->StartTracing(data_source_->CreateTraceWriter(),
data_source_->privacy_filtering_enabled());
}
}
void UnregisterProfiler(TracingSamplerProfiler* profiler) {
base::AutoLock lock(lock_);
if (!profilers_.erase(profiler) || !(is_started_ || is_startup_tracing_)) {
if (!profilers_.erase(profiler)) {
return;
}
profiler->StopTracing();
}
// 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_) {
if (data_source_) {
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:
friend class base::NoDestructor<TracingSamplerProfilerDataSource>;
friend class base::NoDestructor<TracingSamplerProfilerManager>;
TracingSamplerProfilerDataSource()
: DataSourceBase(mojom::kSamplerProfilerSourceName) {
g_sampler_profiler_ds_for_test = this;
}
TracingSamplerProfilerManager() = default;
~TracingSamplerProfilerManager() = default;
~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.
std::set<raw_ptr<TracingSamplerProfiler, SetExperimental>> profilers_;
bool is_startup_tracing_ = false;
bool is_started_ = false;
perfetto::DataSourceConfig data_source_config_;
static std::atomic<uint32_t> incremental_state_reset_id_;
std::set<raw_ptr<TracingSamplerProfiler, SetExperimental>> profilers_
GUARDED_BY(lock_);
raw_ptr<TracingSamplerProfiler::DataSource> data_source_ GUARDED_BY(lock_);
std::atomic<uint32_t> incremental_state_reset_id_{1};
};
using DataSourceProxy = TracingSamplerProfilerDataSource::DataSourceProxy;
// static
std::atomic<uint32_t>
TracingSamplerProfilerDataSource::incremental_state_reset_id_{0};
base::SequenceLocalStorageSlot<TracingSamplerProfiler>&
GetSequenceLocalStorageProfilerSlot() {
static base::SequenceLocalStorageSlot<TracingSamplerProfiler> storage;
@ -376,17 +278,25 @@ perfetto::StaticString UnwinderTypeToString(
} // namespace
TracingSamplerProfiler::TracingProfileBuilder::BufferedSample::BufferedSample(
base::TimeTicks ts,
std::vector<base::Frame>&& s)
: timestamp(ts), sample(std::move(s)) {}
TracingSamplerProfiler::DataSource::~DataSource() = default;
TracingSamplerProfiler::TracingProfileBuilder::BufferedSample::
~BufferedSample() = default;
void TracingSamplerProfiler::DataSource::OnSetup(const SetupArgs& args) {
privacy_filtering_enabled_ =
args.config->chrome_config().privacy_filtering_enabled();
}
TracingSamplerProfiler::TracingProfileBuilder::BufferedSample::BufferedSample(
TracingSamplerProfiler::TracingProfileBuilder::BufferedSample&& other)
: BufferedSample(other.timestamp, std::move(other.sample)) {}
void TracingSamplerProfiler::DataSource::OnStart(const StartArgs&) {
TracingSamplerProfilerManager::Get()->OnDataSourceStart(this);
}
void TracingSamplerProfiler::DataSource::OnStop(const StopArgs&) {
TracingSamplerProfilerManager::Get()->OnDataSourceStop(this);
}
void TracingSamplerProfiler::DataSource::WillClearIncrementalState(
const ClearIncrementalStateArgs& args) {
TracingSamplerProfilerManager::Get()->WillClearIncrementalState();
}
TracingSamplerProfiler::TracingProfileBuilder::TracingProfileBuilder(
base::PlatformThreadId sampled_thread_id,
@ -425,41 +335,22 @@ using SampleDebugProto =
void TracingSamplerProfiler::TracingProfileBuilder::OnSampleCompleted(
std::vector<base::Frame> frames,
base::TimeTicks sample_timestamp) {
base::AutoLock l(trace_writer_lock_);
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));
WriteSampleToTrace(std::move(frames), sample_timestamp);
if (sample_callback_for_testing_) {
sample_callback_for_testing_.Run();
}
}
void TracingSamplerProfiler::TracingProfileBuilder::WriteSampleToTrace(
TracingSamplerProfiler::TracingProfileBuilder::BufferedSample sample) {
auto& frames = sample.sample;
auto reset_id =
TracingSamplerProfilerDataSource::GetIncrementalStateResetID();
if (reset_id != last_incremental_state_reset_id_) {
reset_incremental_state_ = true;
last_incremental_state_reset_id_ = reset_id;
}
std::vector<base::Frame> frames,
base::TimeTicks sample_timestamp) {
uint32_t previous_incremental_state_reset_id = std::exchange(
last_incremental_state_reset_id_,
TracingSamplerProfilerManager::Get()->GetIncrementalStateResetID());
bool reset_incremental_state =
(previous_incremental_state_reset_id != last_incremental_state_reset_id_);
if (reset_incremental_state_) {
if (reset_incremental_state) {
stack_profile_writer_.ResetEmittedState();
TracePacketHandle trace_packet = trace_writer_->NewTracePacket();
@ -473,13 +364,12 @@ void TracingSamplerProfiler::TracingProfileBuilder::WriteSampleToTrace(
thread_descriptor->set_pid(
base::trace_event::TraceLog::GetInstance()->process_id());
thread_descriptor->set_tid(sampled_thread_id_);
last_timestamp_ = sample.timestamp;
last_timestamp_ = sample_timestamp;
thread_descriptor->set_reference_timestamp_us(
last_timestamp_.since_origin().InMicroseconds());
TRACE_EVENT_INSTANT(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
UnwinderTypeToString(unwinder_type_));
reset_incremental_state_ = false;
}
TracePacketHandle trace_packet = trace_writer_->NewTracePacket();
@ -497,14 +387,8 @@ void TracingSamplerProfiler::TracingProfileBuilder::WriteSampleToTrace(
}
streaming_profile_packet->add_timestamp_delta_us(
(sample.timestamp - last_timestamp_).InMicroseconds());
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);
(sample_timestamp - last_timestamp_).InMicroseconds());
last_timestamp_ = sample_timestamp;
}
void TracingSamplerProfiler::TracingProfileBuilder::SetUnwinderType(
@ -520,7 +404,7 @@ TracingSamplerProfiler::StackProfileWriter::~StackProfileWriter() = default;
InterningID
TracingSamplerProfiler::StackProfileWriter::GetCallstackIDAndMaybeEmit(
std::vector<base::Frame>& frames,
perfetto::TraceWriter::TracePacketHandle* trace_packet) {
TracePacketHandle* trace_packet) {
size_t ip_hash = 0;
for (const auto& frame : frames) {
ip_hash = base::HashInts(ip_hash, frame.instruction_pointer);
@ -733,15 +617,11 @@ void TracingSamplerProfiler::DeleteOnChildThreadForTesting() {
GetSequenceLocalStorageProfilerSlot().reset();
}
// static
void TracingSamplerProfiler::ResetDataSourceForTesting() {
TracingSamplerProfilerDataSource::Get()->ResetForTesting();
RegisterDataSource();
}
// static
void TracingSamplerProfiler::RegisterDataSource() {
TracingSamplerProfilerDataSource::Get()->RegisterDataSource();
perfetto::DataSourceDescriptor dsd;
dsd.set_name(mojom::kSamplerProfilerSourceName);
perfetto::DataSource<TracingSamplerProfiler::DataSource>::Register(dsd);
}
// static
@ -763,26 +643,10 @@ void TracingSamplerProfiler::SetAuxUnwinderFactoryOnMainThread(
g_main_thread_instance->SetAuxUnwinderFactory(factory);
}
// TODO(b/336718643): Remove unused code after removing use_perfetto_client_library build
// flag.
// static
void TracingSamplerProfiler::StartTracingForTesting() {
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());
void TracingSamplerProfiler::SetSampleCallbackForTesting(
const base::RepeatingClosure& sample_callback_for_testing) {
base::AutoLock lock(lock_);
sample_callback_for_testing_ = sample_callback_for_testing;
}
TracingSamplerProfiler::TracingSamplerProfiler(
@ -801,11 +665,11 @@ TracingSamplerProfiler::TracingSamplerProfiler(
// created on the profiled thread. See crbug.com/1392158#c26 for details.
base::ThreadDelegatePosix::Create(sampled_thread_token_);
#endif // INITIALIZE_THREAD_DELEGATE_POSIX
TracingSamplerProfilerDataSource::Get()->RegisterProfiler(this);
TracingSamplerProfilerManager::Get()->RegisterProfiler(this);
}
TracingSamplerProfiler::~TracingSamplerProfiler() {
TracingSamplerProfilerDataSource::Get()->UnregisterProfiler(this);
TracingSamplerProfilerManager::Get()->UnregisterProfiler(this);
if (g_main_thread_instance == this)
g_main_thread_instance = nullptr;
}
@ -818,22 +682,11 @@ void TracingSamplerProfiler::SetAuxUnwinderFactory(
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(
std::unique_ptr<perfetto::TraceWriterBase> trace_writer,
bool should_enable_filtering) {
base::AutoLock lock(lock_);
if (profiler_) {
if (trace_writer) {
profile_builder_->SetTraceWriter(std::move(trace_writer));
}
return;
}
DCHECK_EQ(profiler_, nullptr);
if (!base::StackSamplingProfiler::IsSupportedForCurrentPlatform()) {
return;
@ -844,8 +697,7 @@ void TracingSamplerProfiler::StartTracing(
params.sampling_interval = base::Milliseconds(50);
auto profile_builder = std::make_unique<TracingProfileBuilder>(
sampled_thread_token_.id,
std::move(trace_writer),
sampled_thread_token_.id, std::move(trace_writer),
should_enable_filtering, sample_callback_for_testing_);
profile_builder_ = profile_builder.get();
@ -910,19 +762,19 @@ void TracingSamplerProfiler::StopTracing() {
PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS(
COMPONENT_EXPORT(TRACING_CPP),
tracing::TracingSamplerProfilerDataSource::DataSourceProxy);
tracing::TracingSamplerProfiler::DataSource);
// This should go after PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS
// to avoid instantiation of type() template method before specialization.
std::unique_ptr<perfetto::TraceWriterBase>
tracing::TracingSamplerProfilerDataSource::CreateTraceWriter() {
tracing::TracingSamplerProfiler::DataSource::CreateTraceWriter() {
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
// have index 0.
perfetto::internal::DataSourceState* instance_state = static_state->TryGet(0);
CHECK(instance_state);
return perfetto::internal::TracingMuxer::Get()->CreateTraceWriter(
static_state, data_source_config_.target_buffer(), instance_state,
perfetto::BufferExhaustedPolicy::kDrop);
static_state, 0, instance_state, perfetto::BufferExhaustedPolicy::kDrop);
}

@ -25,6 +25,7 @@
#include "services/tracing/public/cpp/buildflags.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/tracing/data_source.h"
#if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_ARM64) && \
BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
@ -57,6 +58,35 @@ class LoaderLockSamplingThread;
// field |profiler_| to be thread-safe.
class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler {
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 {
public:
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
// 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.
InterningID GetCallstackIDAndMaybeEmit(
std::vector<base::Frame>& frames,
perfetto::TraceWriter::TracePacketHandle* trace_packet);
InterningID GetCallstackIDAndMaybeEmit(std::vector<base::Frame>& frames,
TracePacketHandle* trace_packet);
void ResetEmittedState();
@ -119,48 +148,16 @@ class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler {
void OnProfileCompleted(base::TimeDelta profile_duration,
base::TimeDelta sampling_period) override {}
void SetTraceWriter(
std::unique_ptr<perfetto::TraceWriterBase> trace_writer);
void SetUnwinderType(TracingSamplerProfiler::UnwinderType unwinder_type);
private:
struct BufferedSample {
BufferedSample(base::TimeTicks, std::vector<base::Frame>&&);
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_;
void WriteSampleToTrace(std::vector<base::Frame> frames,
base::TimeTicks sample_timestamp);
base::ModuleCache module_cache_;
const base::PlatformThreadId sampled_thread_id_;
base::Lock trace_writer_lock_;
std::unique_ptr<perfetto::TraceWriterBase> trace_writer_;
StackProfileWriter stack_profile_writer_;
bool reset_incremental_state_ = true;
uint32_t last_incremental_state_reset_id_ = 0;
base::TimeTicks last_timestamp_;
base::RepeatingClosure sample_callback_for_testing_;
@ -202,11 +199,7 @@ class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler {
factory);
// For tests.
static void SetupStartupTracingForTesting();
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
// on this platform, ignoring any CoreUnwindersCallback provided.
static bool IsStackUnwindingSupportedForTesting();
@ -247,7 +240,7 @@ class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler {
UnwinderType unwinder_type_;
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:
// 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

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