xref: /aosp_15_r20/external/webrtc/sdk/android/native_unittests/stacktrace/stacktrace_unittest.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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"
12 
13 #include <dlfcn.h>
14 
15 #include <atomic>
16 #include <memory>
17 #include <vector>
18 
19 #include "absl/strings/string_view.h"
20 #include "rtc_base/event.h"
21 #include "rtc_base/logging.h"
22 #include "rtc_base/platform_thread.h"
23 #include "rtc_base/string_utils.h"
24 #include "rtc_base/strings/string_builder.h"
25 #include "rtc_base/synchronization/mutex.h"
26 #include "rtc_base/system/inline.h"
27 #include "system_wrappers/include/sleep.h"
28 #include "test/gtest.h"
29 
30 namespace webrtc {
31 namespace test {
32 
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.
SimpleSpinEvent()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.
Wait()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.
Set()57   void Set() { blocked_.clear(std::memory_order_seq_cst); }
58 
59  private:
60   std::atomic_flag blocked_;
61 };
62 
63 // Returns the execution address relative to the .so base address. This matches
64 // the addresses we get from GetStacktrace().
GetCurrentRelativeExecutionAddress()65 RTC_NO_INLINE uint32_t GetCurrentRelativeExecutionAddress() {
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.
StackTraceContainsRange(const std::vector<StackTraceElement> & stack_trace,uintptr_t pc_low,uintptr_t pc_high)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:
~DeadlockInterface()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.
102   SimpleSpinEvent deadlock_start_event;
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:
Deadlock()113   void Deadlock() override { event.Wait(rtc::Event::kForever); }
Release()114   void Release() override { event.Set(); }
115 
116   rtc::Event event;
117 };
118 
119 class RtcCriticalSectionDeadlock : public DeadlockInterface {
120  public:
RtcCriticalSectionDeadlock()121   RtcCriticalSectionDeadlock()
122       : mutex_lock_(std::make_unique<MutexLock>(&mutex_)) {}
123 
124  private:
Deadlock()125   void Deadlock() override { MutexLock lock(&mutex_); }
126 
Release()127   void Release() override { mutex_lock_.reset(); }
128 
129   Mutex mutex_;
130   std::unique_ptr<MutexLock> mutex_lock_;
131 };
132 
133 class SpinDeadlock : public DeadlockInterface {
134  public:
SpinDeadlock()135   SpinDeadlock() : is_deadlocked_(true) {}
136 
137  private:
Deadlock()138   void Deadlock() override {
139     while (is_deadlocked_) {
140     }
141   }
142 
Release()143   void Release() override { is_deadlocked_ = false; }
144 
145   std::atomic<bool> is_deadlocked_;
146 };
147 
148 class SleepDeadlock : public DeadlockInterface {
149  private:
Deadlock()150   void Deadlock() override { sleep(1000000); }
151 
Release()152   void Release() override {
153     // The interrupt itself will break free from the sleep.
154   }
155 };
156 
TestStacktrace(std::unique_ptr<DeadlockInterface> deadlock_impl)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.
163   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");
175 
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);
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);
198 }
199 
200 class LookoutLogSink final : public rtc::LogSink {
201  public:
LookoutLogSink(std::string look_for)202   explicit LookoutLogSink(std::string look_for)
203       : look_for_(std::move(look_for)) {}
OnLogMessage(const std::string & message)204   void OnLogMessage(const std::string& message) override {
205     OnLogMessage(absl::string_view(message));
206   }
OnLogMessage(absl::string_view message)207   void OnLogMessage(absl::string_view message) override {
208     if (message.find(look_for_) != std::string::npos) {
209       when_found_.Set();
210     }
211   }
WhenFound()212   rtc::Event& WhenFound() { return when_found_; }
213 
214  private:
215   const std::string look_for_;
216   rtc::Event when_found_;
217 };
218 
219 }  // namespace
220 
TEST(Stacktrace,TestCurrentThread)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 
TEST(Stacktrace,TestSpinLock)231 TEST(Stacktrace, TestSpinLock) {
232   TestStacktrace(std::make_unique<SpinDeadlock>());
233 }
234 
TEST(Stacktrace,TestSleep)235 TEST(Stacktrace, TestSleep) {
236   TestStacktrace(std::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
242 
TEST(Stacktrace,TestRtcEvent)243 TEST(Stacktrace, TestRtcEvent) {
244   TestStacktrace(std::make_unique<RtcEventDeadlock>());
245 }
246 
TEST(Stacktrace,TestRtcCriticalSection)247 TEST(Stacktrace, TestRtcCriticalSection) {
248   TestStacktrace(std::make_unique<RtcCriticalSectionDeadlock>());
249 }
250 
251 #endif
252 
TEST(Stacktrace,TestRtcEventDeadlockDetection)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;
260   auto thread = rtc::PlatformThread::SpawnJoinable(
261       [&ev] { ev.Wait(rtc::Event::kForever); },
262       "TestRtcEventDeadlockDetection");
263 
264   // The message should appear after 3 sec. We'll wait up to 10 sec in an
265   // attempt to not be flaky.
266   EXPECT_TRUE(sink.WhenFound().Wait(TimeDelta::Seconds(10)));
267 
268   // Unblock the thread and shut it down.
269   ev.Set();
270   thread.Finalize();
271   rtc::LogMessage::RemoveLogToStream(&sink);
272 }
273 
274 }  // namespace test
275 }  // namespace webrtc
276