bug 601268 - Add canary-in-the-mine instrumentation to detect when the browser process spends too long outside its main-thread event loop(s) r=cjones a=beltzner
authorBrad Lassey <blassey@mozilla.com>
Wed, 27 Oct 2010 11:16:03 -0400
changeset 56625 b866072123fab61f6783210319f9b6164fb54421
parent 56624 fa759e894155909ef0f86c012e9d8dc3df150841
child 56626 df7ea854a68c8c4fedfa1cdd4a1caf3262726014
push id16614
push userblassey@mozilla.com
push dateWed, 27 Oct 2010 18:48:49 +0000
treeherdermozilla-central@b866072123fa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscjones, beltzner
bugs601268
milestone2.0b8pre
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 601268 - Add canary-in-the-mine instrumentation to detect when the browser process spends too long outside its main-thread event loop(s) r=cjones a=beltzner
xpcom/threads/nsThread.cpp
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -41,16 +41,30 @@
 #include "nsIClassInfoImpl.h"
 #include "nsIProgrammingLanguage.h"
 #include "nsAutoLock.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "prlog.h"
 #include "nsThreadUtilsInternal.h"
 
+#define HAVE_UALARM _BSD_SOURCE || (_XOPEN_SOURCE >= 500 ||                 \
+                      _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED) &&           \
+                      !(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
+
+#if defined(XP_UNIX) && !defined(ANDROID) && !defined(DEBUG) && HAVE_UALARM \
+  && defined(_GNU_SOURCE)
+# define MOZ_CANARY
+# include <unistd.h>
+# include <execinfo.h>
+# include <signal.h>
+# include <fcntl.h>
+# include "nsXULAppAPI.h"
+#endif
+
 #include "mozilla/FunctionTimer.h"
 #if defined(NS_FUNCTION_TIMER) && defined(_MSC_VER)
 #include "nsTimerImpl.h"
 #include "nsStackWalk.h"
 #endif
 #ifdef NS_FUNCTION_TIMER
 #include "nsCRT.h"
 #endif
@@ -495,16 +509,61 @@ NS_IMETHODIMP
 nsThread::HasPendingEvents(PRBool *result)
 {
   NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
 
   *result = mEvents->GetEvent(PR_FALSE, nsnull);
   return NS_OK;
 }
 
+#ifdef MOZ_CANARY
+void canary_alarm_handler (int signum);
+
+class Canary {
+//XXX ToDo: support nested loops
+public:
+  Canary() {
+    if (sOutputFD != 0 && NS_IsMainThread() && 
+        XRE_GetProcessType() == GeckoProcessType_Default) {
+      if (sOutputFD == -1) {
+        const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NONBLOCK;
+        const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+        char* env_var_flag = getenv("MOZ_KILL_CANARIES");
+        sOutputFD = env_var_flag ? (env_var_flag[0] ?
+                                    open(env_var_flag, flags, mode) :
+                                    STDERR_FILENO) : 0;
+        if (sOutputFD == 0)
+          return;
+      }
+      signal(SIGALRM, canary_alarm_handler);
+      ualarm(15000, 0);      
+    }
+  }
+
+  ~Canary() {
+    if (sOutputFD != 0 && NS_IsMainThread() && 
+        XRE_GetProcessType() == GeckoProcessType_Default)
+      ualarm(0, 0);
+  }
+  static int sOutputFD;
+};
+
+int Canary::sOutputFD = -1;
+
+void canary_alarm_handler (int signum)
+{
+  void *array[30];
+  const char msg[29] = "event took too long to run:\n";
+  // use write to be safe in the signal handler
+  write(Canary::sOutputFD, msg, sizeof(msg)); 
+  backtrace_symbols_fd(array, backtrace(array, 30), Canary::sOutputFD);
+}
+
+#endif
+
 NS_IMETHODIMP
 nsThread::ProcessNextEvent(PRBool mayWait, PRBool *result)
 {
   LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, mayWait, mRunningEvent));
 
   NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
 
   PRBool notifyGlobalObserver = (sGlobalObserver != nsnull);
@@ -513,16 +572,19 @@ nsThread::ProcessNextEvent(PRBool mayWai
                                         mRunningEvent);
 
   nsCOMPtr<nsIThreadObserver> obs = mObserver;
   if (obs)
     obs->OnProcessNextEvent(this, mayWait && !ShuttingDown(), mRunningEvent);
 
   ++mRunningEvent;
 
+#ifdef MOZ_CANARY
+  Canary canary;
+#endif
   nsresult rv = NS_OK;
 
   {
     // Scope for |event| to make sure that its destructor fires while
     // mRunningEvent has been incremented, since that destructor can
     // also do work.
 
     // If we are shutting down, then do not wait for new events.