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, ¶ms, "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