ART: Improve double-JNI-load exception message
Try to print the involved classloaders. The code is suboptimal
and will not cache any intermediates, as this is an unexpected
failure - but aids in debugging application issues.
Add a test for the behavior of duplicate library loading in
separate classloaders. For ART, add a rough test for the pattern
of the error message.
Bug: 65574359
Test: m test-art-host
Test: art/test/testrunner/testrunner.py -b --host -t 004-JniTest
Change-Id: I6c6c7726a79172f39153d4a458eba4fb3e8e85b0
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index 1593577..c0d1861 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -35,6 +35,7 @@
#include "mirror/class_loader.h"
#include "nativebridge/native_bridge.h"
#include "nativehelper/ScopedLocalRef.h"
+#include "nativehelper/ScopedUtfChars.h"
#include "nativeloader/native_loader.h"
#include "object_callbacks.h"
#include "parsed_options.h"
@@ -833,9 +834,42 @@
// The library will be associated with class_loader. The JNI
// spec says we can't load the same library into more than one
// class loader.
+ //
+ // This isn't very common. So spend some time to get a readable message.
+ auto call_to_string = [&](jobject obj) -> std::string {
+ if (obj == nullptr) {
+ return "null";
+ }
+ // Handle jweaks. Ignore double local-ref.
+ ScopedLocalRef<jobject> local_ref(env, env->NewLocalRef(obj));
+ if (local_ref != nullptr) {
+ ScopedLocalRef<jclass> local_class(env, env->GetObjectClass(local_ref.get()));
+ jmethodID to_string = env->GetMethodID(local_class.get(),
+ "toString",
+ "()Ljava/lang/String;");
+ DCHECK(to_string != nullptr);
+ ScopedLocalRef<jobject> local_string(env,
+ env->CallObjectMethod(local_ref.get(), to_string));
+ if (local_string != nullptr) {
+ ScopedUtfChars utf(env, reinterpret_cast<jstring>(local_string.get()));
+ if (utf.c_str() != nullptr) {
+ return utf.c_str();
+ }
+ }
+ env->ExceptionClear();
+ return "(Error calling toString)";
+ }
+ return "null";
+ };
+ std::string old_class_loader = call_to_string(library->GetClassLoader());
+ std::string new_class_loader = call_to_string(class_loader);
StringAppendF(error_msg, "Shared library \"%s\" already opened by "
- "ClassLoader %p; can't open in ClassLoader %p",
- path.c_str(), library->GetClassLoader(), class_loader);
+ "ClassLoader %p(%s); can't open in ClassLoader %p(%s)",
+ path.c_str(),
+ library->GetClassLoader(),
+ old_class_loader.c_str(),
+ class_loader,
+ new_class_loader.c_str());
LOG(WARNING) << *error_msg;
return false;
}