Bug 1023461 - Add HangStack class to support internal string buffer; r=vladan
authorJim Chen <nchen@mozilla.com>
Fri, 20 Jun 2014 14:28:10 -0400
changeset 211801 5cc1bec060f9ef4ab0ee8d07dfc3a1d781ff0345
parent 211800 c03a1baf9f47d480d42e3065736247a8bd20ec5a
child 211802 cd86c2dc0b139aff38844ce53168c4e6e78e9b1e
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)
reviewersvladan
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 - Add HangStack class to support internal string buffer; r=vladan
toolkit/components/telemetry/Telemetry.cpp
toolkit/components/telemetry/ThreadHangStats.h
xpcom/threads/BackgroundHangMonitor.cpp
xpcom/threads/ThreadStackHelper.h
--- a/toolkit/components/telemetry/Telemetry.cpp
+++ b/toolkit/components/telemetry/Telemetry.cpp
@@ -2061,17 +2061,17 @@ CreateJSTimeHistogram(JSContext* cx, con
 static JSObject*
 CreateJSHangHistogram(JSContext* cx, const Telemetry::HangHistogram& hang)
 {
   JS::RootedObject ret(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
   if (!ret) {
     return nullptr;
   }
 
-  const Telemetry::HangHistogram::Stack& hangStack = hang.GetStack();
+  const Telemetry::HangStack& hangStack = hang.GetStack();
   JS::RootedObject stack(cx,
     JS_NewArrayObject(cx, hangStack.length()));
   if (!ret) {
     return nullptr;
   }
   for (size_t i = 0; i < hangStack.length(); i++) {
     JS::RootedString string(cx, JS_NewStringCopyZ(cx, hangStack[i]));
     if (!JS_SetElement(cx, stack, i, string)) {
@@ -3022,39 +3022,87 @@ SetProfileDir(nsIFile* aProfD)
 void
 TimeHistogram::Add(PRIntervalTime aTime)
 {
   uint32_t timeMs = PR_IntervalToMilliseconds(aTime);
   size_t index = mozilla::FloorLog2(timeMs);
   operator[](index)++;
 }
 
+const char*
+HangStack::InfallibleAppendViaBuffer(const char* aText, size_t aLength)
+{
+  MOZ_ASSERT(this->canAppendWithoutRealloc(1));
+  // Include null-terminator in length count.
+  MOZ_ASSERT(mBuffer.canAppendWithoutRealloc(aLength + 1));
+
+  const char* const entry = mBuffer.end();
+  mBuffer.infallibleAppend(aText, aLength);
+  mBuffer.infallibleAppend('\0'); // Explicitly append null-terminator
+  this->infallibleAppend(entry);
+  return entry;
+}
+
+const char*
+HangStack::AppendViaBuffer(const char* aText, size_t aLength)
+{
+  if (!this->reserve(this->length() + 1)) {
+    return nullptr;
+  }
+
+  // Keep track of the previous buffer in case we need to adjust pointers later.
+  const char* const prevStart = mBuffer.begin();
+  const char* const prevEnd = mBuffer.end();
+
+  // Include null-terminator in length count.
+  if (!mBuffer.reserve(mBuffer.length() + aLength + 1)) {
+    return nullptr;
+  }
+
+  if (prevStart != mBuffer.begin()) {
+    // The buffer has moved; we have to adjust pointers in the stack.
+    for (const char** entry = this->begin(); entry != this->end(); entry++) {
+      if (*entry >= prevStart && *entry < prevEnd) {
+        // Move from old buffer to new buffer.
+        *entry += mBuffer.begin() - prevStart;
+      }
+    }
+  }
+
+  return InfallibleAppendViaBuffer(aText, aLength);
+}
+
 uint32_t
-HangHistogram::GetHash(const Stack& aStack)
+HangHistogram::GetHash(const HangStack& aStack)
 {
   uint32_t hash = 0;
   for (const char* const* label = aStack.begin();
        label != aStack.end(); label++) {
-    /* We only need to hash the pointer instead of the text content
-       because we are assuming constant pointers */
-    hash = AddToHash(hash, *label);
+    /* If the string is within our buffer, we need to hash its content.
+       Otherwise, the string is statically allocated, and we only need
+       to hash the pointer instead of the content. */
+    if (aStack.IsInBuffer(*label)) {
+      hash = AddToHash(hash, HashString(*label));
+    } else {
+      hash = AddToHash(hash, *label);
+    }
   }
   return hash;
 }
 
 bool
 HangHistogram::operator==(const HangHistogram& aOther) const
 {
   if (mHash != aOther.mHash) {
     return false;
   }
   if (mStack.length() != aOther.mStack.length()) {
     return false;
   }
-  return PodEqual(mStack.begin(), aOther.mStack.begin(), mStack.length());
+  return mStack == aOther.mStack;
 }
 
 
 } // namespace Telemetry
 } // namespace mozilla
 
 NSMODULE_DEFN(nsTelemetryModule) = &kTelemetryModule;
 
--- a/toolkit/components/telemetry/ThreadHangStats.h
+++ b/toolkit/components/telemetry/ThreadHangStats.h
@@ -39,48 +39,108 @@ public:
   // Get maximum (inclusive) range of bucket in milliseconds
   uint32_t GetBucketMax(size_t aBucket) const {
     MOZ_ASSERT(aBucket < ArrayLength(*this));
     return (1u << (aBucket + 1u)) - 1u;
   }
   void Add(PRIntervalTime aTime);
 };
 
+/* HangStack stores an array of const char pointers,
+   with optional internal storage for strings. */
+class HangStack : public mozilla::Vector<const char*, 8>
+{
+private:
+  typedef mozilla::Vector<const char*, 8> Base;
+
+  // Stack entries can either be a static const char*
+  // or a pointer to within this buffer.
+  mozilla::Vector<char, 0> mBuffer;
+
+public:
+  HangStack() { }
+
+  HangStack(HangStack&& aOther)
+    : Base(mozilla::Move(aOther))
+    , mBuffer(mozilla::Move(aOther.mBuffer))
+  {
+  }
+
+  bool operator==(const HangStack& aOther) const {
+    for (size_t i = 0; i < length(); i++) {
+      if (!IsSameAsEntry(operator[](i), aOther[i])) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  bool operator!=(const HangStack& aOther) const {
+    return !operator==(aOther);
+  }
+
+  void clear() {
+    Base::clear();
+    mBuffer.clear();
+  }
+
+  bool IsInBuffer(const char* aEntry) const {
+    return aEntry >= mBuffer.begin() && aEntry < mBuffer.end();
+  }
+
+  bool IsSameAsEntry(const char* aEntry, const char* aOther) const {
+    // If the entry came from the buffer, we need to compare its content;
+    // otherwise we only need to compare its pointer.
+    return IsInBuffer(aEntry) ? !strcmp(aEntry, aOther) : (aEntry == aOther);
+  }
+
+  size_t AvailableBufferSize() const {
+    return mBuffer.capacity() - mBuffer.length();
+  }
+
+  bool EnsureBufferCapacity(size_t aCapacity) {
+    // aCapacity is the minimal capacity and Vector may make the actual
+    // capacity larger, in which case we want to use up all the space.
+    return mBuffer.reserve(aCapacity) &&
+           mBuffer.reserve(mBuffer.capacity());
+  }
+
+  const char* InfallibleAppendViaBuffer(const char* aText, size_t aLength);
+  const char* AppendViaBuffer(const char* aText, size_t aLength);
+};
+
 /* A hang histogram consists of a stack associated with the
    hang, along with a time histogram of the hang times. */
 class HangHistogram : public TimeHistogram
 {
-public:
-  typedef mozilla::Vector<const char*, 8> Stack;
+private:
+  static uint32_t GetHash(const HangStack& aStack);
 
-private:
-  static uint32_t GetHash(const Stack& aStack);
-
-  Stack mStack;
+  HangStack mStack;
   // Use a hash to speed comparisons
   const uint32_t mHash;
 
 public:
-  explicit HangHistogram(Stack&& aStack)
+  explicit HangHistogram(HangStack&& aStack)
     : mStack(mozilla::Move(aStack))
     , mHash(GetHash(mStack))
   {
   }
   HangHistogram(HangHistogram&& aOther)
     : TimeHistogram(mozilla::Move(aOther))
     , mStack(mozilla::Move(aOther.mStack))
     , mHash(mozilla::Move(aOther.mHash))
   {
   }
   bool operator==(const HangHistogram& aOther) const;
   bool operator!=(const HangHistogram& aOther) const
   {
     return !operator==(aOther);
   }
-  const Stack& GetStack() const {
+  const HangStack& GetStack() const {
     return mStack;
   }
 };
 
 /* Thread hang stats consist of
  - thread name
  - time histogram of all task run times
  - hang histograms of individual hangs. */
--- a/xpcom/threads/BackgroundHangMonitor.cpp
+++ b/xpcom/threads/BackgroundHangMonitor.cpp
@@ -132,17 +132,17 @@ public:
   PRIntervalTime mHangStart;
   // Is the thread in a hang
   bool mHanging;
   // Is the thread in a waiting state
   bool mWaiting;
   // Platform-specific helper to get hang stacks
   ThreadStackHelper mStackHelper;
   // Stack of current hang
-  Telemetry::HangHistogram::Stack mHangStack;
+  Telemetry::HangStack mHangStack;
   // Statistics for telemetry
   Telemetry::ThreadHangStats mStats;
 
   BackgroundHangThread(const char* aName,
                        uint32_t aTimeoutMs,
                        uint32_t aMaxTimeoutMs);
 
   // Report a hang; aManager->mLock IS locked
--- a/xpcom/threads/ThreadStackHelper.h
+++ b/xpcom/threads/ThreadStackHelper.h
@@ -32,17 +32,17 @@ namespace mozilla {
  * the pseudo-stack of the target thread at that instant.
  *
  * Only non-copying labels are included in the stack, which means labels
  * with custom text and markers are not included.
  */
 class ThreadStackHelper
 {
 public:
-  typedef Telemetry::HangHistogram::Stack Stack;
+  typedef Telemetry::HangStack Stack;
 
 private:
 #ifdef MOZ_ENABLE_PROFILER_SPS
   const PseudoStack* const mPseudoStack;
 #endif
   Stack* mStackToFill;
   size_t mMaxStackSize;