123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- // Copyright 2019 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #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)];
- };
- } // namespace
- 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) {
- // We use this variable only for its address, which is outside of
- // original_stack.
- 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;
- // Fill the stack buffer with increasing uintptr_t values.
- std::iota(
- &original_stack.as_uintptr[0],
- &original_stack.as_uintptr[0] + std::size(original_stack.as_uintptr),
- 100);
- // Replace the third value with an address within the buffer.
- 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;
- // Fill the stack buffer with increasing uint16_t values.
- std::iota(&stack_buffer.as_uint16[0],
- &stack_buffer.as_uint16[0] + std::size(stack_buffer.as_uint16),
- 100);
- // Set the stack bottom to the unaligned location one uint16_t into the
- // buffer.
- uint8_t* unaligned_stack_bottom =
- reinterpret_cast<uint8_t*>(&stack_buffer.as_uint16[1]);
- // Leave extra space within the stack buffer beyond the end of the stack, but
- // preserve the platform alignment.
- 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)];
- // Initialize the copy to all zeros.
- 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]);
- // The stack copy bottom address is expected to be at the same offset into the
- // stack copy buffer as the unaligned stack bottom is from the stack buffer.
- // Since the buffers have the same platform stack alignment this also ensures
- // the alignment of the bottom addresses is the same.
- EXPECT_EQ(unaligned_stack_bottom - &stack_buffer.as_uint8[0],
- stack_copy_bottom - &stack_copy_buffer.as_uint8[0]);
- // The first value in the copy should not be overwritten since the stack
- // starts at the second uint16_t.
- EXPECT_EQ(0u, stack_copy_buffer.as_uint16[0]);
- // The next values up to the extra space should have been copied.
- 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]);
- // None of the values in the empty space should have been copied.
- for (size_t i = max_index; i < std::size(stack_copy_buffer.as_uint16); ++i)
- EXPECT_EQ(0u, stack_copy_buffer.as_uint16[i]);
- }
- // Checks that an unaligned within-stack pointer value at the start of the stack
- // is not rewritten.
- TEST(StackCopierTest, StackCopy_NonAlignedStackPointerUnalignedRewriteAtStart) {
- // Initially fill the buffer with 0s.
- TestStackBuffer stack_buffer = {{0}};
- // Set the stack bottom to the unaligned location one uint16_t into the
- // buffer.
- uint8_t* unaligned_stack_bottom =
- reinterpret_cast<uint8_t*>(&stack_buffer.as_uint16[1]);
- // Set the first unaligned pointer-sized value to an address within the stack.
- 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));
- // The rewriting should only operate on pointer-aligned values so the
- // unaligned value should be copied verbatim.
- EXPECT_EQ(within_stack_pointer, copied_within_stack_pointer);
- }
- // Checks that an unaligned within-stack pointer after the start of the stack is
- // not rewritten.
- TEST(StackCopierTest,
- StackCopy_NonAlignedStackPointerUnalignedRewriteAfterStart) {
- // Initially fill the buffer with 0s.
- TestStackBuffer stack_buffer = {{0}};
- // Set the stack bottom to the unaligned location one uint16_t into the
- // buffer.
- uint8_t* unaligned_stack_bottom =
- reinterpret_cast<uint8_t*>(&stack_buffer.as_uint16[1]);
- // Set the second unaligned pointer-sized value to an address within the
- // stack.
- 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));
- // The rewriting should only operate on pointer-aligned values so the
- // unaligned value should be copied verbatim.
- EXPECT_EQ(within_stack_pointer, copied_within_stack_pointer);
- }
- TEST(StackCopierTest, StackCopy_NonAlignedStackPointerAlignedRewrite) {
- // Initially fill the buffer with 0s.
- TestStackBuffer stack_buffer = {{0}};
- // Set the stack bottom to the unaligned location one uint16_t into the
- // buffer.
- uint8_t* unaligned_stack_bottom =
- reinterpret_cast<uint8_t*>(&stack_buffer.as_uint16[1]);
- // Set the second aligned pointer-sized value to an address within the stack.
- 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]);
- // The aligned pointer should have been rewritten to point within the stack
- // copy.
- EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack_copy_buffer.as_uintptr[2]),
- stack_copy_buffer.as_uintptr[1]);
- }
- } // namespace base
|