Commit 7a2ff0d9 authored by Etienne Pierre-doray's avatar Etienne Pierre-doray Committed by Commit Bot

[Clank SSM]: StackSampler construction takes std::vector of unwinders.

To allow construction with multiple unwinder. This is necessary on android
to provide native and chrome unwinders and will be used in a follow in
stack_sampling_profiler_unittest.cc
https://chromium-review.googlesource.com/c/chromium/src/+/2201658

Bug: 989102
Change-Id: I05ef97403668d82dc7e6e4e9505be95e941174dd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2211862
Commit-Queue: Etienne Pierre-Doray <etiennep@chromium.org>
Reviewed-by: default avatarMike Wittman <wittman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#773353}
parent 427a928a
......@@ -6,6 +6,7 @@
#define BASE_PROFILER_STACK_SAMPLER_H_
#include <memory>
#include <vector>
#include "base/base_export.h"
#include "base/macros.h"
......@@ -28,12 +29,14 @@ class BASE_EXPORT StackSampler {
virtual ~StackSampler();
// Creates a stack sampler that records samples for thread with
// |thread_token|. Returns null if this platform does not support stack
// sampling.
// |thread_token|. Unwinders in |unwinders| must be stored in increasing
// priority to guide unwind attempts. Only the unwinder with the lowest
// priority is allowed to return with UnwindResult::COMPLETED. Returns null if
// this platform does not support stack sampling.
static std::unique_ptr<StackSampler> Create(
SamplingProfilerThreadToken thread_token,
ModuleCache* module_cache,
std::unique_ptr<Unwinder> native_unwinder,
std::vector<std::unique_ptr<Unwinder>> core_unwinders,
StackSamplerTestDelegate* test_delegate);
// Gets the required size of the stack buffer.
......@@ -47,9 +50,8 @@ class BASE_EXPORT StackSampler {
// thread being sampled).
// Adds an auxiliary unwinder to handle additional, non-native-code unwind
// scenarios. When attempting to unwind, the relative priority of auxiliary
// unwinders is the inverse of the order of insertion, and the native
// unwinder is given the lowest priority
// scenarios. Unwinders must be inserted in increasing priority, following
// |unwinders| provided in Create(), to guide unwind attempts.
virtual void AddAuxUnwinder(std::unique_ptr<Unwinder> unwinder) = 0;
// Records a set of frames and returns them.
......
......@@ -18,13 +18,15 @@ namespace base {
std::unique_ptr<StackSampler> StackSampler::Create(
SamplingProfilerThreadToken thread_token,
ModuleCache* module_cache,
std::unique_ptr<Unwinder> native_unwinder,
std::vector<std::unique_ptr<Unwinder>> core_unwinders,
StackSamplerTestDelegate* test_delegate) {
DCHECK(native_unwinder);
// |core_unwinders| must contain NativeUnwinderAndroid and
// ChromeUnwinderAndroid, respectively.
DCHECK_EQ(2U, core_unwinders.size());
return std::make_unique<StackSamplerImpl>(
std::make_unique<StackCopierSignal>(
std::make_unique<ThreadDelegatePosix>(thread_token)),
std::move(native_unwinder), module_cache, test_delegate);
std::move(core_unwinders), module_cache, test_delegate);
}
size_t StackSampler::GetStackBufferSize() {
......
......@@ -4,6 +4,7 @@
#include "base/profiler/stack_sampler_impl.h"
#include <iterator>
#include <utility>
#include "base/check.h"
......@@ -64,15 +65,20 @@ class StackCopierDelegate : public StackCopier::Delegate {
} // namespace
StackSamplerImpl::StackSamplerImpl(std::unique_ptr<StackCopier> stack_copier,
std::unique_ptr<Unwinder> native_unwinder,
ModuleCache* module_cache,
StackSamplerTestDelegate* test_delegate)
// |core_unwinders| is iterated backward since |core_unwinders| is passed in
// increasing priority order while |unwinders_| is stored in decreasing priority
// order.
StackSamplerImpl::StackSamplerImpl(
std::unique_ptr<StackCopier> stack_copier,
std::vector<std::unique_ptr<Unwinder>> core_unwinders,
ModuleCache* module_cache,
StackSamplerTestDelegate* test_delegate)
: stack_copier_(std::move(stack_copier)),
unwinders_(std::make_move_iterator(core_unwinders.rbegin()),
std::make_move_iterator(core_unwinders.rend())),
module_cache_(module_cache),
test_delegate_(test_delegate) {
DCHECK(native_unwinder);
unwinders_.push_front(std::move(native_unwinder));
DCHECK(!unwinders_.empty());
}
StackSamplerImpl::~StackSamplerImpl() = default;
......@@ -155,8 +161,8 @@ std::vector<Frame> StackSamplerImpl::WalkStack(
result = unwinder->get()->TryUnwind(thread_context, stack_top, module_cache,
&stack);
// The native unwinder should be the only one that returns COMPLETED
// since the stack starts in native code.
// The unwinder with the lowest priority should be the only one that returns
// COMPLETED since the stack starts in native code.
DCHECK(result != UnwindResult::COMPLETED ||
unwinder->get() == unwinders.back().get());
} while (result != UnwindResult::ABORTED &&
......
......@@ -23,7 +23,7 @@ class Unwinder;
class BASE_EXPORT StackSamplerImpl : public StackSampler {
public:
StackSamplerImpl(std::unique_ptr<StackCopier> stack_copier,
std::unique_ptr<Unwinder> native_unwinder,
std::vector<std::unique_ptr<Unwinder>> core_unwinders,
ModuleCache* module_cache,
StackSamplerTestDelegate* test_delegate = nullptr);
~StackSamplerImpl() override;
......
......@@ -268,14 +268,21 @@ class FakeTestUnwinder : public Unwinder {
std::vector<Result> results_;
};
base::circular_deque<std::unique_ptr<Unwinder>> MakeUnwinderList(
std::vector<std::unique_ptr<Unwinder>> MakeUnwinderVector(
std::unique_ptr<Unwinder> unwinder) {
std::vector<std::unique_ptr<Unwinder>> unwinders;
unwinders.push_back(std::move(unwinder));
return unwinders;
}
base::circular_deque<std::unique_ptr<Unwinder>> MakeUnwinderCircularDeque(
std::unique_ptr<Unwinder> native_unwinder,
std::unique_ptr<Unwinder> aux_unwinder) {
base::circular_deque<std::unique_ptr<Unwinder>> unwinders;
if (aux_unwinder)
unwinders.push_back(std::move(aux_unwinder));
if (native_unwinder)
unwinders.push_back(std::move(native_unwinder));
unwinders.push_front(std::move(native_unwinder));
if (aux_unwinder)
unwinders.push_front(std::move(aux_unwinder));
return unwinders;
}
......@@ -294,7 +301,9 @@ TEST(StackSamplerImplTest, MAYBE_CopyStack) {
std::vector<uintptr_t> stack_copy;
StackSamplerImpl stack_sampler_impl(
std::make_unique<TestStackCopier>(stack),
std::make_unique<TestUnwinder>(stack.size(), &stack_copy), &module_cache);
MakeUnwinderVector(
std::make_unique<TestUnwinder>(stack.size(), &stack_copy)),
&module_cache);
std::unique_ptr<StackBuffer> stack_buffer =
std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));
......@@ -312,7 +321,9 @@ TEST(StackSamplerImplTest, CopyStackTimestamp) {
TimeTicks timestamp = TimeTicks::UnixEpoch();
StackSamplerImpl stack_sampler_impl(
std::make_unique<TestStackCopier>(stack, timestamp),
std::make_unique<TestUnwinder>(stack.size(), &stack_copy), &module_cache);
MakeUnwinderVector(
std::make_unique<TestUnwinder>(stack.size(), &stack_copy)),
&module_cache);
std::unique_ptr<StackBuffer> stack_buffer =
std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));
......@@ -330,7 +341,7 @@ TEST(StackSamplerImplTest, UnwinderInvokedWhileRecordingStackFrames) {
TestProfileBuilder profile_builder(&module_cache);
StackSamplerImpl stack_sampler_impl(
std::make_unique<DelegateInvokingStackCopier>(),
std::move(owned_unwinder), &module_cache);
MakeUnwinderVector(std::move(owned_unwinder)), &module_cache);
stack_sampler_impl.RecordStackFrames(stack_buffer.get(), &profile_builder);
......@@ -344,7 +355,8 @@ TEST(StackSamplerImplTest, AuxUnwinderInvokedWhileRecordingStackFrames) {
TestProfileBuilder profile_builder(&module_cache);
StackSamplerImpl stack_sampler_impl(
std::make_unique<DelegateInvokingStackCopier>(),
std::make_unique<CallRecordingUnwinder>(), &module_cache);
MakeUnwinderVector(std::make_unique<CallRecordingUnwinder>()),
&module_cache);
auto owned_aux_unwinder = std::make_unique<CallRecordingUnwinder>();
CallRecordingUnwinder* aux_unwinder = owned_aux_unwinder.get();
......@@ -367,7 +379,7 @@ TEST(StackSamplerImplTest, WalkStack_Completed) {
std::vector<Frame> stack = StackSamplerImpl::WalkStackForTesting(
&module_cache, &thread_context, 0u,
MakeUnwinderList(std::move(native_unwinder), nullptr));
MakeUnwinderCircularDeque(std::move(native_unwinder), nullptr));
ASSERT_EQ(2u, stack.size());
EXPECT_EQ(1u, stack[1].instruction_pointer);
......@@ -384,7 +396,7 @@ TEST(StackSamplerImplTest, WalkStack_Aborted) {
std::vector<Frame> stack = StackSamplerImpl::WalkStackForTesting(
&module_cache, &thread_context, 0u,
MakeUnwinderList(std::move(native_unwinder), nullptr));
MakeUnwinderCircularDeque(std::move(native_unwinder), nullptr));
ASSERT_EQ(2u, stack.size());
EXPECT_EQ(1u, stack[1].instruction_pointer);
......@@ -400,7 +412,7 @@ TEST(StackSamplerImplTest, WalkStack_NotUnwound) {
std::vector<Frame> stack = StackSamplerImpl::WalkStackForTesting(
&module_cache, &thread_context, 0u,
MakeUnwinderList(std::move(native_unwinder), nullptr));
MakeUnwinderCircularDeque(std::move(native_unwinder), nullptr));
ASSERT_EQ(1u, stack.size());
}
......@@ -421,7 +433,7 @@ TEST(StackSamplerImplTest, WalkStack_AuxUnwind) {
WrapUnique(new FakeTestUnwinder({{UnwindResult::ABORTED, {1u}}}));
std::vector<Frame> stack = StackSamplerImpl::WalkStackForTesting(
&module_cache, &thread_context, 0u,
MakeUnwinderList(nullptr, std::move(aux_unwinder)));
MakeUnwinderCircularDeque(nullptr, std::move(aux_unwinder)));
ASSERT_EQ(2u, stack.size());
EXPECT_EQ(GetTestInstructionPointer(), stack[0].instruction_pointer);
......@@ -447,7 +459,8 @@ TEST(StackSamplerImplTest, WalkStack_AuxThenNative) {
std::vector<Frame> stack = StackSamplerImpl::WalkStackForTesting(
&module_cache, &thread_context, 0u,
MakeUnwinderList(std::move(native_unwinder), std::move(aux_unwinder)));
MakeUnwinderCircularDeque(std::move(native_unwinder),
std::move(aux_unwinder)));
ASSERT_EQ(3u, stack.size());
EXPECT_EQ(0u, stack[0].instruction_pointer);
......@@ -477,7 +490,8 @@ TEST(StackSamplerImplTest, WalkStack_NativeThenAux) {
std::vector<Frame> stack = StackSamplerImpl::WalkStackForTesting(
&module_cache, &thread_context, 0u,
MakeUnwinderList(std::move(native_unwinder), std::move(aux_unwinder)));
MakeUnwinderCircularDeque(std::move(native_unwinder),
std::move(aux_unwinder)));
ASSERT_EQ(4u, stack.size());
EXPECT_EQ(0u, stack[0].instruction_pointer);
......
......@@ -13,7 +13,7 @@ namespace base {
std::unique_ptr<StackSampler> StackSampler::Create(
SamplingProfilerThreadToken thread_token,
ModuleCache* module_cache,
std::unique_ptr<Unwinder> native_unwinder,
std::vector<std::unique_ptr<Unwinder>> core_unwinders,
StackSamplerTestDelegate* test_delegate) {
return nullptr;
}
......
......@@ -16,14 +16,14 @@ namespace base {
std::unique_ptr<StackSampler> StackSampler::Create(
SamplingProfilerThreadToken thread_token,
ModuleCache* module_cache,
std::unique_ptr<Unwinder> native_unwinder,
std::vector<std::unique_ptr<Unwinder>> core_unwinders,
StackSamplerTestDelegate* test_delegate) {
DCHECK(!native_unwinder);
DCHECK(core_unwinders.empty());
core_unwinders.push_back(std::make_unique<NativeUnwinderMac>(module_cache));
return std::make_unique<StackSamplerImpl>(
std::make_unique<StackCopierSuspend>(
std::make_unique<SuspendableThreadDelegateMac>(thread_token)),
std::make_unique<NativeUnwinderMac>(module_cache), module_cache,
test_delegate);
std::move(core_unwinders), module_cache, test_delegate);
}
// static
......
......@@ -14,7 +14,7 @@ namespace base {
std::unique_ptr<StackSampler> StackSampler::Create(
SamplingProfilerThreadToken thread_token,
ModuleCache* module_cache,
std::unique_ptr<Unwinder> native_unwinder,
std::vector<std::unique_ptr<Unwinder>> core_unwinders,
StackSamplerTestDelegate* test_delegate) {
return nullptr;
}
......
......@@ -17,14 +17,15 @@ namespace base {
std::unique_ptr<StackSampler> StackSampler::Create(
SamplingProfilerThreadToken thread_token,
ModuleCache* module_cache,
std::unique_ptr<Unwinder> native_unwinder,
std::vector<std::unique_ptr<Unwinder>> core_unwinders,
StackSamplerTestDelegate* test_delegate) {
DCHECK(!native_unwinder);
DCHECK(core_unwinders.empty());
#if defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_ARM64)
core_unwinders.push_back(std::make_unique<NativeUnwinderWin>());
return std::make_unique<StackSamplerImpl>(
std::make_unique<StackCopierSuspend>(
std::make_unique<SuspendableThreadDelegateWin>(thread_token)),
std::make_unique<NativeUnwinderWin>(), module_cache, test_delegate);
std::move(core_unwinders), module_cache, test_delegate);
#else
return nullptr;
#endif
......
......@@ -733,12 +733,12 @@ StackSamplingProfiler::StackSamplingProfiler(
SamplingProfilerThreadToken thread_token,
const SamplingParams& params,
std::unique_ptr<ProfileBuilder> profile_builder,
std::unique_ptr<Unwinder> native_unwinder,
std::vector<std::unique_ptr<Unwinder>> unwinders,
StackSamplerTestDelegate* test_delegate)
: StackSamplingProfiler(params, std::move(profile_builder), nullptr) {
sampler_ =
StackSampler::Create(thread_token, profile_builder_->GetModuleCache(),
std::move(native_unwinder), test_delegate);
std::move(unwinders), test_delegate);
}
StackSamplingProfiler::StackSamplingProfiler(
......
......@@ -74,18 +74,20 @@ class BASE_EXPORT StackSamplingProfiler {
TimeDelta sampling_interval = TimeDelta::FromMilliseconds(100);
};
// Creates a profiler for the specified thread. |native_unwinder| is required
// on Android since the unwinder is provided outside StackSamplingProfiler,
// but must be null on other platforms. An optional |test_delegate| can be
// supplied by tests.
// Creates a profiler for the specified thread. |unwinders| is required on
// Android since the unwinder is provided outside StackSamplingProfiler, but
// must be empty on other platforms. When attempting to unwind, the relative
// priority of unwinders is the inverse of the order in |unwinders|. An
// optional |test_delegate| can be supplied by tests.
//
// The caller must ensure that this object gets destroyed before the thread
// exits.
StackSamplingProfiler(SamplingProfilerThreadToken thread_token,
const SamplingParams& params,
std::unique_ptr<ProfileBuilder> profile_builder,
std::unique_ptr<Unwinder> native_unwinder = nullptr,
StackSamplerTestDelegate* test_delegate = nullptr);
StackSamplingProfiler(
SamplingProfilerThreadToken thread_token,
const SamplingParams& params,
std::unique_ptr<ProfileBuilder> profile_builder,
std::vector<std::unique_ptr<Unwinder>> core_unwinders = {},
StackSamplerTestDelegate* test_delegate = nullptr);
// Same as above function, with custom |sampler| implementation. The sampler
// on Android is not implemented in base.
......
......@@ -213,7 +213,7 @@ struct TestProfilerInfo {
profile = std::move(result_profile);
completed.Signal();
})),
nullptr,
{},
delegate) {}
// The order here is important to ensure objects being referenced don't get
......@@ -347,7 +347,7 @@ void TestLibraryUnload(bool wait_until_unloaded, ModuleCache* module_cache) {
profile = std::move(result_profile);
sampling_thread_completed.Signal();
})),
nullptr, &test_delegate);
{}, &test_delegate);
profiler.Start();
......@@ -1427,7 +1427,7 @@ PROFILER_TEST_F(StackSamplingProfilerTest,
BindLambdaForTesting([&profile](Profile result_profile) {
profile = std::move(result_profile);
})),
nullptr, &post_sample_invoker);
{}, &post_sample_invoker);
profiler.Start();
// Wait for 5 samples to be collected.
for (int i = 0; i < 5; ++i)
......
......@@ -6,6 +6,7 @@
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/command_line.h"
......@@ -72,9 +73,9 @@ CallStackProfileParams::Process GetProcess() {
return CallStackProfileParams::UNKNOWN_PROCESS;
}
const base::RepeatingCallback<std::unique_ptr<base::Unwinder>()>&
GetNativeUnwinderFactory() {
const auto create_native_unwinder_factory = []() {
const base::RepeatingCallback<std::vector<std::unique_ptr<base::Unwinder>>()>&
GetCoreUnwindersFactory() {
const auto create_unwinders_factory = []() {
#if defined(OS_ANDROID)
// The module is loadable if the profiler is enabled for the current
// process.
......@@ -85,30 +86,31 @@ GetNativeUnwinderFactory() {
std::unique_ptr<stack_unwinder::Module> module;
std::unique_ptr<stack_unwinder::MemoryRegionsMap> memory_regions_map;
};
const auto create_native_unwinder =
[](UnwinderCreationState* creation_state) {
return creation_state->module->CreateNativeUnwinder(
creation_state->memory_regions_map.get(),
reinterpret_cast<uintptr_t>(&__executable_start));
};
const auto create_unwinders = [](UnwinderCreationState* creation_state) {
std::vector<std::unique_ptr<base::Unwinder>> unwinders;
unwinders.push_back(creation_state->module->CreateNativeUnwinder(
creation_state->memory_regions_map.get(),
reinterpret_cast<uintptr_t>(&__executable_start)));
return unwinders;
};
std::unique_ptr<stack_unwinder::Module> module =
stack_unwinder::Module::Load();
std::unique_ptr<stack_unwinder::MemoryRegionsMap> memory_regions_map =
module->CreateMemoryRegionsMap();
return base::BindRepeating(
create_native_unwinder,
create_unwinders,
base::Owned(new UnwinderCreationState{std::move(module),
std::move(memory_regions_map)}));
#else
return base::BindRepeating(
[]() -> std::unique_ptr<base::Unwinder> { return nullptr; });
[]() -> std::vector<std::unique_ptr<base::Unwinder>> { return {}; });
#endif
};
static base::NoDestructor<
base::RepeatingCallback<std::unique_ptr<base::Unwinder>()>>
native_unwinder_factory(create_native_unwinder_factory());
base::RepeatingCallback<std::vector<std::unique_ptr<base::Unwinder>>()>>
native_unwinder_factory(create_unwinders_factory());
return *native_unwinder_factory;
}
......@@ -285,13 +287,14 @@ ThreadProfiler::ThreadProfiler(
const base::StackSamplingProfiler::SamplingParams sampling_params =
StackSamplingConfiguration::Get()->GetSamplingParams();
startup_profiler_ = std::make_unique<StackSamplingProfiler>(
base::GetSamplingProfilerCurrentThreadToken(), sampling_params,
std::make_unique<CallStackProfileBuilder>(
CallStackProfileParams(GetProcess(), thread,
CallStackProfileParams::PROCESS_STARTUP),
work_id_recorder_.get()),
GetNativeUnwinderFactory().Run());
GetCoreUnwindersFactory().Run());
startup_profiler_->Start();
......@@ -356,7 +359,7 @@ void ThreadProfiler::StartPeriodicSamplingCollection() {
base::BindOnce(&ThreadProfiler::OnPeriodicCollectionCompleted,
owning_thread_task_runner_,
weak_factory_.GetWeakPtr())),
GetNativeUnwinderFactory().Run());
GetCoreUnwindersFactory().Run());
if (aux_unwinder_factory_)
periodic_profiler_->AddAuxUnwinder(aux_unwinder_factory_.Run());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment