Bug 886736 - Disable on-demand decompression when latency to get into segfault handlers is too high. r=nfroyd
authorMike Hommey <mh+mozilla@glandium.org>
Tue, 01 Oct 2013 15:30:45 +0900
changeset 166393 6722e803c598e6719c2be391f302f822ec5538d9
parent 166392 4942026233a46ba5f5e115c6d156d5da20d6cb8c
child 166394 2a344cf7aca2db14a7a6a3982d70459da8c87f7d
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnfroyd
bugs886736
milestone27.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 886736 - Disable on-demand decompression when latency to get into segfault handlers is too high. r=nfroyd
mozglue/linker/ElfLoader.cpp
mozglue/linker/ElfLoader.h
--- a/mozglue/linker/ElfLoader.cpp
+++ b/mozglue/linker/ElfLoader.cpp
@@ -9,16 +9,17 @@
 #include <dlfcn.h>
 #include <unistd.h>
 #include <algorithm>
 #include <fcntl.h>
 #include "ElfLoader.h"
 #include "CustomElf.h"
 #include "Mappable.h"
 #include "Logging.h"
+#include <inttypes.h>
 
 #if defined(ANDROID)
 #include <sys/syscall.h>
 
 #include <android/api-level.h>
 #if __ANDROID_API__ < 8
 /* Android API < 8 doesn't provide sigaltstack */
 
@@ -903,49 +904,82 @@ Divert(T func, T new_func)
 template <typename T>
 static bool
 Divert(T func, T new_func)
 {
   return false;
 }
 #endif
 
+namespace {
+
+/* Clock that only accounts for time spent in the current process. */
+static uint64_t ProcessTimeStamp_Now()
+{
+  struct timespec ts;
+  int rv = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
+
+  if (rv != 0) {
+    return 0;
+  }
+
+  uint64_t baseNs = (uint64_t)ts.tv_sec * 1000000000;
+  return baseNs + (uint64_t)ts.tv_nsec;
+}
+
+}
+
+/* Data structure used to pass data to the temporary signal handler,
+ * as well as triggering a test crash. */
+struct TmpData {
+  volatile int crash_int;
+  volatile uint64_t crash_timestamp;
+};
+
 SEGVHandler::SEGVHandler()
 : registeredHandler(false), signalHandlingBroken(false)
+, signalHandlingSlow(false)
 {
   /* Initialize oldStack.ss_flags to an invalid value when used to set
    * an alternative stack, meaning we haven't got information about the
    * original alternative stack and thus don't mean to restore it */
   oldStack.ss_flags = SS_ONSTACK;
   if (!Divert(sigaction, __wrap_sigaction))
     return;
 
   /* Get the current segfault signal handler. */
   sys_sigaction(SIGSEGV, NULL, &this->action);
 
   /* Some devices don't provide useful information to their SIGSEGV handlers,
    * making it impossible for on-demand decompression to work. To check if
    * we're on such a device, setup a temporary handler and deliberately
    * trigger a segfault. The handler will set signalHandlingBroken if the
-   * provided information is bogus. */
+   * provided information is bogus.
+   * Some other devices have a kernel option enabled that makes SIGSEGV handler
+   * have an overhead so high that it affects how on-demand decompression
+   * performs. The handler will also set signalHandlingSlow if the triggered
+   * SIGSEGV took too much time. */
   struct sigaction action;
   action.sa_sigaction = &SEGVHandler::test_handler;
   sigemptyset(&action.sa_mask);
   action.sa_flags = SA_SIGINFO | SA_NODEFER;
   action.sa_restorer = NULL;
   if (sys_sigaction(SIGSEGV, &action, NULL))
     return;
-  stackPtr.Assign(MemoryRange::mmap(NULL, PageSize(), PROT_NONE,
+  stackPtr.Assign(MemoryRange::mmap(NULL, PageSize(), PROT_READ | PROT_WRITE,
                                     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
   if (stackPtr.get() == MAP_FAILED)
     return;
 
-  *((volatile int*)stackPtr.get()) = 123;
+  TmpData *data = reinterpret_cast<TmpData*>(stackPtr.get());
+  data->crash_timestamp = ProcessTimeStamp_Now();
+  mprotect(stackPtr, stackPtr.GetLength(), PROT_NONE);
+  data->crash_int = 123;
   stackPtr.Assign(MAP_FAILED, 0);
-  if (signalHandlingBroken) {
+  if (signalHandlingBroken || signalHandlingSlow) {
     /* Restore the original segfault signal handler. */
     sys_sigaction(SIGSEGV, &this->action, NULL);
     return;
   }
 
   /* Setup an alternative stack if the already existing one is not big
    * enough, or if there is none. */
   if (sigaltstack(NULL, &oldStack) == 0) {
@@ -978,23 +1012,32 @@ SEGVHandler::~SEGVHandler()
     sigaltstack(&oldStack, NULL);
   /* Restore original signal handler */
   if (registeredHandler)
     sys_sigaction(SIGSEGV, &this->action, NULL);
 }
 
 /* Test handler for a deliberately triggered SIGSEGV that determines whether
  * useful information is provided to signal handlers, particularly whether
- * si_addr is filled in properly. */
+ * si_addr is filled in properly, and whether the segfault handler is called
+ * quickly enough. */
 void SEGVHandler::test_handler(int signum, siginfo_t *info, void *context)
 {
   SEGVHandler &that = ElfLoader::Singleton;
   if (signum != SIGSEGV || info == NULL || info->si_addr != that.stackPtr.get())
     that.signalHandlingBroken = true;
   mprotect(that.stackPtr, that.stackPtr.GetLength(), PROT_READ | PROT_WRITE);
+  TmpData *data = reinterpret_cast<TmpData*>(that.stackPtr.get());
+  uint64_t latency = ProcessTimeStamp_Now() - data->crash_timestamp;
+  DEBUG_LOG("SEGVHandler latency: %" PRIu64, latency);
+  /* See bug 886736 for timings on different devices, 150 ┬Ás is reasonably above
+   * the latency on "working" devices and seems to be reasonably fast to incur
+   * a huge overhead to on-demand decompression. */
+  if (latency > 150000)
+    that.signalHandlingSlow = true;
 }
 
 /* TODO: "properly" handle signal masks and flags */
 void SEGVHandler::handler(int signum, siginfo_t *info, void *context)
 {
   //ASSERT(signum == SIGSEGV);
   DEBUG_LOG("Caught segmentation fault @%p", info->si_addr);
 
--- a/mozglue/linker/ElfLoader.h
+++ b/mozglue/linker/ElfLoader.h
@@ -339,16 +339,17 @@ private:
   /**
    * Pointer to an alternative stack for signals. Only set if oldStack is
    * not set or not big enough.
    */
   MappedPtr stackPtr;
 
   bool registeredHandler;
   bool signalHandlingBroken;
+  bool signalHandlingSlow;
 };
 
 /**
  * Elf Loader class in charge of loading and bookkeeping libraries.
  */
 class ElfLoader: public SEGVHandler
 {
 public: