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 <string.h>
11 #include <algorithm>
12 #include <utility>
13
14 #include "base/debug/alias.h"
15 #include "base/profiler/sampling_profiler_thread_token.h"
16 #include "base/profiler/stack_buffer.h"
17 #include "base/profiler/stack_copier_signal.h"
18 #include "base/profiler/thread_delegate_posix.h"
19 #include "base/synchronization/waitable_event.h"
20 #include "base/threading/platform_thread.h"
21 #include "base/threading/simple_thread.h"
22 #include "build/build_config.h"
23 #include "build/chromeos_buildflags.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25
26 namespace base {
27
28 namespace {
29
30 // Values to write to the stack and look for in the copy.
31 static const uint32_t kStackSentinels[] = {0xf312ecd9, 0x1fcd7f19, 0xe69e617d,
32 0x8245f94f};
33
34 class TargetThread : public SimpleThread {
35 public:
TargetThread()36 TargetThread()
37 : SimpleThread("target", Options()),
38 started_(WaitableEvent::ResetPolicy::MANUAL,
39 WaitableEvent::InitialState::NOT_SIGNALED),
40 copy_finished_(WaitableEvent::ResetPolicy::MANUAL,
41 WaitableEvent::InitialState::NOT_SIGNALED) {}
42
Run()43 void Run() override {
44 thread_token_ = GetSamplingProfilerCurrentThreadToken();
45
46 // Copy the sentinel values onto the stack. Volatile to defeat compiler
47 // optimizations.
48 volatile uint32_t sentinels[std::size(kStackSentinels)];
49 for (size_t i = 0; i < std::size(kStackSentinels); ++i)
50 sentinels[i] = kStackSentinels[i];
51
52 started_.Signal();
53 copy_finished_.Wait();
54 }
55
GetThreadToken()56 SamplingProfilerThreadToken GetThreadToken() {
57 started_.Wait();
58 return thread_token_;
59 }
60
NotifyCopyFinished()61 void NotifyCopyFinished() { copy_finished_.Signal(); }
62
63 private:
64 WaitableEvent started_;
65 WaitableEvent copy_finished_;
66 SamplingProfilerThreadToken thread_token_;
67 };
68
69 class TestStackCopierDelegate : public StackCopier::Delegate {
70 public:
OnStackCopy()71 void OnStackCopy() override {
72 on_stack_copy_was_invoked_ = true;
73 }
74
on_stack_copy_was_invoked() const75 bool on_stack_copy_was_invoked() const { return on_stack_copy_was_invoked_; }
76
77 private:
78 bool on_stack_copy_was_invoked_ = false;
79 };
80
81 } // namespace
82
83 // ASAN moves local variables outside of the stack extents, which breaks the
84 // sentinels.
85 // MSan complains that the memcmp() reads uninitialized memory.
86 // TSAN hangs on the AsyncSafeWaitableEvent FUTEX_WAIT call.
87 #if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
88 defined(THREAD_SANITIZER)
89 #define MAYBE_CopyStack DISABLED_CopyStack
90 #elif BUILDFLAG(IS_LINUX)
91 // We don't support getting the stack base address on Linux, and thus can't
92 // copy the stack. // https://crbug.com/1394278
93 #define MAYBE_CopyStack DISABLED_CopyStack
94 #else
95 #define MAYBE_CopyStack CopyStack
96 #endif
TEST(StackCopierSignalTest,MAYBE_CopyStack)97 TEST(StackCopierSignalTest, MAYBE_CopyStack) {
98 StackBuffer stack_buffer(/* buffer_size = */ 1 << 20);
99 memset(stack_buffer.buffer(), 0, stack_buffer.size());
100 uintptr_t stack_top = 0;
101 TimeTicks timestamp;
102 RegisterContext context;
103 TestStackCopierDelegate stack_copier_delegate;
104
105 auto thread_delegate =
106 ThreadDelegatePosix::Create(GetSamplingProfilerCurrentThreadToken());
107 ASSERT_TRUE(thread_delegate);
108 StackCopierSignal copier(std::move(thread_delegate));
109
110 // Copy the sentinel values onto the stack.
111 uint32_t sentinels[std::size(kStackSentinels)];
112 for (size_t i = 0; i < std::size(kStackSentinels); ++i)
113 sentinels[i] = kStackSentinels[i];
114 base::debug::Alias((void*)sentinels); // Defeat compiler optimizations.
115
116 bool result = copier.CopyStack(&stack_buffer, &stack_top, ×tamp,
117 &context, &stack_copier_delegate);
118 ASSERT_TRUE(result);
119
120 uint32_t* const end = reinterpret_cast<uint32_t*>(stack_top);
121 uint32_t* const sentinel_location = std::find_if(
122 reinterpret_cast<uint32_t*>(RegisterContextStackPointer(&context)), end,
123 [](const uint32_t& location) {
124 return memcmp(&location, &kStackSentinels[0],
125 sizeof(kStackSentinels)) == 0;
126 });
127 EXPECT_NE(end, sentinel_location);
128 }
129
130 // TSAN hangs on the AsyncSafeWaitableEvent FUTEX_WAIT call.
131 #if defined(THREAD_SANITIZER)
132 #define MAYBE_CopyStackTimestamp DISABLED_CopyStackTimestamp
133 #elif BUILDFLAG(IS_LINUX)
134 // We don't support getting the stack base address on Linux, and thus can't
135 // copy the stack. // https://crbug.com/1394278
136 #define MAYBE_CopyStackTimestamp DISABLED_CopyStackTimestamp
137 #else
138 #define MAYBE_CopyStackTimestamp CopyStackTimestamp
139 #endif
TEST(StackCopierSignalTest,MAYBE_CopyStackTimestamp)140 TEST(StackCopierSignalTest, MAYBE_CopyStackTimestamp) {
141 StackBuffer stack_buffer(/* buffer_size = */ 1 << 20);
142 memset(stack_buffer.buffer(), 0, stack_buffer.size());
143 uintptr_t stack_top = 0;
144 TimeTicks timestamp;
145 RegisterContext context;
146 TestStackCopierDelegate stack_copier_delegate;
147
148 auto thread_delegate =
149 ThreadDelegatePosix::Create(GetSamplingProfilerCurrentThreadToken());
150 ASSERT_TRUE(thread_delegate);
151 StackCopierSignal copier(std::move(thread_delegate));
152
153 TimeTicks before = TimeTicks::Now();
154 bool result = copier.CopyStack(&stack_buffer, &stack_top, ×tamp,
155 &context, &stack_copier_delegate);
156 TimeTicks after = TimeTicks::Now();
157 ASSERT_TRUE(result);
158
159 EXPECT_GE(timestamp, before);
160 EXPECT_LE(timestamp, after);
161 }
162
163 // TSAN hangs on the AsyncSafeWaitableEvent FUTEX_WAIT call.
164 #if defined(THREAD_SANITIZER)
165 #define MAYBE_CopyStackDelegateInvoked DISABLED_CopyStackDelegateInvoked
166 #elif BUILDFLAG(IS_LINUX)
167 // We don't support getting the stack base address on Linux, and thus can't
168 // copy the stack. // https://crbug.com/1394278
169 #define MAYBE_CopyStackDelegateInvoked DISABLED_CopyStackDelegateInvoked
170 #else
171 #define MAYBE_CopyStackDelegateInvoked CopyStackDelegateInvoked
172 #endif
TEST(StackCopierSignalTest,MAYBE_CopyStackDelegateInvoked)173 TEST(StackCopierSignalTest, MAYBE_CopyStackDelegateInvoked) {
174 StackBuffer stack_buffer(/* buffer_size = */ 1 << 20);
175 memset(stack_buffer.buffer(), 0, stack_buffer.size());
176 uintptr_t stack_top = 0;
177 TimeTicks timestamp;
178 RegisterContext context;
179 TestStackCopierDelegate stack_copier_delegate;
180
181 auto thread_delegate =
182 ThreadDelegatePosix::Create(GetSamplingProfilerCurrentThreadToken());
183 ASSERT_TRUE(thread_delegate);
184 StackCopierSignal copier(std::move(thread_delegate));
185
186 bool result = copier.CopyStack(&stack_buffer, &stack_top, ×tamp,
187 &context, &stack_copier_delegate);
188 ASSERT_TRUE(result);
189
190 EXPECT_TRUE(stack_copier_delegate.on_stack_copy_was_invoked());
191 }
192
193 // Limit to 32-bit Android, which is the platform we care about for this
194 // functionality. The test is broken on too many other varied platforms to try
195 // to selectively disable.
196 #if !(BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_32_BITS))
197 #define MAYBE_CopyStackFromOtherThread DISABLED_CopyStackFromOtherThread
198 #elif BUILDFLAG(IS_LINUX)
199 // We don't support getting the stack base address on Linux, and thus can't
200 // copy the stack. // https://crbug.com/1394278
201 #define MAYBE_CopyStackFromOtherThread DISABLED_CopyStackFromOtherThread
202 #else
203 #define MAYBE_CopyStackFromOtherThread CopyStackFromOtherThread
204 #endif
TEST(StackCopierSignalTest,MAYBE_CopyStackFromOtherThread)205 TEST(StackCopierSignalTest, MAYBE_CopyStackFromOtherThread) {
206 StackBuffer stack_buffer(/* buffer_size = */ 1 << 20);
207 memset(stack_buffer.buffer(), 0, stack_buffer.size());
208 uintptr_t stack_top = 0;
209 TimeTicks timestamp;
210 RegisterContext context{};
211 TestStackCopierDelegate stack_copier_delegate;
212
213 TargetThread target_thread;
214 target_thread.Start();
215 const SamplingProfilerThreadToken thread_token =
216 target_thread.GetThreadToken();
217
218 auto thread_delegate = ThreadDelegatePosix::Create(thread_token);
219 ASSERT_TRUE(thread_delegate);
220 StackCopierSignal copier(std::move(thread_delegate));
221
222 bool result = copier.CopyStack(&stack_buffer, &stack_top, ×tamp,
223 &context, &stack_copier_delegate);
224 ASSERT_TRUE(result);
225
226 target_thread.NotifyCopyFinished();
227 target_thread.Join();
228
229 uint32_t* const end = reinterpret_cast<uint32_t*>(stack_top);
230 uint32_t* const sentinel_location = std::find_if(
231 reinterpret_cast<uint32_t*>(RegisterContextStackPointer(&context)), end,
232 [](const uint32_t& location) {
233 return memcmp(&location, &kStackSentinels[0],
234 sizeof(kStackSentinels)) == 0;
235 });
236 EXPECT_NE(end, sentinel_location);
237 }
238
239 } // namespace base
240