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