Bug 1066760 - Redirect mozalloc_abort through Java exception handling; r=snorp
authorJim Chen <nchen@mozilla.com>
Wed, 24 Sep 2014 14:12:54 -0400
changeset 207084 421c30629ac5bca71a03dead2a7d8889609e188d
parent 207083 4aafc40bcd62ad4e289fa28ed0269ef33e52b399
child 207085 4cf6c10a0ab9cc18f46f876a86fbe84ec82a5eee
push id8970
push userryanvm@gmail.com
push dateWed, 24 Sep 2014 21:13:34 +0000
treeherderfx-team@ea2894eea2d0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1066760
milestone35.0a1
Bug 1066760 - Redirect mozalloc_abort through Java exception handling; r=snorp
memory/mozalloc/mozalloc_abort.cpp
mobile/android/base/mozglue/GeckoLoader.java.in
mozglue/android/APKOpen.cpp
mozglue/android/APKOpen.h
mozglue/linker/ElfLoader.h
--- a/memory/mozalloc/mozalloc_abort.cpp
+++ b/memory/mozalloc/mozalloc_abort.cpp
@@ -9,29 +9,35 @@
 #  define MOZALLOC_EXPORT __declspec(dllexport)
 #endif
 
 #include "mozilla/mozalloc_abort.h"
 
 #ifdef ANDROID
 # include <android/log.h>
 #endif
+#ifdef MOZ_WIDGET_ANDROID
+# include "APKOpen.h"
+#endif
 #include <stdio.h>
 
 #include "mozilla/Assertions.h"
 
 void
 mozalloc_abort(const char* const msg)
 {
 #ifndef ANDROID
     fputs(msg, stderr);
     fputs("\n", stderr);
 #else
     __android_log_print(ANDROID_LOG_ERROR, "Gecko", "mozalloc_abort: %s", msg);
 #endif
+#ifdef MOZ_WIDGET_ANDROID
+    abortThroughJava(msg);
+#endif
     MOZ_CRASH();
 }
 
 #if defined(XP_UNIX)
 // Define abort() here, so that it is used instead of the system abort(). This
 // lets us control the behavior when aborting, in order to get better results
 // on *NIX platforms. See mozalloc_abort for details.
 void abort(void)
--- a/mobile/android/base/mozglue/GeckoLoader.java.in
+++ b/mobile/android/base/mozglue/GeckoLoader.java.in
@@ -526,16 +526,33 @@ public final class GeckoLoader {
             DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();
 
             putenv("LOCALE_DECIMAL_POINT=" + dfs.getDecimalSeparator());
             putenv("LOCALE_THOUSANDS_SEP=" + dfs.getGroupingSeparator());
             putenv("LOCALE_GROUPING=" + (char)df.getGroupingSize());
         }
     }
 
+    @SuppressWarnings("serial")
+    public static class AbortException extends Exception {
+        public AbortException(String msg) {
+            super(msg);
+        }
+    }
+
+    @JNITarget
+    public static void abort(final String msg) {
+        final Thread thread = Thread.currentThread();
+        final Thread.UncaughtExceptionHandler uncaughtHandler =
+            thread.getUncaughtExceptionHandler();
+        if (uncaughtHandler != null) {
+            uncaughtHandler.uncaughtException(thread, new AbortException(msg));
+        }
+    }
+
     // These methods are implemented in mozglue/android/nsGeckoUtils.cpp
     private static native void putenv(String map);
 
     // These methods are implemented in mozglue/android/APKOpen.cpp
     public static native void nativeRun(String args);
     private static native void loadGeckoLibsNative(String apkName);
     private static native void loadSQLiteLibsNative(String apkName);
     private static native void loadNSSLibsNative(String apkName);
