From fb6f3e0734afb5f92c53e1a9c4a6814032be6523 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Thu, 9 Nov 2017 13:04:12 -0800 Subject: [PATCH] Log stack traces from exceptions thrown by calls from native into Java (#4346) --- fml/platform/android/jni_util.cc | 42 +++++++++++++++++++ fml/platform/android/jni_util.h | 2 + .../android/platform_view_android_jni.cc | 29 +++++++++---- 3 files changed, 66 insertions(+), 7 deletions(-) diff --git a/fml/platform/android/jni_util.cc b/fml/platform/android/jni_util.cc index 2d4375b7e..be53c3ce6 100644 --- a/fml/platform/android/jni_util.cc +++ b/fml/platform/android/jni_util.cc @@ -123,5 +123,47 @@ bool ClearException(JNIEnv* env) { return true; } +std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) { + ScopedJavaLocalRef throwable_clazz( + env, env->FindClass("java/lang/Throwable")); + jmethodID throwable_printstacktrace = env->GetMethodID( + throwable_clazz.obj(), "printStackTrace", "(Ljava/io/PrintStream;)V"); + + // Create an instance of ByteArrayOutputStream. + ScopedJavaLocalRef bytearray_output_stream_clazz( + env, env->FindClass("java/io/ByteArrayOutputStream")); + jmethodID bytearray_output_stream_constructor = + env->GetMethodID(bytearray_output_stream_clazz.obj(), "", "()V"); + jmethodID bytearray_output_stream_tostring = env->GetMethodID( + bytearray_output_stream_clazz.obj(), "toString", "()Ljava/lang/String;"); + ScopedJavaLocalRef bytearray_output_stream( + env, env->NewObject(bytearray_output_stream_clazz.obj(), + bytearray_output_stream_constructor)); + + // Create an instance of PrintStream. + ScopedJavaLocalRef printstream_clazz( + env, env->FindClass("java/io/PrintStream")); + jmethodID printstream_constructor = env->GetMethodID( + printstream_clazz.obj(), "", "(Ljava/io/OutputStream;)V"); + ScopedJavaLocalRef printstream( + env, env->NewObject(printstream_clazz.obj(), printstream_constructor, + bytearray_output_stream.obj())); + + // Call Throwable.printStackTrace(PrintStream) + env->CallVoidMethod(java_throwable, throwable_printstacktrace, + printstream.obj()); + + // Call ByteArrayOutputStream.toString() + ScopedJavaLocalRef exception_string( + env, + static_cast(env->CallObjectMethod( + bytearray_output_stream.obj(), bytearray_output_stream_tostring))); + if (ClearException(env)) { + return "Java OOM'd in exception handling, check logcat"; + } + + return JavaStringToString(env, exception_string.obj()); +} + } // namespace jni } // namespace fml diff --git a/fml/platform/android/jni_util.h b/fml/platform/android/jni_util.h index 358d14d0e..f4ecfc266 100644 --- a/fml/platform/android/jni_util.h +++ b/fml/platform/android/jni_util.h @@ -36,6 +36,8 @@ bool HasException(JNIEnv* env); bool ClearException(JNIEnv* env); +std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable); + } // namespace jni } // namespace fml diff --git a/shell/platform/android/platform_view_android_jni.cc b/shell/platform/android/platform_view_android_jni.cc index 1f08782cd..83b026fff 100644 --- a/shell/platform/android/platform_view_android_jni.cc +++ b/shell/platform/android/platform_view_android_jni.cc @@ -18,6 +18,21 @@ namespace shell { +namespace { + +bool CheckException(JNIEnv* env) { + if (env->ExceptionCheck() == JNI_FALSE) + return true; + + jthrowable exception = env->ExceptionOccurred(); + env->ExceptionClear(); + FXL_LOG(INFO) << fml::jni::GetJavaExceptionInfo(env, exception); + env->DeleteLocalRef(exception); + return false; +} + +} // anonymous namespace + static fml::jni::ScopedJavaGlobalRef* g_flutter_view_class = nullptr; static fml::jni::ScopedJavaGlobalRef* g_flutter_native_view_class = nullptr; @@ -33,7 +48,7 @@ void FlutterViewHandlePlatformMessage(JNIEnv* env, jint responseId) { env->CallVoidMethod(obj, g_handle_platform_message_method, channel, message, responseId); - FXL_CHECK(env->ExceptionCheck() == JNI_FALSE); + FXL_CHECK(CheckException(env)); } static jmethodID g_handle_platform_message_response_method = nullptr; @@ -43,7 +58,7 @@ void FlutterViewHandlePlatformMessageResponse(JNIEnv* env, jobject response) { env->CallVoidMethod(obj, g_handle_platform_message_response_method, responseId, response); - FXL_CHECK(env->ExceptionCheck() == JNI_FALSE); + FXL_CHECK(CheckException(env)); } static jmethodID g_update_semantics_method = nullptr; @@ -52,34 +67,34 @@ void FlutterViewUpdateSemantics(JNIEnv* env, jobject buffer, jobjectArray strings) { env->CallVoidMethod(obj, g_update_semantics_method, buffer, strings); - FXL_CHECK(env->ExceptionCheck() == JNI_FALSE); + FXL_CHECK(CheckException(env)); } static jmethodID g_on_first_frame_method = nullptr; void FlutterViewOnFirstFrame(JNIEnv* env, jobject obj) { env->CallVoidMethod(obj, g_on_first_frame_method); - FXL_CHECK(env->ExceptionCheck() == JNI_FALSE); + FXL_CHECK(CheckException(env)); } static jmethodID g_attach_to_gl_context_method = nullptr; void SurfaceTextureAttachToGLContext(JNIEnv* env, jobject obj, jint textureId) { ASSERT_IS_GPU_THREAD; env->CallVoidMethod(obj, g_attach_to_gl_context_method, textureId); - FXL_CHECK(env->ExceptionCheck() == JNI_FALSE); + FXL_CHECK(CheckException(env)); } static jmethodID g_update_tex_image_method = nullptr; void SurfaceTextureUpdateTexImage(JNIEnv* env, jobject obj) { ASSERT_IS_GPU_THREAD; env->CallVoidMethod(obj, g_update_tex_image_method); - FXL_CHECK(env->ExceptionCheck() == JNI_FALSE); + FXL_CHECK(CheckException(env)); } static jmethodID g_detach_from_gl_context_method = nullptr; void SurfaceTextureDetachFromGLContext(JNIEnv* env, jobject obj) { ASSERT_IS_GPU_THREAD; env->CallVoidMethod(obj, g_detach_from_gl_context_method); - FXL_CHECK(env->ExceptionCheck() == JNI_FALSE); + FXL_CHECK(CheckException(env)); } // Called By Java -- GitLab