Implement RetransformClasses
This CL implements basic support for the RetransformClasses function
and callbacks of the ClassFileLoadHook.
We do not yet support calling the ClassFileLoadHook events on first
load of class.
Bug: 32369913
Bug: 31684920
Test: mma -j40 test-art-host
Change-Id: I7959474f03f9903cc6f10ae3c06d9fd531ec7957
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 90467db..32e3948 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -631,7 +631,17 @@
}
static jvmtiError RetransformClasses(jvmtiEnv* env, jint class_count, const jclass* classes) {
- return ERR(NOT_IMPLEMENTED);
+ std::string error_msg;
+ jvmtiError res = Transformer::RetransformClasses(ArtJvmTiEnv::AsArtJvmTiEnv(env),
+ art::Runtime::Current(),
+ art::Thread::Current(),
+ class_count,
+ classes,
+ &error_msg);
+ if (res != OK) {
+ LOG(WARNING) << "FAILURE TO RETRANFORM " << error_msg;
+ }
+ return res;
}
static jvmtiError RedefineClasses(jvmtiEnv* env,
@@ -1255,78 +1265,6 @@
*format_ptr = jvmtiJlocationFormat::JVMTI_JLOCATION_JVMBCI;
return ERR(NONE);
}
-
- // TODO Remove this once events are working.
- static jvmtiError RetransformClassWithHook(jvmtiEnv* env,
- jclass klass,
- jvmtiEventClassFileLoadHook hook) {
- std::vector<jclass> classes;
- classes.push_back(klass);
- return RetransformClassesWithHook(reinterpret_cast<ArtJvmTiEnv*>(env), classes, hook);
- }
-
- // TODO This will be called by the event handler for the art::ti Event Load Event
- static jvmtiError RetransformClassesWithHook(ArtJvmTiEnv* env,
- const std::vector<jclass>& classes,
- jvmtiEventClassFileLoadHook hook) {
- if (!IsValidEnv(env)) {
- return ERR(INVALID_ENVIRONMENT);
- }
- jvmtiError res = OK;
- std::string error;
- for (jclass klass : classes) {
- JNIEnv* jni_env = nullptr;
- jobject loader = nullptr;
- std::string name;
- jobject protection_domain = nullptr;
- jint data_len = 0;
- unsigned char* dex_data = nullptr;
- jvmtiError ret = OK;
- std::string location;
- if ((ret = GetTransformationData(env,
- klass,
- /*out*/&location,
- /*out*/&jni_env,
- /*out*/&loader,
- /*out*/&name,
- /*out*/&protection_domain,
- /*out*/&data_len,
- /*out*/&dex_data)) != OK) {
- // TODO Do something more here? Maybe give log statements?
- return ret;
- }
- jint new_data_len = 0;
- unsigned char* new_dex_data = nullptr;
- hook(env,
- jni_env,
- klass,
- loader,
- name.c_str(),
- protection_domain,
- data_len,
- dex_data,
- /*out*/&new_data_len,
- /*out*/&new_dex_data);
- // Check if anything actually changed.
- if ((new_data_len != 0 || new_dex_data != nullptr) && new_dex_data != dex_data) {
- jvmtiClassDefinition def = { klass, new_data_len, new_dex_data };
- res = Redefiner::RedefineClasses(env,
- art::Runtime::Current(),
- art::Thread::Current(),
- 1,
- &def,
- &error);
- env->Deallocate(new_dex_data);
- }
- // Deallocate the old dex data.
- env->Deallocate(dex_data);
- if (res != OK) {
- LOG(ERROR) << "FAILURE TO REDEFINE " << error;
- return res;
- }
- }
- return OK;
- }
};
static bool IsJvmtiVersion(jint version) {
@@ -1369,10 +1307,7 @@
// The actual struct holding all of the entrypoints into the jvmti interface.
const jvmtiInterface_1 gJvmtiInterface = {
- // SPECIAL FUNCTION: RetransformClassWithHook Is normally reserved1
- // TODO Remove once we have events working.
- reinterpret_cast<void*>(JvmtiFunctions::RetransformClassWithHook),
- // nullptr, // reserved1
+ nullptr, // reserved1
JvmtiFunctions::SetEventNotificationMode,
nullptr, // reserved3
JvmtiFunctions::GetAllThreads,
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index 5eadc5a..1c84d4d 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -47,6 +47,7 @@
namespace openjdkjvmti {
extern const jvmtiInterface_1 gJvmtiInterface;
+extern EventHandler gEventHandler;
// A structure that is a jvmtiEnv with additional information for the runtime.
struct ArtJvmTiEnv : public jvmtiEnv {
@@ -124,6 +125,29 @@
return ret;
}
+struct ArtClassDefinition {
+ jclass klass;
+ jobject loader;
+ std::string name;
+ jobject protection_domain;
+ jint dex_len;
+ JvmtiUniquePtr dex_data;
+ bool modified;
+
+ ArtClassDefinition() = default;
+ ArtClassDefinition(ArtClassDefinition&& o) = default;
+
+ void SetNewDexData(ArtJvmTiEnv* env, jint new_dex_len, unsigned char* new_dex_data) {
+ if (new_dex_data == nullptr) {
+ return;
+ } else if (new_dex_data != dex_data.get() || new_dex_len != dex_len) {
+ modified = true;
+ dex_len = new_dex_len;
+ dex_data = MakeJvmtiUniquePtr(env, new_dex_data);
+ }
+ }
+};
+
const jvmtiCapabilities kPotentialCapabilities = {
.can_tag_objects = 1,
.can_generate_field_modification_events = 0,
@@ -134,7 +158,7 @@
.can_get_current_contended_monitor = 0,
.can_get_monitor_info = 0,
.can_pop_frame = 0,
- .can_redefine_classes = 0,
+ .can_redefine_classes = 1,
.can_signal_thread = 0,
.can_get_source_file_name = 0,
.can_get_line_numbers = 0,
@@ -162,7 +186,7 @@
.can_get_owned_monitor_stack_depth_info = 0,
.can_get_constant_pool = 0,
.can_set_native_method_prefix = 0,
- .can_retransform_classes = 0,
+ .can_retransform_classes = 1,
.can_retransform_any_class = 0,
.can_generate_resource_exhaustion_heap_events = 0,
.can_generate_resource_exhaustion_threads_events = 0,
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index 1e07bc6..21ec731 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -115,9 +115,95 @@
}
template <typename ...Args>
+inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread*,
+ ArtJvmtiEvent event,
+ Args... args ATTRIBUTE_UNUSED) const {
+ CHECK(event == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
+ event == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
+ LOG(FATAL) << "Incorrect arguments to ClassFileLoadHook!";
+}
+
+// TODO Locking of some type!
+template <>
+inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread,
+ ArtJvmtiEvent event,
+ JNIEnv* jnienv,
+ jclass class_being_redefined,
+ jobject loader,
+ const char* name,
+ jobject protection_domain,
+ jint class_data_len,
+ const unsigned char* class_data,
+ jint* new_class_data_len,
+ unsigned char** new_class_data) const {
+ CHECK(event == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
+ event == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
+ using FnType = void(jvmtiEnv* /* jvmti_env */,
+ JNIEnv* /* jnienv */,
+ jclass /* class_being_redefined */,
+ jobject /* loader */,
+ const char* /* name */,
+ jobject /* protection_domain */,
+ jint /* class_data_len */,
+ const unsigned char* /* class_data */,
+ jint* /* new_class_data_len */,
+ unsigned char** /* new_class_data */);
+ jint current_len = class_data_len;
+ unsigned char* current_class_data = const_cast<unsigned char*>(class_data);
+ ArtJvmTiEnv* last_env = nullptr;
+ for (ArtJvmTiEnv* env : envs) {
+ if (ShouldDispatch(event, env, thread)) {
+ jint new_len;
+ unsigned char* new_data;
+ FnType* callback = GetCallback<FnType>(env, event);
+ callback(env,
+ jnienv,
+ class_being_redefined,
+ loader,
+ name,
+ protection_domain,
+ current_len,
+ current_class_data,
+ &new_len,
+ &new_data);
+ if (new_data != nullptr && new_data != current_class_data) {
+ // Destroy the data the last transformer made. We skip this if the previous state was the
+ // initial one since we don't know here which jvmtiEnv allocated it.
+ // NB Currently this doesn't matter since all allocations just go to malloc but in the
+ // future we might have jvmtiEnv's keep track of their allocations for leak-checking.
+ if (last_env != nullptr) {
+ last_env->Deallocate(current_class_data);
+ }
+ last_env = env;
+ current_class_data = new_data;
+ current_len = new_len;
+ }
+ }
+ }
+ if (last_env != nullptr) {
+ *new_class_data_len = current_len;
+ *new_class_data = current_class_data;
+ }
+}
+
+template <typename ...Args>
inline void EventHandler::DispatchEvent(art::Thread* thread,
ArtJvmtiEvent event,
Args... args) const {
+ switch (event) {
+ case ArtJvmtiEvent::kClassFileLoadHookRetransformable:
+ case ArtJvmtiEvent::kClassFileLoadHookNonRetransformable:
+ return DispatchClassFileLoadHookEvent(thread, event, args...);
+ default:
+ return GenericDispatchEvent(thread, event, args...);
+ }
+}
+
+// TODO Locking of some type!
+template <typename ...Args>
+inline void EventHandler::GenericDispatchEvent(art::Thread* thread,
+ ArtJvmtiEvent event,
+ Args... args) const {
using FnType = void(jvmtiEnv*, Args...);
for (ArtJvmTiEnv* env : envs) {
if (ShouldDispatch(event, env, thread)) {
diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h
index 7990141..08a8765 100644
--- a/runtime/openjdkjvmti/events.h
+++ b/runtime/openjdkjvmti/events.h
@@ -178,6 +178,15 @@
ALWAYS_INLINE
inline void RecalculateGlobalEventMask(ArtJvmtiEvent event);
+ template <typename ...Args>
+ ALWAYS_INLINE inline void GenericDispatchEvent(art::Thread* thread,
+ ArtJvmtiEvent event,
+ Args... args) const;
+ template <typename ...Args>
+ ALWAYS_INLINE inline void DispatchClassFileLoadHookEvent(art::Thread* thread,
+ ArtJvmtiEvent event,
+ Args... args) const;
+
void HandleEventType(ArtJvmtiEvent event, bool enable);
// List of all JvmTiEnv objects that have been created, in their creation order.
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 6af51c4..28ea267 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -242,14 +242,12 @@
}
}
-// TODO This should handle doing multiple classes at once so we need to do less cleanup when things
-// go wrong.
jvmtiError Redefiner::RedefineClasses(ArtJvmTiEnv* env,
art::Runtime* runtime,
art::Thread* self,
jint class_count,
const jvmtiClassDefinition* definitions,
- std::string* error_msg) {
+ /*out*/std::string* error_msg) {
if (env == nullptr) {
*error_msg = "env was null!";
return ERR(INVALID_ENVIRONMENT);
@@ -263,46 +261,83 @@
*error_msg = "null definitions!";
return ERR(NULL_POINTER);
}
+ std::vector<ArtClassDefinition> def_vector;
+ def_vector.reserve(class_count);
+ for (jint i = 0; i < class_count; i++) {
+ ArtClassDefinition def;
+ def.dex_len = definitions[i].class_byte_count;
+ def.dex_data = MakeJvmtiUniquePtr(env, const_cast<unsigned char*>(definitions[i].class_bytes));
+ // We are definitely modified.
+ def.modified = true;
+ jvmtiError res = Transformer::FillInTransformationData(env, definitions[i].klass, &def);
+ if (res != OK) {
+ return res;
+ }
+ def_vector.push_back(std::move(def));
+ }
+ // Call all the transformation events.
+ jvmtiError res = Transformer::RetransformClassesDirect(env,
+ self,
+ &def_vector);
+ if (res != OK) {
+ // Something went wrong with transformation!
+ return res;
+ }
+ return RedefineClassesDirect(env, runtime, self, def_vector, error_msg);
+}
+
+jvmtiError Redefiner::RedefineClassesDirect(ArtJvmTiEnv* env,
+ art::Runtime* runtime,
+ art::Thread* self,
+ const std::vector<ArtClassDefinition>& definitions,
+ std::string* error_msg) {
+ DCHECK(env != nullptr);
+ if (definitions.size() == 0) {
+ // We don't actually need to do anything. Just return OK.
+ return OK;
+ }
// Stop JIT for the duration of this redefine since the JIT might concurrently compile a method we
// are going to redefine.
art::jit::ScopedJitSuspend suspend_jit;
// Get shared mutator lock so we can lock all the classes.
art::ScopedObjectAccess soa(self);
std::vector<Redefiner::ClassRedefinition> redefinitions;
- redefinitions.reserve(class_count);
+ redefinitions.reserve(definitions.size());
Redefiner r(runtime, self, error_msg);
- for (jint i = 0; i < class_count; i++) {
- jvmtiError res = r.AddRedefinition(env, definitions[i]);
- if (res != OK) {
- return res;
+ for (const ArtClassDefinition& def : definitions) {
+ // Only try to transform classes that have been modified.
+ if (def.modified) {
+ jvmtiError res = r.AddRedefinition(env, def);
+ if (res != OK) {
+ return res;
+ }
}
}
return r.Run();
}
-jvmtiError Redefiner::AddRedefinition(ArtJvmTiEnv* env, const jvmtiClassDefinition& def) {
+jvmtiError Redefiner::AddRedefinition(ArtJvmTiEnv* env, const ArtClassDefinition& def) {
std::string original_dex_location;
jvmtiError ret = OK;
if ((ret = GetClassLocation(env, def.klass, &original_dex_location))) {
*error_msg_ = "Unable to get original dex file location!";
return ret;
}
- std::unique_ptr<art::MemMap> map(MoveDataToMemMap(original_dex_location,
- def.class_byte_count,
- def.class_bytes,
- error_msg_));
- std::ostringstream os;
char* generic_ptr_unused = nullptr;
char* signature_ptr = nullptr;
- if (env->GetClassSignature(def.klass, &signature_ptr, &generic_ptr_unused) != OK) {
- *error_msg_ = "A jclass passed in does not seem to be valid";
- return ERR(INVALID_CLASS);
+ if ((ret = env->GetClassSignature(def.klass, &signature_ptr, &generic_ptr_unused)) != OK) {
+ *error_msg_ = "Unable to get class signature!";
+ return ret;
}
- // These will make sure we deallocate the signature.
- JvmtiUniquePtr sig_unique_ptr(MakeJvmtiUniquePtr(env, signature_ptr));
JvmtiUniquePtr generic_unique_ptr(MakeJvmtiUniquePtr(env, generic_ptr_unused));
+ JvmtiUniquePtr signature_unique_ptr(MakeJvmtiUniquePtr(env, signature_ptr));
+ std::unique_ptr<art::MemMap> map(MoveDataToMemMap(original_dex_location,
+ def.dex_len,
+ def.dex_data.get(),
+ error_msg_));
+ std::ostringstream os;
if (map.get() == nullptr) {
- os << "Failed to create anonymous mmap for modified dex file of class " << signature_ptr
+ os << "Failed to create anonymous mmap for modified dex file of class " << def.name
<< "in dex file " << original_dex_location << " because: " << *error_msg_;
*error_msg_ = os.str();
return ERR(OUT_OF_MEMORY);
@@ -319,7 +354,7 @@
/*verify_checksum*/true,
error_msg_));
if (dex_file.get() == nullptr) {
- os << "Unable to load modified dex file for " << signature_ptr << ": " << *error_msg_;
+ os << "Unable to load modified dex file for " << def.name << ": " << *error_msg_;
*error_msg_ = os.str();
return ERR(INVALID_CLASS_FORMAT);
}
@@ -989,17 +1024,16 @@
// Performs updates to class that will allow us to verify it.
void Redefiner::ClassRedefinition::UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
art::ObjPtr<art::mirror::DexCache> new_dex_cache) {
- const art::DexFile::ClassDef* class_def = art::OatFile::OatDexFile::FindClassDef(
- *dex_file_, class_sig_.c_str(), art::ComputeModifiedUtf8Hash(class_sig_.c_str()));
- DCHECK(class_def != nullptr);
- UpdateMethods(mclass, new_dex_cache, *class_def);
+ DCHECK_EQ(dex_file_->NumClassDefs(), 1u);
+ const art::DexFile::ClassDef& class_def = dex_file_->GetClassDef(0);
+ UpdateMethods(mclass, new_dex_cache, class_def);
UpdateFields(mclass);
// Update the class fields.
// Need to update class last since the ArtMethod gets its DexFile from the class (which is needed
// to call GetReturnTypeDescriptor and GetParameterTypeList above).
mclass->SetDexCache(new_dex_cache.Ptr());
- mclass->SetDexClassDefIndex(dex_file_->GetIndexForClassDef(*class_def));
+ mclass->SetDexClassDefIndex(dex_file_->GetIndexForClassDef(class_def));
mclass->SetDexTypeIndex(dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(class_sig_.c_str())));
}
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index 8626bc5..f8d51ad 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -72,13 +72,25 @@
public:
// 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. This
+ // function does not call the transformation events.
+ // TODO Check modified flag of the definitions.
+ static jvmtiError RedefineClassesDirect(ArtJvmTiEnv* env,
+ art::Runtime* runtime,
+ art::Thread* self,
+ const std::vector<ArtClassDefinition>& definitions,
+ /*out*/std::string* error_msg);
+
+ // 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.
+ // TODO This function should call the transformation events.
static jvmtiError RedefineClasses(ArtJvmTiEnv* env,
art::Runtime* runtime,
art::Thread* self,
jint class_count,
const jvmtiClassDefinition* definitions,
- std::string* error_msg);
+ /*out*/std::string* error_msg);
static jvmtiError IsModifiableClass(jvmtiEnv* env, jclass klass, jboolean* is_redefinable);
@@ -209,7 +221,7 @@
redefinitions_(),
error_msg_(error_msg) { }
- jvmtiError AddRedefinition(ArtJvmTiEnv* env, const jvmtiClassDefinition& def)
+ jvmtiError AddRedefinition(ArtJvmTiEnv* env, const ArtClassDefinition& def)
REQUIRES_SHARED(art::Locks::mutator_lock_);
static jvmtiError GetClassRedefinitionError(art::Handle<art::mirror::Class> klass,
diff --git a/runtime/openjdkjvmti/transform.cc b/runtime/openjdkjvmti/transform.cc
index f545125..2809cb6 100644
--- a/runtime/openjdkjvmti/transform.cc
+++ b/runtime/openjdkjvmti/transform.cc
@@ -38,6 +38,7 @@
#include "class_linker.h"
#include "dex_file.h"
#include "dex_file_types.h"
+#include "events-inl.h"
#include "gc_root-inl.h"
#include "globals.h"
#include "jni_env_ext-inl.h"
@@ -52,12 +53,76 @@
#include "scoped_thread_state_change-inl.h"
#include "stack.h"
#include "thread_list.h"
+#include "ti_redefine.h"
#include "transform.h"
#include "utf.h"
#include "utils/dex_cache_arrays_layout-inl.h"
namespace openjdkjvmti {
+jvmtiError Transformer::RetransformClassesDirect(
+ ArtJvmTiEnv* env,
+ art::Thread* self,
+ /*in-out*/std::vector<ArtClassDefinition>* definitions) {
+ for (ArtClassDefinition& def : *definitions) {
+ jint new_len = -1;
+ unsigned char* new_data = nullptr;
+ // Static casts are so that we get the right template initialization for the special event
+ // handling code required by the ClassFileLoadHooks.
+ gEventHandler.DispatchEvent(self,
+ ArtJvmtiEvent::kClassFileLoadHookRetransformable,
+ GetJniEnv(env),
+ static_cast<jclass>(def.klass),
+ static_cast<jobject>(def.loader),
+ static_cast<const char*>(def.name.c_str()),
+ static_cast<jobject>(def.protection_domain),
+ static_cast<jint>(def.dex_len),
+ static_cast<const unsigned char*>(def.dex_data.get()),
+ static_cast<jint*>(&new_len),
+ static_cast<unsigned char**>(&new_data));
+ def.SetNewDexData(env, new_len, new_data);
+ }
+ return OK;
+}
+
+jvmtiError Transformer::RetransformClasses(ArtJvmTiEnv* env,
+ art::Runtime* runtime,
+ art::Thread* self,
+ jint class_count,
+ const jclass* classes,
+ /*out*/std::string* error_msg) {
+ if (env == nullptr) {
+ *error_msg = "env was null!";
+ return ERR(INVALID_ENVIRONMENT);
+ } else if (class_count < 0) {
+ *error_msg = "class_count was less then 0";
+ return ERR(ILLEGAL_ARGUMENT);
+ } else if (class_count == 0) {
+ // We don't actually need to do anything. Just return OK.
+ return OK;
+ } else if (classes == nullptr) {
+ *error_msg = "null classes!";
+ return ERR(NULL_POINTER);
+ }
+ // A holder that will Deallocate all the class bytes buffers on destruction.
+ std::vector<ArtClassDefinition> definitions;
+ jvmtiError res = OK;
+ for (jint i = 0; i < class_count; i++) {
+ ArtClassDefinition def;
+ res = FillInTransformationData(env, classes[i], &def);
+ if (res != OK) {
+ return res;
+ }
+ definitions.push_back(std::move(def));
+ }
+ res = RetransformClassesDirect(env, self, &definitions);
+ if (res != OK) {
+ return res;
+ }
+ return Redefiner::RedefineClassesDirect(env, runtime, self, definitions, error_msg);
+}
+
+// TODO Move this somewhere else, ti_class?
jvmtiError GetClassLocation(ArtJvmTiEnv* env, jclass klass, /*out*/std::string* location) {
JNIEnv* jni_env = nullptr;
jint ret = env->art_vm->GetEnv(reinterpret_cast<void**>(&jni_env), JNI_VERSION_1_1);
@@ -73,42 +138,61 @@
return OK;
}
-// TODO Move this function somewhere more appropriate.
-// Gets the data surrounding the given class.
-jvmtiError GetTransformationData(ArtJvmTiEnv* env,
- jclass klass,
- /*out*/std::string* location,
- /*out*/JNIEnv** jni_env_ptr,
- /*out*/jobject* loader,
- /*out*/std::string* name,
- /*out*/jobject* protection_domain,
- /*out*/jint* data_len,
- /*out*/unsigned char** dex_data) {
- jint ret = env->art_vm->GetEnv(reinterpret_cast<void**>(jni_env_ptr), JNI_VERSION_1_1);
- if (ret != JNI_OK) {
- // TODO Different error might be better?
- return ERR(INTERNAL);
- }
- JNIEnv* jni_env = *jni_env_ptr;
- art::ScopedObjectAccess soa(jni_env);
- art::StackHandleScope<3> hs(art::Thread::Current());
- art::Handle<art::mirror::Class> hs_klass(hs.NewHandle(soa.Decode<art::mirror::Class>(klass)));
- *loader = soa.AddLocalReference<jobject>(hs_klass->GetClassLoader());
- *name = art::mirror::Class::ComputeName(hs_klass)->ToModifiedUtf8();
- // TODO is this always null?
- *protection_domain = nullptr;
- const art::DexFile& dex = hs_klass->GetDexFile();
- *location = dex.GetLocation();
- *data_len = static_cast<jint>(dex.Size());
- // TODO We should maybe change env->Allocate to allow us to mprotect this memory and stop writes.
- jvmtiError alloc_error = env->Allocate(*data_len, dex_data);
+// TODO Implement this for real once transformed dex data is actually saved.
+jvmtiError Transformer::GetDexDataForRetransformation(ArtJvmTiEnv* env,
+ art::Handle<art::mirror::Class> klass,
+ /*out*/jint* dex_data_len,
+ /*out*/unsigned char** dex_data) {
+ // TODO De-quicken the dex file before passing it to the agents.
+ LOG(WARNING) << "Dex file is not de-quickened yet! Quickened dex instructions might be present";
+ LOG(WARNING) << "Caching of initial dex data is not yet performed! Dex data might have been "
+ << "transformed by agent already";
+ const art::DexFile& dex = klass->GetDexFile();
+ *dex_data_len = static_cast<jint>(dex.Size());
+ unsigned char* new_dex_data = nullptr;
+ jvmtiError alloc_error = env->Allocate(*dex_data_len, &new_dex_data);
if (alloc_error != OK) {
return alloc_error;
}
// Copy the data into a temporary buffer.
- memcpy(reinterpret_cast<void*>(*dex_data),
- reinterpret_cast<const void*>(dex.Begin()),
- *data_len);
+ memcpy(reinterpret_cast<void*>(new_dex_data),
+ reinterpret_cast<const void*>(dex.Begin()),
+ *dex_data_len);
+ *dex_data = new_dex_data;
+ return OK;
+}
+
+// TODO Move this function somewhere more appropriate.
+// Gets the data surrounding the given class.
+// TODO Make this less magical.
+jvmtiError Transformer::FillInTransformationData(ArtJvmTiEnv* env,
+ jclass klass,
+ ArtClassDefinition* def) {
+ JNIEnv* jni_env = GetJniEnv(env);
+ if (jni_env == nullptr) {
+ // TODO Different error might be better?
+ return ERR(INTERNAL);
+ }
+ art::ScopedObjectAccess soa(jni_env);
+ art::StackHandleScope<3> hs(art::Thread::Current());
+ art::Handle<art::mirror::Class> hs_klass(hs.NewHandle(soa.Decode<art::mirror::Class>(klass)));
+ if (hs_klass.IsNull()) {
+ return ERR(INVALID_CLASS);
+ }
+ def->klass = klass;
+ def->loader = soa.AddLocalReference<jobject>(hs_klass->GetClassLoader());
+ def->name = art::mirror::Class::ComputeName(hs_klass)->ToModifiedUtf8();
+ // TODO is this always null?
+ def->protection_domain = nullptr;
+ if (def->dex_data.get() == nullptr) {
+ unsigned char* new_data;
+ jvmtiError res = GetDexDataForRetransformation(env, hs_klass, &def->dex_len, &new_data);
+ if (res == OK) {
+ def->dex_data = MakeJvmtiUniquePtr(env, new_data);
+ } else {
+ return res;
+ }
+ }
return OK;
}
diff --git a/runtime/openjdkjvmti/transform.h b/runtime/openjdkjvmti/transform.h
index 0ad5099..0ff2bd1 100644
--- a/runtime/openjdkjvmti/transform.h
+++ b/runtime/openjdkjvmti/transform.h
@@ -43,16 +43,30 @@
jvmtiError GetClassLocation(ArtJvmTiEnv* env, jclass klass, /*out*/std::string* location);
-// Gets the data surrounding the given class.
-jvmtiError GetTransformationData(ArtJvmTiEnv* env,
- jclass klass,
- /*out*/std::string* location,
- /*out*/JNIEnv** jni_env_ptr,
- /*out*/jobject* loader,
- /*out*/std::string* name,
- /*out*/jobject* protection_domain,
- /*out*/jint* data_len,
- /*out*/unsigned char** dex_data);
+class Transformer {
+ public:
+ static jvmtiError RetransformClassesDirect(
+ ArtJvmTiEnv* env, art::Thread* self, /*in-out*/std::vector<ArtClassDefinition>* definitions);
+
+ static jvmtiError RetransformClasses(ArtJvmTiEnv* env,
+ art::Runtime* runtime,
+ art::Thread* self,
+ jint class_count,
+ const jclass* classes,
+ /*out*/std::string* error_msg);
+
+ // Gets the data surrounding the given class.
+ static jvmtiError FillInTransformationData(ArtJvmTiEnv* env,
+ jclass klass,
+ ArtClassDefinition* def);
+
+ private:
+ static jvmtiError GetDexDataForRetransformation(ArtJvmTiEnv* env,
+ art::Handle<art::mirror::Class> klass,
+ /*out*/jint* dex_data_length,
+ /*out*/unsigned char** dex_data)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+};
} // namespace openjdkjvmti