blob: c232e2ff687bcc8ee07aa3919f578f8259e35a53 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string.h>
#include <algorithm>
#include <utility>
#include "base/debug/alias.h"
#include "base/profiler/sampling_profiler_thread_token.h"
#include "base/profiler/stack_buffer.h"
#include "base/profiler/stack_copier_signal.h"
#include "base/profiler/thread_delegate_posix.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/platform_thread.h"
#include "base/threading/simple_thread.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace {
// Values to write to the stack and look for in the copy.
static const uint32_t kStackSentinels[] = {0xf312ecd9, 0x1fcd7f19, 0xe69e617d,
0x8245f94f};
class TargetThread : public SimpleThread {
public:
TargetThread()
: SimpleThread("target", Options()),
started_(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED),
copy_finished_(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED) {}
void Run() override {
thread_token_ = GetSamplingProfilerCurrentThreadToken();
// Copy the sentinel values onto the stack. Volatile to defeat compiler
// optimizations.
volatile uint32_t sentinels[std::size(kStackSentinels)];
for (size_t i = 0; i < std::size(kStackSentinels); ++i)
sentinels[i] = kStackSentinels[i];
started_.Signal();
copy_finished_.Wait();
}
SamplingProfilerThreadToken GetThreadToken() {
started_.Wait();
return thread_token_;
}
void NotifyCopyFinished() { copy_finished_.Signal(); }
private:
WaitableEvent started_;
WaitableEvent copy_finished_;
SamplingProfilerThreadToken thread_token_;
};
class TestStackCopierDelegate : public StackCopier::Delegate {
public:
void OnStackCopy() override {
on_stack_copy_was_invoked_ = true;
}
bool on_stack_copy_was_invoked() const { return on_stack_copy_was_invoked_; }
private:
bool on_stack_copy_was_invoked_ = false;
};
} // namespace
// ASAN moves local variables outside of the stack extents, which breaks the
// sentinels.
// MSan complains that the memcmp() reads uninitialized memory.
// TSAN hangs on the AsyncSafeWaitableEvent FUTEX_WAIT call.
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
defined(THREAD_SANITIZER)
#define MAYBE_CopyStack DISABLED_CopyStack
#elif BUILDFLAG(IS_CHROMEOS_ASH)
// https://crbug.com/1042974
#define MAYBE_CopyStack DISABLED_CopyStack
#elif BUILDFLAG(IS_LINUX)
// We don't support getting the stack base address on Linux, and thus can't
// copy the stack. // https://crbug.com/1394278
#define MAYBE_CopyStack DISABLED_CopyStack
#else
#define MAYBE_CopyStack CopyStack
#endif
TEST(StackCopierSignalTest, MAYBE_CopyStack) {
StackBuffer stack_buffer(/* buffer_size = */ 1 << 20);
memset(stack_buffer.buffer(), 0, stack_buffer.size());
uintptr_t stack_top = 0;
TimeTicks timestamp;
RegisterContext context;
TestStackCopierDelegate stack_copier_delegate;
auto thread_delegate =
ThreadDelegatePosix::Create(GetSamplingProfilerCurrentThreadToken());
ASSERT_TRUE(thread_delegate);
StackCopierSignal copier(std::move(thread_delegate));
// Copy the sentinel values onto the stack.
uint32_t sentinels[std::size(kStackSentinels)];
for (size_t i = 0; i < std::size(kStackSentinels); ++i)
sentinels[i] = kStackSentinels[i];
base::debug::Alias((void*)sentinels); // Defeat compiler optimizations.
bool result = copier.CopyStack(&stack_buffer, &stack_top, &timestamp,
&context, &stack_copier_delegate);
ASSERT_TRUE(result);
uint32_t* const end = reinterpret_cast<uint32_t*>(stack_top);
uint32_t* const sentinel_location = std::find_if(
reinterpret_cast<uint32_t*>(RegisterContextStackPointer(&context)), end,
[](const uint32_t& location) {
return memcmp(&location, &kStackSentinels[0],
sizeof(kStackSentinels)) == 0;
});
EXPECT_NE(end, sentinel_location);
}
// TSAN hangs on the AsyncSafeWaitableEvent FUTEX_WAIT call.
#if defined(THREAD_SANITIZER)
#define MAYBE_CopyStackTimestamp DISABLED_CopyStackTimestamp
#elif BUILDFLAG(IS_LINUX)
// We don't support getting the stack base address on Linux, and thus can't
// copy the stack. // https://crbug.com/1394278
#define MAYBE_CopyStackTimestamp DISABLED_CopyStackTimestamp
#else
#define MAYBE_CopyStackTimestamp CopyStackTimestamp
#endif
TEST(StackCopierSignalTest, MAYBE_CopyStackTimestamp) {
StackBuffer stack_buffer(/* buffer_size = */ 1 << 20);
memset(stack_buffer.buffer(), 0, stack_buffer.size());
uintptr_t stack_top = 0;
TimeTicks timestamp;
RegisterContext context;
TestStackCopierDelegate stack_copier_delegate;
auto thread_delegate =
ThreadDelegatePosix::Create(GetSamplingProfilerCurrentThreadToken());
ASSERT_TRUE(thread_delegate);
StackCopierSignal copier(std::move(thread_delegate));
TimeTicks before = TimeTicks::Now();
bool result = copier.CopyStack(&stack_buffer, &stack_top, &timestamp,
&context, &stack_copier_delegate);
TimeTicks after = TimeTicks::Now();
ASSERT_TRUE(result);
EXPECT_GE(timestamp, before);
EXPECT_LE(timestamp, after);
}
// TSAN hangs on the AsyncSafeWaitableEvent FUTEX_WAIT call.
#if defined(THREAD_SANITIZER)
#define MAYBE_CopyStackDelegateInvoked DISABLED_CopyStackDelegateInvoked
#elif BUILDFLAG(IS_LINUX)
// We don't support getting the stack base address on Linux, and thus can't
// copy the stack. // https://crbug.com/1394278
#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;
TimeTicks timestamp;
RegisterContext context;
TestStackCopierDelegate stack_copier_delegate;
auto thread_delegate =
ThreadDelegatePosix::Create(GetSamplingProfilerCurrentThreadToken());
ASSERT_TRUE(thread_delegate);
StackCopierSignal copier(std::move(thread_delegate));
bool result = copier.CopyStack(&stack_buffer, &stack_top, &timestamp,
&context, &stack_copier_delegate);
ASSERT_TRUE(result);
EXPECT_TRUE(stack_copier_delegate.on_stack_copy_was_invoked());
}
// 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
// to selectively disable.
#if !(BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_32_BITS))
#define MAYBE_CopyStackFromOtherThread DISABLED_CopyStackFromOtherThread
#elif BUILDFLAG(IS_LINUX)
// We don't support getting the stack base address on Linux, and thus can't
// copy the stack. // https://crbug.com/1394278
#define MAYBE_CopyStackFromOtherThread DISABLED_CopyStackFromOtherThread
#else
#define MAYBE_CopyStackFromOtherThread CopyStackFromOtherThread
#endif
TEST(StackCopierSignalTest, MAYBE_CopyStackFromOtherThread) {
StackBuffer stack_buffer(/* buffer_size = */ 1 << 20);
memset(stack_buffer.buffer(), 0, stack_buffer.size());
uintptr_t stack_top = 0;
TimeTicks timestamp;
RegisterContext context{};
TestStackCopierDelegate stack_copier_delegate;
TargetThread target_thread;
target_thread.Start();
const SamplingProfilerThreadToken thread_token =
target_thread.GetThreadToken();
auto thread_delegate = ThreadDelegatePosix::Create(thread_token);
ASSERT_TRUE(thread_delegate);
StackCopierSignal copier(std::move(thread_delegate));
bool result = copier.CopyStack(&stack_buffer, &stack_top, &timestamp,
&context, &stack_copier_delegate);
ASSERT_TRUE(result);
target_thread.NotifyCopyFinished();
target_thread.Join();
uint32_t* const end = reinterpret_cast<uint32_t*>(stack_top);
uint32_t* const sentinel_location = std::find_if(
reinterpret_cast<uint32_t*>(RegisterContextStackPointer(&context)), end,
[](const uint32_t& location) {
return memcmp(&location, &kStackSentinels[0],
sizeof(kStackSentinels)) == 0;
});
EXPECT_NE(end, sentinel_location);
}
} // namespace base