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.
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"
20 #if BUILDFLAG(IS_CHROMEOS)
21 #include "base/memory/page_size.h"
28 using ::testing::Each;
29 using ::testing::ElementsAre;
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 {
35 class TestScopedSuspendThread
36 : public SuspendableThreadDelegate::ScopedSuspendThread {
38 TestScopedSuspendThread() = default;
40 TestScopedSuspendThread(const TestScopedSuspendThread&) = delete;
41 TestScopedSuspendThread& operator=(const TestScopedSuspendThread&) = delete;
43 bool WasSuccessful() const override { return true; }
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) {}
52 TestSuspendableThreadDelegate(const TestSuspendableThreadDelegate&) = delete;
53 TestSuspendableThreadDelegate& operator=(
54 const TestSuspendableThreadDelegate&) = delete;
56 std::unique_ptr<ScopedSuspendThread> CreateScopedSuspendThread() override {
57 return std::make_unique<TestScopedSuspendThread>();
60 bool GetThreadContext(RegisterContext* thread_context) override {
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]);
71 PlatformThreadId GetThreadId() const override { return PlatformThreadId(); }
73 uintptr_t GetStackBaseAddress() const override {
74 return reinterpret_cast<uintptr_t>(&(*fake_stack_)[0] +
78 bool CanCopyStack(uintptr_t stack_pointer) override { return true; }
80 std::vector<uintptr_t*> GetRegistersToRewrite(
81 RegisterContext* thread_context) override {
82 return {&RegisterContextFramePointer(thread_context)};
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_;
92 class TestStackCopierDelegate : public StackCopier::Delegate {
94 void OnStackCopy() override {
95 on_stack_copy_was_invoked_ = true;
98 bool on_stack_copy_was_invoked() const { return on_stack_copy_was_invoked_; }
101 bool on_stack_copy_was_invoked_ = false;
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));
111 std::unique_ptr<StackBuffer> stack_buffer =
112 std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));
113 uintptr_t stack_top = 0;
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);
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);
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
137 stack.reserve(kStackElements);
138 for (size_t i = 0; i < kStackElements; ++i) {
141 StackCopierSuspend stack_copier_suspend(
142 std::make_unique<TestSuspendableThreadDelegate>(stack));
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,
152 uintptr_t stack_top = 0;
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);
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
165 EXPECT_THAT(stack_copy, Each(kBufferInitializer));
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));
176 std::unique_ptr<StackBuffer> stack_buffer =
177 std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));
178 uintptr_t stack_top = 0;
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);
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) +
195 TEST(StackCopierSuspendTest, CopyStackTimeStamp) {
196 const std::vector<uintptr_t> stack = {0};
197 StackCopierSuspend stack_copier_suspend(
198 std::make_unique<TestSuspendableThreadDelegate>(stack));
200 std::unique_ptr<StackBuffer> stack_buffer =
201 std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));
202 uintptr_t stack_top = 0;
204 RegisterContext register_context{};
205 TestStackCopierDelegate stack_copier_delegate;
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();
212 EXPECT_GE(timestamp, before);
213 EXPECT_LE(timestamp, after);
216 TEST(StackCopierSuspendTest, CopyStackDelegateInvoked) {
217 const std::vector<uintptr_t> stack = {0};
218 StackCopierSuspend stack_copier_suspend(
219 std::make_unique<TestSuspendableThreadDelegate>(stack));
221 std::unique_ptr<StackBuffer> stack_buffer =
222 std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));
223 uintptr_t stack_top = 0;
225 RegisterContext register_context{};
226 TestStackCopierDelegate stack_copier_delegate;
228 stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top, ×tamp,
229 ®ister_context, &stack_copier_delegate);
231 EXPECT_TRUE(stack_copier_delegate.on_stack_copy_was_invoked());
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,
244 std::unique_ptr<StackBuffer> stack_buffer =
245 std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));
246 uintptr_t stack_top = 0;
248 stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top, ×tamp,
249 ®ister_context, &stack_copier_delegate);
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));