Bug 932865 - Collect thread hang stats in BackgroundHangMonitor; r=froydnj
authorJim Chen <nchen@mozilla.com>
Fri, 22 Nov 2013 14:17:31 -0500
changeset 157181 5365478bdea9ca9e1eb8299a5f3705a907e34955
parent 157180 4c31b0875e214c33a4abce64b9e8e27d2ef135f6
child 157182 94c3daf6e24247cf1df06674cd97078256e29b0f
push id25703
push userphilringnalda@gmail.com
push dateSat, 23 Nov 2013 16:19:02 +0000
treeherdermozilla-central@ad6589ed742c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs932865
milestone28.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 932865 - Collect thread hang stats in BackgroundHangMonitor; r=froydnj
xpcom/threads/BackgroundHangMonitor.cpp
--- a/xpcom/threads/BackgroundHangMonitor.cpp
+++ b/xpcom/threads/BackgroundHangMonitor.cpp
@@ -1,23 +1,25 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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 "mozilla/BackgroundHangMonitor.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/Monitor.h"
+#include "mozilla/Move.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/ThreadHangStats.h"
 #include "mozilla/ThreadLocal.h"
 
 #include "prinrval.h"
 #include "prthread.h"
+#include "ThreadStackHelper.h"
 
 #include <algorithm>
 
 namespace mozilla {
 
 /**
  * BackgroundHangManager is the global object that
  * manages all instances of BackgroundHangThread.
@@ -112,26 +114,30 @@ public:
   // Time at last activity
   PRIntervalTime mInterval;
   // Time when a hang started
   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;
   // Statistics for telemetry
   Telemetry::ThreadHangStats mStats;
 
   BackgroundHangThread(const char* aName,
                        uint32_t aTimeoutMs,
                        uint32_t aMaxTimeoutMs);
   ~BackgroundHangThread();
 
   // Report a hang; aManager->mLock IS locked
-  void ReportHang(PRIntervalTime aHangTime) const;
+  void ReportHang(PRIntervalTime aHangTime);
   // Report a permanent hang; aManager->mLock IS locked
   void ReportPermaHang() const;
   // Called by BackgroundHangMonitor::NotifyActivity
   void NotifyActivity();
   // Called by BackgroundHangMonitor::NotifyWait
   void NotifyWait()
   {
     NotifyActivity();
@@ -248,16 +254,17 @@ BackgroundHangManager::RunMonitorThread(
         currentThread->mWaiting = true;
         currentThread->ReportPermaHang();
         continue;
       }
 
       if (MOZ_LIKELY(!currentThread->mHanging)) {
         if (MOZ_UNLIKELY(hangTime >= currentThread->mTimeout)) {
           // A hang started
+          currentThread->mStackHelper.GetStack(currentThread->mHangStack);
           currentThread->mHangStart = interval;
           currentThread->mHanging = true;
         }
       } else {
         if (MOZ_LIKELY(interval != currentThread->mHangStart)) {
           // A hang ended
           currentThread->ReportHang(intervalNow - currentThread->mHangStart);
           currentThread->mHanging = false;
@@ -327,22 +334,33 @@ BackgroundHangThread::~BackgroundHangThr
     sTlsKey.set(nullptr);
   }
 
   // Move our copy of ThreadHangStats to Telemetry storage
   Telemetry::RecordThreadHangStats(mStats);
 }
 
 void
-BackgroundHangThread::ReportHang(PRIntervalTime aHangTime) const
+BackgroundHangThread::ReportHang(PRIntervalTime aHangTime)
 {
   // Recovered from a hang; called on the monitor thread
   // mManager->mLock IS locked
 
-  // TODO: Add telemetry reporting for hangs
+  Telemetry::HangHistogram newHistogram(Move(mHangStack));
+  for (Telemetry::HangHistogram* oldHistogram = mStats.mHangs.begin();
+       oldHistogram != mStats.mHangs.end(); oldHistogram++) {
+    if (newHistogram == *oldHistogram) {
+      // New histogram matches old one
+      oldHistogram->Add(aHangTime);
+      return;
+    }
+  }
+  // Add new histogram
+  newHistogram.Add(aHangTime);
+  mStats.mHangs.append(Move(newHistogram));
 }
 
 void
 BackgroundHangThread::ReportPermaHang() const
 {
   // Permanently hanged; called on the monitor thread
   // mManager->mLock IS locked
 
@@ -356,16 +374,17 @@ BackgroundHangThread::NotifyActivity()
   if (mWaiting) {
     mInterval = intervalNow;
     mWaiting = false;
     /* We have to wake up the manager thread because when all threads
        are waiting, the manager thread waits indefinitely as well. */
     mManager->Wakeup();
   } else {
     PRIntervalTime duration = intervalNow - mInterval;
+    mStats.mActivity.Add(duration);
     if (MOZ_UNLIKELY(duration >= mTimeout)) {
       /* Wake up the manager thread to tell it that a hang ended */
       mManager->Wakeup();
     }
     mInterval = intervalNow;
   }
 }
 
@@ -393,29 +412,31 @@ BackgroundHangThread::FindThread()
   return nullptr;
 }
 
 
 void
 BackgroundHangMonitor::Startup()
 {
   MOZ_ASSERT(!BackgroundHangManager::sInstance, "Already initialized");
+  ThreadStackHelper::Startup();
   BackgroundHangThread::Startup();
   BackgroundHangManager::sInstance = new BackgroundHangManager();
 }
 
 void
 BackgroundHangMonitor::Shutdown()
 {
   MOZ_ASSERT(BackgroundHangManager::sInstance, "Not initialized");
   /* Scope our lock inside Shutdown() because the sInstance object can
      be destroyed as soon as we set sInstance to nullptr below, and
      we don't want to hold the lock when it's being destroyed. */
   BackgroundHangManager::sInstance->Shutdown();
   BackgroundHangManager::sInstance = nullptr;
+  ThreadStackHelper::Shutdown();
 }
 
 BackgroundHangMonitor::BackgroundHangMonitor(const char* aName,
                                              uint32_t aTimeoutMs,
                                              uint32_t aMaxTimeoutMs)
   : mThread(BackgroundHangThread::FindThread())
 {
   if (!mThread) {