1 /*
2  *  Copyright 2019 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "sdk/android/native_api/stacktrace/stacktrace.h"
12 
13 #include <dlfcn.h>
14 
15 #include <atomic>
16 #include <memory>
17 #include <vector>
18 
19 #include "rtc_base/event.h"
20 #include "rtc_base/logging.h"
21 #include "rtc_base/platform_thread.h"
22 #include "rtc_base/string_utils.h"
23 #include "rtc_base/strings/string_builder.h"
24 #include "rtc_base/synchronization/mutex.h"
25 #include "rtc_base/system/inline.h"
26 #include "system_wrappers/include/sleep.h"
27 #include "test/gtest.h"
28 
29 namespace webrtc {
30 namespace test {
31 
32 namespace {
33 
34 // A simple atomic spin event. Implemented with std::atomic_flag, since the C++
35 // standard guarantees that that type is implemented with actual atomic
36 // instructions (as opposed to e.g. with a mutex). Uses sequentially consistent
37 // memory order since this is a test, where simplicity trumps performance.
38 class SimpleSpinEvent {
39  public:
40   // Initialize the event to its blocked state.
SimpleSpinEvent()41   SimpleSpinEvent() {
42     static_cast<void>(blocked_.test_and_set(std::memory_order_seq_cst));
43   }
44 
45   // Busy-wait for the event to become unblocked, and block it behind us as we
46   // leave.
Wait()47   void Wait() {
48     bool was_blocked;
49     do {
50       // Check if the event was blocked, and set it to blocked.
51       was_blocked = blocked_.test_and_set(std::memory_order_seq_cst);
52     } while (was_blocked);
53   }
54 
55   // Unblock the event.
Set()56   void Set() { blocked_.clear(std::memory_order_seq_cst); }
57 
58  private:
59   std::atomic_flag blocked_;
60 };
61 
62 // Returns the execution address relative to the .so base address. This matches
63 // the addresses we get from GetStacktrace().
GetCurrentRelativeExecutionAddress()64 RTC_NO_INLINE uint32_t GetCurrentRelativeExecutionAddress() {
65   void* pc = __builtin_return_address(0);
66   Dl_info dl_info = {};
67   const bool success = dladdr(pc, &dl_info);
68   EXPECT_TRUE(success);
69   return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(pc) -
70                                reinterpret_cast<uintptr_t>(dl_info.dli_fbase));
71 }
72 
73 // Returns true if any of the stack trace element is inside the specified
74 // region.
StackTraceContainsRange(const std::vector<StackTraceElement> & stack_trace,uintptr_t pc_low,uintptr_t pc_high)75 bool StackTraceContainsRange(const std::vector<StackTraceElement>& stack_trace,
76                              uintptr_t pc_low,
77                              uintptr_t pc_high) {
78   for (const StackTraceElement& stack_trace_element : stack_trace) {
79     if (pc_low <= stack_trace_element.relative_address &&
80         pc_high >= stack_trace_element.relative_address) {
81       return true;
82     }
83   }
84   return false;
85 }
86 
87 class DeadlockInterface {
88  public:
~DeadlockInterface()89   virtual ~DeadlockInterface() {}
90 
91   // This function should deadlock until Release() is called.
92   virtual void Deadlock() = 0;
93 
94   // This function should release the thread stuck in Deadlock().
95   virtual void Release() = 0;
96 };
97 
98 struct ThreadParams {
99   volatile int tid;
100   // Signaled when the deadlock region is entered.
101   SimpleSpinEvent deadlock_start_event;
102   DeadlockInterface* volatile deadlock_impl;
103   // Defines an address range within the deadlock will occur.
104   volatile uint32_t deadlock_region_start_address;
105   volatile uint32_t deadlock_region_end_address;
106   // Signaled when the deadlock is done.
107   rtc::Event deadlock_done_event;
108 };
109 
110 class RtcEventDeadlock : public DeadlockInterface {
111  private:
Deadlock()112   void Deadlock() override { event.Wait(rtc::Event::kForever); }
Release()113   void Release() override { event.Set(); }
114 
115   rtc::Event event;
116 };
117 
118 class RtcCriticalSectionDeadlock : public DeadlockInterface {
119  public:
RtcCriticalSectionDeadlock()120   RtcCriticalSectionDeadlock()
121       : mutex_lock_(std::make_unique<MutexLock>(&mutex_)) {}
122 
123  private:
Deadlock()124   void Deadlock() override { MutexLock lock(&mutex_); }
125 
Release()126   void Release() override { mutex_lock_.reset(); }
127 
128   Mutex mutex_;
129   std::unique_ptr<MutexLock> mutex_lock_;
130 };
131 
132 class SpinDeadlock : public DeadlockInterface {
133  public:
SpinDeadlock()134   SpinDeadlock() : is_deadlocked_(true) {}
135 
136  private:
Deadlock()137   void Deadlock() override {
138     while (is_deadlocked_) {
139     }
140   }
141 
Release()142   void Release() override { is_deadlocked_ = false; }
143 
144   std::atomic<bool> is_deadlocked_;
145 };
146 
147 class SleepDeadlock : public DeadlockInterface {
148  private:
Deadlock()149   void Deadlock() override { sleep(1000000); }
150 
Release()151   void Release() override {
152     // The interrupt itself will break free from the sleep.
153   }
154 };
155 
156 // This is the function that is exectued by the thread that will deadlock and
157 // have its stacktrace captured.
ThreadFunction(void * void_params)158 void ThreadFunction(void* void_params) {
159   ThreadParams* params = static_cast<ThreadParams*>(void_params);
160   params->tid = gettid();
161 
162   params->deadlock_region_start_address = GetCurrentRelativeExecutionAddress();
163   params->deadlock_start_event.Set();
164   params->deadlock_impl->Deadlock();
165   params->deadlock_region_end_address = GetCurrentRelativeExecutionAddress();
166 
167   params->deadlock_done_event.Set();
168 }
169 
TestStacktrace(std::unique_ptr<DeadlockInterface> deadlock_impl)170 void TestStacktrace(std::unique_ptr<DeadlockInterface> deadlock_impl) {
171   // Set params that will be sent to other thread.
172   ThreadParams params;
173   params.deadlock_impl = deadlock_impl.get();
174 
175   // Spawn thread.
176   rtc::PlatformThread thread(&ThreadFunction, &params, "StacktraceTest");
177   thread.Start();
178 
179   // Wait until the thread has entered the deadlock region, and take a very
180   // brief nap to give it time to reach the actual deadlock.
181   params.deadlock_start_event.Wait();
182   SleepMs(1);
183 
184   // Acquire the stack trace of the thread which should now be deadlocking.
185   std::vector<StackTraceElement> stack_trace = GetStackTrace(params.tid);
186 
187   // Release the deadlock so that the thread can continue.
188   deadlock_impl->Release();
189 
190   // Wait until the thread has left the deadlock.
191   params.deadlock_done_event.Wait(rtc::Event::kForever);
192 
193   // Assert that the stack trace contains the deadlock region.
194   EXPECT_TRUE(StackTraceContainsRange(stack_trace,
195                                       params.deadlock_region_start_address,
196                                       params.deadlock_region_end_address))
197       << "Deadlock region: ["
198       << rtc::ToHex(params.deadlock_region_start_address) << ", "
199       << rtc::ToHex(params.deadlock_region_end_address)
200       << "] not contained in: " << StackTraceToString(stack_trace);
201 
202   thread.Stop();
203 }
204 
205 class LookoutLogSink final : public rtc::LogSink {
206  public:
LookoutLogSink(std::string look_for)207   explicit LookoutLogSink(std::string look_for)
208       : look_for_(std::move(look_for)) {}
OnLogMessage(const std::string & message)209   void OnLogMessage(const std::string& message) override {
210     if (message.find(look_for_) != std::string::npos) {
211       when_found_.Set();
212     }
213   }
WhenFound()214   rtc::Event& WhenFound() { return when_found_; }
215 
216  private:
217   const std::string look_for_;
218   rtc::Event when_found_;
219 };
220 
221 }  // namespace
222 
TEST(Stacktrace,TestCurrentThread)223 TEST(Stacktrace, TestCurrentThread) {
224   const uint32_t start_addr = GetCurrentRelativeExecutionAddress();
225   const std::vector<StackTraceElement> stack_trace = GetStackTrace();
226   const uint32_t end_addr = GetCurrentRelativeExecutionAddress();
227   EXPECT_TRUE(StackTraceContainsRange(stack_trace, start_addr, end_addr))
228       << "Caller region: [" << rtc::ToHex(start_addr) << ", "
229       << rtc::ToHex(end_addr)
230       << "] not contained in: " << StackTraceToString(stack_trace);
231 }
232 
TEST(Stacktrace,TestSpinLock)233 TEST(Stacktrace, TestSpinLock) {
234   TestStacktrace(std::make_unique<SpinDeadlock>());
235 }
236 
TEST(Stacktrace,TestSleep)237 TEST(Stacktrace, TestSleep) {
238   TestStacktrace(std::make_unique<SleepDeadlock>());
239 }
240 
241 // Stack traces originating from kernel space does not include user space stack
242 // traces for ARM 32.
243 #ifdef WEBRTC_ARCH_ARM64
244 
TEST(Stacktrace,TestRtcEvent)245 TEST(Stacktrace, TestRtcEvent) {
246   TestStacktrace(std::make_unique<RtcEventDeadlock>());
247 }
248 
TEST(Stacktrace,TestRtcCriticalSection)249 TEST(Stacktrace, TestRtcCriticalSection) {
250   TestStacktrace(std::make_unique<RtcCriticalSectionDeadlock>());
251 }
252 
253 #endif
254 
TEST(Stacktrace,TestRtcEventDeadlockDetection)255 TEST(Stacktrace, TestRtcEventDeadlockDetection) {
256   // Start looking for the expected log output.
257   LookoutLogSink sink(/*look_for=*/"Probable deadlock");
258   rtc::LogMessage::AddLogToStream(&sink, rtc::LS_WARNING);
259 
260   // Start a thread that waits for an event.
261   rtc::Event ev;
262   rtc::PlatformThread thread(
263       [](void* arg) {
264         auto* ev = static_cast<rtc::Event*>(arg);
265         ev->Wait(rtc::Event::kForever);
266       },
267       &ev, "TestRtcEventDeadlockDetection");
268   thread.Start();
269 
270   // The message should appear after 3 sec. We'll wait up to 10 sec in an
271   // attempt to not be flaky.
272   EXPECT_TRUE(sink.WhenFound().Wait(10000));
273 
274   // Unblock the thread and shut it down.
275   ev.Set();
276   thread.Stop();
277   rtc::LogMessage::RemoveLogToStream(&sink);
278 }
279 
280 }  // namespace test
281 }  // namespace webrtc
282