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" |
| 12 | #include <dlfcn.h> |
| 13 | #include <vector> |
| 14 | #include "absl/memory/memory.h" |
Steve Anton | f380284 | 2019-01-24 19:07:40 -0800 | [diff] [blame] | 15 | #include "rtc_base/critical_section.h" |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 16 | #include "rtc_base/event.h" |
| 17 | #include "rtc_base/logging.h" |
| 18 | #include "rtc_base/platform_thread.h" |
| 19 | #include "rtc_base/strings/string_builder.h" |
| 20 | #include "test/gtest.h" |
| 21 | |
| 22 | namespace webrtc { |
| 23 | namespace test { |
| 24 | |
| 25 | // Returns the execution address relative to the .so base address. This matches |
| 26 | // the addresses we get from GetStacktrace(). |
| 27 | uint32_t GetCurrentRelativeExecutionAddress() { |
| 28 | void* pc = __builtin_return_address(0); |
| 29 | Dl_info dl_info = {}; |
| 30 | const bool success = dladdr(pc, &dl_info); |
| 31 | EXPECT_TRUE(success); |
| 32 | return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(pc) - |
| 33 | reinterpret_cast<uintptr_t>(dl_info.dli_fbase)); |
| 34 | } |
| 35 | |
| 36 | // Returns true if any of the stack trace element is inside the specified |
| 37 | // region. |
| 38 | bool StackTraceContainsRange(const std::vector<StackTraceElement>& stack_trace, |
| 39 | uintptr_t pc_low, |
| 40 | uintptr_t pc_high) { |
| 41 | for (const StackTraceElement& stack_trace_element : stack_trace) { |
| 42 | if (pc_low <= stack_trace_element.relative_address && |
| 43 | pc_high >= stack_trace_element.relative_address) { |
| 44 | return true; |
| 45 | } |
| 46 | } |
| 47 | return false; |
| 48 | } |
| 49 | |
| 50 | class DeadlockInterface { |
| 51 | public: |
| 52 | virtual ~DeadlockInterface() {} |
| 53 | |
| 54 | // This function should deadlock until Release() is called. |
| 55 | virtual void Deadlock() = 0; |
| 56 | |
| 57 | // This function should release the thread stuck in Deadlock(). |
| 58 | virtual void Release() = 0; |
| 59 | }; |
| 60 | |
| 61 | struct ThreadParams { |
| 62 | volatile int tid; |
| 63 | // Signaled when the deadlock region is entered. |
| 64 | rtc::Event deadlock_start_event; |
| 65 | DeadlockInterface* volatile deadlock_impl; |
| 66 | // Defines an address range within the deadlock will occur. |
| 67 | volatile uint32_t deadlock_region_start_address; |
| 68 | volatile uint32_t deadlock_region_end_address; |
| 69 | // Signaled when the deadlock is done. |
| 70 | rtc::Event deadlock_done_event; |
| 71 | }; |
| 72 | |
| 73 | class RtcEventDeadlock : public DeadlockInterface { |
| 74 | private: |
| 75 | void Deadlock() override { event.Wait(rtc::Event::kForever); } |
| 76 | void Release() override { event.Set(); } |
| 77 | |
| 78 | rtc::Event event; |
| 79 | }; |
| 80 | |
| 81 | class RtcCriticalSectionDeadlock : public DeadlockInterface { |
| 82 | public: |
| 83 | RtcCriticalSectionDeadlock() |
| 84 | : critscope_(absl::make_unique<rtc::CritScope>(&crit_)) {} |
| 85 | |
| 86 | private: |
| 87 | void Deadlock() override { rtc::CritScope lock(&crit_); } |
| 88 | |
| 89 | void Release() override { critscope_.reset(); } |
| 90 | |
| 91 | rtc::CriticalSection crit_; |
| 92 | std::unique_ptr<rtc::CritScope> critscope_; |
| 93 | }; |
| 94 | |
| 95 | class SpinDeadlock : public DeadlockInterface { |
| 96 | public: |
| 97 | SpinDeadlock() : is_deadlocked_(true) {} |
| 98 | |
| 99 | private: |
| 100 | void Deadlock() override { |
| 101 | while (is_deadlocked_) { |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | void Release() override { is_deadlocked_ = false; } |
| 106 | |
| 107 | std::atomic<bool> is_deadlocked_; |
| 108 | }; |
| 109 | |
| 110 | class SleepDeadlock : public DeadlockInterface { |
| 111 | private: |
| 112 | void Deadlock() override { sleep(1000000); } |
| 113 | |
| 114 | void Release() override { |
| 115 | // The interrupt itself will break free from the sleep. |
| 116 | } |
| 117 | }; |
| 118 | |
| 119 | // This is the function that is exectued by the thread that will deadlock and |
| 120 | // have its stacktrace captured. |
| 121 | void ThreadFunction(void* void_params) { |
| 122 | ThreadParams* params = static_cast<ThreadParams*>(void_params); |
| 123 | params->tid = gettid(); |
| 124 | |
| 125 | params->deadlock_region_start_address = GetCurrentRelativeExecutionAddress(); |
| 126 | params->deadlock_start_event.Set(); |
| 127 | params->deadlock_impl->Deadlock(); |
| 128 | params->deadlock_region_end_address = GetCurrentRelativeExecutionAddress(); |
| 129 | |
| 130 | params->deadlock_done_event.Set(); |
| 131 | } |
| 132 | |
| 133 | void TestStacktrace(std::unique_ptr<DeadlockInterface> deadlock_impl) { |
| 134 | // Set params that will be sent to other thread. |
| 135 | ThreadParams params; |
| 136 | params.deadlock_impl = deadlock_impl.get(); |
| 137 | |
| 138 | // Spawn thread. |
| 139 | rtc::PlatformThread thread(&ThreadFunction, ¶ms, "StacktraceTest"); |
| 140 | thread.Start(); |
| 141 | |
| 142 | // Wait until the thread has entered the deadlock region. |
| 143 | params.deadlock_start_event.Wait(rtc::Event::kForever); |
| 144 | |
| 145 | // Acquire the stack trace of the thread which should now be deadlocking. |
| 146 | std::vector<StackTraceElement> stack_trace = GetStackTrace(params.tid); |
| 147 | |
| 148 | // Release the deadlock so that the thread can continue. |
| 149 | deadlock_impl->Release(); |
| 150 | |
| 151 | // Wait until the thread has left the deadlock. |
| 152 | params.deadlock_done_event.Wait(rtc::Event::kForever); |
| 153 | |
| 154 | // Assert that the stack trace contains the deadlock region. |
| 155 | EXPECT_TRUE(StackTraceContainsRange(stack_trace, |
| 156 | params.deadlock_region_start_address, |
| 157 | params.deadlock_region_end_address)) |
| 158 | << "Deadlock region: [" |
| 159 | << rtc::ToHex(params.deadlock_region_start_address) << ", " |
| 160 | << rtc::ToHex(params.deadlock_region_end_address) |
| 161 | << "] not contained in: " << StackTraceToString(stack_trace); |
| 162 | |
| 163 | thread.Stop(); |
| 164 | } |
| 165 | |
Karl Wiberg | c130d42 | 2019-02-28 17:06:54 +0100 | [diff] [blame^] | 166 | TEST(Stacktrace, TestCurrentThread) { |
| 167 | const uint32_t start_addr = GetCurrentRelativeExecutionAddress(); |
| 168 | const std::vector<StackTraceElement> stack_trace = GetStackTrace(); |
| 169 | const uint32_t end_addr = GetCurrentRelativeExecutionAddress(); |
| 170 | EXPECT_TRUE(StackTraceContainsRange(stack_trace, start_addr, end_addr)) |
| 171 | << "Caller region: [" << rtc::ToHex(start_addr) << ", " |
| 172 | << rtc::ToHex(end_addr) |
| 173 | << "] not contained in: " << StackTraceToString(stack_trace); |
| 174 | } |
| 175 | |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 176 | TEST(Stacktrace, TestSpinLock) { |
| 177 | TestStacktrace(absl::make_unique<SpinDeadlock>()); |
| 178 | } |
| 179 | |
| 180 | TEST(Stacktrace, TestSleep) { |
| 181 | TestStacktrace(absl::make_unique<SleepDeadlock>()); |
| 182 | } |
| 183 | |
| 184 | // Stack traces originating from kernel space does not include user space stack |
| 185 | // traces for ARM 32. |
| 186 | #ifdef WEBRTC_ARCH_ARM64 |
| 187 | TEST(Stacktrace, TestRtcEvent) { |
| 188 | TestStacktrace(absl::make_unique<RtcEventDeadlock>()); |
| 189 | } |
| 190 | |
| 191 | TEST(Stacktrace, TestRtcCriticalSection) { |
| 192 | TestStacktrace(absl::make_unique<RtcCriticalSectionDeadlock>()); |
| 193 | } |
| 194 | #endif |
| 195 | |
| 196 | } // namespace test |
| 197 | } // namespace webrtc |