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