• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &timestamp,
123                                  &register_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, &timestamp,
162                                  &register_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, &timestamp,
188                                  &register_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, &timestamp,
214                                  &register_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, &timestamp,
234                                  &register_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(&register_context) =
244       reinterpret_cast<uintptr_t>(&stack[1]);
245   StackCopierSuspend stack_copier_suspend(
246       std::make_unique<TestSuspendableThreadDelegate>(stack,
247                                                       &register_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, &timestamp,
254                                  &register_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(&register_context));
260 }
261 
262 }  // namespace base
263