michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "ThreadStackHelper.h" michael@0: #include "MainThreadUtils.h" michael@0: michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/Move.h" michael@0: michael@0: #ifdef XP_LINUX michael@0: #include michael@0: #include michael@0: #endif michael@0: michael@0: #ifdef ANDROID michael@0: #ifndef SYS_gettid michael@0: #define SYS_gettid __NR_gettid michael@0: #endif michael@0: #ifndef SYS_tgkill michael@0: #define SYS_tgkill __NR_tgkill michael@0: #endif michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: michael@0: void michael@0: ThreadStackHelper::Startup() michael@0: { michael@0: #if defined(XP_LINUX) michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: if (!sInitialized) { michael@0: MOZ_ALWAYS_TRUE(!::sem_init(&sSem, 0, 0)); michael@0: } michael@0: sInitialized++; michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: ThreadStackHelper::Shutdown() michael@0: { michael@0: #if defined(XP_LINUX) michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: if (sInitialized == 1) { michael@0: MOZ_ALWAYS_TRUE(!::sem_destroy(&sSem)); michael@0: } michael@0: sInitialized--; michael@0: #endif michael@0: } michael@0: michael@0: ThreadStackHelper::ThreadStackHelper() michael@0: : michael@0: #ifdef MOZ_ENABLE_PROFILER_SPS michael@0: mPseudoStack(mozilla_get_pseudo_stack()), michael@0: #endif michael@0: mStackBuffer() michael@0: , mMaxStackSize(mStackBuffer.capacity()) michael@0: { michael@0: #if defined(XP_LINUX) michael@0: mThreadID = ::syscall(SYS_gettid); michael@0: #elif defined(XP_WIN) michael@0: mInitialized = !!::DuplicateHandle( michael@0: ::GetCurrentProcess(), ::GetCurrentThread(), michael@0: ::GetCurrentProcess(), &mThreadID, michael@0: THREAD_SUSPEND_RESUME, FALSE, 0); michael@0: MOZ_ASSERT(mInitialized); michael@0: #elif defined(XP_MACOSX) michael@0: mThreadID = mach_thread_self(); michael@0: #endif michael@0: } michael@0: michael@0: ThreadStackHelper::~ThreadStackHelper() michael@0: { michael@0: #if defined(XP_WIN) michael@0: if (mInitialized) { michael@0: MOZ_ALWAYS_TRUE(!!::CloseHandle(mThreadID)); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: #if defined(XP_LINUX) && defined(__arm__) michael@0: // Some (old) Linux kernels on ARM have a bug where a signal handler michael@0: // can be called without clearing the IT bits in CPSR first. The result michael@0: // is that the first few instructions of the handler could be skipped, michael@0: // ultimately resulting in crashes. To workaround this bug, the handler michael@0: // on ARM is a trampoline that starts with enough NOP instructions, so michael@0: // that even if the IT bits are not cleared, only the NOP instructions michael@0: // will be skipped over. michael@0: michael@0: template michael@0: __attribute__((naked)) void michael@0: SignalTrampoline(int aSignal, siginfo_t* aInfo, void* aContext) michael@0: { michael@0: asm volatile ( michael@0: "nop; nop; nop; nop" michael@0: : : : "memory"); michael@0: michael@0: // Because the assembler may generate additional insturctions below, we michael@0: // need to ensure NOPs are inserted first by separating them out above. michael@0: michael@0: asm volatile ( michael@0: "bx %0" michael@0: : michael@0: : "r"(H), "l"(aSignal), "l"(aInfo), "l"(aContext) michael@0: : "memory"); michael@0: } michael@0: #endif // XP_LINUX && __arm__ michael@0: michael@0: void michael@0: ThreadStackHelper::GetStack(Stack& aStack) michael@0: { michael@0: // Always run PrepareStackBuffer first to clear aStack michael@0: if (!PrepareStackBuffer(aStack)) { michael@0: // Skip and return empty aStack michael@0: return; michael@0: } michael@0: michael@0: #if defined(XP_LINUX) michael@0: if (profiler_is_active()) { michael@0: // Profiler can interfere with our Linux signal handling michael@0: return; michael@0: } michael@0: if (!sInitialized) { michael@0: MOZ_ASSERT(false); michael@0: return; michael@0: } michael@0: sCurrent = this; michael@0: struct sigaction sigact = {}; michael@0: #ifdef __arm__ michael@0: sigact.sa_sigaction = SignalTrampoline; michael@0: #else michael@0: sigact.sa_sigaction = SigAction; michael@0: #endif michael@0: sigemptyset(&sigact.sa_mask); michael@0: sigact.sa_flags = SA_SIGINFO | SA_RESTART; michael@0: if (::sigaction(SIGPROF, &sigact, &sOldSigAction)) { michael@0: MOZ_ASSERT(false); michael@0: return; michael@0: } michael@0: MOZ_ALWAYS_TRUE(!::syscall(SYS_tgkill, getpid(), mThreadID, SIGPROF)); michael@0: MOZ_ALWAYS_TRUE(!::sem_wait(&sSem)); michael@0: michael@0: #elif defined(XP_WIN) michael@0: if (!mInitialized) { michael@0: MOZ_ASSERT(false); michael@0: return; michael@0: } michael@0: if (::SuspendThread(mThreadID) == DWORD(-1)) { michael@0: MOZ_ASSERT(false); michael@0: return; michael@0: } michael@0: FillStackBuffer(); michael@0: MOZ_ALWAYS_TRUE(::ResumeThread(mThreadID) != DWORD(-1)); michael@0: michael@0: #elif defined(XP_MACOSX) michael@0: if (::thread_suspend(mThreadID) != KERN_SUCCESS) { michael@0: MOZ_ASSERT(false); michael@0: return; michael@0: } michael@0: FillStackBuffer(); michael@0: MOZ_ALWAYS_TRUE(::thread_resume(mThreadID) == KERN_SUCCESS); michael@0: michael@0: #endif michael@0: aStack = Move(mStackBuffer); michael@0: } michael@0: michael@0: #ifdef XP_LINUX michael@0: michael@0: int ThreadStackHelper::sInitialized; michael@0: sem_t ThreadStackHelper::sSem; michael@0: struct sigaction ThreadStackHelper::sOldSigAction; michael@0: ThreadStackHelper* ThreadStackHelper::sCurrent; michael@0: michael@0: void michael@0: ThreadStackHelper::SigAction(int aSignal, siginfo_t* aInfo, void* aContext) michael@0: { michael@0: ::sigaction(SIGPROF, &sOldSigAction, nullptr); michael@0: sCurrent->FillStackBuffer(); michael@0: sCurrent = nullptr; michael@0: ::sem_post(&sSem); michael@0: } michael@0: michael@0: #endif // XP_LINUX michael@0: michael@0: bool michael@0: ThreadStackHelper::PrepareStackBuffer(Stack& aStack) { michael@0: // Return false to skip getting the stack and return an empty stack michael@0: aStack.clear(); michael@0: #ifdef MOZ_ENABLE_PROFILER_SPS michael@0: /* Normally, provided the profiler is enabled, it would be an error if we michael@0: don't have a pseudostack here (the thread probably forgot to call michael@0: profiler_register_thread). However, on B2G, profiling secondary threads michael@0: may be disabled despite profiler being enabled. This is by-design and michael@0: is not an error. */ michael@0: #ifdef MOZ_WIDGET_GONK michael@0: if (!mPseudoStack) { michael@0: return false; michael@0: } michael@0: #endif michael@0: MOZ_ASSERT(mPseudoStack); michael@0: mStackBuffer.clear(); michael@0: MOZ_ALWAYS_TRUE(mStackBuffer.reserve(mMaxStackSize)); michael@0: return true; michael@0: #else michael@0: return false; michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: ThreadStackHelper::FillStackBuffer() { michael@0: #ifdef MOZ_ENABLE_PROFILER_SPS michael@0: size_t reservedSize = mMaxStackSize; michael@0: michael@0: // Go from front to back michael@0: const volatile StackEntry* entry = mPseudoStack->mStack; michael@0: const volatile StackEntry* end = entry + mPseudoStack->stackSize(); michael@0: // Deduplicate identical, consecutive frames michael@0: const char* prevLabel = nullptr; michael@0: for (; reservedSize-- && entry != end; entry++) { michael@0: /* We only accept non-copy labels, because michael@0: we are unable to actually copy labels here */ michael@0: if (entry->isCopyLabel()) { michael@0: continue; michael@0: } michael@0: const char* const label = entry->label(); michael@0: if (label == prevLabel) { michael@0: continue; michael@0: } michael@0: mStackBuffer.infallibleAppend(label); michael@0: prevLabel = label; michael@0: } michael@0: // If we exited early due to buffer size, expand the buffer for next time michael@0: mMaxStackSize += (end - entry); michael@0: #endif michael@0: } michael@0: michael@0: } // namespace mozilla