• 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 <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, &timestamp,
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, &timestamp,
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, &timestamp,
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, &timestamp,
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