blob: 3c643b0eace5b5be811d0060264d77b09386df5e [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 "base/sampling_heap_profiler/sampling_heap_profiler.h"
#include <algorithm>
#include <cmath>
#include <utility>
#include "base/allocator/allocator_shim.h"
#include "base/allocator/buildflags.h"
#include "base/allocator/partition_allocator/partition_alloc.h"
#include "base/atomicops.h"
#include "base/debug/stack_trace.h"
#include "base/macros.h"
#include "base/no_destructor.h"
#include "base/partition_alloc_buildflags.h"
#include "base/sampling_heap_profiler/lock_free_address_hash_set.h"
#include "base/threading/thread_local_storage.h"
#include "build/build_config.h"
#if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
defined(OFFICIAL_BUILD)
#include "base/trace_event/cfi_backtrace_android.h"
#endif
namespace base {
SamplingHeapProfiler::Sample::Sample(size_t size,
size_t total,
uint32_t ordinal)
: size(size), total(total), ordinal(ordinal) {}
SamplingHeapProfiler::Sample::Sample(const Sample&) = default;
SamplingHeapProfiler::Sample::~Sample() = default;
SamplingHeapProfiler::SamplingHeapProfiler() = default;
SamplingHeapProfiler::~SamplingHeapProfiler() = default;
uint32_t SamplingHeapProfiler::Start() {
#if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
defined(OFFICIAL_BUILD)
if (!trace_event::CFIBacktraceAndroid::GetInitializedInstance()
->can_unwind_stack_frames()) {
LOG(WARNING) << "Sampling heap profiler: Stack unwinding is not available.";
return 0;
}
#endif
auto* sampler = PoissonAllocationSampler::Get();
sampler->AddSamplesObserver(this);
sampler->Start();
return last_sample_ordinal_;
}
void SamplingHeapProfiler::Stop() {
auto* sampler = PoissonAllocationSampler::Get();
sampler->Stop();
sampler->RemoveSamplesObserver(this);
}
void SamplingHeapProfiler::SetSamplingInterval(size_t sampling_interval) {
PoissonAllocationSampler::Get()->SetSamplingInterval(sampling_interval);
}
namespace {
void RecordStackTrace(SamplingHeapProfiler::Sample* sample) {
#if !defined(OS_NACL)
constexpr uint32_t kMaxStackEntries = 256;
constexpr uint32_t kSkipProfilerOwnFrames = 2;
uint32_t skip_frames = kSkipProfilerOwnFrames;
#if defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
defined(OFFICIAL_BUILD)
const void* frames[kMaxStackEntries];
size_t frame_count =
trace_event::CFIBacktraceAndroid::GetInitializedInstance()->Unwind(
frames, kMaxStackEntries);
#elif BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
const void* frames[kMaxStackEntries];
size_t frame_count =
debug::TraceStackFramePointers(frames, kMaxStackEntries, skip_frames);
skip_frames = 0;
#else
// Fall-back to capturing the stack with debug::StackTrace,
// which is likely slower, but more reliable.
debug::StackTrace stack_trace(kMaxStackEntries);
size_t frame_count = 0;
const void* const* frames = stack_trace.Addresses(&frame_count);
#endif
sample->stack.insert(
sample->stack.end(), const_cast<void**>(&frames[skip_frames]),
const_cast<void**>(&frames[std::max<size_t>(frame_count, skip_frames)]));
#endif
}
} // namespace
void SamplingHeapProfiler::SampleAdded(void* address,
size_t size,
size_t total,
PoissonAllocationSampler::AllocatorType,
const char*) {
AutoLock lock(mutex_);
Sample sample(size, total, ++last_sample_ordinal_);
RecordStackTrace(&sample);
samples_.emplace(address, std::move(sample));
}
void SamplingHeapProfiler::SampleRemoved(void* address) {
AutoLock lock(mutex_);
auto it = samples_.find(address);
if (it != samples_.end())
samples_.erase(it);
}
std::vector<SamplingHeapProfiler::Sample> SamplingHeapProfiler::GetSamples(
uint32_t profile_id) {
// Make sure the sampler does not invoke |SampleAdded| or |SampleRemoved|
// on this thread. Otherwise it could have end up with a deadlock.
// See crbug.com/882495
PoissonAllocationSampler::ScopedMuteThreadSamples no_samples_scope;
AutoLock lock(mutex_);
std::vector<Sample> samples;
samples.reserve(samples_.size());
for (auto& it : samples_) {
Sample& sample = it.second;
if (sample.ordinal > profile_id)
samples.push_back(sample);
}
return samples;
}
// static
void SamplingHeapProfiler::Init() {
PoissonAllocationSampler::Init();
}
// static
SamplingHeapProfiler* SamplingHeapProfiler::Get() {
static NoDestructor<SamplingHeapProfiler> instance;
return instance.get();
}
} // namespace base