Bug 1023461 - Record filename and line number for chrome JS entries; r=snorp
authorJim Chen <nchen@mozilla.com>
Fri, 20 Jun 2014 14:28:10 -0400
changeset 211802 cd86c2dc0b139aff38844ce53168c4e6e78e9b1e
parent 211801 5cc1bec060f9ef4ab0ee8d07dfc3a1d781ff0345
child 211803 214719d6c99c8d276d564b792b81af6fd9175af9
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1023461
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1023461 - Record filename and line number for chrome JS entries; r=snorp
xpcom/threads/ThreadStackHelper.cpp
xpcom/threads/ThreadStackHelper.h
--- a/xpcom/threads/ThreadStackHelper.cpp
+++ b/xpcom/threads/ThreadStackHelper.cpp
@@ -3,20 +3,25 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ThreadStackHelper.h"
 #include "MainThreadUtils.h"
 #include "nsJSPrincipals.h"
 #include "nsScriptSecurityManager.h"
 #include "jsfriendapi.h"
+#include "prprf.h"
+
+#include "js/OldDebugAPI.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Move.h"
 
+#include <string.h>
+
 #ifdef XP_LINUX
 #include <unistd.h>
 #include <sys/syscall.h>
 #endif
 
 #ifdef ANDROID
 #ifndef SYS_gettid
 #define SYS_gettid __NR_gettid
@@ -54,16 +59,17 @@ ThreadStackHelper::Shutdown()
 
 ThreadStackHelper::ThreadStackHelper()
   :
 #ifdef MOZ_ENABLE_PROFILER_SPS
     mPseudoStack(mozilla_get_pseudo_stack()),
 #endif
     mStackToFill(nullptr)
   , mMaxStackSize(Stack::sMaxInlineStorage)