--- a/mozglue/android/APKOpen.cpp
+++ b/mozglue/android/APKOpen.cpp
@@ -119,16 +119,60 @@ JNI_Throw(JNIEnv* jenv, const char* clas
     int rc = jenv->ThrowNew(cls, msg);
     if (rc < 0) {
         __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Error throwing exception %s\n", msg);
         exit(FAILURE);
     }
     jenv->DeleteLocalRef(cls);
 }
 
+namespace {
+    JavaVM* sJavaVM;
+}
+
+void
+abortThroughJava(const char* msg)
+{
+    struct sigaction sigact = {};
+    if (SEGVHandler::__wrap_sigaction(SIGSEGV, nullptr, &sigact)) {
+        return; // sigaction call failed.
+    }
+
+    Dl_info info = {};
+    if ((sigact.sa_flags & SA_SIGINFO) &&
+        __wrap_dladdr(reinterpret_cast<void*>(sigact.sa_sigaction), &info) &&
+        info.dli_fname && strstr(info.dli_fname, "libxul.so")) {
+
+        return; // Existing signal handler is in libxul (i.e. we have crash reporter).
+    }
+
+    JNIEnv* env = nullptr;
+    if (!sJavaVM || sJavaVM->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
+        return;
+    }
+
+    if (!env || env->PushLocalFrame(2) != JNI_OK) {
+        return;
+    }
+
+    jclass loader = env->FindClass("org/mozilla/gecko/mozglue/GeckoLoader");
+    if (!loader) {
+        return;
+    }
+
+    jmethodID method = env->GetStaticMethodID(loader, "abort", "(Ljava/lang/String;)V");
+    jstring str = env->NewStringUTF(msg);
+
+    if (method && str) {
+        env->CallStaticVoidMethod(loader, method, str);
+    }
+
+    env->PopLocalFrame(nullptr);
+}
+
 #define JNI_STUBS
 #include "jni-stubs.inc"
 #undef JNI_STUBS
 
 static void * xul_handle = nullptr;
 #ifndef MOZ_FOLD_LIBS
 static void * sqlite_handle = nullptr;
 static void * nspr_handle = nullptr;
@@ -297,16 +341,18 @@ loadNSSLibs(const char *apkName)
 #endif
 
   return setup_nss_functions(nss_handle, nspr_handle, plc_handle);
 }
 
 extern "C" NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_mozglue_GeckoLoader_loadGeckoLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName)
 {
+  jenv->GetJavaVM(&sJavaVM);
+
   const char* str;
   // XXX: java doesn't give us true UTF8, we should figure out something
   // better to do here
   str = jenv->GetStringUTFChars(jApkName, nullptr);
   if (str == nullptr)
     return;
 
   int res = loadGeckoLibs(str);
--- a/mozglue/android/APKOpen.h
+++ b/mozglue/android/APKOpen.h
@@ -14,14 +14,15 @@
 struct mapping_info {
   char * name;
   uintptr_t base;
   size_t len;
   size_t offset;
 };
 
 NS_EXPORT const struct mapping_info * getLibraryMapping();
+NS_EXPORT void abortThroughJava(const char* msg);
 
 static const int SUCCESS = 0;
 static const int FAILURE = 1;
 void JNI_Throw(JNIEnv* jenv, const char* classname, const char* msg);
 
 #endif /* APKOpen_h */
--- a/mozglue/linker/ElfLoader.h
+++ b/mozglue/linker/ElfLoader.h
@@ -322,23 +322,24 @@ public:
       FinishInitialization();
     return registeredHandler;
   }
 
   bool isSignalHandlingBroken() {
     return signalHandlingBroken;
   }
 
+  static int __wrap_sigaction(int signum, const struct sigaction *act,
+                              struct sigaction *oldact);
+
 protected:
   SEGVHandler();
   ~SEGVHandler();
 
 private:
-  static int __wrap_sigaction(int signum, const struct sigaction *act,
-                              struct sigaction *oldact);
 
   /**
    * The constructor doesn't do all initialization, and the tail is done
    * at a later time.
    */
   void FinishInitialization();
 
   /**