LCOV - code coverage report
Current view: top level - xpcom/threads - ThreadStackHelper.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 20 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 7 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "ThreadStackHelper.h"
       8             : #include "MainThreadUtils.h"
       9             : #include "nsJSPrincipals.h"
      10             : #include "nsScriptSecurityManager.h"
      11             : #include "jsfriendapi.h"
      12             : #ifdef MOZ_THREADSTACKHELPER_PSEUDO
      13             : #include "js/ProfilingStack.h"
      14             : #endif
      15             : 
      16             : #include "mozilla/Assertions.h"
      17             : #include "mozilla/Attributes.h"
      18             : #include "mozilla/IntegerPrintfMacros.h"
      19             : #include "mozilla/Move.h"
      20             : #include "mozilla/Scoped.h"
      21             : #include "mozilla/UniquePtr.h"
      22             : #include "mozilla/MemoryChecking.h"
      23             : #include "mozilla/Sprintf.h"
      24             : #include "nsThread.h"
      25             : 
      26             : #ifdef __GNUC__
      27             : # pragma GCC diagnostic push
      28             : # pragma GCC diagnostic ignored "-Wshadow"
      29             : #endif
      30             : 
      31             : #if defined(MOZ_VALGRIND)
      32             : # include <valgrind/valgrind.h>
      33             : #endif
      34             : 
      35             : #include <string.h>
      36             : #include <vector>
      37             : #include <cstdlib>
      38             : 
      39             : #ifdef XP_LINUX
      40             : #include <ucontext.h>
      41             : #include <unistd.h>
      42             : #include <sys/syscall.h>
      43             : #endif
      44             : 
      45             : #ifdef __GNUC__
      46             : # pragma GCC diagnostic pop // -Wshadow
      47             : #endif
      48             : 
      49             : #if defined(XP_LINUX) || defined(XP_MACOSX)
      50             : #include <pthread.h>
      51             : #endif
      52             : 
      53             : #ifdef ANDROID
      54             : #ifndef SYS_gettid
      55             : #define SYS_gettid __NR_gettid
      56             : #endif
      57             : #if defined(__arm__) && !defined(__NR_rt_tgsigqueueinfo)
      58             : // Some NDKs don't define this constant even though the kernel supports it.
      59             : #define __NR_rt_tgsigqueueinfo (__NR_SYSCALL_BASE+363)
      60             : #endif
      61             : #ifndef SYS_rt_tgsigqueueinfo
      62             : #define SYS_rt_tgsigqueueinfo __NR_rt_tgsigqueueinfo
      63             : #endif
      64             : #endif
      65             : 
      66             : namespace mozilla {
      67             : 
      68           0 : ThreadStackHelper::ThreadStackHelper()
      69             : #ifdef MOZ_THREADSTACKHELPER_PSEUDO
      70             :   : mStackToFill(nullptr)
      71             :   , mPseudoStack(profiler_get_pseudo_stack())
      72             :   , mMaxStackSize(Stack::sMaxInlineStorage)
      73             :   , mMaxBufferSize(512)
      74             : #endif
      75             : #ifdef MOZ_THREADSTACKHELPER_NATIVE
      76             :   , mNativeStackToFill(nullptr)
      77             : #endif
      78             : {
      79           0 :   mThreadId = profiler_current_thread_id();
      80           0 : }
      81             : 
      82             : namespace {
      83             : template<typename T>
      84             : class ScopedSetPtr
      85             : {
      86             : private:
      87             :   T*& mPtr;
      88             : public:
      89             :   ScopedSetPtr(T*& p, T* val) : mPtr(p) { mPtr = val; }
      90             :   ~ScopedSetPtr() { mPtr = nullptr; }
      91             : };
      92             : } // namespace
      93             : 
      94             : void
      95           0 : ThreadStackHelper::GetPseudoStack(Stack& aStack, nsACString& aRunnableName)
      96             : {
      97           0 :   GetStacksInternal(&aStack, nullptr, aRunnableName);
      98           0 : }
      99             : 
     100             : void
     101           0 : ThreadStackHelper::GetNativeStack(NativeStack& aNativeStack, nsACString& aRunnableName)
     102             : {
     103           0 :   GetStacksInternal(nullptr, &aNativeStack, aRunnableName);
     104           0 : }
     105             : 
     106             : void
     107           0 : ThreadStackHelper::GetPseudoAndNativeStack(Stack& aStack,
     108             :                                            NativeStack& aNativeStack,
     109             :                                            nsACString& aRunnableName)
     110             : {
     111           0 :   GetStacksInternal(&aStack, &aNativeStack, aRunnableName);
     112           0 : }
     113             : 
     114             : void
     115           0 : ThreadStackHelper::GetStacksInternal(Stack* aStack,
     116             :                                      NativeStack* aNativeStack,
     117             :                                      nsACString& aRunnableName)
     118             : {
     119           0 :   aRunnableName.AssignLiteral("???");
     120             : 
     121             : #if defined(MOZ_THREADSTACKHELPER_PSEUDO) || defined(MOZ_THREADSTACKHELPER_NATIVE)
     122             :   // Always run PrepareStackBuffer first to clear aStack
     123             :   if (aStack && !PrepareStackBuffer(*aStack)) {
     124             :     // Skip and return empty aStack
     125             :     return;
     126             :   }
     127             : 
     128             :   // Prepare the native stack
     129             :   if (aNativeStack) {
     130             :     aNativeStack->clear();
     131             :     aNativeStack->reserve(Telemetry::HangStack::sMaxNativeFrames);
     132             :   }
     133             : 
     134             : #ifdef MOZ_THREADSTACKHELPER_PSEUDO
     135             :   ScopedSetPtr<Stack> stackPtr(mStackToFill, aStack);
     136             : #endif
     137             : #ifdef MOZ_THREADSTACKHELPER_NATIVE
     138             :   ScopedSetPtr<NativeStack> nativeStackPtr(mNativeStackToFill, aNativeStack);
     139             : #endif
     140             : 
     141             :   char nameBuffer[1000] = {0};
     142             :   auto callback = [&, this] (void** aPCs, size_t aCount, bool aIsMainThread) {
     143             :     // NOTE: We cannot allocate any memory in this callback, as the target
     144             :     // thread is suspended, so we first copy it into a stack-allocated buffer,
     145             :     // and then once the target thread is resumed, we can copy it into a real
     146             :     // nsCString.
     147             :     //
     148             :     // Currently we only store the names of runnables which are running on the
     149             :     // main thread, so we only want to read sMainThreadRunnableName and copy its
     150             :     // value in the case that we are currently suspending the main thread.
     151             :     if (aIsMainThread && nsThread::sMainThreadRunnableName) {
     152             :       strncpy(nameBuffer, nsThread::sMainThreadRunnableName, sizeof(nameBuffer));
     153             :       // Make sure the string is null-terminated.
     154             :       nameBuffer[sizeof(nameBuffer) - 1] = '\0';
     155             :     }
     156             : 
     157             : #ifdef MOZ_THREADSTACKHELPER_PSEUDO
     158             :     if (mStackToFill) {
     159             :       FillStackBuffer();
     160             :     }
     161             : #endif
     162             : 
     163             : #ifdef MOZ_THREADSTACKHELPER_NATIVE
     164             :     if (mNativeStackToFill) {
     165             :       while (aCount-- &&
     166             :              mNativeStackToFill->size() < mNativeStackToFill->capacity()) {
     167             :         mNativeStackToFill->push_back(reinterpret_cast<uintptr_t>(aPCs[aCount]));
     168             :       }
     169             :     }
     170             : #endif
     171             :   };
     172             : 
     173             :   if (mStackToFill || mNativeStackToFill) {
     174             :     profiler_suspend_and_sample_thread(mThreadId,
     175             :                                        callback,
     176             :                                        /* aSampleNative = */ !!aNativeStack);
     177             :   }
     178             : 
     179             :   // Copy the name buffer allocation into the output string.
     180             :   if (nameBuffer[0] != 0) {
     181             :     aRunnableName = nameBuffer;
     182             :   }
     183             : #endif
     184           0 : }
     185             : 
     186             : bool
     187           0 : ThreadStackHelper::PrepareStackBuffer(Stack& aStack)
     188             : {
     189             :   // Return false to skip getting the stack and return an empty stack
     190           0 :   aStack.clear();
     191             : #ifdef MOZ_THREADSTACKHELPER_PSEUDO
     192             :   /* Normally, provided the profiler is enabled, it would be an error if we
     193             :      don't have a pseudostack here (the thread probably forgot to call
     194             :      profiler_register_thread). However, on B2G, profiling secondary threads
     195             :      may be disabled despite profiler being enabled. This is by-design and
     196             :      is not an error. */
     197             : #ifdef MOZ_WIDGET_GONK
     198             :   if (!mPseudoStack) {
     199             :     return false;
     200             :   }
     201             : #endif
     202             :   MOZ_ASSERT(mPseudoStack);
     203             :   if (!aStack.reserve(mMaxStackSize) ||
     204             :       !aStack.reserve(aStack.capacity()) || // reserve up to the capacity
     205             :       !aStack.EnsureBufferCapacity(mMaxBufferSize)) {
     206             :     return false;
     207             :   }
     208             :   return true;
     209             : #else
     210           0 :   return false;
     211             : #endif
     212             : }
     213             : 
     214             : #ifdef MOZ_THREADSTACKHELPER_PSEUDO
     215             : 
     216             : namespace {
     217             : 
     218             : bool
     219             : IsChromeJSScript(JSScript* aScript)
     220             : {
     221             :   // May be called from another thread or inside a signal handler.
     222             :   // We assume querying the script is safe but we must not manipulate it.
     223             : 
     224             :   nsIScriptSecurityManager* const secman =
     225             :     nsScriptSecurityManager::GetScriptSecurityManager();
     226             :   NS_ENSURE_TRUE(secman, false);
     227             : 
     228             :   JSPrincipals* const principals = JS_GetScriptPrincipals(aScript);
     229             :   return secman->IsSystemPrincipal(nsJSPrincipals::get(principals));
     230             : }
     231             : 
     232             : // Get the full path after the URI scheme, if the URI matches the scheme.
     233             : // For example, GetFullPathForScheme("a://b/c/d/e", "a://") returns "b/c/d/e".
     234             : template <size_t LEN>
     235             : const char*
     236             : GetFullPathForScheme(const char* filename, const char (&scheme)[LEN]) {
     237             :   // Account for the null terminator included in LEN.
     238             :   if (!strncmp(filename, scheme, LEN - 1)) {
     239             :     return filename + LEN - 1;
     240             :   }
     241             :   return nullptr;
     242             : }
     243             : 
     244             : // Get the full path after a URI component, if the URI contains the component.
     245             : // For example, GetPathAfterComponent("a://b/c/d/e", "/c/") returns "d/e".
     246             : template <size_t LEN>
     247             : const char*
     248             : GetPathAfterComponent(const char* filename, const char (&component)[LEN]) {
     249             :   const char* found = nullptr;
     250             :   const char* next = strstr(filename, component);
     251             :   while (next) {
     252             :     // Move 'found' to end of the component, after the separator '/'.
     253             :     // 'LEN - 1' accounts for the null terminator included in LEN,
     254             :     found = next + LEN - 1;
     255             :     // Resume searching before the separator '/'.
     256             :     next = strstr(found - 1, component);
     257             :   }
     258             :   return found;
     259             : }
     260             : 
     261             : } // namespace
     262             : 
     263             : const char*
     264             : ThreadStackHelper::AppendJSEntry(const js::ProfileEntry* aEntry,
     265             :                                  intptr_t& aAvailableBufferSize,
     266             :                                  const char* aPrevLabel)
     267             : {
     268             :   // May be called from another thread or inside a signal handler.
     269             :   // We assume querying the script is safe but we must not manupulate it.
     270             :   // Also we must not allocate any memory from heap.
     271             :   MOZ_ASSERT(aEntry->isJs());
     272             : 
     273             :   const char* label;
     274             :   JSScript* script = aEntry->script();
     275             :   if (!script) {
     276             :     label = "(profiling suppressed)";
     277             :   } else if (IsChromeJSScript(aEntry->script())) {
     278             :     const char* filename = JS_GetScriptFilename(aEntry->script());
     279             :     const unsigned lineno = JS_PCToLineNumber(aEntry->script(), aEntry->pc());
     280             :     MOZ_ASSERT(filename);
     281             : 
     282             :     char buffer[128]; // Enough to fit longest js file name from the tree
     283             : 
     284             :     // Some script names are in the form "foo -> bar -> baz".
     285             :     // Here we find the origin of these redirected scripts.
     286             :     const char* basename = GetPathAfterComponent(filename, " -> ");
     287             :     if (basename) {
     288             :       filename = basename;
     289             :     }
     290             : 
     291             :     basename = GetFullPathForScheme(filename, "chrome://");
     292             :     if (!basename) {
     293             :       basename = GetFullPathForScheme(filename, "resource://");
     294             :     }
     295             :     if (!basename) {
     296             :       // If the (add-on) script is located under the {profile}/extensions
     297             :       // directory, extract the path after the /extensions/ part.
     298             :       basename = GetPathAfterComponent(filename, "/extensions/");
     299             :     }
     300             :     if (!basename) {
     301             :       // Only keep the file base name for paths outside the above formats.
     302             :       basename = strrchr(filename, '/');
     303             :       basename = basename ? basename + 1 : filename;
     304             :       // Look for Windows path separator as well.
     305             :       filename = strrchr(basename, '\\');
     306             :       if (filename) {
     307             :         basename = filename + 1;
     308             :       }
     309             :     }
     310             : 
     311             :     size_t len = SprintfLiteral(buffer, "%s:%u", basename, lineno);
     312             :     if (len < sizeof(buffer)) {
     313             :       if (mStackToFill->IsSameAsEntry(aPrevLabel, buffer)) {
     314             :         return aPrevLabel;
     315             :       }
     316             : 
     317             :       // Keep track of the required buffer size
     318             :       aAvailableBufferSize -= (len + 1);
     319             :       if (aAvailableBufferSize >= 0) {
     320             :         // Buffer is big enough.
     321             :         return mStackToFill->InfallibleAppendViaBuffer(buffer, len);
     322             :       }
     323             :       // Buffer is not big enough; fall through to using static label below.
     324             :     }
     325             :     // snprintf failed or buffer is not big enough.
     326             :     label = "(chrome script)";
     327             :   } else {
     328             :     label = "(content script)";
     329             :   }
     330             : 
     331             :   if (mStackToFill->IsSameAsEntry(aPrevLabel, label)) {
     332             :     return aPrevLabel;
     333             :   }
     334             :   mStackToFill->infallibleAppend(label);
     335             :   return label;
     336             : }
     337             : 
     338             : #endif // MOZ_THREADSTACKHELPER_PSEUDO
     339             : 
     340             : void
     341           0 : ThreadStackHelper::FillStackBuffer()
     342             : {
     343             : #ifdef MOZ_THREADSTACKHELPER_PSEUDO
     344             :   MOZ_ASSERT(mStackToFill->empty());
     345             : 
     346             :   size_t reservedSize = mStackToFill->capacity();
     347             :   size_t reservedBufferSize = mStackToFill->AvailableBufferSize();
     348             :   intptr_t availableBufferSize = intptr_t(reservedBufferSize);
     349             : 
     350             :   // Go from front to back
     351             :   const js::ProfileEntry* entry = mPseudoStack->entries;
     352             :   const js::ProfileEntry* end = entry + mPseudoStack->stackSize();
     353             :   // Deduplicate identical, consecutive frames
     354             :   const char* prevLabel = nullptr;
     355             :   for (; reservedSize-- && entry != end; entry++) {
     356             :     if (entry->isJs()) {
     357             :       prevLabel = AppendJSEntry(entry, availableBufferSize, prevLabel);
     358             :       continue;
     359             :     }
     360             :     const char* const label = entry->label();
     361             :     if (mStackToFill->IsSameAsEntry(prevLabel, label)) {
     362             :       // Avoid duplicate labels to save space in the stack.
     363             :       continue;
     364             :     }
     365             :     mStackToFill->infallibleAppend(label);
     366             :     prevLabel = label;
     367             :   }
     368             : 
     369             :   // end != entry if we exited early due to not enough reserved frames.
     370             :   // Expand the number of reserved frames for next time.
     371             :   mMaxStackSize = mStackToFill->capacity() + (end - entry);
     372             : 
     373             :   // availableBufferSize < 0 if we needed a larger buffer than we reserved.
     374             :   // Calculate a new reserve size for next time.
     375             :   if (availableBufferSize < 0) {
     376             :     mMaxBufferSize = reservedBufferSize - availableBufferSize;
     377             :   }
     378             : #endif
     379           0 : }
     380             : 
     381             : } // namespace mozilla

Generated by: LCOV version 1.13