#include "base/profiler/stack_sampler.h"
#include <iterator>
#include <utility>
#include "base/check.h"
#include "base/compiler_specific.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/numerics/safe_conversions.h"
#include "base/profiler/metadata_recorder.h"
#include "base/profiler/profile_builder.h"
#include "base/profiler/sample_metadata.h"
#include "base/profiler/stack_buffer.h"
#include "base/profiler/stack_copier.h"
#include "base/profiler/suspendable_thread_delegate.h"
#include "base/profiler/unwinder.h"
#include "base/ranges/algorithm.h"
namespace base {
namespace {
class StackCopierDelegate : public StackCopier::Delegate {
public:
StackCopierDelegate(
const base::circular_deque<std::unique_ptr<Unwinder>>* unwinders,
ProfileBuilder* profile_builder,
MetadataRecorder::MetadataProvider* metadata_provider)
: unwinders_(unwinders),
profile_builder_(profile_builder),
metadata_provider_(metadata_provider) {}
StackCopierDelegate(const StackCopierDelegate&) = delete;
StackCopierDelegate& operator=(const StackCopierDelegate&) = delete;
void OnStackCopy() override {
for (const auto& unwinder : *unwinders_)
unwinder->OnStackCapture();
profile_builder_->RecordMetadata(*metadata_provider_);
}
private:
raw_ptr<const base::circular_deque<std::unique_ptr<Unwinder>>> unwinders_;
const raw_ptr<ProfileBuilder> profile_builder_;
const raw_ptr<const MetadataRecorder::MetadataProvider> metadata_provider_;
};
}
StackSampler::~StackSampler() = default;
std::unique_ptr<StackBuffer> StackSampler::CreateStackBuffer() {
size_t size = GetStackBufferSize();
if (size == 0)
return nullptr;
return std::make_unique<StackBuffer>(size);
}
void StackSampler::Initialize() {
std::vector<std::unique_ptr<Unwinder>> unwinders =
std::move(unwinders_factory_).Run();
unwinders_.insert(unwinders_.end(),
std::make_move_iterator(unwinders.rbegin()),
std::make_move_iterator(unwinders.rend()));
for (const auto& unwinder : unwinders_)
unwinder->Initialize(module_cache_);
was_initialized_ = true;
}
void StackSampler::AddAuxUnwinder(std::unique_ptr<Unwinder> unwinder) {
if (was_initialized_)
unwinder->Initialize(module_cache_);
unwinders_.push_front(std::move(unwinder));
}
void StackSampler::RecordStackFrames(StackBuffer* stack_buffer,
ProfileBuilder* profile_builder,
PlatformThreadId thread_id) {
DCHECK(stack_buffer);
if (record_sample_callback_)
record_sample_callback_.Run();
RegisterContext thread_context;
uintptr_t stack_top;
TimeTicks timestamp;
bool copy_stack_succeeded;
{
MetadataRecorder::MetadataProvider metadata_provider(
GetSampleMetadataRecorder(), thread_id);
StackCopierDelegate delegate(&unwinders_, profile_builder,
&metadata_provider);
copy_stack_succeeded = stack_copier_->CopyStack(
stack_buffer, &stack_top, ×tamp, &thread_context, &delegate);
}
if (!copy_stack_succeeded) {
profile_builder->OnSampleCompleted(
{}, timestamp.is_null() ? TimeTicks::Now() : timestamp);
return;
}
for (const auto& unwinder : unwinders_)
unwinder->UpdateModules();
if (test_delegate_)
test_delegate_->OnPreStackWalk();
profile_builder->OnSampleCompleted(
WalkStack(module_cache_, &thread_context, stack_top, unwinders_),
timestamp);
#if BUILDFLAG(IS_CHROMEOS)
ptrdiff_t stack_size = reinterpret_cast<uint8_t*>(stack_top) -
reinterpret_cast<uint8_t*>(stack_buffer->buffer());
constexpr int kBytesPerKilobyte = 1024;
if ((++stack_size_histogram_sampling_counter_ %
kUMAHistogramDownsampleAmount) == 0) {
UmaHistogramCustomCounts(
"Memory.StackSamplingProfiler.StackSampleSize2",
saturated_cast<int>(stack_size / kBytesPerKilobyte), 4, 8 * 1024, 50);
}
constexpr ptrdiff_t kLargeStackSize = 512 * kBytesPerKilobyte;
if (stack_size > kLargeStackSize) {
stack_buffer->MarkUpperBufferContentsAsUnneeded(kLargeStackSize);
}
#endif
}
std::vector<Frame> StackSampler::WalkStackForTesting(
ModuleCache* module_cache,
RegisterContext* thread_context,
uintptr_t stack_top,
const base::circular_deque<std::unique_ptr<Unwinder>>& unwinders) {
return WalkStack(module_cache, thread_context, stack_top, unwinders);
}
std::unique_ptr<StackSampler> StackSampler::CreateForTesting(
std::unique_ptr<StackCopier> stack_copier,
UnwindersFactory core_unwinders_factory,
ModuleCache* module_cache,
RepeatingClosure record_sample_callback,
StackSamplerTestDelegate* test_delegate) {
return base::WrapUnique(new StackSampler(
std::move(stack_copier), std::move(core_unwinders_factory), module_cache,
record_sample_callback, test_delegate));
}
StackSampler::StackSampler(std::unique_ptr<StackCopier> stack_copier,
UnwindersFactory core_unwinders_factory,
ModuleCache* module_cache,
RepeatingClosure record_sample_callback,
StackSamplerTestDelegate* test_delegate)
: stack_copier_(std::move(stack_copier)),
unwinders_factory_(std::move(core_unwinders_factory)),
module_cache_(module_cache),
record_sample_callback_(std::move(record_sample_callback)),
test_delegate_(test_delegate) {
DCHECK(unwinders_factory_);
}
std::vector<Frame> StackSampler::WalkStack(
ModuleCache* module_cache,
RegisterContext* thread_context,
uintptr_t stack_top,
const base::circular_deque<std::unique_ptr<Unwinder>>& unwinders) {
std::vector<Frame> stack;
stack.reserve(128);
stack.emplace_back(RegisterContextInstructionPointer(thread_context),
module_cache->GetModuleForAddress(
RegisterContextInstructionPointer(thread_context)));
size_t prior_stack_size;
UnwindResult result;
do {
auto unwinder = ranges::find_if(
unwinders, [&stack](const std::unique_ptr<Unwinder>& unwinder) {
return unwinder->CanUnwindFrom(stack.back());
});
if (unwinder == unwinders.end())
return stack;
prior_stack_size = stack.size();
result = unwinder->get()->TryUnwind(thread_context, stack_top, &stack);
DCHECK(result != UnwindResult::kCompleted ||
unwinder->get() == unwinders.back().get());
} while (result != UnwindResult::kAborted &&
result != UnwindResult::kCompleted &&
stack.size() > prior_stack_size);
return stack;
}
StackSamplerTestDelegate::~StackSamplerTestDelegate() = default;
StackSamplerTestDelegate::StackSamplerTestDelegate() = default;
}