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