blob: cc0e740376faff3c06c379927b6cc8fc5c4cfc73 [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
Ali Tofigh6364d082022-03-14 13:32:04 +010019#include "absl/strings/string_view.h"
Magnus Jedvert7510e4a2019-01-20 11:46:32 +010020#include "rtc_base/event.h"
21#include "rtc_base/logging.h"
22#include "rtc_base/platform_thread.h"
Niels Möller198cf002019-05-10 12:29:13 +020023#include "rtc_base/string_utils.h"
Magnus Jedvert7510e4a2019-01-20 11:46:32 +010024#include "rtc_base/strings/string_builder.h"
Markus Handell85585f42020-07-08 23:04:37 +020025#include "rtc_base/synchronization/mutex.h"
Karl Wiberg0611a152019-03-18 11:37:48 +010026#include "rtc_base/system/inline.h"
27#include "system_wrappers/include/sleep.h"
Magnus Jedvert7510e4a2019-01-20 11:46:32 +010028#include "test/gtest.h"
29
30namespace webrtc {
31namespace test {
32
Karl Wiberg0611a152019-03-18 11:37:48 +010033namespace {
34
35// A simple atomic spin event. Implemented with std::atomic_flag, since the C++
36// standard guarantees that that type is implemented with actual atomic
37// instructions (as opposed to e.g. with a mutex). Uses sequentially consistent
38// memory order since this is a test, where simplicity trumps performance.
39class SimpleSpinEvent {
40 public:
41 // Initialize the event to its blocked state.
42 SimpleSpinEvent() {
43 static_cast<void>(blocked_.test_and_set(std::memory_order_seq_cst));
44 }
45
46 // Busy-wait for the event to become unblocked, and block it behind us as we
47 // leave.
48 void Wait() {
49 bool was_blocked;
50 do {
51 // Check if the event was blocked, and set it to blocked.
52 was_blocked = blocked_.test_and_set(std::memory_order_seq_cst);
53 } while (was_blocked);
54 }
55
56 // Unblock the event.
57 void Set() { blocked_.clear(std::memory_order_seq_cst); }
58
59 private:
60 std::atomic_flag blocked_;
61};
62
Magnus Jedvert7510e4a2019-01-20 11:46:32 +010063// Returns the execution address relative to the .so base address. This matches
64// the addresses we get from GetStacktrace().
Karl Wiberg0611a152019-03-18 11:37:48 +010065RTC_NO_INLINE uint32_t GetCurrentRelativeExecutionAddress() {
Magnus Jedvert7510e4a2019-01-20 11:46:32 +010066 void* pc = __builtin_return_address(0);
67 Dl_info dl_info = {};
68 const bool success = dladdr(pc, &dl_info);
69 EXPECT_TRUE(success);
70 return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(pc) -
71 reinterpret_cast<uintptr_t>(dl_info.dli_fbase));
72}
73
74// Returns true if any of the stack trace element is inside the specified
75// region.
76bool StackTraceContainsRange(const std::vector<StackTraceElement>& stack_trace,
77 uintptr_t pc_low,
78 uintptr_t pc_high) {
79 for (const StackTraceElement& stack_trace_element : stack_trace) {
80 if (pc_low <= stack_trace_element.relative_address &&
81 pc_high >= stack_trace_element.relative_address) {
82 return true;
83 }
84 }
85 return false;
86}
87
88class DeadlockInterface {
89 public:
90 virtual ~DeadlockInterface() {}
91
92 // This function should deadlock until Release() is called.
93 virtual void Deadlock() = 0;
94
95 // This function should release the thread stuck in Deadlock().
96 virtual void Release() = 0;
97};
98
99struct ThreadParams {
100 volatile int tid;
101 // Signaled when the deadlock region is entered.
Karl Wiberg0611a152019-03-18 11:37:48 +0100102 SimpleSpinEvent deadlock_start_event;
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100103 DeadlockInterface* volatile deadlock_impl;
104 // Defines an address range within the deadlock will occur.
105 volatile uint32_t deadlock_region_start_address;
106 volatile uint32_t deadlock_region_end_address;
107 // Signaled when the deadlock is done.
108 rtc::Event deadlock_done_event;
109};
110
111class RtcEventDeadlock : public DeadlockInterface {
112 private:
113 void Deadlock() override { event.Wait(rtc::Event::kForever); }
114 void Release() override { event.Set(); }
115
116 rtc::Event event;
117};
118
119class RtcCriticalSectionDeadlock : public DeadlockInterface {
120 public:
121 RtcCriticalSectionDeadlock()
Markus Handell85585f42020-07-08 23:04:37 +0200122 : mutex_lock_(std::make_unique<MutexLock>(&mutex_)) {}
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100123
124 private:
Markus Handell85585f42020-07-08 23:04:37 +0200125 void Deadlock() override { MutexLock lock(&mutex_); }
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100126
Markus Handell85585f42020-07-08 23:04:37 +0200127 void Release() override { mutex_lock_.reset(); }
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100128
Markus Handell85585f42020-07-08 23:04:37 +0200129 Mutex mutex_;
130 std::unique_ptr<MutexLock> mutex_lock_;
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100131};
132
133class SpinDeadlock : public DeadlockInterface {
134 public:
135 SpinDeadlock() : is_deadlocked_(true) {}
136
137 private:
138 void Deadlock() override {
139 while (is_deadlocked_) {
140 }
141 }
142
143 void Release() override { is_deadlocked_ = false; }
144
145 std::atomic<bool> is_deadlocked_;
146};
147
148class SleepDeadlock : public DeadlockInterface {
149 private:
150 void Deadlock() override { sleep(1000000); }
151
152 void Release() override {
153 // The interrupt itself will break free from the sleep.
154 }
155};
156
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100157void TestStacktrace(std::unique_ptr<DeadlockInterface> deadlock_impl) {
158 // Set params that will be sent to other thread.
159 ThreadParams params;
160 params.deadlock_impl = deadlock_impl.get();
161
162 // Spawn thread.
Markus Handellad5037b2021-05-07 15:02:36 +0200163 auto thread = rtc::PlatformThread::SpawnJoinable(
164 [&params] {
165 params.tid = gettid();
166 params.deadlock_region_start_address =
167 GetCurrentRelativeExecutionAddress();
168 params.deadlock_start_event.Set();
169 params.deadlock_impl->Deadlock();
170 params.deadlock_region_end_address =
171 GetCurrentRelativeExecutionAddress();
172 params.deadlock_done_event.Set();
173 },
174 "StacktraceTest");
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100175
Karl Wiberg0611a152019-03-18 11:37:48 +0100176 // Wait until the thread has entered the deadlock region, and take a very
177 // brief nap to give it time to reach the actual deadlock.
178 params.deadlock_start_event.Wait();
179 SleepMs(1);
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100180
181 // Acquire the stack trace of the thread which should now be deadlocking.
182 std::vector<StackTraceElement> stack_trace = GetStackTrace(params.tid);
183
184 // Release the deadlock so that the thread can continue.
185 deadlock_impl->Release();
186
187 // Wait until the thread has left the deadlock.
188 params.deadlock_done_event.Wait(rtc::Event::kForever);
189
190 // Assert that the stack trace contains the deadlock region.
191 EXPECT_TRUE(StackTraceContainsRange(stack_trace,
192 params.deadlock_region_start_address,
193 params.deadlock_region_end_address))
194 << "Deadlock region: ["
195 << rtc::ToHex(params.deadlock_region_start_address) << ", "
196 << rtc::ToHex(params.deadlock_region_end_address)
197 << "] not contained in: " << StackTraceToString(stack_trace);
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100198}
199
Karl Wiberg98417032019-03-24 19:12:40 +0100200class LookoutLogSink final : public rtc::LogSink {
201 public:
202 explicit LookoutLogSink(std::string look_for)
203 : look_for_(std::move(look_for)) {}
204 void OnLogMessage(const std::string& message) override {
Ali Tofigh6364d082022-03-14 13:32:04 +0100205 OnLogMessage(absl::string_view(message));
206 }
207 void OnLogMessage(absl::string_view message) override {
Karl Wiberg98417032019-03-24 19:12:40 +0100208 if (message.find(look_for_) != std::string::npos) {
209 when_found_.Set();
210 }
211 }
212 rtc::Event& WhenFound() { return when_found_; }
213
214 private:
215 const std::string look_for_;
216 rtc::Event when_found_;
217};
218
Karl Wiberg0611a152019-03-18 11:37:48 +0100219} // namespace
220
Karl Wibergc130d422019-02-28 17:06:54 +0100221TEST(Stacktrace, TestCurrentThread) {
222 const uint32_t start_addr = GetCurrentRelativeExecutionAddress();
223 const std::vector<StackTraceElement> stack_trace = GetStackTrace();
224 const uint32_t end_addr = GetCurrentRelativeExecutionAddress();
225 EXPECT_TRUE(StackTraceContainsRange(stack_trace, start_addr, end_addr))
226 << "Caller region: [" << rtc::ToHex(start_addr) << ", "
227 << rtc::ToHex(end_addr)
228 << "] not contained in: " << StackTraceToString(stack_trace);
229}
230
Daniel.L (Byoungchan Lee)c561d0a2021-12-16 09:56:38 +0900231TEST(Stacktrace, TestSpinLock) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200232 TestStacktrace(std::make_unique<SpinDeadlock>());
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100233}
234
Daniel.L (Byoungchan Lee)c561d0a2021-12-16 09:56:38 +0900235TEST(Stacktrace, TestSleep) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200236 TestStacktrace(std::make_unique<SleepDeadlock>());
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100237}
238
239// Stack traces originating from kernel space does not include user space stack
240// traces for ARM 32.
241#ifdef WEBRTC_ARCH_ARM64
Karl Wiberg98417032019-03-24 19:12:40 +0100242
Daniel.L (Byoungchan Lee)c561d0a2021-12-16 09:56:38 +0900243TEST(Stacktrace, TestRtcEvent) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200244 TestStacktrace(std::make_unique<RtcEventDeadlock>());
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100245}
246
Daniel.L (Byoungchan Lee)c561d0a2021-12-16 09:56:38 +0900247TEST(Stacktrace, TestRtcCriticalSection) {
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200248 TestStacktrace(std::make_unique<RtcCriticalSectionDeadlock>());
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100249}
Karl Wiberg98417032019-03-24 19:12:40 +0100250
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100251#endif
252
Karl Wiberg98417032019-03-24 19:12:40 +0100253TEST(Stacktrace, TestRtcEventDeadlockDetection) {
254 // Start looking for the expected log output.
255 LookoutLogSink sink(/*look_for=*/"Probable deadlock");
256 rtc::LogMessage::AddLogToStream(&sink, rtc::LS_WARNING);
257
258 // Start a thread that waits for an event.
259 rtc::Event ev;
Markus Handellad5037b2021-05-07 15:02:36 +0200260 auto thread = rtc::PlatformThread::SpawnJoinable(
261 [&ev] { ev.Wait(rtc::Event::kForever); },
262 "TestRtcEventDeadlockDetection");
Karl Wiberg98417032019-03-24 19:12:40 +0100263
264 // The message should appear after 3 sec. We'll wait up to 10 sec in an
265 // attempt to not be flaky.
Markus Handell0cd0dd32022-08-19 12:42:31 +0000266 EXPECT_TRUE(sink.WhenFound().Wait(TimeDelta::Seconds(10)));
Karl Wiberg98417032019-03-24 19:12:40 +0100267
268 // Unblock the thread and shut it down.
269 ev.Set();
Markus Handellad5037b2021-05-07 15:02:36 +0200270 thread.Finalize();
Karl Wiberg98417032019-03-24 19:12:40 +0100271 rtc::LogMessage::RemoveLogToStream(&sink);
272}
273
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100274} // namespace test
275} // namespace webrtc