stack_copier_unittest.cc 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include <cstring>
  5. #include <iterator>
  6. #include <memory>
  7. #include <numeric>
  8. #include "base/profiler/stack_buffer.h"
  9. #include "base/profiler/stack_copier.h"
  10. #include "testing/gmock/include/gmock/gmock.h"
  11. #include "testing/gtest/include/gtest/gtest.h"
  12. namespace base {
  13. namespace {
  14. class CopyFunctions : public StackCopier {
  15. public:
  16. using StackCopier::CopyStackContentsAndRewritePointers;
  17. using StackCopier::RewritePointerIfInOriginalStack;
  18. };
  19. static constexpr size_t kTestStackBufferSize = sizeof(uintptr_t) * 4;
  20. union alignas(StackBuffer::kPlatformStackAlignment) TestStackBuffer {
  21. uintptr_t as_uintptr[kTestStackBufferSize / sizeof(uintptr_t)];
  22. uint16_t as_uint16[kTestStackBufferSize / sizeof(uint16_t)];
  23. uint8_t as_uint8[kTestStackBufferSize / sizeof(uint8_t)];
  24. };
  25. } // namespace
  26. TEST(StackCopierTest, RewritePointerIfInOriginalStack_InStack) {
  27. uintptr_t original_stack[4];
  28. uintptr_t stack_copy[4];
  29. EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack_copy[2]),
  30. CopyFunctions::RewritePointerIfInOriginalStack(
  31. reinterpret_cast<uint8_t*>(&original_stack[0]),
  32. &original_stack[0] + std::size(original_stack),
  33. reinterpret_cast<uint8_t*>(&stack_copy[0]),
  34. reinterpret_cast<uintptr_t>(&original_stack[2])));
  35. }
  36. TEST(StackCopierTest, RewritePointerIfInOriginalStack_NotInStack) {
  37. // We use this variable only for its address, which is outside of
  38. // original_stack.
  39. uintptr_t non_stack_location;
  40. uintptr_t original_stack[4];
  41. uintptr_t stack_copy[4];
  42. EXPECT_EQ(reinterpret_cast<uintptr_t>(&non_stack_location),
  43. CopyFunctions::RewritePointerIfInOriginalStack(
  44. reinterpret_cast<uint8_t*>(&original_stack[0]),
  45. &original_stack[0] + std::size(original_stack),
  46. reinterpret_cast<uint8_t*>(&stack_copy[0]),
  47. reinterpret_cast<uintptr_t>(&non_stack_location)));
  48. }
  49. TEST(StackCopierTest, StackCopy) {
  50. TestStackBuffer original_stack;
  51. // Fill the stack buffer with increasing uintptr_t values.
  52. std::iota(
  53. &original_stack.as_uintptr[0],
  54. &original_stack.as_uintptr[0] + std::size(original_stack.as_uintptr),
  55. 100);
  56. // Replace the third value with an address within the buffer.
  57. original_stack.as_uintptr[2] =
  58. reinterpret_cast<uintptr_t>(&original_stack.as_uintptr[1]);
  59. TestStackBuffer stack_copy;
  60. CopyFunctions::CopyStackContentsAndRewritePointers(
  61. &original_stack.as_uint8[0],
  62. &original_stack.as_uintptr[0] + std::size(original_stack.as_uintptr),
  63. StackBuffer::kPlatformStackAlignment, &stack_copy.as_uintptr[0]);
  64. EXPECT_EQ(original_stack.as_uintptr[0], stack_copy.as_uintptr[0]);
  65. EXPECT_EQ(original_stack.as_uintptr[1], stack_copy.as_uintptr[1]);
  66. EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack_copy.as_uintptr[1]),
  67. stack_copy.as_uintptr[2]);
  68. EXPECT_EQ(original_stack.as_uintptr[3], stack_copy.as_uintptr[3]);
  69. }
  70. TEST(StackCopierTest, StackCopy_NonAlignedStackPointerCopy) {
  71. TestStackBuffer stack_buffer;
  72. // Fill the stack buffer with increasing uint16_t values.
  73. std::iota(&stack_buffer.as_uint16[0],
  74. &stack_buffer.as_uint16[0] + std::size(stack_buffer.as_uint16),
  75. 100);
  76. // Set the stack bottom to the unaligned location one uint16_t into the
  77. // buffer.
  78. uint8_t* unaligned_stack_bottom =
  79. reinterpret_cast<uint8_t*>(&stack_buffer.as_uint16[1]);
  80. // Leave extra space within the stack buffer beyond the end of the stack, but
  81. // preserve the platform alignment.
  82. const size_t extra_space = StackBuffer::kPlatformStackAlignment;
  83. uintptr_t* stack_top =
  84. &stack_buffer.as_uintptr[std::size(stack_buffer.as_uintptr) -
  85. extra_space / sizeof(uintptr_t)];
  86. // Initialize the copy to all zeros.
  87. TestStackBuffer stack_copy_buffer = {{0}};
  88. const uint8_t* stack_copy_bottom =
  89. CopyFunctions::CopyStackContentsAndRewritePointers(
  90. unaligned_stack_bottom, stack_top,
  91. StackBuffer::kPlatformStackAlignment,
  92. &stack_copy_buffer.as_uintptr[0]);
  93. // The stack copy bottom address is expected to be at the same offset into the
  94. // stack copy buffer as the unaligned stack bottom is from the stack buffer.
  95. // Since the buffers have the same platform stack alignment this also ensures
  96. // the alignment of the bottom addresses is the same.
  97. EXPECT_EQ(unaligned_stack_bottom - &stack_buffer.as_uint8[0],
  98. stack_copy_bottom - &stack_copy_buffer.as_uint8[0]);
  99. // The first value in the copy should not be overwritten since the stack
  100. // starts at the second uint16_t.
  101. EXPECT_EQ(0u, stack_copy_buffer.as_uint16[0]);
  102. // The next values up to the extra space should have been copied.
  103. const size_t max_index =
  104. std::size(stack_copy_buffer.as_uint16) - extra_space / sizeof(uint16_t);
  105. for (size_t i = 1; i < max_index; ++i)
  106. EXPECT_EQ(i + 100, stack_copy_buffer.as_uint16[i]);
  107. // None of the values in the empty space should have been copied.
  108. for (size_t i = max_index; i < std::size(stack_copy_buffer.as_uint16); ++i)
  109. EXPECT_EQ(0u, stack_copy_buffer.as_uint16[i]);
  110. }
  111. // Checks that an unaligned within-stack pointer value at the start of the stack
  112. // is not rewritten.
  113. TEST(StackCopierTest, StackCopy_NonAlignedStackPointerUnalignedRewriteAtStart) {
  114. // Initially fill the buffer with 0s.
  115. TestStackBuffer stack_buffer = {{0}};
  116. // Set the stack bottom to the unaligned location one uint16_t into the
  117. // buffer.
  118. uint8_t* unaligned_stack_bottom =
  119. reinterpret_cast<uint8_t*>(&stack_buffer.as_uint16[1]);
  120. // Set the first unaligned pointer-sized value to an address within the stack.
  121. uintptr_t within_stack_pointer =
  122. reinterpret_cast<uintptr_t>(&stack_buffer.as_uintptr[2]);
  123. std::memcpy(unaligned_stack_bottom, &within_stack_pointer,
  124. sizeof(within_stack_pointer));
  125. TestStackBuffer stack_copy_buffer = {{0}};
  126. const uint8_t* stack_copy_bottom =
  127. CopyFunctions::CopyStackContentsAndRewritePointers(
  128. unaligned_stack_bottom,
  129. &stack_buffer.as_uintptr[0] + std::size(stack_buffer.as_uintptr),
  130. StackBuffer::kPlatformStackAlignment,
  131. &stack_copy_buffer.as_uintptr[0]);
  132. uintptr_t copied_within_stack_pointer;
  133. std::memcpy(&copied_within_stack_pointer, stack_copy_bottom,
  134. sizeof(copied_within_stack_pointer));
  135. // The rewriting should only operate on pointer-aligned values so the
  136. // unaligned value should be copied verbatim.
  137. EXPECT_EQ(within_stack_pointer, copied_within_stack_pointer);
  138. }
  139. // Checks that an unaligned within-stack pointer after the start of the stack is
  140. // not rewritten.
  141. TEST(StackCopierTest,
  142. StackCopy_NonAlignedStackPointerUnalignedRewriteAfterStart) {
  143. // Initially fill the buffer with 0s.
  144. TestStackBuffer stack_buffer = {{0}};
  145. // Set the stack bottom to the unaligned location one uint16_t into the
  146. // buffer.
  147. uint8_t* unaligned_stack_bottom =
  148. reinterpret_cast<uint8_t*>(&stack_buffer.as_uint16[1]);
  149. // Set the second unaligned pointer-sized value to an address within the
  150. // stack.
  151. uintptr_t within_stack_pointer =
  152. reinterpret_cast<uintptr_t>(&stack_buffer.as_uintptr[2]);
  153. std::memcpy(unaligned_stack_bottom + sizeof(uintptr_t), &within_stack_pointer,
  154. sizeof(within_stack_pointer));
  155. TestStackBuffer stack_copy_buffer = {{0}};
  156. const uint8_t* stack_copy_bottom =
  157. CopyFunctions::CopyStackContentsAndRewritePointers(
  158. unaligned_stack_bottom,
  159. &stack_buffer.as_uintptr[0] + std::size(stack_buffer.as_uintptr),
  160. StackBuffer::kPlatformStackAlignment,
  161. &stack_copy_buffer.as_uintptr[0]);
  162. uintptr_t copied_within_stack_pointer;
  163. std::memcpy(&copied_within_stack_pointer,
  164. stack_copy_bottom + sizeof(uintptr_t),
  165. sizeof(copied_within_stack_pointer));
  166. // The rewriting should only operate on pointer-aligned values so the
  167. // unaligned value should be copied verbatim.
  168. EXPECT_EQ(within_stack_pointer, copied_within_stack_pointer);
  169. }
  170. TEST(StackCopierTest, StackCopy_NonAlignedStackPointerAlignedRewrite) {
  171. // Initially fill the buffer with 0s.
  172. TestStackBuffer stack_buffer = {{0}};
  173. // Set the stack bottom to the unaligned location one uint16_t into the
  174. // buffer.
  175. uint8_t* unaligned_stack_bottom =
  176. reinterpret_cast<uint8_t*>(&stack_buffer.as_uint16[1]);
  177. // Set the second aligned pointer-sized value to an address within the stack.
  178. stack_buffer.as_uintptr[1] =
  179. reinterpret_cast<uintptr_t>(&stack_buffer.as_uintptr[2]);
  180. TestStackBuffer stack_copy_buffer = {{0}};
  181. CopyFunctions::CopyStackContentsAndRewritePointers(
  182. unaligned_stack_bottom,
  183. &stack_buffer.as_uintptr[0] + std::size(stack_buffer.as_uintptr),
  184. StackBuffer::kPlatformStackAlignment, &stack_copy_buffer.as_uintptr[0]);
  185. // The aligned pointer should have been rewritten to point within the stack
  186. // copy.
  187. EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack_copy_buffer.as_uintptr[2]),
  188. stack_copy_buffer.as_uintptr[1]);
  189. }
  190. } // namespace base