xpcom/threads/ThreadStackHelper.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "ThreadStackHelper.h"
     7 #include "MainThreadUtils.h"
     9 #include "mozilla/Assertions.h"
    10 #include "mozilla/Move.h"
    12 #ifdef XP_LINUX
    13 #include <unistd.h>
    14 #include <sys/syscall.h>
    15 #endif
    17 #ifdef ANDROID
    18 #ifndef SYS_gettid
    19 #define SYS_gettid __NR_gettid
    20 #endif
    21 #ifndef SYS_tgkill
    22 #define SYS_tgkill __NR_tgkill
    23 #endif
    24 #endif
    26 namespace mozilla {
    28 void
    29 ThreadStackHelper::Startup()
    30 {
    31 #if defined(XP_LINUX)
    32   MOZ_ASSERT(NS_IsMainThread());
    33   if (!sInitialized) {
    34     MOZ_ALWAYS_TRUE(!::sem_init(&sSem, 0, 0));
    35   }
    36   sInitialized++;
    37 #endif
    38 }
    40 void
    41 ThreadStackHelper::Shutdown()
    42 {
    43 #if defined(XP_LINUX)
    44   MOZ_ASSERT(NS_IsMainThread());
    45   if (sInitialized == 1) {
    46     MOZ_ALWAYS_TRUE(!::sem_destroy(&sSem));
    47   }
    48   sInitialized--;
    49 #endif
    50 }
    52 ThreadStackHelper::ThreadStackHelper()
    53   :
    54 #ifdef MOZ_ENABLE_PROFILER_SPS
    55     mPseudoStack(mozilla_get_pseudo_stack()),
    56 #endif
    57     mStackBuffer()
    58   , mMaxStackSize(mStackBuffer.capacity())
    59 {
    60 #if defined(XP_LINUX)
    61   mThreadID = ::syscall(SYS_gettid);
    62 #elif defined(XP_WIN)
    63   mInitialized = !!::DuplicateHandle(
    64     ::GetCurrentProcess(), ::GetCurrentThread(),
    65     ::GetCurrentProcess(), &mThreadID,
    66     THREAD_SUSPEND_RESUME, FALSE, 0);
    67   MOZ_ASSERT(mInitialized);
    68 #elif defined(XP_MACOSX)
    69   mThreadID = mach_thread_self();
    70 #endif
    71 }
    73 ThreadStackHelper::~ThreadStackHelper()
    74 {
    75 #if defined(XP_WIN)
    76   if (mInitialized) {
    77     MOZ_ALWAYS_TRUE(!!::CloseHandle(mThreadID));
    78   }
    79 #endif
    80 }
    82 #if defined(XP_LINUX) && defined(__arm__)
    83 // Some (old) Linux kernels on ARM have a bug where a signal handler
    84 // can be called without clearing the IT bits in CPSR first. The result
    85 // is that the first few instructions of the handler could be skipped,
    86 // ultimately resulting in crashes. To workaround this bug, the handler
    87 // on ARM is a trampoline that starts with enough NOP instructions, so
    88 // that even if the IT bits are not cleared, only the NOP instructions
    89 // will be skipped over.
    91 template <void (*H)(int, siginfo_t*, void*)>
    92 __attribute__((naked)) void
    93 SignalTrampoline(int aSignal, siginfo_t* aInfo, void* aContext)
    94 {
    95   asm volatile (
    96     "nop; nop; nop; nop"
    97     : : : "memory");
    99   // Because the assembler may generate additional insturctions below, we
   100   // need to ensure NOPs are inserted first by separating them out above.
   102   asm volatile (
   103     "bx %0"
   104     :
   105     : "r"(H), "l"(aSignal), "l"(aInfo), "l"(aContext)
   106     : "memory");
   107 }
   108 #endif // XP_LINUX && __arm__
   110 void
   111 ThreadStackHelper::GetStack(Stack& aStack)
   112 {
   113   // Always run PrepareStackBuffer first to clear aStack
   114   if (!PrepareStackBuffer(aStack)) {
   115     // Skip and return empty aStack
   116     return;
   117   }
   119 #if defined(XP_LINUX)
   120   if (profiler_is_active()) {
   121     // Profiler can interfere with our Linux signal handling
   122     return;
   123   }
   124   if (!sInitialized) {
   125     MOZ_ASSERT(false);
   126     return;
   127   }
   128   sCurrent = this;
   129   struct sigaction sigact = {};
   130 #ifdef __arm__
   131   sigact.sa_sigaction = SignalTrampoline<SigAction>;
   132 #else
   133   sigact.sa_sigaction = SigAction;
   134 #endif
   135   sigemptyset(&sigact.sa_mask);
   136   sigact.sa_flags = SA_SIGINFO | SA_RESTART;
   137   if (::sigaction(SIGPROF, &sigact, &sOldSigAction)) {
   138     MOZ_ASSERT(false);
   139     return;
   140   }
   141   MOZ_ALWAYS_TRUE(!::syscall(SYS_tgkill, getpid(), mThreadID, SIGPROF));
   142   MOZ_ALWAYS_TRUE(!::sem_wait(&sSem));
   144 #elif defined(XP_WIN)
   145   if (!mInitialized) {
   146     MOZ_ASSERT(false);
   147     return;
   148   }
   149   if (::SuspendThread(mThreadID) == DWORD(-1)) {
   150     MOZ_ASSERT(false);
   151     return;
   152   }
   153   FillStackBuffer();
   154   MOZ_ALWAYS_TRUE(::ResumeThread(mThreadID) != DWORD(-1));
   156 #elif defined(XP_MACOSX)
   157   if (::thread_suspend(mThreadID) != KERN_SUCCESS) {
   158     MOZ_ASSERT(false);
   159     return;
   160   }
   161   FillStackBuffer();
   162   MOZ_ALWAYS_TRUE(::thread_resume(mThreadID) == KERN_SUCCESS);
   164 #endif
   165   aStack = Move(mStackBuffer);
   166 }
   168 #ifdef XP_LINUX
   170 int ThreadStackHelper::sInitialized;
   171 sem_t ThreadStackHelper::sSem;
   172 struct sigaction ThreadStackHelper::sOldSigAction;
   173 ThreadStackHelper* ThreadStackHelper::sCurrent;
   175 void
   176 ThreadStackHelper::SigAction(int aSignal, siginfo_t* aInfo, void* aContext)
   177 {
   178   ::sigaction(SIGPROF, &sOldSigAction, nullptr);
   179   sCurrent->FillStackBuffer();
   180   sCurrent = nullptr;
   181   ::sem_post(&sSem);
   182 }
   184 #endif // XP_LINUX
   186 bool
   187 ThreadStackHelper::PrepareStackBuffer(Stack& aStack) {
   188   // Return false to skip getting the stack and return an empty stack
   189   aStack.clear();
   190 #ifdef MOZ_ENABLE_PROFILER_SPS
   191   /* Normally, provided the profiler is enabled, it would be an error if we
   192      don't have a pseudostack here (the thread probably forgot to call
   193      profiler_register_thread). However, on B2G, profiling secondary threads
   194      may be disabled despite profiler being enabled. This is by-design and
   195      is not an error. */
   196 #ifdef MOZ_WIDGET_GONK
   197   if (!mPseudoStack) {
   198     return false;
   199   }
   200 #endif
   201   MOZ_ASSERT(mPseudoStack);
   202   mStackBuffer.clear();
   203   MOZ_ALWAYS_TRUE(mStackBuffer.reserve(mMaxStackSize));
   204   return true;
   205 #else
   206   return false;
   207 #endif
   208 }
   210 void
   211 ThreadStackHelper::FillStackBuffer() {
   212 #ifdef MOZ_ENABLE_PROFILER_SPS
   213   size_t reservedSize = mMaxStackSize;
   215   // Go from front to back
   216   const volatile StackEntry* entry = mPseudoStack->mStack;
   217   const volatile StackEntry* end = entry + mPseudoStack->stackSize();
   218   // Deduplicate identical, consecutive frames
   219   const char* prevLabel = nullptr;
   220   for (; reservedSize-- && entry != end; entry++) {
   221     /* We only accept non-copy labels, because
   222        we are unable to actually copy labels here */
   223     if (entry->isCopyLabel()) {
   224       continue;
   225     }
   226     const char* const label = entry->label();
   227     if (label == prevLabel) {
   228       continue;
   229     }
   230     mStackBuffer.infallibleAppend(label);
   231     prevLabel = label;
   232   }
   233   // If we exited early due to buffer size, expand the buffer for next time
   234   mMaxStackSize += (end - entry);
   235 #endif
   236 }
   238 } // namespace mozilla

mercurial