+  , mMaxBufferSize(0)
 {
 #if defined(XP_LINUX)
   mThreadID = ::syscall(SYS_gettid);
 #elif defined(XP_WIN)
   mInitialized = !!::DuplicateHandle(
     ::GetCurrentProcess(), ::GetCurrentThread(),
     ::GetCurrentProcess(), &mThreadID,
     THREAD_SUSPEND_RESUME, FALSE, 0);
@@ -197,17 +203,21 @@ ThreadStackHelper::PrepareStackBuffer(St
      may be disabled despite profiler being enabled. This is by-design and
      is not an error. */
 #ifdef MOZ_WIDGET_GONK
   if (!mPseudoStack) {
     return false;
   }
 #endif
   MOZ_ASSERT(mPseudoStack);
-  MOZ_ALWAYS_TRUE(aStack.reserve(mMaxStackSize));
+  if (!aStack.reserve(mMaxStackSize) ||
+      !aStack.reserve(aStack.capacity()) || // reserve up to the capacity
+      !aStack.EnsureBufferCapacity(mMaxBufferSize)) {
+    return false;
+  }
   mStackToFill = &aStack;
   return true;
 #else
   return false;
 #endif
 }
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
@@ -227,65 +237,102 @@ IsChromeJSScript(JSScript* aScript)
   JSPrincipals* const principals = JS_GetScriptPrincipals(aScript);
   return secman->IsSystemPrincipal(nsJSPrincipals::get(principals));
 }
 
 } // namespace
 
 const char*
 ThreadStackHelper::AppendJSEntry(const volatile StackEntry* aEntry,
+                                 intptr_t& aAvailableBufferSize,
                                  const char* aPrevLabel)
 {
   // May be called from another thread or inside a signal handler.
   // We assume querying the script is safe but we must not manupulate it.
+  // Also we must not allocate any memory from heap.
   MOZ_ASSERT(aEntry->isJs());
   MOZ_ASSERT(aEntry->script());
 
   const char* label;
   if (IsChromeJSScript(aEntry->script())) {
+    const char* const filename = JS_GetScriptFilename(aEntry->script());
+    unsigned lineno = JS_PCToLineNumber(nullptr, aEntry->script(),
+                                        aEntry->pc());
+    MOZ_ASSERT(filename);
+
+    char buffer[64]; // Enough to fit longest js file name from the tree
+    const char* const basename = strrchr(filename, '/');
+    size_t len = PR_snprintf(buffer, sizeof(buffer), "%s:%u",
+                             basename ? basename + 1 : filename, lineno);
+    if (len < sizeof(buffer)) {
+      if (mStackToFill->IsSameAsEntry(aPrevLabel, buffer)) {
+        return aPrevLabel;
+      }
+
+      // Keep track of the required buffer size
+      aAvailableBufferSize -= (len + 1);
+      if (aAvailableBufferSize >= 0) {
+        // Buffer is big enough.
+        return mStackToFill->InfallibleAppendViaBuffer(buffer, len);
+      }
+      // Buffer is not big enough; fall through to using static label below.
+    }
+    // snprintf failed or buffer is not big enough.
     label = "(chrome script)";
   } else {
     label = "(content script)";
   }
 
-  if (label == aPrevLabel) {
+  if (mStackToFill->IsSameAsEntry(aPrevLabel, label)) {
     return aPrevLabel;
   }
   mStackToFill->infallibleAppend(label);
   return label;
 }
 
 #endif // MOZ_ENABLE_PROFILER_SPS
 
 void
 ThreadStackHelper::FillStackBuffer()
 {
+  MOZ_ASSERT(mStackToFill->empty());
+
 #ifdef MOZ_ENABLE_PROFILER_SPS
-  size_t reservedSize = mMaxStackSize;
+  size_t reservedSize = mStackToFill->capacity();
+  size_t reservedBufferSize = mStackToFill->AvailableBufferSize();
+  intptr_t availableBufferSize = intptr_t(reservedBufferSize);
 
   // Go from front to back
   const volatile StackEntry* entry = mPseudoStack->mStack;
   const volatile StackEntry* end = entry + mPseudoStack->stackSize();
   // Deduplicate identical, consecutive frames
   const char* prevLabel = nullptr;
   for (; reservedSize-- && entry != end; entry++) {
-    /* We only accept non-copy labels, because
-       we are unable to actually copy labels here */
+    /* We only accept non-copy labels, including js::RunScript,
+       because we only want static labels in the hang stack. */
     if (entry->isCopyLabel()) {
       continue;
     }
     if (entry->isJs()) {
-      prevLabel = AppendJSEntry(entry, prevLabel);
+      prevLabel = AppendJSEntry(entry, availableBufferSize, prevLabel);
       continue;
     }
     const char* const label = entry->label();
-    if (label == prevLabel) {
+    if (mStackToFill->IsSameAsEntry(prevLabel, label)) {
       continue;
     }
     mStackToFill->infallibleAppend(label);
     prevLabel = label;
   }
-  // If we exited early due to buffer size, expand the buffer for next time
-  mMaxStackSize += (end - entry);
+
+  // end != entry if we exited early due to not enough reserved frames.
+  // Expand the number of reserved frames for next time.
+  mMaxStackSize = mStackToFill->capacity() + (end - entry);
+
+  // availableBufferSize < 0 if we needed a larger buffer than we reserved.
+  // Calculate a new reserve size for next time.
+  if (availableBufferSize < 0) {
+    mMaxBufferSize = reservedBufferSize - availableBufferSize;
+  }
 #endif
 }
 
 } // namespace mozilla
--- a/xpcom/threads/ThreadStackHelper.h
+++ b/xpcom/threads/ThreadStackHelper.h
@@ -40,21 +40,23 @@ public:
   typedef Telemetry::HangStack Stack;
 
 private:
 #ifdef MOZ_ENABLE_PROFILER_SPS
   const PseudoStack* const mPseudoStack;
 #endif
   Stack* mStackToFill;
   size_t mMaxStackSize;
+  size_t mMaxBufferSize;
 
   bool PrepareStackBuffer(Stack& aStack);
   void FillStackBuffer();
 #ifdef MOZ_ENABLE_PROFILER_SPS
   const char* AppendJSEntry(const volatile StackEntry* aEntry,
+                            intptr_t& aAvailableBufferSize,
                             const char* aPrevLabel);
 #endif
 
 public:
   /**
    * Initialize ThreadStackHelper. Must be called from main thread.
    */
   static void Startup();