Framework: Improve binder Error logging
In protracted cases, diagnostic code cannot retrieve any Java-side
data. Add two specific cases and instanceof checks (which require
no allocation etc), and a special message, so that aborts have
meaningful data (in case logcat is missing).
Sample:
Abort message: 'JNI FatalError called: java.lang.Error thrown during binder transaction: (Unknown exception message) (Error was OutOfMemoryError) (toString() error was OutOfMemoryError)'
Bug: 136482507
Test: m
Test: manual
Change-Id: I5c0c1d74ba8a6aeafee95b578a422a80b9899196
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 9556333..fc977f1 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -99,7 +99,9 @@
static struct error_offsets_t
{
- jclass mClass;
+ jclass mError;
+ jclass mOutOfMemory;
+ jclass mStackOverflow;
} gErrorOffsets;
// ----------------------------------------------------------------------------
@@ -208,6 +210,16 @@
return vm->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0 ? env : NULL;
}
+static const char* GetErrorTypeName(JNIEnv* env, jthrowable error) {
+ if (env->IsInstanceOf(error, gErrorOffsets.mOutOfMemory)) {
+ return "OutOfMemoryError";
+ }
+ if (env->IsInstanceOf(error, gErrorOffsets.mStackOverflow)) {
+ return "StackOverflowError";
+ }
+ return nullptr;
+}
+
// Report a java.lang.Error (or subclass). This will terminate the runtime by
// calling FatalError with a message derived from the given error.
static void report_java_lang_error_fatal_error(JNIEnv* env, jthrowable error,
@@ -217,7 +229,7 @@
// Try to get the exception string. Sometimes logcat isn't available,
// so try to add it to the abort message.
- std::string exc_msg = "(Unknown exception message)";
+ std::string exc_msg;
{
ScopedLocalRef<jclass> exc_class(env, env->GetObjectClass(error));
jmethodID method_id = env->GetMethodID(exc_class.get(), "toString",
@@ -226,15 +238,36 @@
env,
reinterpret_cast<jstring>(
env->CallObjectMethod(error, method_id)));
- env->ExceptionClear(); // Just for good measure.
+ ScopedLocalRef<jthrowable> new_error(env, nullptr);
+ bool got_jstr = false;
+ if (env->ExceptionCheck()) {
+ new_error = ScopedLocalRef<jthrowable>(env, env->ExceptionOccurred());
+ env->ExceptionClear();
+ }
if (jstr.get() != nullptr) {
ScopedUtfChars jstr_utf(env, jstr.get());
if (jstr_utf.c_str() != nullptr) {
exc_msg = jstr_utf.c_str();
+ got_jstr = true;
} else {
+ new_error = ScopedLocalRef<jthrowable>(env, env->ExceptionOccurred());
env->ExceptionClear();
}
}
+ if (!got_jstr) {
+ exc_msg = "(Unknown exception message)";
+ const char* orig_type = GetErrorTypeName(env, error);
+ if (orig_type != nullptr) {
+ exc_msg = base::StringPrintf("%s (Error was %s)", exc_msg.c_str(), orig_type);
+ }
+ const char* new_type =
+ new_error == nullptr ? nullptr : GetErrorTypeName(env, new_error.get());
+ if (new_type != nullptr) {
+ exc_msg = base::StringPrintf("%s (toString() error was %s)",
+ exc_msg.c_str(),
+ new_type);
+ }
+ }
}
env->Throw(error);
@@ -292,7 +325,7 @@
ALOGE("%s", msg);
}
- if (env->IsInstanceOf(excep, gErrorOffsets.mClass)) {
+ if (env->IsInstanceOf(excep, gErrorOffsets.mError)) {
report_java_lang_error(env, excep, msg);
}
}
@@ -1417,10 +1450,13 @@
static int int_register_android_os_BinderProxy(JNIEnv* env)
{
- jclass clazz = FindClassOrDie(env, "java/lang/Error");
- gErrorOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
+ gErrorOffsets.mError = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/Error"));
+ gErrorOffsets.mOutOfMemory =
+ MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/OutOfMemoryError"));
+ gErrorOffsets.mStackOverflow =
+ MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/StackOverflowError"));
- clazz = FindClassOrDie(env, kBinderProxyPathName);
+ jclass clazz = FindClassOrDie(env, kBinderProxyPathName);
gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
gBinderProxyOffsets.mGetInstance = GetStaticMethodIDOrDie(env, clazz, "getInstance",
"(JJ)Landroid/os/BinderProxy;");