Implement RedefineClasses, also redefine multiple classes atomically.
We need to be able to redefine multiple classes atomically for JVMTI.
This implements that behavior. It also implements RedefineClasses
since until we have class transformation it is trivial.
Test: mma -j40 test-art-host
Change-Id: I80784f919a4366c465b93fede94f4bf763c0ee70
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index 5852309..8626bc5 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -63,50 +63,154 @@
namespace openjdkjvmti {
+class RedefinitionDataHolder;
+
// 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 Redefiner {
public:
- // Redefine the given class with the given dex data. Note this function does not take ownership of
- // the dex_data pointer. It is not used after this call however and may be freed if desired.
+ // Redefine the given classes with the given dex data. Note this function does not take ownership
+ // of the dex_data pointers. It is not used after this call however and may be freed if desired.
// The caller is responsible for freeing it. The runtime makes its own copy of the data.
- static jvmtiError RedefineClass(ArtJvmTiEnv* env,
- art::Runtime* runtime,
- art::Thread* self,
- jclass klass,
- const std::string& original_dex_location,
- jint data_len,
- unsigned char* dex_data,
- std::string* error_msg);
+ static jvmtiError RedefineClasses(ArtJvmTiEnv* env,
+ art::Runtime* runtime,
+ art::Thread* self,
+ jint class_count,
+ const jvmtiClassDefinition* definitions,
+ std::string* error_msg);
static jvmtiError IsModifiableClass(jvmtiEnv* env, jclass klass, jboolean* is_redefinable);
private:
+ class ClassRedefinition {
+ public:
+ ClassRedefinition(Redefiner* driver,
+ jclass klass,
+ const art::DexFile* redefined_dex_file,
+ const char* class_sig)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ // NO_THREAD_SAFETY_ANALYSIS so we can unlock the class in the destructor.
+ ~ClassRedefinition() NO_THREAD_SAFETY_ANALYSIS;
+
+ // Move constructor so we can put these into a vector.
+ ClassRedefinition(ClassRedefinition&& other)
+ : driver_(other.driver_),
+ klass_(other.klass_),
+ dex_file_(std::move(other.dex_file_)),
+ class_sig_(std::move(other.class_sig_)) {
+ other.driver_ = nullptr;
+ }
+
+ 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_);
+
+ void RecordFailure(jvmtiError e, const std::string& err) {
+ driver_->RecordFailure(e, class_sig_, err);
+ }
+
+ bool FinishRemainingAllocations(
+ /*out*/art::MutableHandle<art::mirror::ClassLoader>* source_class_loader,
+ /*out*/art::MutableHandle<art::mirror::Object>* source_dex_file_obj,
+ /*out*/art::MutableHandle<art::mirror::LongArray>* new_dex_file_cookie,
+ /*out*/art::MutableHandle<art::mirror::DexCache>* new_dex_cache)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ void FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass)
+ REQUIRES(art::Locks::mutator_lock_);
+
+ void FillObsoleteMethodMap(
+ art::mirror::Class* art_klass,
+ const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsoletes)
+ REQUIRES(art::Locks::mutator_lock_);
+
+
+ // Checks that the dex file contains only the single expected class and that the top-level class
+ // data has not been modified in an incompatible manner.
+ bool CheckClass() REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ // Preallocates all needed allocations in klass so that we can pause execution safely.
+ // TODO We should be able to free the arrays if they end up not being used. Investigate doing
+ // this in the future. For now we will just take the memory hit.
+ bool EnsureClassAllocationsFinished() REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ // This will check that no constraints are violated (more than 1 class in dex file, any changes
+ // in number/declaration of methods & fields, changes in access flags, etc.)
+ bool CheckRedefinitionIsValid() REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ // Checks that the class can even be redefined.
+ bool CheckRedefinable() REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ // Checks that the dex file does not add/remove methods.
+ bool CheckSameMethods() REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ LOG(WARNING) << "methods are not checked for modification currently";
+ return true;
+ }
+
+ // Checks that the dex file does not modify fields
+ bool CheckSameFields() REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ LOG(WARNING) << "Fields are not checked for modification currently";
+ return true;
+ }
+
+ void UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file,
+ art::ObjPtr<art::mirror::LongArray> new_cookie)
+ REQUIRES(art::Locks::mutator_lock_);
+
+ void UpdateFields(art::ObjPtr<art::mirror::Class> mclass)
+ REQUIRES(art::Locks::mutator_lock_);
+
+ void UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
+ art::ObjPtr<art::mirror::DexCache> new_dex_cache,
+ const art::DexFile::ClassDef& class_def)
+ REQUIRES(art::Locks::mutator_lock_);
+
+ void UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
+ art::ObjPtr<art::mirror::DexCache> new_dex_cache)
+ REQUIRES(art::Locks::mutator_lock_);
+
+ void ReleaseDexFile() REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ private:
+ Redefiner* driver_;
+ jclass klass_;
+ std::unique_ptr<const art::DexFile> dex_file_;
+ std::string class_sig_;
+ };
+
jvmtiError result_;
art::Runtime* runtime_;
art::Thread* self_;
+ std::vector<ClassRedefinition> redefinitions_;
// Kept as a jclass since we have weird run-state changes that make keeping it around as a
// mirror::Class difficult and confusing.
- jclass klass_;
- std::unique_ptr<const art::DexFile> dex_file_;
std::string* error_msg_;
- char* class_sig_;
// TODO Maybe change jclass to a mirror::Class
Redefiner(art::Runtime* runtime,
art::Thread* self,
- jclass klass,
- char* class_sig,
- std::unique_ptr<const art::DexFile>& redefined_dex_file,
std::string* error_msg)
: result_(ERR(INTERNAL)),
runtime_(runtime),
self_(self),
- klass_(klass),
- dex_file_(std::move(redefined_dex_file)),
- error_msg_(error_msg),
- class_sig_(class_sig) { }
+ redefinitions_(),
+ error_msg_(error_msg) { }
+
+ jvmtiError AddRedefinition(ArtJvmTiEnv* env, const jvmtiClassDefinition& def)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
static jvmtiError GetClassRedefinitionError(art::Handle<art::mirror::Class> klass,
/*out*/std::string* error_msg)
@@ -114,23 +218,17 @@
static std::unique_ptr<art::MemMap> MoveDataToMemMap(const std::string& original_location,
jint data_len,
- unsigned char* dex_data,
+ const unsigned char* dex_data,
std::string* error_msg);
// TODO Put on all the lock qualifiers.
jvmtiError Run() REQUIRES_SHARED(art::Locks::mutator_lock_);
- bool FinishRemainingAllocations(
- /*out*/art::MutableHandle<art::mirror::ClassLoader>* source_class_loader,
- /*out*/art::MutableHandle<art::mirror::Object>* source_dex_file_obj,
- /*out*/art::MutableHandle<art::mirror::LongArray>* new_dex_file_cookie,
- /*out*/art::MutableHandle<art::mirror::DexCache>* new_dex_cache)
+ bool CheckAllRedefinitionAreValid() REQUIRES_SHARED(art::Locks::mutator_lock_);
+ bool EnsureAllClassAllocationsFinished() REQUIRES_SHARED(art::Locks::mutator_lock_);
+ bool FinishAllRemainingAllocations(RedefinitionDataHolder& holder)
REQUIRES_SHARED(art::Locks::mutator_lock_);
-
- // Preallocates all needed allocations in klass so that we can pause execution safely.
- // TODO We should be able to free the arrays if they end up not being used. Investigate doing this
- // in the future. For now we will just take the memory hit.
- bool EnsureClassAllocationsFinished() REQUIRES_SHARED(art::Locks::mutator_lock_);
+ void ReleaseAllDexFiles() REQUIRES_SHARED(art::Locks::mutator_lock_);
// Ensure that obsolete methods are deoptimized. This is needed since optimized methods may have
// pointers to their ArtMethods stashed in registers that they then use to attempt to hit the
@@ -140,70 +238,12 @@
REQUIRES(!art::Locks::thread_list_lock_,
!art::Locks::classlinker_classes_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::Class* GetMirrorClass() REQUIRES_SHARED(art::Locks::mutator_lock_);
-
- // Allocates and fills the new DexFileCookie
- art::mirror::LongArray* AllocateDexFileCookie(art::Handle<art::mirror::Object> java_dex_file_obj)
- REQUIRES_SHARED(art::Locks::mutator_lock_);
-
- art::mirror::DexCache* CreateNewDexCache(art::Handle<art::mirror::ClassLoader> loader)
- REQUIRES_SHARED(art::Locks::mutator_lock_);
-
- void RecordFailure(jvmtiError result, const std::string& error_msg);
-
- // This will check that no constraints are violated (more than 1 class in dex file, any changes in
- // number/declaration of methods & fields, changes in access flags, etc.)
- bool CheckRedefinitionIsValid() REQUIRES_SHARED(art::Locks::mutator_lock_);
-
- // Checks that the class can even be redefined.
- bool CheckRedefinable() REQUIRES_SHARED(art::Locks::mutator_lock_);
-
- // Checks that the dex file does not add/remove methods.
- bool CheckSameMethods() REQUIRES_SHARED(art::Locks::mutator_lock_) {
- LOG(WARNING) << "methods are not checked for modification currently";
- return true;
+ void RecordFailure(jvmtiError result, const std::string& class_sig, const std::string& error_msg);
+ void RecordFailure(jvmtiError result, const std::string& error_msg) {
+ RecordFailure(result, "NO CLASS", error_msg);
}
- // Checks that the dex file does not modify fields
- bool CheckSameFields() REQUIRES_SHARED(art::Locks::mutator_lock_) {
- LOG(WARNING) << "Fields are not checked for modification currently";
- return true;
- }
-
- // Checks that the dex file contains only the single expected class and that the top-level class
- // data has not been modified in an incompatible manner.
- bool CheckClass() REQUIRES_SHARED(art::Locks::mutator_lock_);
-
- void UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file,
- art::ObjPtr<art::mirror::LongArray> new_cookie,
- /*out*/art::ObjPtr<art::mirror::LongArray>* original_cookie)
- REQUIRES(art::Locks::mutator_lock_);
-
- void UpdateFields(art::ObjPtr<art::mirror::Class> mclass)
- REQUIRES(art::Locks::mutator_lock_);
-
- void UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
- art::ObjPtr<art::mirror::DexCache> new_dex_cache,
- const art::DexFile::ClassDef& class_def)
- REQUIRES(art::Locks::mutator_lock_);
-
- void UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
- art::ObjPtr<art::mirror::DexCache> new_dex_cache)
- REQUIRES(art::Locks::mutator_lock_);
-
- void FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass)
- REQUIRES(art::Locks::mutator_lock_);
-
- void FillObsoleteMethodMap(art::mirror::Class* art_klass,
- const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsoletes)
- REQUIRES(art::Locks::mutator_lock_);
+ friend struct CallbackCtx;
};
} // namespace openjdkjvmti