1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/threads/ThreadStackHelper.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,238 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "ThreadStackHelper.h" 1.10 +#include "MainThreadUtils.h" 1.11 + 1.12 +#include "mozilla/Assertions.h" 1.13 +#include "mozilla/Move.h" 1.14 + 1.15 +#ifdef XP_LINUX 1.16 +#include <unistd.h> 1.17 +#include <sys/syscall.h> 1.18 +#endif 1.19 + 1.20 +#ifdef ANDROID 1.21 +#ifndef SYS_gettid 1.22 +#define SYS_gettid __NR_gettid 1.23 +#endif 1.24 +#ifndef SYS_tgkill 1.25 +#define SYS_tgkill __NR_tgkill 1.26 +#endif 1.27 +#endif 1.28 + 1.29 +namespace mozilla { 1.30 + 1.31 +void 1.32 +ThreadStackHelper::Startup() 1.33 +{ 1.34 +#if defined(XP_LINUX) 1.35 + MOZ_ASSERT(NS_IsMainThread()); 1.36 + if (!sInitialized) { 1.37 + MOZ_ALWAYS_TRUE(!::sem_init(&sSem, 0, 0)); 1.38 + } 1.39 + sInitialized++; 1.40 +#endif 1.41 +} 1.42 + 1.43 +void 1.44 +ThreadStackHelper::Shutdown() 1.45 +{ 1.46 +#if defined(XP_LINUX) 1.47 + MOZ_ASSERT(NS_IsMainThread()); 1.48 + if (sInitialized == 1) { 1.49 + MOZ_ALWAYS_TRUE(!::sem_destroy(&sSem)); 1.50 + } 1.51 + sInitialized--; 1.52 +#endif 1.53 +} 1.54 + 1.55 +ThreadStackHelper::ThreadStackHelper() 1.56 + : 1.57 +#ifdef MOZ_ENABLE_PROFILER_SPS 1.58 + mPseudoStack(mozilla_get_pseudo_stack()), 1.59 +#endif 1.60 + mStackBuffer() 1.61 + , mMaxStackSize(mStackBuffer.capacity()) 1.62 +{ 1.63 +#if defined(XP_LINUX) 1.64 + mThreadID = ::syscall(SYS_gettid); 1.65 +#elif defined(XP_WIN) 1.66 + mInitialized = !!::DuplicateHandle( 1.67 + ::GetCurrentProcess(), ::GetCurrentThread(), 1.68 + ::GetCurrentProcess(), &mThreadID, 1.69 + THREAD_SUSPEND_RESUME, FALSE, 0); 1.70 + MOZ_ASSERT(mInitialized); 1.71 +#elif defined(XP_MACOSX) 1.72 + mThreadID = mach_thread_self(); 1.73 +#endif 1.74 +} 1.75 + 1.76 +ThreadStackHelper::~ThreadStackHelper() 1.77 +{ 1.78 +#if defined(XP_WIN) 1.79 + if (mInitialized) { 1.80 + MOZ_ALWAYS_TRUE(!!::CloseHandle(mThreadID)); 1.81 + } 1.82 +#endif 1.83 +} 1.84 + 1.85 +#if defined(XP_LINUX) && defined(__arm__) 1.86 +// Some (old) Linux kernels on ARM have a bug where a signal handler 1.87 +// can be called without clearing the IT bits in CPSR first. The result 1.88 +// is that the first few instructions of the handler could be skipped, 1.89 +// ultimately resulting in crashes. To workaround this bug, the handler 1.90 +// on ARM is a trampoline that starts with enough NOP instructions, so 1.91 +// that even if the IT bits are not cleared, only the NOP instructions 1.92 +// will be skipped over. 1.93 + 1.94 +template <void (*H)(int, siginfo_t*, void*)> 1.95 +__attribute__((naked)) void 1.96 +SignalTrampoline(int aSignal, siginfo_t* aInfo, void* aContext) 1.97 +{ 1.98 + asm volatile ( 1.99 + "nop; nop; nop; nop" 1.100 + : : : "memory"); 1.101 + 1.102 + // Because the assembler may generate additional insturctions below, we 1.103 + // need to ensure NOPs are inserted first by separating them out above. 1.104 + 1.105 + asm volatile ( 1.106 + "bx %0" 1.107 + : 1.108 + : "r"(H), "l"(aSignal), "l"(aInfo), "l"(aContext) 1.109 + : "memory"); 1.110 +} 1.111 +#endif // XP_LINUX && __arm__ 1.112 + 1.113 +void 1.114 +ThreadStackHelper::GetStack(Stack& aStack) 1.115 +{ 1.116 + // Always run PrepareStackBuffer first to clear aStack 1.117 + if (!PrepareStackBuffer(aStack)) { 1.118 + // Skip and return empty aStack 1.119 + return; 1.120 + } 1.121 + 1.122 +#if defined(XP_LINUX) 1.123 + if (profiler_is_active()) { 1.124 + // Profiler can interfere with our Linux signal handling 1.125 + return; 1.126 + } 1.127 + if (!sInitialized) { 1.128 + MOZ_ASSERT(false); 1.129 + return; 1.130 + } 1.131 + sCurrent = this; 1.132 + struct sigaction sigact = {}; 1.133 +#ifdef __arm__ 1.134 + sigact.sa_sigaction = SignalTrampoline<SigAction>; 1.135 +#else 1.136 + sigact.sa_sigaction = SigAction; 1.137 +#endif 1.138 + sigemptyset(&sigact.sa_mask); 1.139 + sigact.sa_flags = SA_SIGINFO | SA_RESTART; 1.140 + if (::sigaction(SIGPROF, &sigact, &sOldSigAction)) { 1.141 + MOZ_ASSERT(false); 1.142 + return; 1.143 + } 1.144 + MOZ_ALWAYS_TRUE(!::syscall(SYS_tgkill, getpid(), mThreadID, SIGPROF)); 1.145 + MOZ_ALWAYS_TRUE(!::sem_wait(&sSem)); 1.146 + 1.147 +#elif defined(XP_WIN) 1.148 + if (!mInitialized) { 1.149 + MOZ_ASSERT(false); 1.150 + return; 1.151 + } 1.152 + if (::SuspendThread(mThreadID) == DWORD(-1)) { 1.153 + MOZ_ASSERT(false); 1.154 + return; 1.155 + } 1.156 + FillStackBuffer(); 1.157 + MOZ_ALWAYS_TRUE(::ResumeThread(mThreadID) != DWORD(-1)); 1.158 + 1.159 +#elif defined(XP_MACOSX) 1.160 + if (::thread_suspend(mThreadID) != KERN_SUCCESS) { 1.161 + MOZ_ASSERT(false); 1.162 + return; 1.163 + } 1.164 + FillStackBuffer(); 1.165 + MOZ_ALWAYS_TRUE(::thread_resume(mThreadID) == KERN_SUCCESS); 1.166 + 1.167 +#endif 1.168 + aStack = Move(mStackBuffer); 1.169 +} 1.170 + 1.171 +#ifdef XP_LINUX 1.172 + 1.173 +int ThreadStackHelper::sInitialized; 1.174 +sem_t ThreadStackHelper::sSem; 1.175 +struct sigaction ThreadStackHelper::sOldSigAction; 1.176 +ThreadStackHelper* ThreadStackHelper::sCurrent; 1.177 + 1.178 +void 1.179 +ThreadStackHelper::SigAction(int aSignal, siginfo_t* aInfo, void* aContext) 1.180 +{ 1.181 + ::sigaction(SIGPROF, &sOldSigAction, nullptr); 1.182 + sCurrent->FillStackBuffer(); 1.183 + sCurrent = nullptr; 1.184 + ::sem_post(&sSem); 1.185 +} 1.186 + 1.187 +#endif // XP_LINUX 1.188 + 1.189 +bool 1.190 +ThreadStackHelper::PrepareStackBuffer(Stack& aStack) { 1.191 + // Return false to skip getting the stack and return an empty stack 1.192 + aStack.clear(); 1.193 +#ifdef MOZ_ENABLE_PROFILER_SPS 1.194 + /* Normally, provided the profiler is enabled, it would be an error if we 1.195 + don't have a pseudostack here (the thread probably forgot to call 1.196 + profiler_register_thread). However, on B2G, profiling secondary threads 1.197 + may be disabled despite profiler being enabled. This is by-design and 1.198 + is not an error. */ 1.199 +#ifdef MOZ_WIDGET_GONK 1.200 + if (!mPseudoStack) { 1.201 + return false; 1.202 + } 1.203 +#endif 1.204 + MOZ_ASSERT(mPseudoStack); 1.205 + mStackBuffer.clear(); 1.206 + MOZ_ALWAYS_TRUE(mStackBuffer.reserve(mMaxStackSize)); 1.207 + return true; 1.208 +#else 1.209 + return false; 1.210 +#endif 1.211 +} 1.212 + 1.213 +void 1.214 +ThreadStackHelper::FillStackBuffer() { 1.215 +#ifdef MOZ_ENABLE_PROFILER_SPS 1.216 + size_t reservedSize = mMaxStackSize; 1.217 + 1.218 + // Go from front to back 1.219 + const volatile StackEntry* entry = mPseudoStack->mStack; 1.220 + const volatile StackEntry* end = entry + mPseudoStack->stackSize(); 1.221 + // Deduplicate identical, consecutive frames 1.222 + const char* prevLabel = nullptr; 1.223 + for (; reservedSize-- && entry != end; entry++) { 1.224 + /* We only accept non-copy labels, because 1.225 + we are unable to actually copy labels here */ 1.226 + if (entry->isCopyLabel()) { 1.227 + continue; 1.228 + } 1.229 + const char* const label = entry->label(); 1.230 + if (label == prevLabel) { 1.231 + continue; 1.232 + } 1.233 + mStackBuffer.infallibleAppend(label); 1.234 + prevLabel = label; 1.235 + } 1.236 + // If we exited early due to buffer size, expand the buffer for next time 1.237 + mMaxStackSize += (end - entry); 1.238 +#endif 1.239 +} 1.240 + 1.241 +} // namespace mozilla