Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 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" |
Karl Wiberg | 0611a15 | 2019-03-18 11:37:48 +0100 | [diff] [blame] | 12 | |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 13 | #include <dlfcn.h> |
Mirko Bonadei | 317a1f0 | 2019-09-17 17:06:18 +0200 | [diff] [blame] | 14 | |
Karl Wiberg | 0611a15 | 2019-03-18 11:37:48 +0100 | [diff] [blame] | 15 | #include <atomic> |
Mirko Bonadei | 317a1f0 | 2019-09-17 17:06:18 +0200 | [diff] [blame] | 16 | #include <memory> |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 17 | #include <vector> |
Karl Wiberg | 0611a15 | 2019-03-18 11:37:48 +0100 | [diff] [blame] | 18 | |
Ali Tofigh | 6364d08 | 2022-03-14 13:32:04 +0100 | [diff] [blame] | 19 | #include "absl/strings/string_view.h" |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 20 | #include "rtc_base/event.h" |
| 21 | #include "rtc_base/logging.h" |
| 22 | #include "rtc_base/platform_thread.h" |
Niels Möller | 198cf00 | 2019-05-10 12:29:13 +0200 | [diff] [blame] | 23 | #include "rtc_base/string_utils.h" |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 24 | #include "rtc_base/strings/string_builder.h" |
Markus Handell | 85585f4 | 2020-07-08 23:04:37 +0200 | [diff] [blame] | 25 | #include "rtc_base/synchronization/mutex.h" |
Karl Wiberg | 0611a15 | 2019-03-18 11:37:48 +0100 | [diff] [blame] | 26 | #include "rtc_base/system/inline.h" |
| 27 | #include "system_wrappers/include/sleep.h" |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 28 | #include "test/gtest.h" |
| 29 | |
| 30 | namespace webrtc { |
| 31 | namespace test { |
| 32 | |
Karl Wiberg | 0611a15 | 2019-03-18 11:37:48 +0100 | [diff] [blame] | 33 | namespace { |
| 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. |
| 39 | class 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 Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 63 | // Returns the execution address relative to the .so base address. This matches |
| 64 | // the addresses we get from GetStacktrace(). |
Karl Wiberg | 0611a15 | 2019-03-18 11:37:48 +0100 | [diff] [blame] | 65 | RTC_NO_INLINE uint32_t GetCurrentRelativeExecutionAddress() { |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 66 | 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. |
| 76 | bool 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 | |
| 88 | class 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 | |
| 99 | struct ThreadParams { |
| 100 | volatile int tid; |
| 101 | // Signaled when the deadlock region is entered. |
Karl Wiberg | 0611a15 | 2019-03-18 11:37:48 +0100 | [diff] [blame] | 102 | SimpleSpinEvent deadlock_start_event; |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 103 | 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 | |
| 111 | class 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 | |
| 119 | class RtcCriticalSectionDeadlock : public DeadlockInterface { |
| 120 | public: |
| 121 | RtcCriticalSectionDeadlock() |
Markus Handell | 85585f4 | 2020-07-08 23:04:37 +0200 | [diff] [blame] | 122 | : mutex_lock_(std::make_unique<MutexLock>(&mutex_)) {} |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 123 | |
| 124 | private: |
Markus Handell | 85585f4 | 2020-07-08 23:04:37 +0200 | [diff] [blame] | 125 | void Deadlock() override { MutexLock lock(&mutex_); } |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 126 | |
Markus Handell | 85585f4 | 2020-07-08 23:04:37 +0200 | [diff] [blame] | 127 | void Release() override { mutex_lock_.reset(); } |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 128 | |
Markus Handell | 85585f4 | 2020-07-08 23:04:37 +0200 | [diff] [blame] | 129 | Mutex mutex_; |
| 130 | std::unique_ptr<MutexLock> mutex_lock_; |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 131 | }; |
| 132 | |
| 133 | class 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 | |
| 148 | class 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 Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 157 | void 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 Handell | ad5037b | 2021-05-07 15:02:36 +0200 | [diff] [blame] | 163 | auto thread = rtc::PlatformThread::SpawnJoinable( |
| 164 | [¶ms] { |
| 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 Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 175 | |
Karl Wiberg | 0611a15 | 2019-03-18 11:37:48 +0100 | [diff] [blame] | 176 | // 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 Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 180 | |
| 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 Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 198 | } |
| 199 | |
Karl Wiberg | 9841703 | 2019-03-24 19:12:40 +0100 | [diff] [blame] | 200 | class 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 Tofigh | 6364d08 | 2022-03-14 13:32:04 +0100 | [diff] [blame] | 205 | OnLogMessage(absl::string_view(message)); |
| 206 | } |
| 207 | void OnLogMessage(absl::string_view message) override { |
Karl Wiberg | 9841703 | 2019-03-24 19:12:40 +0100 | [diff] [blame] | 208 | 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 Wiberg | 0611a15 | 2019-03-18 11:37:48 +0100 | [diff] [blame] | 219 | } // namespace |
| 220 | |
Karl Wiberg | c130d42 | 2019-02-28 17:06:54 +0100 | [diff] [blame] | 221 | TEST(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) | c561d0a | 2021-12-16 09:56:38 +0900 | [diff] [blame] | 231 | TEST(Stacktrace, TestSpinLock) { |
Mirko Bonadei | 317a1f0 | 2019-09-17 17:06:18 +0200 | [diff] [blame] | 232 | TestStacktrace(std::make_unique<SpinDeadlock>()); |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 233 | } |
| 234 | |
Daniel.L (Byoungchan Lee) | c561d0a | 2021-12-16 09:56:38 +0900 | [diff] [blame] | 235 | TEST(Stacktrace, TestSleep) { |
Mirko Bonadei | 317a1f0 | 2019-09-17 17:06:18 +0200 | [diff] [blame] | 236 | TestStacktrace(std::make_unique<SleepDeadlock>()); |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 237 | } |
| 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 Wiberg | 9841703 | 2019-03-24 19:12:40 +0100 | [diff] [blame] | 242 | |
Daniel.L (Byoungchan Lee) | c561d0a | 2021-12-16 09:56:38 +0900 | [diff] [blame] | 243 | TEST(Stacktrace, TestRtcEvent) { |
Mirko Bonadei | 317a1f0 | 2019-09-17 17:06:18 +0200 | [diff] [blame] | 244 | TestStacktrace(std::make_unique<RtcEventDeadlock>()); |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 245 | } |
| 246 | |
Daniel.L (Byoungchan Lee) | c561d0a | 2021-12-16 09:56:38 +0900 | [diff] [blame] | 247 | TEST(Stacktrace, TestRtcCriticalSection) { |
Mirko Bonadei | 317a1f0 | 2019-09-17 17:06:18 +0200 | [diff] [blame] | 248 | TestStacktrace(std::make_unique<RtcCriticalSectionDeadlock>()); |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 249 | } |
Karl Wiberg | 9841703 | 2019-03-24 19:12:40 +0100 | [diff] [blame] | 250 | |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 251 | #endif |
| 252 | |
Karl Wiberg | 9841703 | 2019-03-24 19:12:40 +0100 | [diff] [blame] | 253 | TEST(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 Handell | ad5037b | 2021-05-07 15:02:36 +0200 | [diff] [blame] | 260 | auto thread = rtc::PlatformThread::SpawnJoinable( |
| 261 | [&ev] { ev.Wait(rtc::Event::kForever); }, |
| 262 | "TestRtcEventDeadlockDetection"); |
Karl Wiberg | 9841703 | 2019-03-24 19:12:40 +0100 | [diff] [blame] | 263 | |
| 264 | // The message should appear after 3 sec. We'll wait up to 10 sec in an |
| 265 | // attempt to not be flaky. |
Markus Handell | 0cd0dd3 | 2022-08-19 12:42:31 +0000 | [diff] [blame^] | 266 | EXPECT_TRUE(sink.WhenFound().Wait(TimeDelta::Seconds(10))); |
Karl Wiberg | 9841703 | 2019-03-24 19:12:40 +0100 | [diff] [blame] | 267 | |
| 268 | // Unblock the thread and shut it down. |
| 269 | ev.Set(); |
Markus Handell | ad5037b | 2021-05-07 15:02:36 +0200 | [diff] [blame] | 270 | thread.Finalize(); |
Karl Wiberg | 9841703 | 2019-03-24 19:12:40 +0100 | [diff] [blame] | 271 | rtc::LogMessage::RemoveLogToStream(&sink); |
| 272 | } |
| 273 | |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 274 | } // namespace test |
| 275 | } // namespace webrtc |