blob: 67b1bdd518d115d34888f4c37e559429069473e6 [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/bind_helpers.h"
#include "base/debug/leak_annotations.h"
#include "base/hash/hash.h"
#include "base/memory/ptr_util.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/threading/sequence_local_storage_slot.h"
#include "base/trace_event/trace_event.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 defined(OS_ANDROID)
#include "base/android/reached_code_profiler.h"
#endif
#if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
defined(OFFICIAL_BUILD)
#include <dlfcn.h>
#include "base/trace_event/cfi_backtrace_android.h"
#include "services/tracing/public/cpp/stack_sampling/stack_sampler_android.h"
#endif
#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
#include "base/threading/platform_thread.h"
#include "services/tracing/public/cpp/stack_sampling/loader_lock_sampler_win.h"
#endif
using StreamingProfilePacketHandle =
protozero::MessageHandle<perfetto::protos::pbzero::StreamingProfilePacket>;
namespace tracing {
namespace {
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 StartTracing(
PerfettoProducer* producer,
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 (auto* profiler : profilers_) {
profiler->StartTracing(
producer->CreateTraceWriter(data_source_config.target_buffer()),
should_enable_filtering);
}
}
void StopTracing(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:
base::Lock lock_; // Protects subsequent members.
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::NoDestructor<
base::SequenceLocalStorageSlot<TracingSamplerProfiler>>
storage;
return *storage;
}
#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
// The loader lock is process-wide so should only be sampled on a single
// thread.
base::PlatformThreadId g_loader_lock_sample_thread_id = base::kInvalidThreadId;
bool g_loader_lock_is_held = false;
TracingSamplerProfiler::LoaderLockSampler* g_test_loader_lock_sampler = nullptr;
void InitializeLoaderLockSamplingForThread(base::PlatformThreadId thread_id) {
InitializeLoaderLockSampling();
g_loader_lock_sample_thread_id = thread_id;
// InitializeLoaderLockSamplingForThread can be called multiple times during
// tests. Make sure each test starts from a known state.
g_loader_lock_is_held = false;
}
// Checks the state of the loader lock while samples are being taken for thread
// |thread_id|. This function should only be called from the profiler thread
// since it updates global data.
void SampleLoaderLockForThread(base::PlatformThreadId thread_id) {
if (thread_id != g_loader_lock_sample_thread_id)
return;
bool loader_lock_now_held =
g_test_loader_lock_sampler
? g_test_loader_lock_sampler->IsLoaderLockHeld()
: IsLoaderLockHeld();
// TODO(crbug.com/1065077): It would be cleaner to save the loader lock state
// alongside buffered_samples_ and then add it to the ProcessDescriptor
// packet in
// TracingSamplerProfiler::TracingProfileBuilder::WriteSampleToTrace. But
// ProcessDescriptor is currently not being collected correctly. See the full
// discussion in the linked crbug.
if (loader_lock_now_held && !g_loader_lock_is_held) {
TRACE_EVENT_ASYNC_BEGIN0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
TracingSamplerProfiler::kLoaderLockHeldEventName,
thread_id);
} else if (!loader_lock_now_held && g_loader_lock_is_held) {
TRACE_EVENT_ASYNC_END0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler"),
TracingSamplerProfiler::kLoaderLockHeldEventName,
thread_id);
}
g_loader_lock_is_held = loader_lock_now_held;
}
#endif // BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
} // namespace
#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
const char TracingSamplerProfiler::kLoaderLockHeldEventName[] =
"LoaderLockHeld (sampled)";
#endif
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)),
should_enable_filtering_(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_;
}
void TracingSamplerProfiler::TracingProfileBuilder::OnSampleCompleted(
std::vector<base::Frame> frames,
base::TimeTicks sample_timestamp) {
#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
SampleLoaderLockForThread(sampled_thread_id_);
#endif
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_) {
interned_callstacks_.ResetEmittedState();
interned_frames_.ResetEmittedState();
interned_frame_names_.ResetEmittedState();
interned_module_names_.ResetEmittedState();
interned_module_ids_.ResetEmittedState();
interned_modules_.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 = 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);
}
InterningID
TracingSamplerProfiler::TracingProfileBuilder::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) {
std::string frame_name;
std::string module_name;
std::string module_id;
uintptr_t rel_pc = 0;
#if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
defined(OFFICIAL_BUILD)
Dl_info info = {};
// For chrome address we do not have symbols on the binary. So, just write
// the offset address. For addresses on framework libraries, symbolize
// and write the function name.
if (frame.instruction_pointer == 0) {
frame_name = "Scanned";
} else if (base::trace_event::CFIBacktraceAndroid::is_chrome_address(
frame.instruction_pointer)) {
rel_pc = frame.instruction_pointer -
base::trace_event::CFIBacktraceAndroid::executable_start_addr();
} else if (dladdr(reinterpret_cast<void*>(frame.instruction_pointer),
&info) != 0) {
// TODO(ssid): Add offset and module debug id if symbol was not resolved
// in case this might be useful to send report to vendors.
if (info.dli_sname)
frame_name = info.dli_sname;
if (info.dli_fname)
module_name = info.dli_fname;
}
if (frame.module) {
module_id = frame.module->GetId();
if (module_name.empty())
module_name = frame.module->GetDebugBasename().MaybeAsASCII();
}
// If no module is available, then name it unknown. Adding PC would be
// useless anyway.
if (module_name.empty()) {
DCHECK(!base::trace_event::CFIBacktraceAndroid::is_chrome_address(
frame.instruction_pointer));
frame_name = "Unknown";
rel_pc = 0;
}
#else
if (frame.module) {
module_name = frame.module->GetDebugBasename().MaybeAsASCII();
module_id = frame.module->GetId();
rel_pc = frame.instruction_pointer - frame.module->GetBaseAddress();
} else {
module_name = module_id = "";
frame_name = "Unknown";
}
#endif
MangleModuleIDIfNeeded(&module_id);
// We never emit frame names in privacy filtered mode.
bool should_emit_frame_names =
!frame_name.empty() && !should_enable_filtering_;
if (should_enable_filtering_ && !rel_pc && frame.module) {
rel_pc = frame.instruction_pointer - frame.module->GetBaseAddress();
}
InterningIndexEntry interned_frame;
if (should_emit_frame_names) {
interned_frame =
interned_frames_.LookupOrAdd(std::make_pair(frame_name, module_id));
} else {
interned_frame =
interned_frames_.LookupOrAdd(std::make_pair(rel_pc, module_id));
}
if (!interned_frame.was_emitted) {
InterningIndexEntry interned_frame_name;
if (should_emit_frame_names) {
interned_frame_name = interned_frame_names_.LookupOrAdd(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_name.data()),
frame_name.length());
}
}
InterningIndexEntry interned_module;
if (frame.module) {
interned_module =
interned_modules_.LookupOrAdd(frame.module->GetBaseAddress());
if (!interned_module.was_emitted) {
InterningIndexEntry interned_module_id =
interned_module_ids_.LookupOrAdd(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*>(module_id.data()),
module_id.length());
}
InterningIndexEntry interned_module_name =
interned_module_names_.LookupOrAdd(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*>(module_name.data()),
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(rel_pc);
}
if (frame.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;
}
// static
void TracingSamplerProfiler::MangleModuleIDIfNeeded(std::string* module_id) {
#if defined(OS_ANDROID) || defined(OS_LINUX)
// 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 =
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 thread_token = base::GetSamplingProfilerCurrentThreadToken();
#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
InitializeLoaderLockSamplingForThread(thread_token.id);
#endif
return std::make_unique<TracingSamplerProfiler>(std::move(thread_token));
}
// 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());
}
// static
void TracingSamplerProfiler::StartTracingForTesting(
PerfettoProducer* producer) {
TracingSamplerProfilerDataSource::Get()->StartTracingWithID(
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());
}
#if BUILDFLAG(ENABLE_LOADER_LOCK_SAMPLING)
// static
void TracingSamplerProfiler::SetLoaderLockSamplerForTesting(
LoaderLockSampler* sampler) {
g_test_loader_lock_sampler = sampler;
}
#endif
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);
}
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 defined(OS_ANDROID)
// 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;
#endif
base::StackSamplingProfiler::SamplingParams params;
params.samples_per_profile = std::numeric_limits<int>::max();
params.sampling_interval = base::TimeDelta::FromMilliseconds(50);
// If the sampled thread is stopped for too long for sampling then it is ok to
// get next sample at a later point of time. We do not want very accurate
// metrics when looking at traces.
params.keep_consistent_sampling_interval = false;
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 defined(OS_ANDROID)
#if BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && defined(OFFICIAL_BUILD)
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 // BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && defined(OFFICIAL_BUILD)
#else // defined(OS_ANDROID)
profiler_ = std::make_unique<base::StackSamplingProfiler>(
sampled_thread_token_, params, std::move(profile_builder));
profiler_->Start();
#endif // defined(OS_ANDROID)
}
void TracingSamplerProfiler::StopTracing() {
base::AutoLock lock(lock_);
if (!profiler_) {
return;
}
// Stop and release the stack sampling profiler.
profiler_->Stop();
profile_builder_ = nullptr;
profiler_.reset();
}
} // namespace tracing