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 151632 6722e803c598e6719c2be391f302f822ec5538d9
parent 151631 4942026233a46ba5f5e115c6d156d5da20d6cb8c
child 151633 2a344cf7aca2db14a7a6a3982d70459da8c87f7d
push id3163
push userkwierso@gmail.com
push dateTue, 22 Oct 2013 22:48:21 +0000
treeherderfx-team@8f8964ee7d12 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnfroyd
bugs886736
milestone27.0a1
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: