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 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include <algorithm>
11 #include <cstring>
12 #include <memory>
13 #include <numeric>
14 #include <utility>
15
16 #include "base/memory/raw_ptr.h"
17 #include "base/memory/raw_ref.h"
18 #include "base/profiler/stack_buffer.h"
19 #include "base/profiler/stack_copier_suspend.h"
20 #include "base/profiler/suspendable_thread_delegate.h"
21 #include "build/build_config.h"
22 #include "testing/gmock/include/gmock/gmock.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24
25 #if BUILDFLAG(IS_CHROMEOS)
26 #include "base/memory/page_size.h"
27 #endif
28
29 namespace base {
30
31 namespace {
32
33 using ::testing::Each;
34 using ::testing::ElementsAre;
35
36 // A thread delegate for use in tests that provides the expected behavior when
37 // operating on the supplied fake stack.
38 class TestSuspendableThreadDelegate : public SuspendableThreadDelegate {
39 public:
40 class TestScopedSuspendThread
41 : public SuspendableThreadDelegate::ScopedSuspendThread {
42 public:
43 TestScopedSuspendThread() = default;
44
45 TestScopedSuspendThread(const TestScopedSuspendThread&) = delete;
46 TestScopedSuspendThread& operator=(const TestScopedSuspendThread&) = delete;
47
WasSuccessful() const48 bool WasSuccessful() const override { return true; }
49 };
50
TestSuspendableThreadDelegate(const std::vector<uintptr_t> & fake_stack,RegisterContext * thread_context=nullptr)51 TestSuspendableThreadDelegate(const std::vector<uintptr_t>& fake_stack,
52 // The register context will be initialized to
53 // *|thread_context| if non-null.
54 RegisterContext* thread_context = nullptr)
55 : fake_stack_(fake_stack), thread_context_(thread_context) {}
56
57 TestSuspendableThreadDelegate(const TestSuspendableThreadDelegate&) = delete;
58 TestSuspendableThreadDelegate& operator=(
59 const TestSuspendableThreadDelegate&) = delete;
60
CreateScopedSuspendThread()61 std::unique_ptr<ScopedSuspendThread> CreateScopedSuspendThread() override {
62 return std::make_unique<TestScopedSuspendThread>();
63 }
64
GetThreadContext(RegisterContext * thread_context)65 bool GetThreadContext(RegisterContext* thread_context) override {
66 if (thread_context_)
67 *thread_context = *thread_context_;
68 // Set the stack pointer to be consistent with the provided fake stack.
69 RegisterContextStackPointer(thread_context) =
70 reinterpret_cast<uintptr_t>(&(*fake_stack_)[0]);
71 RegisterContextInstructionPointer(thread_context) =
72 reinterpret_cast<uintptr_t>((*fake_stack_)[0]);
73 return true;
74 }
75
GetThreadId() const76 PlatformThreadId GetThreadId() const override { return PlatformThreadId(); }
77
GetStackBaseAddress() const78 uintptr_t GetStackBaseAddress() const override {
79 return reinterpret_cast<uintptr_t>(&(*fake_stack_)[0] +
80 fake_stack_->size());
81 }
82
CanCopyStack(uintptr_t stack_pointer)83 bool CanCopyStack(uintptr_t stack_pointer) override { return true; }
84
GetRegistersToRewrite(RegisterContext * thread_context)85 std::vector<uintptr_t*> GetRegistersToRewrite(
86 RegisterContext* thread_context) override {
87 return {&RegisterContextFramePointer(thread_context)};
88 }
89
90 private:
91 // Must be a reference to retain the underlying allocation from the vector
92 // passed to the constructor.
93 const raw_ref<const std::vector<uintptr_t>> fake_stack_;
94 raw_ptr<RegisterContext> thread_context_;
95 };
96
97 class TestStackCopierDelegate : public StackCopier::Delegate {
98 public:
OnStackCopy()99 void OnStackCopy() override {
100 on_stack_copy_was_invoked_ = true;
101 }
102
on_stack_copy_was_invoked() const103 bool on_stack_copy_was_invoked() const { return on_stack_copy_was_invoked_; }
104
105 private:
106 bool on_stack_copy_was_invoked_ = false;
107 };
108
109 } // namespace
110
TEST(StackCopierSuspendTest,CopyStack)111 TEST(StackCopierSuspendTest, CopyStack) {
112 const std::vector<uintptr_t> stack = {0, 1, 2, 3, 4};
113 StackCopierSuspend stack_copier_suspend(
114 std::make_unique<TestSuspendableThreadDelegate>(stack));
115
116 std::unique_ptr<StackBuffer> stack_buffer =
117 std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));
118 uintptr_t stack_top = 0;
119 TimeTicks timestamp;
120 RegisterContext register_context{};
121 TestStackCopierDelegate stack_copier_delegate;
122 stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top, ×tamp,
123 ®ister_context, &stack_copier_delegate);
124
125 uintptr_t* stack_copy_bottom =
126 reinterpret_cast<uintptr_t*>(stack_buffer.get()->buffer());
127 std::vector<uintptr_t> stack_copy(stack_copy_bottom,
128 stack_copy_bottom + stack.size());
129 EXPECT_EQ(stack, stack_copy);
130 }
131
TEST(StackCopierSuspendTest,CopyStackBufferTooSmall)132 TEST(StackCopierSuspendTest, CopyStackBufferTooSmall) {
133 std::vector<uintptr_t> stack;
134 #if BUILDFLAG(IS_CHROMEOS)
135 // ChromeOS will round up the size of the stack up to the next multiple of
136 // the page size. To make the buffer "too small", the stack must be 1 element
137 // larger than the page size.
138 const size_t kStackElements = (GetPageSize() / sizeof(stack[0])) + 1;
139 #else // #if BUILDFLAG(IS_CHROMEOS)
140 const size_t kStackElements = 5; // Arbitrary
141 #endif
142 stack.reserve(kStackElements);
143 for (size_t i = 0; i < kStackElements; ++i) {
144 stack.push_back(i);
145 }
146 StackCopierSuspend stack_copier_suspend(
147 std::make_unique<TestSuspendableThreadDelegate>(stack));
148
149 std::unique_ptr<StackBuffer> stack_buffer =
150 std::make_unique<StackBuffer>((stack.size() - 1) * sizeof(stack[0]));
151 // Make the buffer different than the input stack.
152 constexpr uintptr_t kBufferInitializer = 100;
153 size_t stack_buffer_elements =
154 stack_buffer->size() / sizeof(stack_buffer->buffer()[0]);
155 std::fill_n(stack_buffer->buffer(), stack_buffer_elements,
156 kBufferInitializer);
157 uintptr_t stack_top = 0;
158 TimeTicks timestamp;
159 RegisterContext register_context{};
160 TestStackCopierDelegate stack_copier_delegate;
161 stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top, ×tamp,
162 ®ister_context, &stack_copier_delegate);
163
164 uintptr_t* stack_copy_bottom =
165 reinterpret_cast<uintptr_t*>(stack_buffer.get()->buffer());
166 std::vector<uintptr_t> stack_copy(stack_copy_bottom,
167 stack_copy_bottom + stack_buffer_elements);
168 // Use the buffer not being overwritten as a proxy for the unwind being
169 // aborted.
170 EXPECT_THAT(stack_copy, Each(kBufferInitializer));
171 }
172
TEST(StackCopierSuspendTest,CopyStackAndRewritePointers)173 TEST(StackCopierSuspendTest, CopyStackAndRewritePointers) {
174 // Allocate space for the stack, then make its elements point to themselves.
175 std::vector<uintptr_t> stack(2);
176 stack[0] = reinterpret_cast<uintptr_t>(&stack[0]);
177 stack[1] = reinterpret_cast<uintptr_t>(&stack[1]);
178 StackCopierSuspend stack_copier_suspend(
179 std::make_unique<TestSuspendableThreadDelegate>(stack));
180
181 std::unique_ptr<StackBuffer> stack_buffer =
182 std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));
183 uintptr_t stack_top = 0;
184 TimeTicks timestamp;
185 RegisterContext register_context{};
186 TestStackCopierDelegate stack_copier_delegate;
187 stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top, ×tamp,
188 ®ister_context, &stack_copier_delegate);
189
190 uintptr_t* stack_copy_bottom =
191 reinterpret_cast<uintptr_t*>(stack_buffer.get()->buffer());
192 std::vector<uintptr_t> stack_copy(stack_copy_bottom,
193 stack_copy_bottom + stack.size());
194 EXPECT_THAT(stack_copy,
195 ElementsAre(reinterpret_cast<uintptr_t>(stack_copy_bottom),
196 reinterpret_cast<uintptr_t>(stack_copy_bottom) +
197 sizeof(uintptr_t)));
198 }
199
TEST(StackCopierSuspendTest,CopyStackTimeStamp)200 TEST(StackCopierSuspendTest, CopyStackTimeStamp) {
201 const std::vector<uintptr_t> stack = {0};
202 StackCopierSuspend stack_copier_suspend(
203 std::make_unique<TestSuspendableThreadDelegate>(stack));
204
205 std::unique_ptr<StackBuffer> stack_buffer =
206 std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));
207 uintptr_t stack_top = 0;
208 TimeTicks timestamp;
209 RegisterContext register_context{};
210 TestStackCopierDelegate stack_copier_delegate;
211
212 TimeTicks before = TimeTicks::Now();
213 stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top, ×tamp,
214 ®ister_context, &stack_copier_delegate);
215 TimeTicks after = TimeTicks::Now();
216
217 EXPECT_GE(timestamp, before);
218 EXPECT_LE(timestamp, after);
219 }
220
TEST(StackCopierSuspendTest,CopyStackDelegateInvoked)221 TEST(StackCopierSuspendTest, CopyStackDelegateInvoked) {
222 const std::vector<uintptr_t> stack = {0};
223 StackCopierSuspend stack_copier_suspend(
224 std::make_unique<TestSuspendableThreadDelegate>(stack));
225
226 std::unique_ptr<StackBuffer> stack_buffer =
227 std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));
228 uintptr_t stack_top = 0;
229 TimeTicks timestamp;
230 RegisterContext register_context{};
231 TestStackCopierDelegate stack_copier_delegate;
232
233 stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top, ×tamp,
234 ®ister_context, &stack_copier_delegate);
235
236 EXPECT_TRUE(stack_copier_delegate.on_stack_copy_was_invoked());
237 }
238
TEST(StackCopierSuspendTest,RewriteRegisters)239 TEST(StackCopierSuspendTest, RewriteRegisters) {
240 std::vector<uintptr_t> stack = {0, 1, 2};
241 RegisterContext register_context{};
242 TestStackCopierDelegate stack_copier_delegate;
243 RegisterContextFramePointer(®ister_context) =
244 reinterpret_cast<uintptr_t>(&stack[1]);
245 StackCopierSuspend stack_copier_suspend(
246 std::make_unique<TestSuspendableThreadDelegate>(stack,
247 ®ister_context));
248
249 std::unique_ptr<StackBuffer> stack_buffer =
250 std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));
251 uintptr_t stack_top = 0;
252 TimeTicks timestamp;
253 stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top, ×tamp,
254 ®ister_context, &stack_copier_delegate);
255
256 uintptr_t stack_copy_bottom =
257 reinterpret_cast<uintptr_t>(stack_buffer.get()->buffer());
258 EXPECT_EQ(stack_copy_bottom + sizeof(uintptr_t),
259 RegisterContextFramePointer(®ister_context));
260 }
261
262 } // namespace base
263