blob: 1dc9bee190b208582ac5ce68a25c2ea12555ec0e [file] [log] [blame]
Magnus Jedvert7510e4a2019-01-20 11:46:32 +01001/*
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"
Karl Wiberg0611a152019-03-18 11:37:48 +010012
Magnus Jedvert7510e4a2019-01-20 11:46:32 +010013#include <dlfcn.h>
Mirko Bonadei317a1f02019-09-17 17:06:18 +020014
Karl Wiberg0611a152019-03-18 11:37:48 +010015#include <atomic>
Mirko Bonadei317a1f02019-09-17 17:06:18 +020016#include <memory>
Magnus Jedvert7510e4a2019-01-20 11:46:32 +010017#include <vector>
Karl Wiberg0611a152019-03-18 11:37:48 +010018
Magnus Jedvert7510e4a2019-01-20 11:46:32 +010019#include "rtc_base/event.h"
20#include "rtc_base/logging.h"
21#include "rtc_base/platform_thread.h"
Niels Möller198cf002019-05-10 12:29:13 +020022#include "rtc_base/string_utils.h"
Magnus Jedvert7510e4a2019-01-20 11:46:32 +010023#include "rtc_base/strings/string_builder.h"
Markus Handell85585f42020-07-08 23:04:37 +020024#include "rtc_base/synchronization/mutex.h"
Karl Wiberg0611a152019-03-18 11:37:48 +010025#include "rtc_base/system/inline.h"
26#include "system_wrappers/include/sleep.h"
Magnus Jedvert7510e4a2019-01-20 11:46:32 +010027#include "test/gtest.h"
28
29namespace webrtc {
30namespace test {
31
Karl Wiberg0611a152019-03-18 11:37:48 +010032namespace {
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.
38class SimpleSpinEvent {
39 public:
40 // Initialize the event to its blocked state.
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.
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.
56 void Set() { blocked_.clear(std::memory_order_seq_cst); }
57
58 private:
59 std::atomic_flag blocked_;
60};
61
Magnus Jedvert7510e4a2019-01-20 11:46:32 +010062// Returns the execution address relative to the .so base address. This matches
63// the addresses we get from GetStacktrace().
Karl Wiberg0611a152019-03-18 11:37:48 +010064RTC_NO_INLINE uint32_t GetCurrentRelativeExecutionAddress() {
Magnus Jedvert7510e4a2019-01-20 11:46:32 +010065 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.
75bool 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
87class DeadlockInterface {
88 public:
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
98struct ThreadParams {
99 volatile int tid;
100 // Signaled when the deadlock region is entered.
Karl Wiberg0611a152019-03-18 11:37:48 +0100101 SimpleSpinEvent deadlock_start_event;
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100102 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
110class RtcEventDeadlock : public DeadlockInterface {
111 private:
112 void Deadlock() override { event.Wait(rtc::Event::kForever); }
113 void Release() override { event.Set(); }
114
115 rtc::Event event;
116};
117
118class RtcCriticalSectionDeadlock : public DeadlockInterface {
119 public:
120 RtcCriticalSectionDeadlock()
Markus Handell85585f42020-07-08 23:04:37 +0200121 : mutex_lock_(std::make_unique<MutexLock>(&mutex_)) {}
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100122
123 private:
Markus Handell85585f42020-07-08 23:04:37 +0200124 void Deadlock() override { MutexLock lock(&mutex_); }
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100125
Markus Handell85585f42020-07-08 23:04:37 +0200126 void Release() override { mutex_lock_.reset(); }
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100127
Markus Handell85585f42020-07-08 23:04:37 +0200128 Mutex mutex_;
129 std::unique_ptr<MutexLock> mutex_lock_;
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100130};
131
132class SpinDeadlock : public DeadlockInterface {
133 public:
134 SpinDeadlock() : is_deadlocked_(true) {}
135
136 private:
137 void Deadlock() override {
138 while (is_deadlocked_) {
139 }
140 }
141
142 void Release() override { is_deadlocked_ = false; }
143
144 std::atomic<bool> is_deadlocked_;
145};
146
147class SleepDeadlock : public DeadlockInterface {
148 private:
149 void Deadlock() override { sleep(1000000); }
150
151 void Release() override {
152 // The interrupt itself will break free from the sleep.
153 }
154};
155
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100156void TestStacktrace(std::unique_ptr<DeadlockInterface> deadlock_impl) {
157 // Set params that will be sent to other thread.
158 ThreadParams params;
159 params.deadlock_impl = deadlock_impl.get();
160
161 // Spawn thread.
Markus Handellad5037b2021-05-07 15:02:36 +0200162 auto thread = rtc::PlatformThread::SpawnJoinable(
163 [&params] {
164 params.tid = gettid();
165 params.deadlock_region_start_address =
166 GetCurrentRelativeExecutionAddress();
167 params.deadlock_start_event.Set();
168 params.deadlock_impl->Deadlock();
169 params.deadlock_region_end_address =
170 GetCurrentRelativeExecutionAddress();
171 params.deadlock_done_event.Set();
172 },
173 "StacktraceTest");
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100174
Karl Wiberg0611a152019-03-18 11:37:48 +0100175 // Wait until the thread has entered the deadlock region, and take a very
176 // brief nap to give it time to reach the actual deadlock.
177 params.deadlock_start_event.Wait();
178 SleepMs(1);
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100179
180 // Acquire the stack trace of the thread which should now be deadlocking.
181 std::vector<StackTraceElement> stack_trace = GetStackTrace(params.tid);
182
183 // Release the deadlock so that the thread can continue.
184 deadlock_impl->Release();
185
186 // Wait until the thread has left the deadlock.
187 params.deadlock_done_event.Wait(rtc::Event::kForever);
188
189 // Assert that the stack trace contains the deadlock region.
190 EXPECT_TRUE(StackTraceContainsRange(stack_trace,
191 params.deadlock_region_start_address,
192 params.deadlock_region_end_address))
193 << "Deadlock region: ["
194 << rtc::ToHex(params.deadlock_region_start_address) << ", "
195 << rtc::ToHex(params.deadlock_region_end_address)
196 << "] not contained in: " << StackTraceToString(stack_trace);
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100197}
198
Karl Wiberg98417032019-03-24 19:12:40 +0100199class LookoutLogSink final : public rtc::LogSink {
200 public:
201 explicit LookoutLogSink(std::string look_for)
202 : look_for_(std::move(look_for)) {}
203 void OnLogMessage(const std::string& message) override {
204 if (message.find(look_for_) != std::string::npos) {
205 when_found_.Set();
206 }
207 }
208 rtc::Event& WhenFound() { return when_found_; }
209
210 private:
211 const std::string look_for_;
212 rtc::Event when_found_;
213};
214
Karl Wiberg0611a152019-03-18 11:37:48 +0100215} // namespace
216
Karl Wibergc130d422019-02-28 17:06:54 +0100217TEST(Stacktrace, TestCurrentThread) {
218 const uint32_t start_addr = GetCurrentRelativeExecutionAddress();
219 const std::vector<StackTraceElement> stack_trace = GetStackTrace();
220 const uint32_t end_addr = GetCurrentRelativeExecutionAddress();
221 EXPECT_TRUE(StackTraceContainsRange(stack_trace, start_addr, end_addr))
222 << "Caller region: [" << rtc::ToHex(start_addr) << ", "
223 << rtc::ToHex(end_addr)
224 << "] not contained in: " << StackTraceToString(stack_trace);
225}
226
Mirko Bonadeif5ea3b92021-11-15 15:41:28 +0100227// TODO(bugs.webrtc.org/13383): Re-enable once stack unwinding with
228// compiler-rt/libunwind works on Android arm64.
229#ifdef WEBRTC_ARCH_ARM64
230#define MAYBE_TestSpinLock DISABLED_TestSpinLock
231#else
232#define MAYBE_TestSpinLock TestSpinLock
233#endif
234TEST(Stacktrace, MAYBE_TestSpinLock) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200235 TestStacktrace(std::make_unique<SpinDeadlock>());
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100236}
237
Mirko Bonadeif5ea3b92021-11-15 15:41:28 +0100238// TODO(bugs.webrtc.org/13383): Re-enable once stack unwinding with
239// compiler-rt/libunwind works on Android arm64.
240#ifdef WEBRTC_ARCH_ARM64
241#define MAYBE_TestSleep DISABLED_TestSleep
242#else
243#define MAYBE_TestSleep TestSleep
244#endif
245TEST(Stacktrace, MAYBE_TestSleep) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200246 TestStacktrace(std::make_unique<SleepDeadlock>());
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100247}
248
249// Stack traces originating from kernel space does not include user space stack
250// traces for ARM 32.
251#ifdef WEBRTC_ARCH_ARM64
Karl Wiberg98417032019-03-24 19:12:40 +0100252
Mirko Bonadeif5ea3b92021-11-15 15:41:28 +0100253// TODO(bugs.webrtc.org/13383): Re-enable once stack unwinding with
254// compiler-rt/libunwind works on Android arm64.
255TEST(Stacktrace, DISABLED_TestRtcEvent) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200256 TestStacktrace(std::make_unique<RtcEventDeadlock>());
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100257}
258
Mirko Bonadeif5ea3b92021-11-15 15:41:28 +0100259// TODO(bugs.webrtc.org/13383): Re-enable once stack unwinding with
260// compiler-rt/libunwind works on Android arm64.
261TEST(Stacktrace, DISABLED_TestRtcCriticalSection) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200262 TestStacktrace(std::make_unique<RtcCriticalSectionDeadlock>());
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100263}
Karl Wiberg98417032019-03-24 19:12:40 +0100264
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100265#endif
266
Karl Wiberg98417032019-03-24 19:12:40 +0100267TEST(Stacktrace, TestRtcEventDeadlockDetection) {
268 // Start looking for the expected log output.
269 LookoutLogSink sink(/*look_for=*/"Probable deadlock");
270 rtc::LogMessage::AddLogToStream(&sink, rtc::LS_WARNING);
271
272 // Start a thread that waits for an event.
273 rtc::Event ev;
Markus Handellad5037b2021-05-07 15:02:36 +0200274 auto thread = rtc::PlatformThread::SpawnJoinable(
275 [&ev] { ev.Wait(rtc::Event::kForever); },
276 "TestRtcEventDeadlockDetection");
Karl Wiberg98417032019-03-24 19:12:40 +0100277
278 // The message should appear after 3 sec. We'll wait up to 10 sec in an
279 // attempt to not be flaky.
280 EXPECT_TRUE(sink.WhenFound().Wait(10000));
281
282 // Unblock the thread and shut it down.
283 ev.Set();
Markus Handellad5037b2021-05-07 15:02:36 +0200284 thread.Finalize();
Karl Wiberg98417032019-03-24 19:12:40 +0100285 rtc::LogMessage::RemoveLogToStream(&sink);
286}
287
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100288} // namespace test
289} // namespace webrtc