blob: cc58f4f573fa9a148a34c11b71b7466322c53fe5 [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/stack_sampling/tracing_sampler_profiler.h"
#include <limits>
#include <set>
#include "base/android/library_loader/anchor_functions.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/debug/leak_annotations.h"
#include "base/debug/stack_trace.h"
#include "base/hash/hash.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/no_destructor.h"
#include "base/process/process.h"
#include "base/process/process_handle.h"
#include "base/profiler/sampling_profiler_thread_token.h"
#include "base/profiler/stack_sampling_profiler.h"
#include "base/strings/strcat.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/thread_annotations.h"
#include "base/threading/sequence_local_storage_slot.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/typed_macros.h"
#include "build/build_config.h"
#include "services/tracing/public/cpp/buildflags.h"
#include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
#include "services/tracing/public/cpp/perfetto/producer_client.h"
#include "third_party/perfetto/protos/perfetto/trace/interned_data/interned_data.pbzero.h"
#include "third_party/perfetto/protos/perfetto/trace/profiling/profile_common.pbzero.h"
#include "third_party/perfetto/protos/perfetto/trace/profiling/profile_packet.pbzero.h"
#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
#if ANDROID_ARM64_UNWINDING_SUPPORTED || ANDROID_CFI_UNWINDING_SUPPORTED
#include <dlfcn.h>
#include "base/android/reached_code_profiler.h"
#include "base/debug/elf_reader.h"
#if ANDROID_ARM64_UNWINDING_SUPPORTED
#include "services/tracing/public/cpp/stack_sampling/stack_unwinder_arm64_android.h"
#elif ANDROID_CFI_UNWINDING_SUPPORTED
#include "base/trace_event/cfi_backtrace_android.h"
#include "services/tracing/public/cpp/stack_sampling/stack_sampler_android.h"
#endif // ANDROID_ARM64_UNWINDING_SUPPORTED
#endif // ANDROID_ARM64_UNWINDING_SUPPORTED || ANDROID_CFI_UNWINDING_SUPPORTED
#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
#include "services/tracing/public/cpp/stack_sampling/loader_lock_sampling_thread_win.h"
#endif
using StreamingProfilePacketHandle =
protozero::MessageHandle<perfetto::protos::pbzero::StreamingProfilePacket>;
namespace tracing {
namespace {
#if ANDROID_ARM64_UNWINDING_SUPPORTED || ANDROID_CFI_UNWINDING_SUPPORTED
extern "C" {
// The address of |__executable_start| gives the start address of the
// executable or shared library. This value is used to find the offset address
// of the instruction in binary from PC.
extern char __executable_start;
} // extern "C"
bool is_chrome_address(uintptr_t pc) {
return pc >= base::android::kStartOfText && pc < base::android::kEndOfText;
}
uintptr_t executable_start_addr() {
return reinterpret_cast<uintptr_t>(&__executable_start);
}
#endif // ANDROID_ARM64_UNWINDING_SUPPORTED || ANDROID_CFI_UNWINDING_SUPPORTED
// Pointer to the main thread instance, if any.
TracingSamplerProfiler* g_main_thread_instance = nullptr;
class TracingSamplerProfilerDataSource
: public PerfettoTracedProcess::DataSourceBase {
public:
static TracingSamplerProfilerDataSource* Get() {
static base::NoDestructor<TracingSamplerProfilerDataSource> instance;
return instance.get();
}
TracingSamplerProfilerDataSource()
: DataSourceBase(mojom::kSamplerProfilerSourceName) {}
~TracingSamplerProfilerDataSource() override { NOTREACHED(); }
void RegisterProfiler(TracingSamplerProfiler* profiler) {
base::AutoLock lock(lock_);
if (!profilers_.insert(profiler).second) {
return;
}
if (is_started_) {
profiler->StartTracing(
producer_->CreateTraceWriter(data_source_config_.target_buffer()),
data_source_config_.chrome_config().privacy_filtering_enabled());
} else if (is_startup_tracing_) {
profiler->StartTracing(nullptr, /*should_enable_filtering=*/true);
}
}
void UnregisterProfiler(TracingSamplerProfiler* profiler) {
base::AutoLock lock(lock_);
if (!profilers_.erase(profiler) || !(is_started_ || is_startup_tracing_)) {
return;
}
profiler->StopTracing();
}
// PerfettoTracedProcess::DataSourceBase implementation, called by
// ProducerClient.
void StartTracingImpl(
PerfettoProducer* producer,
const perfetto::DataSourceConfig& data_source_config) override {
base::AutoLock lock(lock_);
DCHECK(!producer_);
DCHECK(!is_started_);
producer_ = producer;
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 (auto* profiler : profilers_) {
profiler->StartTracing(
producer->CreateTraceWriter(data_source_config.target_buffer()),
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;
producer_ = nullptr;
for (auto* profiler : profilers_) {
profiler->StopTracing();
}
std::move(stop_complete_callback).Run();
}
void Flush(base::RepeatingClosure flush_complete_callback) override {
flush_complete_callback.Run();
}
void SetupStartupTracing(PerfettoProducer* producer,
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 (auto* 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 (auto* 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);
}
private:
// TODO(eseckler): Use GUARDED_BY annotations for all members below.
base::Lock lock_; // Protects subsequent members.
raw_ptr<tracing::PerfettoProducer> producer_ GUARDED_BY(lock_) = nullptr;
std::set<TracingSamplerProfiler*> profilers_;
bool is_startup_tracing_ = false;
bool is_started_ = false;
perfetto::DataSourceConfig data_source_config_;
static std::atomic<uint32_t> incremental_state_reset_id_;
};
// static
std::atomic<uint32_t>
TracingSamplerProfilerDataSource::incremental_state_reset_id_{0};
base::SequenceLocalStorageSlot<TracingSamplerProfiler>&
GetSequenceLocalStorageProfilerSlot() {
static base::SequenceLocalStorageSlot<TracingSamplerProfiler> storage;
return storage;
}
// Stores information about the StackFrame, to emit to the trace.
struct FrameDetails {
std::string frame_name;
std::string module_name;
std::string module_id;
uintptr_t module_base_address = 0;
uintptr_t rel_pc = 0;
// True if the module of the stack frame will be considered valid by the trace
// processor.
bool has_valid_module() const {
return !module_name.empty() && !module_id.empty() &&
module_base_address > 0;
}
bool has_valid_frame() const {
// Valid only if |rel_pc|, since filtering mode does not record frame names.
return rel_pc > 0;
}
// Gets module from the frame's module cache.
void SetModule(const base::ModuleCache::Module& module) {
module_base_address = module.GetBaseAddress();
module_id = module.GetId();
if (module_name.empty()) {
module_name = module.GetDebugBasename().MaybeAsASCII();
}
}
// Leaves the valid fields as is and fills in dummy values for invalid fields.
// Useful to observe errors in traces.
void FillWithDummyFields(uintptr_t frame_ip) {
if (rel_pc == 0) {
// Record the |frame_ip| as |rel_pc| if available, might be useful to
// debug.
rel_pc = frame_ip > 0 ? frame_ip : 1;
}
if (module_base_address == 0) {
module_base_address = 1;
}
if (module_id.empty()) {
module_id = "missing";
}
if (module_name.empty()) {
module_name = "missing";
}
DCHECK(has_valid_frame());
DCHECK(has_valid_module());
}
#if ANDROID_ARM64_UNWINDING_SUPPORTED || ANDROID_CFI_UNWINDING_SUPPORTED
// Sets Chrome's module info for the frame.
void SetChromeModuleInfo() {
module_base_address = executable_start_addr();
static const absl::optional<base::StringPiece> library_name =
base::debug::ReadElfLibraryName(
reinterpret_cast<void*>(executable_start_addr()));
static const base::NoDestructor<std::string> chrome_debug_id([] {
base::debug::ElfBuildIdBuffer build_id;
size_t build_id_length = base::debug::ReadElfBuildId(
reinterpret_cast<void*>(executable_start_addr()), true, build_id);
return std::string(build_id, build_id_length);
}());
if (library_name) {
module_name = std::string(*library_name);
}
module_id = *chrome_debug_id;
}
// Sets system library module info for the frame.
void SetSystemModuleInfo(uintptr_t frame_ip) {
Dl_info info = {};
// For addresses in framework libraries, symbolize and write the function
// name.
if (dladdr(reinterpret_cast<void*>(frame_ip), &info) == 0) {
return;
}
if (info.dli_sname) {
frame_name = info.dli_sname;
}
if (info.dli_fname) {
module_name = info.dli_fname;
}
module_base_address = reinterpret_cast<uintptr_t>(info.dli_fbase);
rel_pc = frame_ip - module_base_address;
// We have already symbolized these frames, so module ID is not necessary.
// Reading the real ID can cause crashes and we can't symbolize these
// server-side anyways.
// TODO(ssid): Remove this once perfetto can keep the frames without module
// ID.
module_id = "system";
DCHECK(has_valid_frame());
DCHECK(has_valid_module());
}
#endif
};
} // namespace
TracingSamplerProfiler::TracingProfileBuilder::BufferedSample::BufferedSample(
base::TimeTicks ts,
std::vector<base::Frame>&& s)
: timestamp(ts), sample(std::move(s)) {}
TracingSamplerProfiler::TracingProfileBuilder::BufferedSample::
~BufferedSample() = default;
TracingSamplerProfiler::TracingProfileBuilder::BufferedSample::BufferedSample(
TracingSamplerProfiler::TracingProfileBuilder::BufferedSample&& other)
: BufferedSample(other.timestamp, std::move(other.sample)) {}
TracingSamplerProfiler::TracingProfileBuilder::TracingProfileBuilder(
base::PlatformThreadId sampled_thread_id,
std::unique_ptr<perfetto::TraceWriter> trace_writer,
bool should_enable_filtering,
const base::RepeatingClosure& sample_callback_for_testing)
: sampled_thread_id_(sampled_thread_id),
trace_writer_(std::move(trace_writer)),
stack_profile_writer_(should_enable_filtering),
sample_callback_for_testing_(sample_callback_for_testing) {}
TracingSamplerProfiler::TracingProfileBuilder::~TracingProfileBuilder() {
// Deleting a TraceWriter can end up triggering a Mojo call which calls
// TaskRunnerHandle::Get() and isn't safe on thread shutdown, which is when
// TracingProfileBuilder gets destructed, so we make sure this happens on
// a different sequence.
if (base::ThreadPoolInstance::Get()) {
PerfettoTracedProcess::GetTaskRunner()->GetOrCreateTaskRunner()->DeleteSoon(
FROM_HERE, std::move(trace_writer_));
} else {
// Intentionally leak; we have no way of safely destroying this at this
// point.
ANNOTATE_LEAKING_OBJECT_PTR(trace_writer_.get());
trace_writer_.release();
}
}
base::ModuleCache*
TracingSamplerProfiler::TracingProfileBuilder::GetModuleCache() {
return &module_cache_;
}
using SampleDebugProto =
perfetto::protos::pbzero::ChromeSamplingProfilerSampleCollected;
void TracingSamplerProfiler::TracingProfileBuilder::OnSampleCompleted(
std::vector<base::Frame> frames,
base::TimeTicks sample_timestamp) {
base::AutoLock l(trace_writer_lock_);
if (!trace_writer_) {
if (buffered_samples_.size() < kMaxBufferedSamples) {
buffered_samples_.emplace_back(
BufferedSample(sample_timestamp, std::move(frames)));
}
return;
}
if (!buffered_samples_.empty()) {
for (const auto& sample : buffered_samples_) {
WriteSampleToTrace(sample);
}
buffered_samples_.clear();
}
WriteSampleToTrace(BufferedSample(sample_timestamp, std::move(frames)));
if (sample_callback_for_testing_) {
sample_callback_for_testing_.Run();
}
}
void TracingSamplerProfiler::TracingProfileBuilder::WriteSampleToTrace(
const TracingSamplerProfiler::TracingProfileBuilder::BufferedSample&
sample) {
const 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;
}
if (reset_incremental_state_) {
stack_profile_writer_.ResetEmittedState();
auto trace_packet = trace_writer_->NewTracePacket();
trace_packet->set_sequence_flags(
perfetto::protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);
// Note: Make sure ThreadDescriptors we emit here won't cause
// metadata events to be emitted from the JSON exporter which conflict
// with the metadata events emitted by the regular TrackEventDataSource.
auto* thread_descriptor = trace_packet->set_thread_descriptor();
thread_descriptor->set_pid(base::GetCurrentProcId());
thread_descriptor->set_tid(sampled_thread_id_);
last_timestamp_ = sample.timestamp;
thread_descriptor->set_reference_timestamp_us(
last_timestamp_.since_origin().InMicroseconds());
reset_incremental_state_ = false;
}
auto trace_packet = trace_writer_->NewTracePacket();
// Delta encoded timestamps and interned data require incremental state.
trace_packet->set_sequence_flags(
perfetto::protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
auto callstack_id =
stack_profile_writer_.GetCallstackIDAndMaybeEmit(frames, &trace_packet);
auto* streaming_profile_packet = trace_packet->set_streaming_profile_packet();
streaming_profile_packet->add_callstack_iid(callstack_id);
int32_t current_process_priority = base::Process::Current().GetPriority();
if (current_process_priority != 0) {
streaming_profile_packet->set_process_priority(current_process_priority);
}
streaming_profile_packet->add_timestamp_delta_us(
(sample.timestamp - last_timestamp_).InMicroseconds());
last_timestamp_ = sample.timestamp;
}
void TracingSamplerProfiler::TracingProfileBuilder::SetTraceWriter(
std::unique_ptr<perfetto::TraceWriter> writer) {
base::AutoLock l(trace_writer_lock_);
trace_writer_ = std::move(writer);
}
TracingSamplerProfiler::StackProfileWriter::StackProfileWriter(
bool enable_filtering)
: should_enable_filtering_(enable_filtering) {}
TracingSamplerProfiler::StackProfileWriter::~StackProfileWriter() = default;
InterningID
TracingSamplerProfiler::StackProfileWriter::GetCallstackIDAndMaybeEmit(
const std::vector<base::Frame>& frames,
perfetto::TraceWriter::TracePacketHandle* trace_packet) {
size_t ip_hash = 0;
for (const auto& frame : frames) {
ip_hash = base::HashInts(ip_hash, frame.instruction_pointer);
}
InterningIndexEntry interned_callstack =
interned_callstacks_.LookupOrAdd(ip_hash);
if (interned_callstack.was_emitted)
return interned_callstack.id;
auto* interned_data = (*trace_packet)->set_interned_data();
std::vector<InterningID> frame_ids;
for (const auto& frame : frames) {
FrameDetails frame_details;
if (frame.module) {
frame_details.SetModule(*frame.module);
frame_details.rel_pc =
frame.instruction_pointer - frame_details.module_base_address;
}
#if ANDROID_ARM64_UNWINDING_SUPPORTED || ANDROID_CFI_UNWINDING_SUPPORTED
if (is_chrome_address(frame.instruction_pointer)) {
frame_details.rel_pc =
frame.instruction_pointer - executable_start_addr();
if (!frame_details.has_valid_module()) {
frame_details.SetChromeModuleInfo();
}
} else if (frame.instruction_pointer == 0) {
// TODO(ssid): This frame is currently skipped from inserting. Find a way
// to specify that this frame is scanned in the trace.
frame_details.frame_name = "Scanned";
} else if (!frame_details.has_valid_module()) {
frame_details.SetSystemModuleInfo(frame.instruction_pointer);
}
#endif // !(ANDROID_ARM64_UNWINDING_SUPPORTED ||
// ANDROID_CFI_UNWINDING_SUPPORTED)
// If we do not have a valid module and a valid frame, add a frame with
// dummy details. Adding invalid frame would make trace processor invalidate
// the whole sample.
if (!frame_details.has_valid_module() || !frame_details.has_valid_frame()) {
frame_details.FillWithDummyFields(frame.instruction_pointer);
}
MangleModuleIDIfNeeded(&frame_details.module_id);
// We never emit frame names in privacy filtered mode.
bool should_emit_frame_names =
!frame_details.frame_name.empty() && !should_enable_filtering_;
InterningIndexEntry interned_frame;
if (should_emit_frame_names) {
interned_frame = interned_frames_.LookupOrAdd(
std::make_pair(frame_details.frame_name, frame_details.module_id));
} else {
interned_frame = interned_frames_.LookupOrAdd(
std::make_pair(frame_details.rel_pc, frame_details.module_id));
}
if (!interned_frame.was_emitted) {
InterningIndexEntry interned_frame_name;
if (should_emit_frame_names) {
interned_frame_name =
interned_frame_names_.LookupOrAdd(frame_details.frame_name);
if (!interned_frame_name.was_emitted) {
auto* frame_name_entry = interned_data->add_function_names();
frame_name_entry->set_iid(interned_frame_name.id);
frame_name_entry->set_str(
reinterpret_cast<const uint8_t*>(frame_details.frame_name.data()),
frame_details.frame_name.length());
}
}
InterningIndexEntry interned_module;
if (frame_details.has_valid_module()) {
interned_module =
interned_modules_.LookupOrAdd(frame_details.module_base_address);
if (!interned_module.was_emitted) {
InterningIndexEntry interned_module_id =
interned_module_ids_.LookupOrAdd(frame_details.module_id);
if (!interned_module_id.was_emitted) {
auto* module_id_entry = interned_data->add_build_ids();
module_id_entry->set_iid(interned_module_id.id);
module_id_entry->set_str(reinterpret_cast<const uint8_t*>(
frame_details.module_id.data()),
frame_details.module_id.length());
}
InterningIndexEntry interned_module_name =
interned_module_names_.LookupOrAdd(frame_details.module_name);
if (!interned_module_name.was_emitted) {
auto* module_name_entry = interned_data->add_mapping_paths();
module_name_entry->set_iid(interned_module_name.id);
module_name_entry->set_str(reinterpret_cast<const uint8_t*>(
frame_details.module_name.data()),
frame_details.module_name.length());
}
auto* module_entry = interned_data->add_mappings();
module_entry->set_iid(interned_module.id);
module_entry->set_build_id(interned_module_id.id);
module_entry->add_path_string_ids(interned_module_name.id);
}
}
auto* frame_entry = interned_data->add_frames();
frame_entry->set_iid(interned_frame.id);
if (should_emit_frame_names) {
frame_entry->set_function_name_id(interned_frame_name.id);
} else {
frame_entry->set_rel_pc(frame_details.rel_pc);
}
if (frame_details.has_valid_module()) {
frame_entry->set_mapping_id(interned_module.id);
}
}
frame_ids.push_back(interned_frame.id);
}
auto* callstack_entry = interned_data->add_callstacks();
callstack_entry->set_iid(interned_callstack.id);
for (auto& frame_id : frame_ids)
callstack_entry->add_frame_ids(frame_id);
return interned_callstack.id;
}
void TracingSamplerProfiler::StackProfileWriter::ResetEmittedState() {
interned_callstacks_.ResetEmittedState();
interned_frames_.ResetEmittedState();
interned_frame_names_.ResetEmittedState();
interned_module_names_.ResetEmittedState();
interned_module_ids_.ResetEmittedState();
interned_modules_.ResetEmittedState();
}
// static
void TracingSamplerProfiler::MangleModuleIDIfNeeded(std::string* module_id) {
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
// Linux ELF module IDs are 160bit integers, which we need to mangle
// down to 128bit integers to match the id that Breakpad outputs.
// Example on version '66.0.3359.170' x64:
// Build-ID: "7f0715c2 86f8 b16c 10e4ad349cda3b9b 56c7a773
// Debug-ID "C215077F F886 6CB1 10E4AD349CDA3B9B 0"
if (module_id->size() < 32) {
module_id->resize(32, '0');
}
*module_id =
base::StrCat({module_id->substr(6, 2), module_id->substr(4, 2),
module_id->substr(2, 2), module_id->substr(0, 2),
module_id->substr(10, 2), module_id->substr(8, 2),
module_id->substr(14, 2), module_id->substr(12, 2),
module_id->substr(16, 16), "0"});
#endif
}
// static
std::unique_ptr<TracingSamplerProfiler>
TracingSamplerProfiler::CreateOnMainThread() {
auto profiler = std::make_unique<TracingSamplerProfiler>(
base::GetSamplingProfilerCurrentThreadToken());
// If running in single process mode, there may be multiple "main thread"
// profilers created. In this case, we assume the first created one is the
// browser one.
if (!g_main_thread_instance) {
g_main_thread_instance = profiler.get();
#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
// The loader lock is process-wide so should only be sampled on a single
// thread. So only one TracingSamplerProfiler should create a
// LoaderLockSamplingThread.
profiler->loader_lock_sampling_thread_ =
std::make_unique<LoaderLockSamplingThread>();
#endif
}
return profiler;
}
// static
void TracingSamplerProfiler::CreateOnChildThread() {
base::SequenceLocalStorageSlot<TracingSamplerProfiler>& slot =
GetSequenceLocalStorageProfilerSlot();
if (slot)
return;
slot.emplace(base::GetSamplingProfilerCurrentThreadToken());
}
// static
void TracingSamplerProfiler::DeleteOnChildThreadForTesting() {
GetSequenceLocalStorageProfilerSlot().reset();
}
// static
void TracingSamplerProfiler::RegisterDataSource() {
PerfettoTracedProcess::Get()->AddDataSource(
TracingSamplerProfilerDataSource::Get());
}
void TracingSamplerProfiler::SetAuxUnwinderFactoryOnMainThread(
const base::RepeatingCallback<std::unique_ptr<base::Unwinder>()>& factory) {
DCHECK(g_main_thread_instance);
g_main_thread_instance->SetAuxUnwinderFactory(factory);
}
// static
void TracingSamplerProfiler::StartTracingForTesting(
PerfettoProducer* producer) {
TracingSamplerProfilerDataSource::Get()->StartTracing(
1, producer, 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(
/*producer=*/nullptr, config, /*privacy_filtering_enabled=*/false);
}
// static
void TracingSamplerProfiler::StopTracingForTesting() {
TracingSamplerProfilerDataSource::Get()->StopTracing(base::DoNothing());
}
TracingSamplerProfiler::TracingSamplerProfiler(
base::SamplingProfilerThreadToken sampled_thread_token)
: sampled_thread_token_(sampled_thread_token) {
DCHECK_NE(sampled_thread_token_.id, base::kInvalidThreadId);
TracingSamplerProfilerDataSource::Get()->RegisterProfiler(this);
}
TracingSamplerProfiler::~TracingSamplerProfiler() {
TracingSamplerProfilerDataSource::Get()->UnregisterProfiler(this);
if (g_main_thread_instance == this)
g_main_thread_instance = nullptr;
}
void TracingSamplerProfiler::SetAuxUnwinderFactory(
const base::RepeatingCallback<std::unique_ptr<base::Unwinder>()>& factory) {
base::AutoLock lock(lock_);
aux_unwinder_factory_ = factory;
if (profiler_)
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::TraceWriter> trace_writer,
bool should_enable_filtering) {
base::AutoLock lock(lock_);
if (profiler_) {
if (trace_writer) {
profile_builder_->SetTraceWriter(std::move(trace_writer));
}
return;
}
#if ANDROID_ARM64_UNWINDING_SUPPORTED || ANDROID_CFI_UNWINDING_SUPPORTED
// The sampler profiler would conflict with the reached code profiler if they
// run at the same time because they use the same signal to suspend threads.
if (base::android::IsReachedCodeProfilerEnabled())
return;
#else // ANDROID_ARM64_UNWINDING_SUPPORTED || ANDROID_CFI_UNWINDING_SUPPORTED
// On Android the sampling profiler is implemented by tracing service and is
// not yet supported by base::StackSamplingProfiler. So, only check this if
// service does not support unwinding in current platform.
if (!base::StackSamplingProfiler::IsSupportedForCurrentPlatform())
return;
#endif // !(ANDROID_ARM64_UNWINDING_SUPPORTED ||
// ANDROID_CFI_UNWINDING_SUPPORTED)
base::StackSamplingProfiler::SamplingParams params;
params.samples_per_profile = std::numeric_limits<int>::max();
params.sampling_interval = base::Milliseconds(50);
auto profile_builder = std::make_unique<TracingProfileBuilder>(
sampled_thread_token_.id, std::move(trace_writer),
should_enable_filtering, sample_callback_for_testing_);
profile_builder_ = profile_builder.get();
// Create and start the stack sampling profiler.
#if BUILDFLAG(IS_ANDROID)
#if ANDROID_ARM64_UNWINDING_SUPPORTED
const auto create_unwinders = []() {
std::vector<std::unique_ptr<base::Unwinder>> unwinders;
unwinders.push_back(std::make_unique<UnwinderArm64>());
return unwinders;
};
profiler_ = std::make_unique<base::StackSamplingProfiler>(
sampled_thread_token_, params, std::move(profile_builder),
base::BindOnce(create_unwinders));
profiler_->Start();
#elif ANDROID_CFI_UNWINDING_SUPPORTED
auto* module_cache = profile_builder->GetModuleCache();
profiler_ = std::make_unique<base::StackSamplingProfiler>(
params, std::move(profile_builder),
std::make_unique<StackSamplerAndroid>(sampled_thread_token_,
module_cache));
profiler_->Start();
#endif
#else // BUILDFLAG(IS_ANDROID)
profiler_ = std::make_unique<base::StackSamplingProfiler>(
sampled_thread_token_, params, std::move(profile_builder));
if (aux_unwinder_factory_)
profiler_->AddAuxUnwinder(aux_unwinder_factory_.Run());
profiler_->Start();
#endif // BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
if (loader_lock_sampling_thread_)
loader_lock_sampling_thread_->StartSampling();
#endif
}
void TracingSamplerProfiler::StopTracing() {
base::AutoLock lock(lock_);
if (!profiler_) {
return;
}
// Stop and release the stack sampling profiler.
profiler_->Stop();
profile_builder_ = nullptr;
profiler_.reset();
#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
if (loader_lock_sampling_thread_)
loader_lock_sampling_thread_->StopSampling();
#endif
}
} // namespace tracing