summaryrefslogtreecommitdiff
path: root/runtime/openjdkjvmti
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/openjdkjvmti')
-rw-r--r--runtime/openjdkjvmti/Android.bp1
-rw-r--r--runtime/openjdkjvmti/OpenjdkJvmTi.cc2
-rw-r--r--runtime/openjdkjvmti/ti_class.cc12
-rw-r--r--runtime/openjdkjvmti/ti_class_loader.cc202
-rw-r--r--runtime/openjdkjvmti/ti_class_loader.h96
-rw-r--r--runtime/openjdkjvmti/ti_phase.cc4
-rw-r--r--runtime/openjdkjvmti/ti_phase.h2
-rw-r--r--runtime/openjdkjvmti/ti_redefine.cc143
-rw-r--r--runtime/openjdkjvmti/ti_redefine.h9
-rw-r--r--runtime/openjdkjvmti/ti_search.cc179
-rw-r--r--runtime/openjdkjvmti/ti_search.h5
11 files changed, 504 insertions, 151 deletions
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index 976a1e7902..c01e3f4152 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -22,6 +22,7 @@ cc_defaults {
"OpenjdkJvmTi.cc",
"ti_class.cc",
"ti_class_definition.cc",
+ "ti_class_loader.cc",
"ti_dump.cc",
"ti_field.cc",
"ti_heap.cc",
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index c0c301fae5..a815a603a7 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -1368,6 +1368,7 @@ extern "C" bool ArtPlugin_Initialize() {
ThreadUtil::Register(&gEventHandler);
ClassUtil::Register(&gEventHandler);
DumpUtil::Register(&gEventHandler);
+ SearchUtil::Register();
runtime->GetJavaVM()->AddEnvironmentHook(GetEnvHandler);
runtime->AddSystemWeakHolder(&gObjectTagTable);
@@ -1380,6 +1381,7 @@ extern "C" bool ArtPlugin_Deinitialize() {
ThreadUtil::Unregister();
ClassUtil::Unregister();
DumpUtil::Unregister();
+ SearchUtil::Unregister();
return true;
}
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index d484c98483..c14fd84264 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -54,6 +54,7 @@
#include "scoped_thread_state_change-inl.h"
#include "thread-inl.h"
#include "thread_list.h"
+#include "ti_class_loader.h"
#include "ti_redefine.h"
#include "utils.h"
@@ -228,12 +229,15 @@ struct ClassCallback : public art::ClassLoadCallback {
return;
}
- // TODO Check Redefined dex file for invariants.
+ // TODO Check Redefined dex file for all invariants.
LOG(WARNING) << "Dex file created by class-definition time transformation of "
<< descriptor << " is not checked for all retransformation invariants.";
- // TODO Put it in classpath
- LOG(WARNING) << "Dex file created for class-definition time transformation of "
- << descriptor << " was not added to any classpaths!";
+
+ if (!ClassLoaderHelper::AddToClassLoader(self, class_loader, dex_file.get())) {
+ LOG(ERROR) << "Unable to add " << descriptor << " to class loader!";
+ return;
+ }
+
// Actually set the ClassExt's original bytes once we have actually succeeded.
ext->SetOriginalDexFileBytes(arr.Get());
// Set the return values
diff --git a/runtime/openjdkjvmti/ti_class_loader.cc b/runtime/openjdkjvmti/ti_class_loader.cc
new file mode 100644
index 0000000000..b68fc60c6c
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_class_loader.cc
@@ -0,0 +1,202 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_class_loader.h"
+
+#include <limits>
+
+#include "android-base/stringprintf.h"
+
+#include "art_jvmti.h"
+#include "base/array_slice.h"
+#include "base/logging.h"
+#include "dex_file.h"
+#include "dex_file_types.h"
+#include "events-inl.h"
+#include "gc/allocation_listener.h"
+#include "gc/heap.h"
+#include "instrumentation.h"
+#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
+#include "jni_env_ext-inl.h"
+#include "jvmti_allocator.h"
+#include "mirror/class.h"
+#include "mirror/class_ext.h"
+#include "mirror/object.h"
+#include "object_lock.h"
+#include "runtime.h"
+#include "ScopedLocalRef.h"
+#include "transform.h"
+
+namespace openjdkjvmti {
+
+bool ClassLoaderHelper::AddToClassLoader(art::Thread* self,
+ art::Handle<art::mirror::ClassLoader> loader,
+ const art::DexFile* dex_file) {
+ art::StackHandleScope<2> hs(self);
+ art::Handle<art::mirror::Object> java_dex_file_obj(hs.NewHandle(FindSourceDexFileObject(self,
+ loader)));
+ if (java_dex_file_obj.IsNull()) {
+ return false;
+ }
+ art::Handle<art::mirror::LongArray> cookie(hs.NewHandle(
+ AllocateNewDexFileCookie(self, java_dex_file_obj, dex_file)));
+ if (cookie.IsNull()) {
+ return false;
+ }
+ art::ScopedAssertNoThreadSuspension nts("Replacing cookie fields in j.l.DexFile object");
+ UpdateJavaDexFile(java_dex_file_obj.Get(), cookie.Get());
+ return true;
+}
+
+void ClassLoaderHelper::UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file,
+ art::ObjPtr<art::mirror::LongArray> new_cookie) {
+ art::ArtField* internal_cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField(
+ "mInternalCookie", "Ljava/lang/Object;");
+ art::ArtField* cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField(
+ "mCookie", "Ljava/lang/Object;");
+ CHECK(internal_cookie_field != nullptr);
+ art::ObjPtr<art::mirror::LongArray> orig_internal_cookie(
+ internal_cookie_field->GetObject(java_dex_file)->AsLongArray());
+ art::ObjPtr<art::mirror::LongArray> orig_cookie(
+ cookie_field->GetObject(java_dex_file)->AsLongArray());
+ internal_cookie_field->SetObject<false>(java_dex_file, new_cookie);
+ if (!orig_cookie.IsNull()) {
+ cookie_field->SetObject<false>(java_dex_file, new_cookie);
+ }
+}
+
+// TODO Really wishing I had that mirror of java.lang.DexFile now.
+art::ObjPtr<art::mirror::LongArray> ClassLoaderHelper::AllocateNewDexFileCookie(
+ art::Thread* self,
+ art::Handle<art::mirror::Object> java_dex_file_obj,
+ const art::DexFile* dex_file) {
+ art::StackHandleScope<2> hs(self);
+ // mCookie is nulled out if the DexFile has been closed but mInternalCookie sticks around until
+ // the object is finalized. Since they always point to the same array if mCookie is not null we
+ // just use the mInternalCookie field. We will update one or both of these fields later.
+ // TODO Should I get the class from the classloader or directly?
+ art::ArtField* internal_cookie_field = java_dex_file_obj->GetClass()->FindDeclaredInstanceField(
+ "mInternalCookie", "Ljava/lang/Object;");
+ // TODO Add check that mCookie is either null or same as mInternalCookie
+ CHECK(internal_cookie_field != nullptr);
+ art::Handle<art::mirror::LongArray> cookie(
+ hs.NewHandle(internal_cookie_field->GetObject(java_dex_file_obj.Get())->AsLongArray()));
+ // TODO Maybe make these non-fatal.
+ CHECK(cookie.Get() != nullptr);
+ CHECK_GE(cookie->GetLength(), 1);
+ art::Handle<art::mirror::LongArray> new_cookie(
+ hs.NewHandle(art::mirror::LongArray::Alloc(self, cookie->GetLength() + 1)));
+ if (new_cookie.Get() == nullptr) {
+ self->AssertPendingOOMException();
+ return nullptr;
+ }
+ // Copy the oat-dex field at the start.
+ // TODO Should I clear this field?
+ // TODO This is a really crappy thing here with the first element being different.
+ new_cookie->SetWithoutChecks<false>(0, cookie->GetWithoutChecks(0));
+ new_cookie->SetWithoutChecks<false>(
+ 1, static_cast<int64_t>(reinterpret_cast<intptr_t>(dex_file)));
+ new_cookie->Memcpy(2, cookie.Get(), 1, cookie->GetLength() - 1);
+ return new_cookie.Get();
+}
+
+// TODO This should return the actual source java.lang.DexFile object for the klass being loaded.
+art::ObjPtr<art::mirror::Object> ClassLoaderHelper::FindSourceDexFileObject(
+ art::Thread* self, art::Handle<art::mirror::ClassLoader> loader) {
+ const char* dex_path_list_element_array_name = "[Ldalvik/system/DexPathList$Element;";
+ const char* dex_path_list_element_name = "Ldalvik/system/DexPathList$Element;";
+ const char* dex_file_name = "Ldalvik/system/DexFile;";
+ const char* dex_path_list_name = "Ldalvik/system/DexPathList;";
+ const char* dex_class_loader_name = "Ldalvik/system/BaseDexClassLoader;";
+
+ CHECK(!self->IsExceptionPending());
+ art::StackHandleScope<5> hs(self);
+ art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker();
+
+ art::Handle<art::mirror::ClassLoader> null_loader(hs.NewHandle<art::mirror::ClassLoader>(
+ nullptr));
+ art::Handle<art::mirror::Class> base_dex_loader_class(hs.NewHandle(class_linker->FindClass(
+ self, dex_class_loader_name, null_loader)));
+
+ // Get all the ArtFields so we can look in the BaseDexClassLoader
+ art::ArtField* path_list_field = base_dex_loader_class->FindDeclaredInstanceField(
+ "pathList", dex_path_list_name);
+ CHECK(path_list_field != nullptr);
+
+ art::ArtField* dex_path_list_element_field =
+ class_linker->FindClass(self, dex_path_list_name, null_loader)
+ ->FindDeclaredInstanceField("dexElements", dex_path_list_element_array_name);
+ CHECK(dex_path_list_element_field != nullptr);
+
+ art::ArtField* element_dex_file_field =
+ class_linker->FindClass(self, dex_path_list_element_name, null_loader)
+ ->FindDeclaredInstanceField("dexFile", dex_file_name);
+ CHECK(element_dex_file_field != nullptr);
+
+ // Check if loader is a BaseDexClassLoader
+ art::Handle<art::mirror::Class> loader_class(hs.NewHandle(loader->GetClass()));
+ // Currently only base_dex_loader is allowed to actually define classes but if this changes in the
+ // future we should make sure to support all class loader types.
+ if (!loader_class->IsSubClass(base_dex_loader_class.Get())) {
+ LOG(ERROR) << "The classloader is not a BaseDexClassLoader which is currently the only "
+ << "supported class loader type!";
+ return nullptr;
+ }
+ // Start navigating the fields of the loader (now known to be a BaseDexClassLoader derivative)
+ art::Handle<art::mirror::Object> path_list(
+ hs.NewHandle(path_list_field->GetObject(loader.Get())));
+ CHECK(path_list.Get() != nullptr);
+ CHECK(!self->IsExceptionPending());
+ art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list(hs.NewHandle(
+ dex_path_list_element_field->GetObject(path_list.Get())->
+ AsObjectArray<art::mirror::Object>()));
+ CHECK(!self->IsExceptionPending());
+ CHECK(dex_elements_list.Get() != nullptr);
+ size_t num_elements = dex_elements_list->GetLength();
+ // Iterate over the DexPathList$Element to find the right one
+ for (size_t i = 0; i < num_elements; i++) {
+ art::ObjPtr<art::mirror::Object> current_element = dex_elements_list->Get(i);
+ CHECK(!current_element.IsNull());
+ // TODO It would be cleaner to put the art::DexFile into the dalvik.system.DexFile the class
+ // comes from but it is more annoying because we would need to find this class. It is not
+ // necessary for proper function since we just need to be in front of the classes old dex file
+ // in the path.
+ art::ObjPtr<art::mirror::Object> first_dex_file(
+ element_dex_file_field->GetObject(current_element));
+ if (!first_dex_file.IsNull()) {
+ return first_dex_file;
+ }
+ }
+ return nullptr;
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_class_loader.h b/runtime/openjdkjvmti/ti_class_loader.h
new file mode 100644
index 0000000000..17ed0eb196
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_class_loader.h
@@ -0,0 +1,96 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_LOADER_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_LOADER_H_
+
+#include <string>
+
+#include <jni.h>
+
+#include "art_jvmti.h"
+#include "art_method.h"
+#include "base/array_slice.h"
+#include "class_linker.h"
+#include "dex_file.h"
+#include "gc_root-inl.h"
+#include "globals.h"
+#include "jni_env_ext-inl.h"
+#include "jvmti.h"
+#include "linear_alloc.h"
+#include "mem_map.h"
+#include "mirror/array-inl.h"
+#include "mirror/array.h"
+#include "mirror/class-inl.h"
+#include "mirror/class.h"
+#include "mirror/class_loader-inl.h"
+#include "mirror/string-inl.h"
+#include "oat_file.h"
+#include "obj_ptr.h"
+#include "scoped_thread_state_change-inl.h"
+#include "stack.h"
+#include "ti_class_definition.h"
+#include "thread_list.h"
+#include "transform.h"
+#include "utf.h"
+#include "utils/dex_cache_arrays_layout-inl.h"
+
+namespace openjdkjvmti {
+
+// Class that can redefine a single class's methods.
+// TODO We should really make this be driven by an outside class so we can do multiple classes at
+// the same time and have less required cleanup.
+class ClassLoaderHelper {
+ public:
+ static bool AddToClassLoader(art::Thread* self,
+ art::Handle<art::mirror::ClassLoader> loader,
+ const art::DexFile* dex_file)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ // Finds a java.lang.DexFile object that is associated with the given ClassLoader. Each of these
+ // j.l.DexFile objects holds several art::DexFile*s in it.
+ // TODO This should return the actual source java.lang.DexFile object for the klass being loaded.
+ static art::ObjPtr<art::mirror::Object> FindSourceDexFileObject(
+ art::Thread* self, art::Handle<art::mirror::ClassLoader> loader)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ static art::ObjPtr<art::mirror::LongArray> AllocateNewDexFileCookie(
+ art::Thread* self,
+ art::Handle<art::mirror::Object> java_dex_file,
+ const art::DexFile* new_dex_file) REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ static void UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file,
+ art::ObjPtr<art::mirror::LongArray> new_cookie)
+ REQUIRES(art::Roles::uninterruptible_) REQUIRES_SHARED(art::Locks::mutator_lock_);
+};
+
+} // namespace openjdkjvmti
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_CLASS_LOADER_H_
diff --git a/runtime/openjdkjvmti/ti_phase.cc b/runtime/openjdkjvmti/ti_phase.cc
index 4970288250..60371cfafe 100644
--- a/runtime/openjdkjvmti/ti_phase.cc
+++ b/runtime/openjdkjvmti/ti_phase.cc
@@ -136,4 +136,8 @@ void PhaseUtil::Unregister() {
art::Runtime::Current()->GetRuntimeCallbacks()->RemoveRuntimePhaseCallback(&gPhaseCallback);
}
+jvmtiPhase PhaseUtil::GetPhaseUnchecked() {
+ return PhaseUtil::current_phase_;
+}
+
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_phase.h b/runtime/openjdkjvmti/ti_phase.h
index bd15fa6891..851fc27de5 100644
--- a/runtime/openjdkjvmti/ti_phase.h
+++ b/runtime/openjdkjvmti/ti_phase.h
@@ -57,6 +57,8 @@ class PhaseUtil {
struct PhaseCallback;
+ static jvmtiPhase GetPhaseUnchecked();
+
private:
static jvmtiPhase current_phase_;
};
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index d2ddc21cd4..b76d74ae79 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -54,6 +54,7 @@
#include "object_lock.h"
#include "runtime.h"
#include "ScopedLocalRef.h"
+#include "ti_class_loader.h"
#include "transform.h"
namespace openjdkjvmti {
@@ -386,85 +387,6 @@ jvmtiError Redefiner::AddRedefinition(ArtJvmTiEnv* env, const ArtClassDefinition
return OK;
}
-// TODO *MAJOR* This should return the actual source java.lang.DexFile object for the klass.
-// TODO Make mirror of DexFile and associated types to make this less hellish.
-// TODO Make mirror of BaseDexClassLoader and associated types to make this less hellish.
-art::mirror::Object* Redefiner::ClassRedefinition::FindSourceDexFileObject(
- art::Handle<art::mirror::ClassLoader> loader) {
- const char* dex_path_list_element_array_name = "[Ldalvik/system/DexPathList$Element;";
- const char* dex_path_list_element_name = "Ldalvik/system/DexPathList$Element;";
- const char* dex_file_name = "Ldalvik/system/DexFile;";
- const char* dex_path_list_name = "Ldalvik/system/DexPathList;";
- const char* dex_class_loader_name = "Ldalvik/system/BaseDexClassLoader;";
-
- CHECK(!driver_->self_->IsExceptionPending());
- art::StackHandleScope<11> hs(driver_->self_);
- art::ClassLinker* class_linker = driver_->runtime_->GetClassLinker();
-
- art::Handle<art::mirror::ClassLoader> null_loader(hs.NewHandle<art::mirror::ClassLoader>(
- nullptr));
- art::Handle<art::mirror::Class> base_dex_loader_class(hs.NewHandle(class_linker->FindClass(
- driver_->self_, dex_class_loader_name, null_loader)));
-
- // Get all the ArtFields so we can look in the BaseDexClassLoader
- art::ArtField* path_list_field = base_dex_loader_class->FindDeclaredInstanceField(
- "pathList", dex_path_list_name);
- CHECK(path_list_field != nullptr);
-
- art::ArtField* dex_path_list_element_field =
- class_linker->FindClass(driver_->self_, dex_path_list_name, null_loader)
- ->FindDeclaredInstanceField("dexElements", dex_path_list_element_array_name);
- CHECK(dex_path_list_element_field != nullptr);
-
- art::ArtField* element_dex_file_field =
- class_linker->FindClass(driver_->self_, dex_path_list_element_name, null_loader)
- ->FindDeclaredInstanceField("dexFile", dex_file_name);
- CHECK(element_dex_file_field != nullptr);
-
- // Check if loader is a BaseDexClassLoader
- art::Handle<art::mirror::Class> loader_class(hs.NewHandle(loader->GetClass()));
- if (!loader_class->IsSubClass(base_dex_loader_class.Get())) {
- LOG(ERROR) << "The classloader is not a BaseDexClassLoader which is currently the only "
- << "supported class loader type!";
- return nullptr;
- }
- // Start navigating the fields of the loader (now known to be a BaseDexClassLoader derivative)
- art::Handle<art::mirror::Object> path_list(
- hs.NewHandle(path_list_field->GetObject(loader.Get())));
- CHECK(path_list.Get() != nullptr);
- CHECK(!driver_->self_->IsExceptionPending());
- art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list(hs.NewHandle(
- dex_path_list_element_field->GetObject(path_list.Get())->
- AsObjectArray<art::mirror::Object>()));
- CHECK(!driver_->self_->IsExceptionPending());
- CHECK(dex_elements_list.Get() != nullptr);
- size_t num_elements = dex_elements_list->GetLength();
- art::MutableHandle<art::mirror::Object> current_element(
- hs.NewHandle<art::mirror::Object>(nullptr));
- art::MutableHandle<art::mirror::Object> first_dex_file(
- hs.NewHandle<art::mirror::Object>(nullptr));
- // Iterate over the DexPathList$Element to find the right one
- // TODO Or not ATM just return the first one.
- for (size_t i = 0; i < num_elements; i++) {
- current_element.Assign(dex_elements_list->Get(i));
- CHECK(current_element.Get() != nullptr);
- CHECK(!driver_->self_->IsExceptionPending());
- CHECK(dex_elements_list.Get() != nullptr);
- CHECK_EQ(current_element->GetClass(), class_linker->FindClass(driver_->self_,
- dex_path_list_element_name,
- null_loader));
- // TODO It would be cleaner to put the art::DexFile into the dalvik.system.DexFile the class
- // comes from but it is more annoying because we would need to find this class. It is not
- // necessary for proper function since we just need to be in front of the classes old dex file
- // in the path.
- first_dex_file.Assign(element_dex_file_field->GetObject(current_element.Get()));
- if (first_dex_file.Get() != nullptr) {
- return first_dex_file.Get();
- }
- }
- return nullptr;
-}
-
art::mirror::Class* Redefiner::ClassRedefinition::GetMirrorClass() {
return driver_->self_->DecodeJObject(klass_)->AsClass();
}
@@ -478,39 +400,6 @@ art::mirror::DexCache* Redefiner::ClassRedefinition::CreateNewDexCache(
return driver_->runtime_->GetClassLinker()->RegisterDexFile(*dex_file_, loader.Get());
}
-// TODO Really wishing I had that mirror of java.lang.DexFile now.
-art::mirror::LongArray* Redefiner::ClassRedefinition::AllocateDexFileCookie(
- art::Handle<art::mirror::Object> java_dex_file_obj) {
- art::StackHandleScope<2> hs(driver_->self_);
- // mCookie is nulled out if the DexFile has been closed but mInternalCookie sticks around until
- // the object is finalized. Since they always point to the same array if mCookie is not null we
- // just use the mInternalCookie field. We will update one or both of these fields later.
- // TODO Should I get the class from the classloader or directly?
- art::ArtField* internal_cookie_field = java_dex_file_obj->GetClass()->FindDeclaredInstanceField(
- "mInternalCookie", "Ljava/lang/Object;");
- // TODO Add check that mCookie is either null or same as mInternalCookie
- CHECK(internal_cookie_field != nullptr);
- art::Handle<art::mirror::LongArray> cookie(
- hs.NewHandle(internal_cookie_field->GetObject(java_dex_file_obj.Get())->AsLongArray()));
- // TODO Maybe make these non-fatal.
- CHECK(cookie.Get() != nullptr);
- CHECK_GE(cookie->GetLength(), 1);
- art::Handle<art::mirror::LongArray> new_cookie(
- hs.NewHandle(art::mirror::LongArray::Alloc(driver_->self_, cookie->GetLength() + 1)));
- if (new_cookie.Get() == nullptr) {
- driver_->self_->AssertPendingOOMException();
- return nullptr;
- }
- // Copy the oat-dex field at the start.
- // TODO Should I clear this field?
- // TODO This is a really crappy thing here with the first element being different.
- new_cookie->SetWithoutChecks<false>(0, cookie->GetWithoutChecks(0));
- new_cookie->SetWithoutChecks<false>(
- 1, static_cast<int64_t>(reinterpret_cast<intptr_t>(dex_file_.get())));
- new_cookie->Memcpy(2, cookie.Get(), 1, cookie->GetLength() - 1);
- return new_cookie.Get();
-}
-
void Redefiner::RecordFailure(jvmtiError result,
const std::string& class_sig,
const std::string& error_msg) {
@@ -854,14 +743,18 @@ bool Redefiner::ClassRedefinition::FinishRemainingAllocations(
RecordFailure(ERR(INTERNAL), "Unable to find class loader!");
return false;
}
- art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle(FindSourceDexFileObject(loader)));
+ art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle(
+ ClassLoaderHelper::FindSourceDexFileObject(driver_->self_, loader)));
holder->SetJavaDexFile(klass_index, dex_file_obj.Get());
if (dex_file_obj.Get() == nullptr) {
// TODO Better error msg.
RecordFailure(ERR(INTERNAL), "Unable to find class loader!");
return false;
}
- holder->SetNewDexFileCookie(klass_index, AllocateDexFileCookie(dex_file_obj));
+ holder->SetNewDexFileCookie(klass_index,
+ ClassLoaderHelper::AllocateNewDexFileCookie(driver_->self_,
+ dex_file_obj,
+ dex_file_.get()).Ptr());
if (holder->GetNewDexFileCookie(klass_index) == nullptr) {
driver_->self_->AssertPendingOOMException();
driver_->self_->ClearException();
@@ -973,8 +866,10 @@ jvmtiError Redefiner::Run() {
// TODO We need to decide on & implement semantics for JNI jmethodids when we redefine methods.
int32_t cnt = 0;
for (Redefiner::ClassRedefinition& redef : redefinitions_) {
+ art::ScopedAssertNoThreadSuspension nts("Updating runtime objects for redefinition");
art::mirror::Class* klass = holder.GetMirrorClass(cnt);
- redef.UpdateJavaDexFile(holder.GetJavaDexFile(cnt), holder.GetNewDexFileCookie(cnt));
+ ClassLoaderHelper::UpdateJavaDexFile(holder.GetJavaDexFile(cnt),
+ holder.GetNewDexFileCookie(cnt));
// TODO Rewrite so we don't do a stack walk for each and every class.
redef.FindAndAllocateObsoleteMethods(klass);
redef.UpdateClass(klass, holder.GetNewDexCache(cnt), holder.GetOriginalDexFileBytes(cnt));
@@ -1090,24 +985,6 @@ void Redefiner::ClassRedefinition::UpdateClass(
ext->SetOriginalDexFileBytes(original_dex_file);
}
-void Redefiner::ClassRedefinition::UpdateJavaDexFile(
- art::ObjPtr<art::mirror::Object> java_dex_file,
- art::ObjPtr<art::mirror::LongArray> new_cookie) {
- art::ArtField* internal_cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField(
- "mInternalCookie", "Ljava/lang/Object;");
- art::ArtField* cookie_field = java_dex_file->GetClass()->FindDeclaredInstanceField(
- "mCookie", "Ljava/lang/Object;");
- CHECK(internal_cookie_field != nullptr);
- art::ObjPtr<art::mirror::LongArray> orig_internal_cookie(
- internal_cookie_field->GetObject(java_dex_file)->AsLongArray());
- art::ObjPtr<art::mirror::LongArray> orig_cookie(
- cookie_field->GetObject(java_dex_file)->AsLongArray());
- internal_cookie_field->SetObject<false>(java_dex_file, new_cookie);
- if (!orig_cookie.IsNull()) {
- cookie_field->SetObject<false>(java_dex_file, new_cookie);
- }
-}
-
// This function does all (java) allocations we need to do for the Class being redefined.
// TODO Change this name maybe?
bool Redefiner::ClassRedefinition::EnsureClassAllocationsFinished() {
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index fdc13eee32..85df6e1024 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -127,18 +127,9 @@ class Redefiner {
art::mirror::Class* GetMirrorClass() REQUIRES_SHARED(art::Locks::mutator_lock_);
art::mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(art::Locks::mutator_lock_);
- // This finds the java.lang.DexFile we will add the native DexFile to as part of the classpath.
- // TODO Make sure the DexFile object returned is the one that the klass_ actually comes from.
- art::mirror::Object* FindSourceDexFileObject(art::Handle<art::mirror::ClassLoader> loader)
- REQUIRES_SHARED(art::Locks::mutator_lock_);
-
art::mirror::DexCache* CreateNewDexCache(art::Handle<art::mirror::ClassLoader> loader)
REQUIRES_SHARED(art::Locks::mutator_lock_);
- // Allocates and fills the new DexFileCookie
- art::mirror::LongArray* AllocateDexFileCookie(art::Handle<art::mirror::Object> j_dex_file_obj)
- REQUIRES_SHARED(art::Locks::mutator_lock_);
-
// This may return nullptr with a OOME pending if allocation fails.
art::mirror::ByteArray* AllocateOrGetOriginalDexFileBytes()
REQUIRES_SHARED(art::Locks::mutator_lock_);
diff --git a/runtime/openjdkjvmti/ti_search.cc b/runtime/openjdkjvmti/ti_search.cc
index 913d2b6a74..df80f85ed8 100644
--- a/runtime/openjdkjvmti/ti_search.cc
+++ b/runtime/openjdkjvmti/ti_search.cc
@@ -34,15 +34,177 @@
#include "jni.h"
#include "art_jvmti.h"
+#include "base/enums.h"
#include "base/macros.h"
#include "class_linker.h"
#include "dex_file.h"
+#include "jni_internal.h"
+#include "mirror/class-inl.h"
+#include "mirror/object.h"
+#include "mirror/string.h"
+#include "obj_ptr-inl.h"
#include "runtime.h"
+#include "runtime_callbacks.h"
#include "scoped_thread_state_change-inl.h"
#include "ScopedLocalRef.h"
+#include "ti_phase.h"
+#include "thread-inl.h"
+#include "thread_list.h"
namespace openjdkjvmti {
+static std::vector<std::string> gSystemOnloadSegments;
+
+static art::ObjPtr<art::mirror::Object> GetSystemProperties(art::Thread* self,
+ art::ClassLinker* class_linker)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::ObjPtr<art::mirror::Class> system_class =
+ class_linker->LookupClass(self, "Ljava/lang/System;", nullptr);
+ DCHECK(system_class != nullptr);
+ DCHECK(system_class->IsInitialized());
+
+ art::ArtField* props_field =
+ system_class->FindDeclaredStaticField("props", "Ljava/util/Properties;");
+ DCHECK(props_field != nullptr);
+
+ art::ObjPtr<art::mirror::Object> props_obj = props_field->GetObject(system_class);
+ DCHECK(props_obj != nullptr);
+
+ return props_obj;
+}
+
+static void Update() REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (gSystemOnloadSegments.empty()) {
+ return;
+ }
+
+ // In the on-load phase we have to modify java.class.path to influence the system classloader.
+ // As this is an unmodifiable system property, we have to access the "defaults" field.
+ art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker();
+ DCHECK(class_linker != nullptr);
+ art::Thread* self = art::Thread::Current();
+
+ // Prepare: collect classes, fields and methods.
+ art::ObjPtr<art::mirror::Class> properties_class =
+ class_linker->LookupClass(self, "Ljava/util/Properties;", nullptr);
+ DCHECK(properties_class != nullptr);
+
+ ScopedLocalRef<jobject> defaults_jobj(self->GetJniEnv(), nullptr);
+ {
+ art::ObjPtr<art::mirror::Object> props_obj = GetSystemProperties(self, class_linker);
+
+ art::ArtField* defaults_field =
+ properties_class->FindDeclaredInstanceField("defaults", "Ljava/util/Properties;");
+ DCHECK(defaults_field != nullptr);
+
+ art::ObjPtr<art::mirror::Object> defaults_obj = defaults_field->GetObject(props_obj);
+ DCHECK(defaults_obj != nullptr);
+ defaults_jobj.reset(self->GetJniEnv()->AddLocalReference<jobject>(defaults_obj));
+ }
+
+ art::ArtMethod* get_property =
+ properties_class->FindDeclaredVirtualMethod(
+ "getProperty",
+ "(Ljava/lang/String;)Ljava/lang/String;",
+ art::kRuntimePointerSize);
+ DCHECK(get_property != nullptr);
+ art::ArtMethod* set_property =
+ properties_class->FindDeclaredVirtualMethod(
+ "setProperty",
+ "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;",
+ art::kRuntimePointerSize);
+ DCHECK(set_property != nullptr);
+
+ // This is an allocation. Do this late to avoid the need for handles.
+ ScopedLocalRef<jobject> cp_jobj(self->GetJniEnv(), nullptr);
+ {
+ art::ObjPtr<art::mirror::Object> cp_key =
+ art::mirror::String::AllocFromModifiedUtf8(self, "java.class.path");
+ if (cp_key == nullptr) {
+ self->AssertPendingOOMException();
+ self->ClearException();
+ return;
+ }
+ cp_jobj.reset(self->GetJniEnv()->AddLocalReference<jobject>(cp_key));
+ }
+
+ // OK, now get the current value.
+ std::string str_value;
+ {
+ ScopedLocalRef<jobject> old_value(self->GetJniEnv(),
+ self->GetJniEnv()->CallObjectMethod(
+ defaults_jobj.get(),
+ art::jni::EncodeArtMethod(get_property),
+ cp_jobj.get()));
+ DCHECK(old_value.get() != nullptr);
+
+ str_value = self->DecodeJObject(old_value.get())->AsString()->ToModifiedUtf8();
+ self->GetJniEnv()->DeleteLocalRef(old_value.release());
+ }
+
+ // Update the value by appending the new segments.
+ for (const std::string& segment : gSystemOnloadSegments) {
+ if (!str_value.empty()) {
+ str_value += ":";
+ }
+ str_value += segment;
+ }
+ gSystemOnloadSegments.clear();
+
+ // Create the new value object.
+ ScopedLocalRef<jobject> new_val_jobj(self->GetJniEnv(), nullptr);
+ {
+ art::ObjPtr<art::mirror::Object> new_value =
+ art::mirror::String::AllocFromModifiedUtf8(self, str_value.c_str());
+ if (new_value == nullptr) {
+ self->AssertPendingOOMException();
+ self->ClearException();
+ return;
+ }
+
+ new_val_jobj.reset(self->GetJniEnv()->AddLocalReference<jobject>(new_value));
+ }
+
+ // Write to the defaults.
+ ScopedLocalRef<jobject> res_obj(self->GetJniEnv(),
+ self->GetJniEnv()->CallObjectMethod(defaults_jobj.get(),
+ art::jni::EncodeArtMethod(set_property),
+ cp_jobj.get(),
+ new_val_jobj.get()));
+ if (self->IsExceptionPending()) {
+ self->ClearException();
+ return;
+ }
+}
+
+struct SearchCallback : public art::RuntimePhaseCallback {
+ void NextRuntimePhase(RuntimePhase phase) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (phase == RuntimePhase::kStart) {
+ // It's time to update the system properties.
+ Update();
+ }
+ }
+};
+
+static SearchCallback gSearchCallback;
+
+void SearchUtil::Register() {
+ art::Runtime* runtime = art::Runtime::Current();
+
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Add search callback");
+ runtime->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&gSearchCallback);
+}
+
+void SearchUtil::Unregister() {
+ art::ScopedThreadStateChange stsc(art::Thread::Current(),
+ art::ThreadState::kWaitingForDebuggerToAttach);
+ art::ScopedSuspendAll ssa("Remove search callback");
+ art::Runtime* runtime = art::Runtime::Current();
+ runtime->GetRuntimeCallbacks()->RemoveRuntimePhaseCallback(&gSearchCallback);
+}
+
jvmtiError SearchUtil::AddToBootstrapClassLoaderSearch(jvmtiEnv* env ATTRIBUTE_UNUSED,
const char* segment) {
art::Runtime* current = art::Runtime::Current();
@@ -78,14 +240,21 @@ jvmtiError SearchUtil::AddToSystemClassLoaderSearch(jvmtiEnv* jvmti_env ATTRIBUT
return ERR(NULL_POINTER);
}
- art::Runtime* current = art::Runtime::Current();
- if (current == nullptr) {
+ jvmtiPhase phase = PhaseUtil::GetPhaseUnchecked();
+
+ if (phase == jvmtiPhase::JVMTI_PHASE_ONLOAD) {
+ // We could try and see whether it is a valid path. We could also try to allocate Java
+ // objects to avoid later OOME.
+ gSystemOnloadSegments.push_back(segment);
+ return ERR(NONE);
+ } else if (phase != jvmtiPhase::JVMTI_PHASE_LIVE) {
return ERR(WRONG_PHASE);
}
- jobject sys_class_loader = current->GetSystemClassLoader();
+
+ jobject sys_class_loader = art::Runtime::Current()->GetSystemClassLoader();
if (sys_class_loader == nullptr) {
- // TODO: Support classpath change in OnLoad.
- return ERR(WRONG_PHASE);
+ // This is unexpected.
+ return ERR(INTERNAL);
}
// We'll use BaseDexClassLoader.addDexPath, as it takes care of array resizing etc. As a downside,
diff --git a/runtime/openjdkjvmti/ti_search.h b/runtime/openjdkjvmti/ti_search.h
index 6a52e80405..cd7b4be28c 100644
--- a/runtime/openjdkjvmti/ti_search.h
+++ b/runtime/openjdkjvmti/ti_search.h
@@ -32,12 +32,17 @@
#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
#define ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
+#include <vector>
+
#include "jvmti.h"
namespace openjdkjvmti {
class SearchUtil {
public:
+ static void Register();
+ static void Unregister();
+
static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment);
static jvmtiError AddToSystemClassLoaderSearch(jvmtiEnv* env, const char* segment);