blob: 4ea3c9f3766bf79f0983e66bc2901e68889eaf98 [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"
22#include "rtc_base/strings/string_builder.h"
Karl Wiberg0611a152019-03-18 11:37:48 +010023#include "rtc_base/system/inline.h"
24#include "system_wrappers/include/sleep.h"
Magnus Jedvert7510e4a2019-01-20 11:46:32 +010025#include "test/gtest.h"
26
27namespace webrtc {
28namespace test {
29
Karl Wiberg0611a152019-03-18 11:37:48 +010030namespace {
31
32// A simple atomic spin event. Implemented with std::atomic_flag, since the C++
33// standard guarantees that that type is implemented with actual atomic
34// instructions (as opposed to e.g. with a mutex). Uses sequentially consistent
35// memory order since this is a test, where simplicity trumps performance.
36class SimpleSpinEvent {
37 public:
38 // Initialize the event to its blocked state.
39 SimpleSpinEvent() {
40 static_cast<void>(blocked_.test_and_set(std::memory_order_seq_cst));
41 }
42
43 // Busy-wait for the event to become unblocked, and block it behind us as we
44 // leave.
45 void Wait() {
46 bool was_blocked;
47 do {
48 // Check if the event was blocked, and set it to blocked.
49 was_blocked = blocked_.test_and_set(std::memory_order_seq_cst);
50 } while (was_blocked);
51 }
52
53 // Unblock the event.
54 void Set() { blocked_.clear(std::memory_order_seq_cst); }
55
56 private:
57 std::atomic_flag blocked_;
58};
59
Magnus Jedvert7510e4a2019-01-20 11:46:32 +010060// Returns the execution address relative to the .so base address. This matches
61// the addresses we get from GetStacktrace().
Karl Wiberg0611a152019-03-18 11:37:48 +010062RTC_NO_INLINE uint32_t GetCurrentRelativeExecutionAddress() {
Magnus Jedvert7510e4a2019-01-20 11:46:32 +010063 void* pc = __builtin_return_address(0);
64 Dl_info dl_info = {};
65 const bool success = dladdr(pc, &dl_info);
66 EXPECT_TRUE(success);
67 return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(pc) -
68 reinterpret_cast<uintptr_t>(dl_info.dli_fbase));
69}
70
71// Returns true if any of the stack trace element is inside the specified
72// region.
73bool StackTraceContainsRange(const std::vector<StackTraceElement>& stack_trace,
74 uintptr_t pc_low,
75 uintptr_t pc_high) {
76 for (const StackTraceElement& stack_trace_element : stack_trace) {
77 if (pc_low <= stack_trace_element.relative_address &&
78 pc_high >= stack_trace_element.relative_address) {
79 return true;
80 }
81 }
82 return false;
83}
84
85class DeadlockInterface {
86 public:
87 virtual ~DeadlockInterface() {}
88
89 // This function should deadlock until Release() is called.
90 virtual void Deadlock() = 0;
91
92 // This function should release the thread stuck in Deadlock().
93 virtual void Release() = 0;
94};
95
96struct ThreadParams {
97 volatile int tid;
98 // Signaled when the deadlock region is entered.
Karl Wiberg0611a152019-03-18 11:37:48 +010099 SimpleSpinEvent deadlock_start_event;
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100100 DeadlockInterface* volatile deadlock_impl;
101 // Defines an address range within the deadlock will occur.
102 volatile uint32_t deadlock_region_start_address;
103 volatile uint32_t deadlock_region_end_address;
104 // Signaled when the deadlock is done.
105 rtc::Event deadlock_done_event;
106};
107
108class RtcEventDeadlock : public DeadlockInterface {
109 private:
110 void Deadlock() override { event.Wait(rtc::Event::kForever); }
111 void Release() override { event.Set(); }
112
113 rtc::Event event;
114};
115
116class RtcCriticalSectionDeadlock : public DeadlockInterface {
117 public:
118 RtcCriticalSectionDeadlock()
119 : critscope_(absl::make_unique<rtc::CritScope>(&crit_)) {}
120
121 private:
122 void Deadlock() override { rtc::CritScope lock(&crit_); }
123
124 void Release() override { critscope_.reset(); }
125
126 rtc::CriticalSection crit_;
127 std::unique_ptr<rtc::CritScope> critscope_;
128};
129
130class SpinDeadlock : public DeadlockInterface {
131 public:
132 SpinDeadlock() : is_deadlocked_(true) {}
133
134 private:
135 void Deadlock() override {
136 while (is_deadlocked_) {
137 }
138 }
139
140 void Release() override { is_deadlocked_ = false; }
141
142 std::atomic<bool> is_deadlocked_;
143};
144
145class SleepDeadlock : public DeadlockInterface {
146 private:
147 void Deadlock() override { sleep(1000000); }
148
149 void Release() override {
150 // The interrupt itself will break free from the sleep.
151 }
152};
153
154// This is the function that is exectued by the thread that will deadlock and
155// have its stacktrace captured.
156void ThreadFunction(void* void_params) {
157 ThreadParams* params = static_cast<ThreadParams*>(void_params);
158 params->tid = gettid();
159
160 params->deadlock_region_start_address = GetCurrentRelativeExecutionAddress();
161 params->deadlock_start_event.Set();
162 params->deadlock_impl->Deadlock();
163 params->deadlock_region_end_address = GetCurrentRelativeExecutionAddress();
164
165 params->deadlock_done_event.Set();
166}
167
168void TestStacktrace(std::unique_ptr<DeadlockInterface> deadlock_impl) {
169 // Set params that will be sent to other thread.
170 ThreadParams params;
171 params.deadlock_impl = deadlock_impl.get();
172
173 // Spawn thread.
174 rtc::PlatformThread thread(&ThreadFunction, &params, "StacktraceTest");
175 thread.Start();
176
Karl Wiberg0611a152019-03-18 11:37:48 +0100177 // Wait until the thread has entered the deadlock region, and take a very
178 // brief nap to give it time to reach the actual deadlock.
179 params.deadlock_start_event.Wait();
180 SleepMs(1);
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100181
182 // Acquire the stack trace of the thread which should now be deadlocking.
183 std::vector<StackTraceElement> stack_trace = GetStackTrace(params.tid);
184
185 // Release the deadlock so that the thread can continue.
186 deadlock_impl->Release();
187
188 // Wait until the thread has left the deadlock.
189 params.deadlock_done_event.Wait(rtc::Event::kForever);
190
191 // Assert that the stack trace contains the deadlock region.
192 EXPECT_TRUE(StackTraceContainsRange(stack_trace,
193 params.deadlock_region_start_address,
194 params.deadlock_region_end_address))
195 << "Deadlock region: ["
196 << rtc::ToHex(params.deadlock_region_start_address) << ", "
197 << rtc::ToHex(params.deadlock_region_end_address)
198 << "] not contained in: " << StackTraceToString(stack_trace);
199
200 thread.Stop();
201}
202
Karl Wiberg98417032019-03-24 19:12:40 +0100203class LookoutLogSink final : public rtc::LogSink {
204 public:
205 explicit LookoutLogSink(std::string look_for)
206 : look_for_(std::move(look_for)) {}
207 void OnLogMessage(const std::string& message) override {
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 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
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100231TEST(Stacktrace, TestSpinLock) {
232 TestStacktrace(absl::make_unique<SpinDeadlock>());
233}
234
235TEST(Stacktrace, TestSleep) {
236 TestStacktrace(absl::make_unique<SleepDeadlock>());
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 Wiberg98417032019-03-24 19:12:40 +0100242
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100243TEST(Stacktrace, TestRtcEvent) {
244 TestStacktrace(absl::make_unique<RtcEventDeadlock>());
245}
246
247TEST(Stacktrace, TestRtcCriticalSection) {
248 TestStacktrace(absl::make_unique<RtcCriticalSectionDeadlock>());
249}
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;
260 rtc::PlatformThread thread(
261 [](void* arg) {
262 auto* ev = static_cast<rtc::Event*>(arg);
263 ev->Wait(rtc::Event::kForever);
264 },
265 &ev, "TestRtcEventDeadlockDetection");
266 thread.Start();
267
268 // The message should appear after 3 sec. We'll wait up to 10 sec in an
269 // attempt to not be flaky.
270 EXPECT_TRUE(sink.WhenFound().Wait(10000));
271
272 // Unblock the thread and shut it down.
273 ev.Set();
274 thread.Stop();
275 rtc::LogMessage::RemoveLogToStream(&sink);
276}
277
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100278} // namespace test
279} // namespace webrtc