Commit 16c85c3e authored by Mike Wittman's avatar Mike Wittman Committed by Commit Bot

[Sampling profiler] Add Unwinder::OnStackCapture

Adds a function to the Unwinder interface that is invoked at the time the
stack is captured, while the target thread is suspended, and wires up the
StackCopiers and StackSamplerImpl to invoke it at the appropriate time.

This function will be overridden in the new V8 unwinder to save off the
active V8 code pages while the thread is suspended.

Bug: 1035855
Change-Id: Ide287443faed211e2e6d205ebe78b8a536caa729
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2074806
Commit-Queue: Mike Wittman <wittman@chromium.org>
Reviewed-by: default avatarEtienne Pierre-Doray <etiennep@chromium.org>
Cr-Commit-Position: refs/heads/master@{#747393}
parent 9a3eda3b
...@@ -23,17 +23,33 @@ class ProfileBuilder; ...@@ -23,17 +23,33 @@ class ProfileBuilder;
// is performed by the OS through signals (Android). // is performed by the OS through signals (Android).
class BASE_EXPORT StackCopier { class BASE_EXPORT StackCopier {
public: public:
// Interface that may be implemented by the caller of CopyStack() to receive a
// callback when the stack is copied, while the target thread is suspended.
class BASE_EXPORT Delegate {
public:
virtual ~Delegate() {}
// IMPORTANT NOTE: to avoid deadlock implementations of this interface must
// not invoke any non-reentrant code that is also invoked by the target
// thread. In particular, it may not perform any heap allocation or
// deallocation, including indirectly via use of DCHECK/CHECK or other
// logging statements.
virtual void OnStackCopy() = 0;
};
virtual ~StackCopier(); virtual ~StackCopier();
// Copies the thread's register context into |thread_context|, the stack into // Copies the thread's register context into |thread_context|, the stack into
// |stack_buffer|, and the top of stack address into |stack_top|. Records // |stack_buffer|, and the top of stack address into |stack_top|. Records
// metadata while the thread is suspended via |profile_builder|. Records // metadata while the thread is suspended via |profile_builder|. Records
// |timestamp| at the time the stack was copied. Returns true if successful. // |timestamp| at the time the stack was copied. delegate->OnStackCopy() will
// be invoked while the thread is suspended. Returns true if successful.
virtual bool CopyStack(StackBuffer* stack_buffer, virtual bool CopyStack(StackBuffer* stack_buffer,
uintptr_t* stack_top, uintptr_t* stack_top,
ProfileBuilder* profile_builder, ProfileBuilder* profile_builder,
TimeTicks* timestamp, TimeTicks* timestamp,
RegisterContext* thread_context) = 0; RegisterContext* thread_context,
Delegate* delegate) = 0;
protected: protected:
// If the value at |pointer| points to the original stack, rewrite it to point // If the value at |pointer| points to the original stack, rewrite it to point
......
...@@ -95,6 +95,9 @@ struct HandlerParams { ...@@ -95,6 +95,9 @@ struct HandlerParams {
// The timestamp when the stack was copied. // The timestamp when the stack was copied.
TimeTicks* timestamp; TimeTicks* timestamp;
// The delegate provided to the StackCopier.
StackCopier::Delegate* stack_copier_delegate;
}; };
// Pointer to the parameters to be "passed" to the CopyStackSignalHandler() from // Pointer to the parameters to be "passed" to the CopyStackSignalHandler() from
...@@ -127,13 +130,16 @@ void CopyStackSignalHandler(int n, siginfo_t* siginfo, void* sigcontext) { ...@@ -127,13 +130,16 @@ void CopyStackSignalHandler(int n, siginfo_t* siginfo, void* sigcontext) {
return; return;
} }
// TODO(https://crbug.com/988579): Record metadata while the thread is
// suspended.
params->stack_copier_delegate->OnStackCopy();
*params->stack_copy_bottom = *params->stack_copy_bottom =
StackCopierSignal::CopyStackContentsAndRewritePointers( StackCopierSignal::CopyStackContentsAndRewritePointers(
reinterpret_cast<uint8_t*>(bottom), reinterpret_cast<uintptr_t*>(top), reinterpret_cast<uint8_t*>(bottom), reinterpret_cast<uintptr_t*>(top),
StackBuffer::kPlatformStackAlignment, params->stack_buffer->buffer()); StackBuffer::kPlatformStackAlignment, params->stack_buffer->buffer());
// TODO(https://crbug.com/988579): Record metadata while the thread is
// suspended.
*params->success = true; *params->success = true;
} }
...@@ -188,14 +194,15 @@ bool StackCopierSignal::CopyStack(StackBuffer* stack_buffer, ...@@ -188,14 +194,15 @@ bool StackCopierSignal::CopyStack(StackBuffer* stack_buffer,
uintptr_t* stack_top, uintptr_t* stack_top,
ProfileBuilder* profile_builder, ProfileBuilder* profile_builder,
TimeTicks* timestamp, TimeTicks* timestamp,
RegisterContext* thread_context) { RegisterContext* thread_context,
Delegate* delegate) {
AsyncSafeWaitableEvent wait_event; AsyncSafeWaitableEvent wait_event;
bool copied = false; bool copied = false;
const uint8_t* stack_copy_bottom = nullptr; const uint8_t* stack_copy_bottom = nullptr;
const uintptr_t stack_base_address = thread_delegate_->GetStackBaseAddress(); const uintptr_t stack_base_address = thread_delegate_->GetStackBaseAddress();
HandlerParams params = {stack_base_address, &wait_event, &copied, HandlerParams params = {stack_base_address, &wait_event, &copied,
thread_context, stack_buffer, &stack_copy_bottom, thread_context, stack_buffer, &stack_copy_bottom,
timestamp}; timestamp, delegate};
{ {
ScopedSetSignalHandlerParams scoped_handler_params(&params); ScopedSetSignalHandlerParams scoped_handler_params(&params);
......
...@@ -26,7 +26,8 @@ class BASE_EXPORT StackCopierSignal : public StackCopier { ...@@ -26,7 +26,8 @@ class BASE_EXPORT StackCopierSignal : public StackCopier {
uintptr_t* stack_top, uintptr_t* stack_top,
ProfileBuilder* profile_builder, ProfileBuilder* profile_builder,
TimeTicks* timestamp, TimeTicks* timestamp,
RegisterContext* thread_context) override; RegisterContext* thread_context,
Delegate* delegate) override;
using StackCopier::CopyStackContentsAndRewritePointers; using StackCopier::CopyStackContentsAndRewritePointers;
......
...@@ -82,6 +82,15 @@ class TargetThread : public SimpleThread { ...@@ -82,6 +82,15 @@ class TargetThread : public SimpleThread {
SamplingProfilerThreadToken thread_token_; SamplingProfilerThreadToken thread_token_;
}; };
class TestStackCopierDelegate : public StackCopier::Delegate {
public:
void OnStackCopy() override { was_invoked_ = true; }
bool was_invoked() const { return was_invoked_; }
private:
bool was_invoked_ = false;
};
} // namespace } // namespace
// ASAN moves local variables outside of the stack extents, which breaks the // ASAN moves local variables outside of the stack extents, which breaks the
...@@ -104,6 +113,7 @@ TEST(StackCopierSignalTest, MAYBE_CopyStack) { ...@@ -104,6 +113,7 @@ TEST(StackCopierSignalTest, MAYBE_CopyStack) {
TestProfileBuilder profiler_builder; TestProfileBuilder profiler_builder;
TimeTicks timestamp; TimeTicks timestamp;
RegisterContext context; RegisterContext context;
TestStackCopierDelegate stack_copier_delegate;
StackCopierSignal copier(std::make_unique<ThreadDelegatePosix>( StackCopierSignal copier(std::make_unique<ThreadDelegatePosix>(
GetSamplingProfilerCurrentThreadToken())); GetSamplingProfilerCurrentThreadToken()));
...@@ -115,7 +125,7 @@ TEST(StackCopierSignalTest, MAYBE_CopyStack) { ...@@ -115,7 +125,7 @@ TEST(StackCopierSignalTest, MAYBE_CopyStack) {
sentinels[i] = kStackSentinels[i]; sentinels[i] = kStackSentinels[i];
bool result = copier.CopyStack(&stack_buffer, &stack_top, &profiler_builder, bool result = copier.CopyStack(&stack_buffer, &stack_top, &profiler_builder,
&timestamp, &context); &timestamp, &context, &stack_copier_delegate);
ASSERT_TRUE(result); ASSERT_TRUE(result);
uint32_t* const end = reinterpret_cast<uint32_t*>(stack_top); uint32_t* const end = reinterpret_cast<uint32_t*>(stack_top);
...@@ -141,13 +151,14 @@ TEST(StackCopierSignalTest, MAYBE_CopyStackTimestamp) { ...@@ -141,13 +151,14 @@ TEST(StackCopierSignalTest, MAYBE_CopyStackTimestamp) {
TestProfileBuilder profiler_builder; TestProfileBuilder profiler_builder;
TimeTicks timestamp; TimeTicks timestamp;
RegisterContext context; RegisterContext context;
TestStackCopierDelegate stack_copier_delegate;
StackCopierSignal copier(std::make_unique<ThreadDelegatePosix>( StackCopierSignal copier(std::make_unique<ThreadDelegatePosix>(
GetSamplingProfilerCurrentThreadToken())); GetSamplingProfilerCurrentThreadToken()));
TimeTicks before = TimeTicks::Now(); TimeTicks before = TimeTicks::Now();
bool result = copier.CopyStack(&stack_buffer, &stack_top, &profiler_builder, bool result = copier.CopyStack(&stack_buffer, &stack_top, &profiler_builder,
&timestamp, &context); &timestamp, &context, &stack_copier_delegate);
TimeTicks after = TimeTicks::Now(); TimeTicks after = TimeTicks::Now();
ASSERT_TRUE(result); ASSERT_TRUE(result);
...@@ -155,6 +166,31 @@ TEST(StackCopierSignalTest, MAYBE_CopyStackTimestamp) { ...@@ -155,6 +166,31 @@ TEST(StackCopierSignalTest, MAYBE_CopyStackTimestamp) {
EXPECT_LE(timestamp, after); EXPECT_LE(timestamp, after);
} }
// TSAN hangs on the AsyncSafeWaitableEvent FUTEX_WAIT call.
#if defined(THREAD_SANITIZER)
#define MAYBE_CopyStackDelegateInvoked DISABLED_CopyStackDelegateInvoked
#else
#define MAYBE_CopyStackDelegateInvoked CopyStackDelegateInvoked
#endif
TEST(StackCopierSignalTest, MAYBE_CopyStackDelegateInvoked) {
StackBuffer stack_buffer(/* buffer_size = */ 1 << 20);
memset(stack_buffer.buffer(), 0, stack_buffer.size());
uintptr_t stack_top = 0;
TestProfileBuilder profiler_builder;
TimeTicks timestamp;
RegisterContext context;
TestStackCopierDelegate stack_copier_delegate;
StackCopierSignal copier(std::make_unique<ThreadDelegatePosix>(
GetSamplingProfilerCurrentThreadToken()));
bool result = copier.CopyStack(&stack_buffer, &stack_top, &profiler_builder,
&timestamp, &context, &stack_copier_delegate);
ASSERT_TRUE(result);
EXPECT_TRUE(stack_copier_delegate.was_invoked());
}
// Limit to 32-bit Android, which is the platform we care about for this // Limit to 32-bit Android, which is the platform we care about for this
// functionality. The test is broken on too many other varied platforms to try // functionality. The test is broken on too many other varied platforms to try
// to selectively disable. // to selectively disable.
...@@ -170,6 +206,7 @@ TEST(StackCopierSignalTest, MAYBE_CopyStackFromOtherThread) { ...@@ -170,6 +206,7 @@ TEST(StackCopierSignalTest, MAYBE_CopyStackFromOtherThread) {
TestProfileBuilder profiler_builder; TestProfileBuilder profiler_builder;
TimeTicks timestamp; TimeTicks timestamp;
RegisterContext context{}; RegisterContext context{};
TestStackCopierDelegate stack_copier_delegate;
TargetThread target_thread; TargetThread target_thread;
target_thread.Start(); target_thread.Start();
...@@ -179,7 +216,7 @@ TEST(StackCopierSignalTest, MAYBE_CopyStackFromOtherThread) { ...@@ -179,7 +216,7 @@ TEST(StackCopierSignalTest, MAYBE_CopyStackFromOtherThread) {
StackCopierSignal copier(std::make_unique<ThreadDelegatePosix>(thread_token)); StackCopierSignal copier(std::make_unique<ThreadDelegatePosix>(thread_token));
bool result = copier.CopyStack(&stack_buffer, &stack_top, &profiler_builder, bool result = copier.CopyStack(&stack_buffer, &stack_top, &profiler_builder,
&timestamp, &context); &timestamp, &context, &stack_copier_delegate);
ASSERT_TRUE(result); ASSERT_TRUE(result);
target_thread.NotifyCopyFinished(); target_thread.NotifyCopyFinished();
......
...@@ -27,7 +27,8 @@ bool StackCopierSuspend::CopyStack(StackBuffer* stack_buffer, ...@@ -27,7 +27,8 @@ bool StackCopierSuspend::CopyStack(StackBuffer* stack_buffer,
uintptr_t* stack_top, uintptr_t* stack_top,
ProfileBuilder* profile_builder, ProfileBuilder* profile_builder,
TimeTicks* timestamp, TimeTicks* timestamp,
RegisterContext* thread_context) { RegisterContext* thread_context,
Delegate* delegate) {
const uintptr_t top = thread_delegate_->GetStackBaseAddress(); const uintptr_t top = thread_delegate_->GetStackBaseAddress();
uintptr_t bottom = 0; uintptr_t bottom = 0;
const uint8_t* stack_copy_bottom = nullptr; const uint8_t* stack_copy_bottom = nullptr;
...@@ -67,6 +68,8 @@ bool StackCopierSuspend::CopyStack(StackBuffer* stack_buffer, ...@@ -67,6 +68,8 @@ bool StackCopierSuspend::CopyStack(StackBuffer* stack_buffer,
profile_builder->RecordMetadata(get_metadata_items.get()); profile_builder->RecordMetadata(get_metadata_items.get());
delegate->OnStackCopy();
stack_copy_bottom = CopyStackContentsAndRewritePointers( stack_copy_bottom = CopyStackContentsAndRewritePointers(
reinterpret_cast<uint8_t*>(bottom), reinterpret_cast<uintptr_t*>(top), reinterpret_cast<uint8_t*>(bottom), reinterpret_cast<uintptr_t*>(top),
StackBuffer::kPlatformStackAlignment, stack_buffer->buffer()); StackBuffer::kPlatformStackAlignment, stack_buffer->buffer());
......
...@@ -28,7 +28,8 @@ class BASE_EXPORT StackCopierSuspend : public StackCopier { ...@@ -28,7 +28,8 @@ class BASE_EXPORT StackCopierSuspend : public StackCopier {
uintptr_t* stack_top, uintptr_t* stack_top,
ProfileBuilder* profile_builder, ProfileBuilder* profile_builder,
TimeTicks* timestamp, TimeTicks* timestamp,
RegisterContext* thread_context) override; RegisterContext* thread_context,
Delegate* delegate) override;
private: private:
std::unique_ptr<SuspendableThreadDelegate> thread_delegate_; std::unique_ptr<SuspendableThreadDelegate> thread_delegate_;
......
...@@ -107,6 +107,15 @@ class TestProfileBuilder : public ProfileBuilder { ...@@ -107,6 +107,15 @@ class TestProfileBuilder : public ProfileBuilder {
bool recorded_metadata_ = false; bool recorded_metadata_ = false;
}; };
class TestStackCopierDelegate : public StackCopier::Delegate {
public:
void OnStackCopy() override { was_invoked_ = true; }
bool was_invoked() const { return was_invoked_; }
private:
bool was_invoked_ = false;
};
} // namespace } // namespace
TEST(StackCopierSuspendTest, CopyStack) { TEST(StackCopierSuspendTest, CopyStack) {
...@@ -120,9 +129,10 @@ TEST(StackCopierSuspendTest, CopyStack) { ...@@ -120,9 +129,10 @@ TEST(StackCopierSuspendTest, CopyStack) {
TestProfileBuilder profile_builder; TestProfileBuilder profile_builder;
TimeTicks timestamp; TimeTicks timestamp;
RegisterContext register_context{}; RegisterContext register_context{};
TestStackCopierDelegate stack_copier_delegate;
stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top, stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top,
&profile_builder, &timestamp, &profile_builder, &timestamp,
&register_context); &register_context, &stack_copier_delegate);
uintptr_t* stack_copy_bottom = uintptr_t* stack_copy_bottom =
reinterpret_cast<uintptr_t*>(stack_buffer.get()->buffer()); reinterpret_cast<uintptr_t*>(stack_buffer.get()->buffer());
...@@ -144,9 +154,10 @@ TEST(StackCopierSuspendTest, CopyStackBufferTooSmall) { ...@@ -144,9 +154,10 @@ TEST(StackCopierSuspendTest, CopyStackBufferTooSmall) {
TestProfileBuilder profile_builder; TestProfileBuilder profile_builder;
TimeTicks timestamp; TimeTicks timestamp;
RegisterContext register_context{}; RegisterContext register_context{};
TestStackCopierDelegate stack_copier_delegate;
stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top, stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top,
&profile_builder, &timestamp, &profile_builder, &timestamp,
&register_context); &register_context, &stack_copier_delegate);
uintptr_t* stack_copy_bottom = uintptr_t* stack_copy_bottom =
reinterpret_cast<uintptr_t*>(stack_buffer.get()->buffer()); reinterpret_cast<uintptr_t*>(stack_buffer.get()->buffer());
...@@ -171,9 +182,10 @@ TEST(StackCopierSuspendTest, CopyStackAndRewritePointers) { ...@@ -171,9 +182,10 @@ TEST(StackCopierSuspendTest, CopyStackAndRewritePointers) {
TestProfileBuilder profile_builder; TestProfileBuilder profile_builder;
TimeTicks timestamp; TimeTicks timestamp;
RegisterContext register_context{}; RegisterContext register_context{};
TestStackCopierDelegate stack_copier_delegate;
stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top, stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top,
&profile_builder, &timestamp, &profile_builder, &timestamp,
&register_context); &register_context, &stack_copier_delegate);
uintptr_t* stack_copy_bottom = uintptr_t* stack_copy_bottom =
reinterpret_cast<uintptr_t*>(stack_buffer.get()->buffer()); reinterpret_cast<uintptr_t*>(stack_buffer.get()->buffer());
...@@ -196,20 +208,42 @@ TEST(StackCopierSuspendTest, CopyStackTimeStamp) { ...@@ -196,20 +208,42 @@ TEST(StackCopierSuspendTest, CopyStackTimeStamp) {
TestProfileBuilder profile_builder; TestProfileBuilder profile_builder;
TimeTicks timestamp; TimeTicks timestamp;
RegisterContext register_context{}; RegisterContext register_context{};
TestStackCopierDelegate stack_copier_delegate;
TimeTicks before = TimeTicks::Now(); TimeTicks before = TimeTicks::Now();
stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top, stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top,
&profile_builder, &timestamp, &profile_builder, &timestamp,
&register_context); &register_context, &stack_copier_delegate);
TimeTicks after = TimeTicks::Now(); TimeTicks after = TimeTicks::Now();
EXPECT_GE(timestamp, before); EXPECT_GE(timestamp, before);
EXPECT_LE(timestamp, after); EXPECT_LE(timestamp, after);
} }
TEST(StackCopierSuspendTest, CopyStackDelegateInvoked) {
const std::vector<uintptr_t> stack = {0};
StackCopierSuspend stack_copier_suspend(
std::make_unique<TestSuspendableThreadDelegate>(stack));
std::unique_ptr<StackBuffer> stack_buffer =
std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));
uintptr_t stack_top = 0;
TestProfileBuilder profile_builder;
TimeTicks timestamp;
RegisterContext register_context{};
TestStackCopierDelegate stack_copier_delegate;
stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top,
&profile_builder, &timestamp,
&register_context, &stack_copier_delegate);
EXPECT_TRUE(stack_copier_delegate.was_invoked());
}
TEST(StackCopierSuspendTest, RewriteRegisters) { TEST(StackCopierSuspendTest, RewriteRegisters) {
std::vector<uintptr_t> stack = {0, 1, 2}; std::vector<uintptr_t> stack = {0, 1, 2};
RegisterContext register_context{}; RegisterContext register_context{};
TestStackCopierDelegate stack_copier_delegate;
RegisterContextFramePointer(&register_context) = RegisterContextFramePointer(&register_context) =
reinterpret_cast<uintptr_t>(&stack[1]); reinterpret_cast<uintptr_t>(&stack[1]);
StackCopierSuspend stack_copier_suspend( StackCopierSuspend stack_copier_suspend(
...@@ -223,7 +257,7 @@ TEST(StackCopierSuspendTest, RewriteRegisters) { ...@@ -223,7 +257,7 @@ TEST(StackCopierSuspendTest, RewriteRegisters) {
TestProfileBuilder profile_builder; TestProfileBuilder profile_builder;
stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top, stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top,
&profile_builder, &timestamp, &profile_builder, &timestamp,
&register_context); &register_context, &stack_copier_delegate);
uintptr_t stack_copy_bottom = uintptr_t stack_copy_bottom =
reinterpret_cast<uintptr_t>(stack_buffer.get()->buffer()); reinterpret_cast<uintptr_t>(stack_buffer.get()->buffer());
......
...@@ -46,8 +46,9 @@ void StackSamplerImpl::RecordStackFrames(StackBuffer* stack_buffer, ...@@ -46,8 +46,9 @@ void StackSamplerImpl::RecordStackFrames(StackBuffer* stack_buffer,
RegisterContext thread_context; RegisterContext thread_context;
uintptr_t stack_top; uintptr_t stack_top;
TimeTicks timestamp; TimeTicks timestamp;
bool success = stack_copier_->CopyStack( bool success =
stack_buffer, &stack_top, profile_builder, &timestamp, &thread_context); stack_copier_->CopyStack(stack_buffer, &stack_top, profile_builder,
&timestamp, &thread_context, this);
if (!success) if (!success)
return; return;
...@@ -59,8 +60,18 @@ void StackSamplerImpl::RecordStackFrames(StackBuffer* stack_buffer, ...@@ -59,8 +60,18 @@ void StackSamplerImpl::RecordStackFrames(StackBuffer* stack_buffer,
native_unwinder_.get(), aux_unwinder_.get()), native_unwinder_.get(), aux_unwinder_.get()),
timestamp); timestamp);
} }
// static
// IMPORTANT NOTE: to avoid deadlock this function must not invoke any
// non-reentrant code that is also invoked by the target thread. In particular,
// it may not perform any heap allocation or deallocation, including indirectly
// via use of DCHECK/CHECK or other logging statements.
void StackSamplerImpl::OnStackCopy() {
native_unwinder_->OnStackCapture();
if (aux_unwinder_)
aux_unwinder_->OnStackCapture();
}
// static
std::vector<Frame> StackSamplerImpl::WalkStackForTesting( std::vector<Frame> StackSamplerImpl::WalkStackForTesting(
ModuleCache* module_cache, ModuleCache* module_cache,
RegisterContext* thread_context, RegisterContext* thread_context,
......
...@@ -10,16 +10,17 @@ ...@@ -10,16 +10,17 @@
#include "base/base_export.h" #include "base/base_export.h"
#include "base/profiler/frame.h" #include "base/profiler/frame.h"
#include "base/profiler/register_context.h" #include "base/profiler/register_context.h"
#include "base/profiler/stack_copier.h"
#include "base/profiler/stack_sampler.h" #include "base/profiler/stack_sampler.h"
namespace base { namespace base {
class StackCopier;
class Unwinder; class Unwinder;
// Cross-platform stack sampler implementation. Delegates to StackCopier for the // Cross-platform stack sampler implementation. Delegates to StackCopier for the
// platform-specific stack copying implementation. // platform-specific stack copying implementation.
class BASE_EXPORT StackSamplerImpl : public StackSampler { class BASE_EXPORT StackSamplerImpl : public StackSampler,
public StackCopier::Delegate {
public: public:
StackSamplerImpl(std::unique_ptr<StackCopier> stack_copier, StackSamplerImpl(std::unique_ptr<StackCopier> stack_copier,
std::unique_ptr<Unwinder> native_unwinder, std::unique_ptr<Unwinder> native_unwinder,
...@@ -35,6 +36,9 @@ class BASE_EXPORT StackSamplerImpl : public StackSampler { ...@@ -35,6 +36,9 @@ class BASE_EXPORT StackSamplerImpl : public StackSampler {
void RecordStackFrames(StackBuffer* stack_buffer, void RecordStackFrames(StackBuffer* stack_buffer,
ProfileBuilder* profile_builder) override; ProfileBuilder* profile_builder) override;
// StackCopier::Delegate:
void OnStackCopy() override;
// Exposes the internal function for unit testing. // Exposes the internal function for unit testing.
static std::vector<Frame> WalkStackForTesting(ModuleCache* module_cache, static std::vector<Frame> WalkStackForTesting(ModuleCache* module_cache,
RegisterContext* thread_context, RegisterContext* thread_context,
......
...@@ -66,7 +66,8 @@ class TestStackCopier : public StackCopier { ...@@ -66,7 +66,8 @@ class TestStackCopier : public StackCopier {
uintptr_t* stack_top, uintptr_t* stack_top,
ProfileBuilder* profile_builder, ProfileBuilder* profile_builder,
TimeTicks* timestamp, TimeTicks* timestamp,
RegisterContext* thread_context) override { RegisterContext* thread_context,
Delegate* delegate) override {
std::memcpy(stack_buffer->buffer(), &fake_stack_[0], fake_stack_.size()); std::memcpy(stack_buffer->buffer(), &fake_stack_[0], fake_stack_.size());
*stack_top = *stack_top =
reinterpret_cast<uintptr_t>(&fake_stack_[0] + fake_stack_.size()); reinterpret_cast<uintptr_t>(&fake_stack_[0] + fake_stack_.size());
......
...@@ -31,8 +31,7 @@ enum class UnwindResult { ...@@ -31,8 +31,7 @@ enum class UnwindResult {
// use with the StackSamplingProfiler. The profiler is expected to call // use with the StackSamplingProfiler. The profiler is expected to call
// CanUnwind() to determine if the Unwinder thinks it can unwind from the frame // CanUnwind() to determine if the Unwinder thinks it can unwind from the frame
// represented by the context values, then TryUnwind() to attempt the // represented by the context values, then TryUnwind() to attempt the
// unwind. Note that the stack samples for multiple collection scenarios are // unwind.
// interleaved on a single Unwinder instance.
class Unwinder { class Unwinder {
public: public:
virtual ~Unwinder() = default; virtual ~Unwinder() = default;
...@@ -41,6 +40,13 @@ class Unwinder { ...@@ -41,6 +40,13 @@ class Unwinder {
// ModuleCache. // ModuleCache.
virtual void AddInitialModules(ModuleCache* module_cache) {} virtual void AddInitialModules(ModuleCache* module_cache) {}
// Invoked at the time the stack is captured. IMPORTANT NOTE: this function is
// invoked while the target thread is suspended. To avoid deadlock it must not
// invoke any non-reentrant code that is also invoked by the target thread. In
// particular, it may not perform any heap allocation or deallocation,
// including indirectly via use of DCHECK/CHECK or other logging statements.
virtual void OnStackCapture() {}
// Returns true if the unwinder recognizes the code referenced by // Returns true if the unwinder recognizes the code referenced by
// |current_frame| as code from which it should be able to unwind. When // |current_frame| as code from which it should be able to unwind. When
// multiple unwinders are in use, each should return true for a disjoint set // multiple unwinders are in use, each should return true for a disjoint set
......
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