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