#include <cstring>
#include <iterator>
#include <memory>
#include <numeric>
#include "base/profiler/stack_buffer.h"
#include "base/profiler/stack_copier.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace {
class CopyFunctions : public StackCopier {
public:
using StackCopier::CopyStackContentsAndRewritePointers;
using StackCopier::RewritePointerIfInOriginalStack;
};
static constexpr size_t kTestStackBufferSize = sizeof(uintptr_t) * 4;
union alignas(StackBuffer::kPlatformStackAlignment) TestStackBuffer {
uintptr_t as_uintptr[kTestStackBufferSize / sizeof(uintptr_t)];
uint16_t as_uint16[kTestStackBufferSize / sizeof(uint16_t)];
uint8_t as_uint8[kTestStackBufferSize / sizeof(uint8_t)];
};
}
TEST(StackCopierTest, RewritePointerIfInOriginalStack_InStack) {
uintptr_t original_stack[4];
uintptr_t stack_copy[4];
EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack_copy[2]),
CopyFunctions::RewritePointerIfInOriginalStack(
reinterpret_cast<uint8_t*>(&original_stack[0]),
&original_stack[0] + std::size(original_stack),
reinterpret_cast<uint8_t*>(&stack_copy[0]),
reinterpret_cast<uintptr_t>(&original_stack[2])));
}
TEST(StackCopierTest, RewritePointerIfInOriginalStack_NotInStack) {
uintptr_t non_stack_location;
uintptr_t original_stack[4];
uintptr_t stack_copy[4];
EXPECT_EQ(reinterpret_cast<uintptr_t>(&non_stack_location),
CopyFunctions::RewritePointerIfInOriginalStack(
reinterpret_cast<uint8_t*>(&original_stack[0]),
&original_stack[0] + std::size(original_stack),
reinterpret_cast<uint8_t*>(&stack_copy[0]),
reinterpret_cast<uintptr_t>(&non_stack_location)));
}
TEST(StackCopierTest, StackCopy) {
TestStackBuffer original_stack;
std::iota(
&original_stack.as_uintptr[0],
&original_stack.as_uintptr[0] + std::size(original_stack.as_uintptr),
100);
original_stack.as_uintptr[2] =
reinterpret_cast<uintptr_t>(&original_stack.as_uintptr[1]);
TestStackBuffer stack_copy;
CopyFunctions::CopyStackContentsAndRewritePointers(
&original_stack.as_uint8[0],
&original_stack.as_uintptr[0] + std::size(original_stack.as_uintptr),
StackBuffer::kPlatformStackAlignment, &stack_copy.as_uintptr[0]);
EXPECT_EQ(original_stack.as_uintptr[0], stack_copy.as_uintptr[0]);
EXPECT_EQ(original_stack.as_uintptr[1], stack_copy.as_uintptr[1]);
EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack_copy.as_uintptr[1]),
stack_copy.as_uintptr[2]);
EXPECT_EQ(original_stack.as_uintptr[3], stack_copy.as_uintptr[3]);
}
TEST(StackCopierTest, StackCopy_NonAlignedStackPointerCopy) {
TestStackBuffer stack_buffer;
std::iota(&stack_buffer.as_uint16[0],
&stack_buffer.as_uint16[0] + std::size(stack_buffer.as_uint16),
100);
uint8_t* unaligned_stack_bottom =
reinterpret_cast<uint8_t*>(&stack_buffer.as_uint16[1]);
const size_t extra_space = StackBuffer::kPlatformStackAlignment;
uintptr_t* stack_top =
&stack_buffer.as_uintptr[std::size(stack_buffer.as_uintptr) -
extra_space / sizeof(uintptr_t)];
TestStackBuffer stack_copy_buffer = {{0}};
const uint8_t* stack_copy_bottom =
CopyFunctions::CopyStackContentsAndRewritePointers(
unaligned_stack_bottom, stack_top,
StackBuffer::kPlatformStackAlignment,
&stack_copy_buffer.as_uintptr[0]);
EXPECT_EQ(unaligned_stack_bottom - &stack_buffer.as_uint8[0],
stack_copy_bottom - &stack_copy_buffer.as_uint8[0]);
EXPECT_EQ(0u, stack_copy_buffer.as_uint16[0]);
const size_t max_index =
std::size(stack_copy_buffer.as_uint16) - extra_space / sizeof(uint16_t);
for (size_t i = 1; i < max_index; ++i)
EXPECT_EQ(i + 100, stack_copy_buffer.as_uint16[i]);
for (size_t i = max_index; i < std::size(stack_copy_buffer.as_uint16); ++i)
EXPECT_EQ(0u, stack_copy_buffer.as_uint16[i]);
}
TEST(StackCopierTest, StackCopy_NonAlignedStackPointerUnalignedRewriteAtStart) {
TestStackBuffer stack_buffer = {{0}};
uint8_t* unaligned_stack_bottom =
reinterpret_cast<uint8_t*>(&stack_buffer.as_uint16[1]);
uintptr_t within_stack_pointer =
reinterpret_cast<uintptr_t>(&stack_buffer.as_uintptr[2]);
std::memcpy(unaligned_stack_bottom, &within_stack_pointer,
sizeof(within_stack_pointer));
TestStackBuffer stack_copy_buffer = {{0}};
const uint8_t* stack_copy_bottom =
CopyFunctions::CopyStackContentsAndRewritePointers(
unaligned_stack_bottom,
&stack_buffer.as_uintptr[0] + std::size(stack_buffer.as_uintptr),
StackBuffer::kPlatformStackAlignment,
&stack_copy_buffer.as_uintptr[0]);
uintptr_t copied_within_stack_pointer;
std::memcpy(&copied_within_stack_pointer, stack_copy_bottom,
sizeof(copied_within_stack_pointer));
EXPECT_EQ(within_stack_pointer, copied_within_stack_pointer);
}
TEST(StackCopierTest,
StackCopy_NonAlignedStackPointerUnalignedRewriteAfterStart) {
TestStackBuffer stack_buffer = {{0}};
uint8_t* unaligned_stack_bottom =
reinterpret_cast<uint8_t*>(&stack_buffer.as_uint16[1]);
uintptr_t within_stack_pointer =
reinterpret_cast<uintptr_t>(&stack_buffer.as_uintptr[2]);
std::memcpy(unaligned_stack_bottom + sizeof(uintptr_t), &within_stack_pointer,
sizeof(within_stack_pointer));
TestStackBuffer stack_copy_buffer = {{0}};
const uint8_t* stack_copy_bottom =
CopyFunctions::CopyStackContentsAndRewritePointers(
unaligned_stack_bottom,
&stack_buffer.as_uintptr[0] + std::size(stack_buffer.as_uintptr),
StackBuffer::kPlatformStackAlignment,
&stack_copy_buffer.as_uintptr[0]);
uintptr_t copied_within_stack_pointer;
std::memcpy(&copied_within_stack_pointer,
stack_copy_bottom + sizeof(uintptr_t),
sizeof(copied_within_stack_pointer));
EXPECT_EQ(within_stack_pointer, copied_within_stack_pointer);
}
TEST(StackCopierTest, StackCopy_NonAlignedStackPointerAlignedRewrite) {
TestStackBuffer stack_buffer = {{0}};
uint8_t* unaligned_stack_bottom =
reinterpret_cast<uint8_t*>(&stack_buffer.as_uint16[1]);
stack_buffer.as_uintptr[1] =
reinterpret_cast<uintptr_t>(&stack_buffer.as_uintptr[2]);
TestStackBuffer stack_copy_buffer = {{0}};
CopyFunctions::CopyStackContentsAndRewritePointers(
unaligned_stack_bottom,
&stack_buffer.as_uintptr[0] + std::size(stack_buffer.as_uintptr),
StackBuffer::kPlatformStackAlignment, &stack_copy_buffer.as_uintptr[0]);
EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack_copy_buffer.as_uintptr[2]),
stack_copy_buffer.as_uintptr[1]);
}
}