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.cc59
-rw-r--r--runtime/openjdkjvmti/art_jvmti.h25
-rw-r--r--runtime/openjdkjvmti/events-inl.h262
-rw-r--r--runtime/openjdkjvmti/events.cc81
-rw-r--r--runtime/openjdkjvmti/events.h24
-rw-r--r--runtime/openjdkjvmti/object_tagging.cc2
-rw-r--r--runtime/openjdkjvmti/ti_class.cc222
-rw-r--r--runtime/openjdkjvmti/ti_class_loader.cc211
-rw-r--r--runtime/openjdkjvmti/ti_class_loader.h99
-rw-r--r--runtime/openjdkjvmti/ti_dump.cc2
-rw-r--r--runtime/openjdkjvmti/ti_heap.cc8
-rw-r--r--runtime/openjdkjvmti/ti_monitor.cc6
-rw-r--r--runtime/openjdkjvmti/ti_phase.cc13
-rw-r--r--runtime/openjdkjvmti/ti_phase.h2
-rw-r--r--runtime/openjdkjvmti/ti_redefine.cc454
-rw-r--r--runtime/openjdkjvmti/ti_redefine.h50
-rw-r--r--runtime/openjdkjvmti/ti_search.cc179
-rw-r--r--runtime/openjdkjvmti/ti_search.h5
-rw-r--r--runtime/openjdkjvmti/ti_stack.cc5
-rw-r--r--runtime/openjdkjvmti/ti_thread.cc17
-rw-r--r--runtime/openjdkjvmti/ti_threadgroup.cc2
-rw-r--r--runtime/openjdkjvmti/transform.cc42
23 files changed, 1304 insertions, 467 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 417d1041a8..a815a603a7 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -138,6 +138,7 @@ class JvmtiFunctions {
}
static jvmtiError SuspendThread(jvmtiEnv* env, jthread thread) {
+ ENSURE_HAS_CAP(env, can_suspend);
return ERR(NOT_IMPLEMENTED);
}
@@ -145,10 +146,12 @@ class JvmtiFunctions {
jint request_count,
const jthread* request_list,
jvmtiError* results) {
+ ENSURE_HAS_CAP(env, can_suspend);
return ERR(NOT_IMPLEMENTED);
}
static jvmtiError ResumeThread(jvmtiEnv* env, jthread thread) {
+ ENSURE_HAS_CAP(env, can_suspend);
return ERR(NOT_IMPLEMENTED);
}
@@ -156,14 +159,17 @@ class JvmtiFunctions {
jint request_count,
const jthread* request_list,
jvmtiError* results) {
+ ENSURE_HAS_CAP(env, can_suspend);
return ERR(NOT_IMPLEMENTED);
}
static jvmtiError StopThread(jvmtiEnv* env, jthread thread, jobject exception) {
+ ENSURE_HAS_CAP(env, can_signal_thread);
return ERR(NOT_IMPLEMENTED);
}
static jvmtiError InterruptThread(jvmtiEnv* env, jthread thread) {
+ ENSURE_HAS_CAP(env, can_signal_thread);
return ERR(NOT_IMPLEMENTED);
}
@@ -175,6 +181,7 @@ class JvmtiFunctions {
jthread thread,
jint* owned_monitor_count_ptr,
jobject** owned_monitors_ptr) {
+ ENSURE_HAS_CAP(env, can_get_owned_monitor_info);
return ERR(NOT_IMPLEMENTED);
}
@@ -182,12 +189,14 @@ class JvmtiFunctions {
jthread thread,
jint* monitor_info_count_ptr,
jvmtiMonitorStackDepthInfo** monitor_info_ptr) {
+ ENSURE_HAS_CAP(env, can_get_owned_monitor_stack_depth_info);
return ERR(NOT_IMPLEMENTED);
}
static jvmtiError GetCurrentContendedMonitor(jvmtiEnv* env,
jthread thread,
jobject* monitor_ptr) {
+ ENSURE_HAS_CAP(env, can_get_current_contended_monitor);
return ERR(NOT_IMPLEMENTED);
}
@@ -271,6 +280,7 @@ class JvmtiFunctions {
}
static jvmtiError PopFrame(jvmtiEnv* env, jthread thread) {
+ ENSURE_HAS_CAP(env, can_pop_frame);
return ERR(NOT_IMPLEMENTED);
}
@@ -283,30 +293,37 @@ class JvmtiFunctions {
}
static jvmtiError NotifyFramePop(jvmtiEnv* env, jthread thread, jint depth) {
+ ENSURE_HAS_CAP(env, can_generate_frame_pop_events);
return ERR(NOT_IMPLEMENTED);
}
static jvmtiError ForceEarlyReturnObject(jvmtiEnv* env, jthread thread, jobject value) {
+ ENSURE_HAS_CAP(env, can_force_early_return);
return ERR(NOT_IMPLEMENTED);
}
static jvmtiError ForceEarlyReturnInt(jvmtiEnv* env, jthread thread, jint value) {
+ ENSURE_HAS_CAP(env, can_force_early_return);
return ERR(NOT_IMPLEMENTED);
}
static jvmtiError ForceEarlyReturnLong(jvmtiEnv* env, jthread thread, jlong value) {
+ ENSURE_HAS_CAP(env, can_force_early_return);
return ERR(NOT_IMPLEMENTED);
}
static jvmtiError ForceEarlyReturnFloat(jvmtiEnv* env, jthread thread, jfloat value) {
+ ENSURE_HAS_CAP(env, can_force_early_return);
return ERR(NOT_IMPLEMENTED);
}
static jvmtiError ForceEarlyReturnDouble(jvmtiEnv* env, jthread thread, jdouble value) {
+ ENSURE_HAS_CAP(env, can_force_early_return);
return ERR(NOT_IMPLEMENTED);
}
static jvmtiError ForceEarlyReturnVoid(jvmtiEnv* env, jthread thread) {
+ ENSURE_HAS_CAP(env, can_force_early_return);
return ERR(NOT_IMPLEMENTED);
}
@@ -316,6 +333,7 @@ class JvmtiFunctions {
jobject initial_object,
const jvmtiHeapCallbacks* callbacks,
const void* user_data) {
+ ENSURE_HAS_CAP(env, can_tag_objects);
HeapUtil heap_util(&gObjectTagTable);
return heap_util.FollowReferences(env,
heap_filter,
@@ -402,6 +420,7 @@ class JvmtiFunctions {
jobject object,
jvmtiObjectReferenceCallback object_reference_callback,
const void* user_data) {
+ ENSURE_HAS_CAP(env, can_tag_objects);
return ERR(NOT_IMPLEMENTED);
}
@@ -410,6 +429,7 @@ class JvmtiFunctions {
jvmtiStackReferenceCallback stack_ref_callback,
jvmtiObjectReferenceCallback object_ref_callback,
const void* user_data) {
+ ENSURE_HAS_CAP(env, can_tag_objects);
return ERR(NOT_IMPLEMENTED);
}
@@ -417,6 +437,7 @@ class JvmtiFunctions {
jvmtiHeapObjectFilter object_filter,
jvmtiHeapObjectCallback heap_object_callback,
const void* user_data) {
+ ENSURE_HAS_CAP(env, can_tag_objects);
return ERR(NOT_IMPLEMENTED);
}
@@ -425,6 +446,7 @@ class JvmtiFunctions {
jvmtiHeapObjectFilter object_filter,
jvmtiHeapObjectCallback heap_object_callback,
const void* user_data) {
+ ENSURE_HAS_CAP(env, can_tag_objects);
return ERR(NOT_IMPLEMENTED);
}
@@ -433,6 +455,7 @@ class JvmtiFunctions {
jint depth,
jint slot,
jobject* value_ptr) {
+ ENSURE_HAS_CAP(env, can_access_local_variables);
return ERR(NOT_IMPLEMENTED);
}
@@ -440,6 +463,7 @@ class JvmtiFunctions {
jthread thread,
jint depth,
jobject* value_ptr) {
+ ENSURE_HAS_CAP(env, can_access_local_variables);
return ERR(NOT_IMPLEMENTED);
}
@@ -448,6 +472,7 @@ class JvmtiFunctions {
jint depth,
jint slot,
jint* value_ptr) {
+ ENSURE_HAS_CAP(env, can_access_local_variables);
return ERR(NOT_IMPLEMENTED);
}
@@ -456,6 +481,7 @@ class JvmtiFunctions {
jint depth,
jint slot,
jlong* value_ptr) {
+ ENSURE_HAS_CAP(env, can_access_local_variables);
return ERR(NOT_IMPLEMENTED);
}
@@ -464,6 +490,7 @@ class JvmtiFunctions {
jint depth,
jint slot,
jfloat* value_ptr) {
+ ENSURE_HAS_CAP(env, can_access_local_variables);
return ERR(NOT_IMPLEMENTED);
}
@@ -472,6 +499,7 @@ class JvmtiFunctions {
jint depth,
jint slot,
jdouble* value_ptr) {
+ ENSURE_HAS_CAP(env, can_access_local_variables);
return ERR(NOT_IMPLEMENTED);
}
@@ -480,6 +508,7 @@ class JvmtiFunctions {
jint depth,
jint slot,
jobject value) {
+ ENSURE_HAS_CAP(env, can_access_local_variables);
return ERR(NOT_IMPLEMENTED);
}
@@ -488,6 +517,7 @@ class JvmtiFunctions {
jint depth,
jint slot,
jint value) {
+ ENSURE_HAS_CAP(env, can_access_local_variables);
return ERR(NOT_IMPLEMENTED);
}
@@ -496,6 +526,7 @@ class JvmtiFunctions {
jint depth,
jint slot,
jlong value) {
+ ENSURE_HAS_CAP(env, can_access_local_variables);
return ERR(NOT_IMPLEMENTED);
}
@@ -504,6 +535,7 @@ class JvmtiFunctions {
jint depth,
jint slot,
jfloat value) {
+ ENSURE_HAS_CAP(env, can_access_local_variables);
return ERR(NOT_IMPLEMENTED);
}
@@ -512,30 +544,37 @@ class JvmtiFunctions {
jint depth,
jint slot,
jdouble value) {
+ ENSURE_HAS_CAP(env, can_access_local_variables);
return ERR(NOT_IMPLEMENTED);
}
static jvmtiError SetBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location) {
+ ENSURE_HAS_CAP(env, can_generate_breakpoint_events);
return ERR(NOT_IMPLEMENTED);
}
static jvmtiError ClearBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location) {
+ ENSURE_HAS_CAP(env, can_generate_breakpoint_events);
return ERR(NOT_IMPLEMENTED);
}
static jvmtiError SetFieldAccessWatch(jvmtiEnv* env, jclass klass, jfieldID field) {
+ ENSURE_HAS_CAP(env, can_generate_field_access_events);
return ERR(NOT_IMPLEMENTED);
}
static jvmtiError ClearFieldAccessWatch(jvmtiEnv* env, jclass klass, jfieldID field) {
+ ENSURE_HAS_CAP(env, can_generate_field_access_events);
return ERR(NOT_IMPLEMENTED);
}
static jvmtiError SetFieldModificationWatch(jvmtiEnv* env, jclass klass, jfieldID field) {
+ ENSURE_HAS_CAP(env, can_generate_field_modification_events);
return ERR(NOT_IMPLEMENTED);
}
static jvmtiError ClearFieldModificationWatch(jvmtiEnv* env, jclass klass, jfieldID field) {
+ ENSURE_HAS_CAP(env, can_generate_field_modification_events);
return ERR(NOT_IMPLEMENTED);
}
@@ -563,6 +602,7 @@ class JvmtiFunctions {
}
static jvmtiError GetSourceFileName(jvmtiEnv* env, jclass klass, char** source_name_ptr) {
+ ENSURE_HAS_CAP(env, can_get_source_file_name);
return ERR(NOT_IMPLEMENTED);
}
@@ -603,6 +643,7 @@ class JvmtiFunctions {
jint* constant_pool_count_ptr,
jint* constant_pool_byte_count_ptr,
unsigned char** constant_pool_bytes_ptr) {
+ ENSURE_HAS_CAP(env, can_get_constant_pool);
return ERR(NOT_IMPLEMENTED);
}
@@ -629,10 +670,12 @@ class JvmtiFunctions {
static jvmtiError GetSourceDebugExtension(jvmtiEnv* env,
jclass klass,
char** source_debug_extension_ptr) {
+ ENSURE_HAS_CAP(env, can_get_source_debug_extension);
return ERR(NOT_IMPLEMENTED);
}
static jvmtiError RetransformClasses(jvmtiEnv* env, jint class_count, const jclass* classes) {
+ ENSURE_HAS_CAP(env, can_retransform_classes);
std::string error_msg;
jvmtiError res = Transformer::RetransformClasses(ArtJvmTiEnv::AsArtJvmTiEnv(env),
art::Runtime::Current(),
@@ -649,6 +692,7 @@ class JvmtiFunctions {
static jvmtiError RedefineClasses(jvmtiEnv* env,
jint class_count,
const jvmtiClassDefinition* class_definitions) {
+ ENSURE_HAS_CAP(env, can_redefine_classes);
std::string error_msg;
jvmtiError res = Redefiner::RedefineClasses(ArtJvmTiEnv::AsArtJvmTiEnv(env),
art::Runtime::Current(),
@@ -673,6 +717,7 @@ class JvmtiFunctions {
static jvmtiError GetObjectMonitorUsage(jvmtiEnv* env,
jobject object,
jvmtiMonitorUsage* info_ptr) {
+ ENSURE_HAS_CAP(env, can_get_monitor_info);
return ERR(NOT_IMPLEMENTED);
}
@@ -703,6 +748,7 @@ class JvmtiFunctions {
jclass klass,
jfieldID field,
jboolean* is_synthetic_ptr) {
+ ENSURE_HAS_CAP(env, can_get_synthetic_attribute);
return FieldUtil::IsFieldSynthetic(env, klass, field, is_synthetic_ptr);
}
@@ -742,6 +788,7 @@ class JvmtiFunctions {
jmethodID method,
jint* entry_count_ptr,
jvmtiLineNumberEntry** table_ptr) {
+ ENSURE_HAS_CAP(env, can_get_line_numbers);
return MethodUtil::GetLineNumberTable(env, method, entry_count_ptr, table_ptr);
}
@@ -756,6 +803,7 @@ class JvmtiFunctions {
jmethodID method,
jint* entry_count_ptr,
jvmtiLocalVariableEntry** table_ptr) {
+ ENSURE_HAS_CAP(env, can_access_local_variables);
return ERR(NOT_IMPLEMENTED);
}
@@ -763,6 +811,7 @@ class JvmtiFunctions {
jmethodID method,
jint* bytecode_count_ptr,
unsigned char** bytecodes_ptr) {
+ ENSURE_HAS_CAP(env, can_get_bytecodes);
return ERR(NOT_IMPLEMENTED);
}
@@ -771,6 +820,7 @@ class JvmtiFunctions {
}
static jvmtiError IsMethodSynthetic(jvmtiEnv* env, jmethodID method, jboolean* is_synthetic_ptr) {
+ ENSURE_HAS_CAP(env, can_get_synthetic_attribute);
return MethodUtil::IsMethodSynthetic(env, method, is_synthetic_ptr);
}
@@ -779,10 +829,12 @@ class JvmtiFunctions {
}
static jvmtiError SetNativeMethodPrefix(jvmtiEnv* env, const char* prefix) {
+ ENSURE_HAS_CAP(env, can_set_native_method_prefix);
return ERR(NOT_IMPLEMENTED);
}
static jvmtiError SetNativeMethodPrefixes(jvmtiEnv* env, jint prefix_count, char** prefixes) {
+ ENSURE_HAS_CAP(env, can_set_native_method_prefix);
return ERR(NOT_IMPLEMENTED);
}
@@ -855,7 +907,6 @@ class JvmtiFunctions {
jthread event_thread,
...) {
ENSURE_VALID_ENV(env);
- // TODO: Check for capabilities.
art::Thread* art_thread = nullptr;
if (event_thread != nullptr) {
// TODO: Need non-aborting call here, to return JVMTI_ERROR_INVALID_THREAD.
@@ -1053,18 +1104,22 @@ class JvmtiFunctions {
}
static jvmtiError GetCurrentThreadCpuTimerInfo(jvmtiEnv* env, jvmtiTimerInfo* info_ptr) {
+ ENSURE_HAS_CAP(env, can_get_current_thread_cpu_time);
return ERR(NOT_IMPLEMENTED);
}
static jvmtiError GetCurrentThreadCpuTime(jvmtiEnv* env, jlong* nanos_ptr) {
+ ENSURE_HAS_CAP(env, can_get_current_thread_cpu_time);
return ERR(NOT_IMPLEMENTED);
}
static jvmtiError GetThreadCpuTimerInfo(jvmtiEnv* env, jvmtiTimerInfo* info_ptr) {
+ ENSURE_HAS_CAP(env, can_get_thread_cpu_time);
return ERR(NOT_IMPLEMENTED);
}
static jvmtiError GetThreadCpuTime(jvmtiEnv* env, jthread thread, jlong* nanos_ptr) {
+ ENSURE_HAS_CAP(env, can_get_thread_cpu_time);
return ERR(NOT_IMPLEMENTED);
}
@@ -1313,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);
@@ -1325,6 +1381,7 @@ extern "C" bool ArtPlugin_Deinitialize() {
ThreadUtil::Unregister();
ClassUtil::Unregister();
DumpUtil::Unregister();
+ SearchUtil::Unregister();
return true;
}
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index 256c3a6cec..106165c38b 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -114,6 +114,21 @@ static inline JvmtiUniquePtr MakeJvmtiUniquePtr(jvmtiEnv* env, T* mem) {
}
ALWAYS_INLINE
+static inline jvmtiError CopyDataIntoJvmtiBuffer(ArtJvmTiEnv* env,
+ const unsigned char* source,
+ jint len,
+ /*out*/unsigned char** dest) {
+ jvmtiError res = env->Allocate(len, dest);
+ if (res != OK) {
+ return res;
+ }
+ memcpy(reinterpret_cast<void*>(*dest),
+ reinterpret_cast<const void*>(source),
+ len);
+ return OK;
+}
+
+ALWAYS_INLINE
static inline jvmtiError CopyString(jvmtiEnv* env, const char* src, unsigned char** copy) {
size_t len = strlen(src) + 1;
unsigned char* buf;
@@ -131,7 +146,7 @@ const jvmtiCapabilities kPotentialCapabilities = {
.can_generate_field_modification_events = 0,
.can_generate_field_access_events = 0,
.can_get_bytecodes = 0,
- .can_get_synthetic_attribute = 0,
+ .can_get_synthetic_attribute = 1,
.can_get_owned_monitor_info = 0,
.can_get_current_contended_monitor = 0,
.can_get_monitor_info = 0,
@@ -139,7 +154,7 @@ const jvmtiCapabilities kPotentialCapabilities = {
.can_redefine_classes = 1,
.can_signal_thread = 0,
.can_get_source_file_name = 0,
- .can_get_line_numbers = 0,
+ .can_get_line_numbers = 1,
.can_get_source_debug_extension = 0,
.can_access_local_variables = 0,
.can_maintain_original_method_order = 0,
@@ -156,10 +171,10 @@ const jvmtiCapabilities kPotentialCapabilities = {
.can_generate_all_class_hook_events = 0,
.can_generate_compiled_method_load_events = 0,
.can_generate_monitor_events = 0,
- .can_generate_vm_object_alloc_events = 0,
+ .can_generate_vm_object_alloc_events = 1,
.can_generate_native_method_bind_events = 0,
- .can_generate_garbage_collection_events = 0,
- .can_generate_object_free_events = 0,
+ .can_generate_garbage_collection_events = 1,
+ .can_generate_object_free_events = 1,
.can_force_early_return = 0,
.can_get_owned_monitor_stack_depth_info = 0,
.can_get_constant_pool = 0,
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index 21ec731ba5..4f5eb0c33f 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -37,96 +37,84 @@ static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e) {
}
}
-template <typename FnType>
-ALWAYS_INLINE static inline FnType* GetCallback(ArtJvmTiEnv* env, ArtJvmtiEvent event) {
- if (env->event_callbacks == nullptr) {
- return nullptr;
- }
+namespace impl {
- // TODO: Add a type check. Can be done, for example, by an explicitly instantiated template
- // function.
+// Infrastructure to achieve type safety for event dispatch.
- switch (event) {
- case ArtJvmtiEvent::kVmInit:
- return reinterpret_cast<FnType*>(env->event_callbacks->VMInit);
- case ArtJvmtiEvent::kVmDeath:
- return reinterpret_cast<FnType*>(env->event_callbacks->VMDeath);
- case ArtJvmtiEvent::kThreadStart:
- return reinterpret_cast<FnType*>(env->event_callbacks->ThreadStart);
- case ArtJvmtiEvent::kThreadEnd:
- return reinterpret_cast<FnType*>(env->event_callbacks->ThreadEnd);
- case ArtJvmtiEvent::kClassFileLoadHookRetransformable:
- case ArtJvmtiEvent::kClassFileLoadHookNonRetransformable:
- return reinterpret_cast<FnType*>(env->event_callbacks->ClassFileLoadHook);
- case ArtJvmtiEvent::kClassLoad:
- return reinterpret_cast<FnType*>(env->event_callbacks->ClassLoad);
- case ArtJvmtiEvent::kClassPrepare:
- return reinterpret_cast<FnType*>(env->event_callbacks->ClassPrepare);
- case ArtJvmtiEvent::kVmStart:
- return reinterpret_cast<FnType*>(env->event_callbacks->VMStart);
- case ArtJvmtiEvent::kException:
- return reinterpret_cast<FnType*>(env->event_callbacks->Exception);
- case ArtJvmtiEvent::kExceptionCatch:
- return reinterpret_cast<FnType*>(env->event_callbacks->ExceptionCatch);
- case ArtJvmtiEvent::kSingleStep:
- return reinterpret_cast<FnType*>(env->event_callbacks->SingleStep);
- case ArtJvmtiEvent::kFramePop:
- return reinterpret_cast<FnType*>(env->event_callbacks->FramePop);
- case ArtJvmtiEvent::kBreakpoint:
- return reinterpret_cast<FnType*>(env->event_callbacks->Breakpoint);
- case ArtJvmtiEvent::kFieldAccess:
- return reinterpret_cast<FnType*>(env->event_callbacks->FieldAccess);
- case ArtJvmtiEvent::kFieldModification:
- return reinterpret_cast<FnType*>(env->event_callbacks->FieldModification);
- case ArtJvmtiEvent::kMethodEntry:
- return reinterpret_cast<FnType*>(env->event_callbacks->MethodEntry);
- case ArtJvmtiEvent::kMethodExit:
- return reinterpret_cast<FnType*>(env->event_callbacks->MethodExit);
- case ArtJvmtiEvent::kNativeMethodBind:
- return reinterpret_cast<FnType*>(env->event_callbacks->NativeMethodBind);
- case ArtJvmtiEvent::kCompiledMethodLoad:
- return reinterpret_cast<FnType*>(env->event_callbacks->CompiledMethodLoad);
- case ArtJvmtiEvent::kCompiledMethodUnload:
- return reinterpret_cast<FnType*>(env->event_callbacks->CompiledMethodUnload);
- case ArtJvmtiEvent::kDynamicCodeGenerated:
- return reinterpret_cast<FnType*>(env->event_callbacks->DynamicCodeGenerated);
- case ArtJvmtiEvent::kDataDumpRequest:
- return reinterpret_cast<FnType*>(env->event_callbacks->DataDumpRequest);
- case ArtJvmtiEvent::kMonitorWait:
- return reinterpret_cast<FnType*>(env->event_callbacks->MonitorWait);
- case ArtJvmtiEvent::kMonitorWaited:
- return reinterpret_cast<FnType*>(env->event_callbacks->MonitorWaited);
- case ArtJvmtiEvent::kMonitorContendedEnter:
- return reinterpret_cast<FnType*>(env->event_callbacks->MonitorContendedEnter);
- case ArtJvmtiEvent::kMonitorContendedEntered:
- return reinterpret_cast<FnType*>(env->event_callbacks->MonitorContendedEntered);
- case ArtJvmtiEvent::kResourceExhausted:
- return reinterpret_cast<FnType*>(env->event_callbacks->ResourceExhausted);
- case ArtJvmtiEvent::kGarbageCollectionStart:
- return reinterpret_cast<FnType*>(env->event_callbacks->GarbageCollectionStart);
- case ArtJvmtiEvent::kGarbageCollectionFinish:
- return reinterpret_cast<FnType*>(env->event_callbacks->GarbageCollectionFinish);
- case ArtJvmtiEvent::kObjectFree:
- return reinterpret_cast<FnType*>(env->event_callbacks->ObjectFree);
- case ArtJvmtiEvent::kVmObjectAlloc:
- return reinterpret_cast<FnType*>(env->event_callbacks->VMObjectAlloc);
- }
- return nullptr;
-}
+#define FORALL_EVENT_TYPES(fn) \
+ fn(VMInit, ArtJvmtiEvent::kVmInit) \
+ fn(VMDeath, ArtJvmtiEvent::kVmDeath) \
+ fn(ThreadStart, ArtJvmtiEvent::kThreadStart) \
+ fn(ThreadEnd, ArtJvmtiEvent::kThreadEnd) \
+ fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookRetransformable) \
+ fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookNonRetransformable) \
+ fn(ClassLoad, ArtJvmtiEvent::kClassLoad) \
+ fn(ClassPrepare, ArtJvmtiEvent::kClassPrepare) \
+ fn(VMStart, ArtJvmtiEvent::kVmStart) \
+ fn(Exception, ArtJvmtiEvent::kException) \
+ fn(ExceptionCatch, ArtJvmtiEvent::kExceptionCatch) \
+ fn(SingleStep, ArtJvmtiEvent::kSingleStep) \
+ fn(FramePop, ArtJvmtiEvent::kFramePop) \
+ fn(Breakpoint, ArtJvmtiEvent::kBreakpoint) \
+ fn(FieldAccess, ArtJvmtiEvent::kFieldAccess) \
+ fn(FieldModification, ArtJvmtiEvent::kFieldModification) \
+ fn(MethodEntry, ArtJvmtiEvent::kMethodEntry) \
+ fn(MethodExit, ArtJvmtiEvent::kMethodExit) \
+ fn(NativeMethodBind, ArtJvmtiEvent::kNativeMethodBind) \
+ fn(CompiledMethodLoad, ArtJvmtiEvent::kCompiledMethodLoad) \
+ fn(CompiledMethodUnload, ArtJvmtiEvent::kCompiledMethodUnload) \
+ fn(DynamicCodeGenerated, ArtJvmtiEvent::kDynamicCodeGenerated) \
+ fn(DataDumpRequest, ArtJvmtiEvent::kDataDumpRequest) \
+ fn(MonitorWait, ArtJvmtiEvent::kMonitorWait) \
+ fn(MonitorWaited, ArtJvmtiEvent::kMonitorWaited) \
+ fn(MonitorContendedEnter, ArtJvmtiEvent::kMonitorContendedEnter) \
+ fn(MonitorContendedEntered, ArtJvmtiEvent::kMonitorContendedEntered) \
+ fn(ResourceExhausted, ArtJvmtiEvent::kResourceExhausted) \
+ fn(GarbageCollectionStart, ArtJvmtiEvent::kGarbageCollectionStart) \
+ fn(GarbageCollectionFinish, ArtJvmtiEvent::kGarbageCollectionFinish) \
+ fn(ObjectFree, ArtJvmtiEvent::kObjectFree) \
+ fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc)
+
+template <ArtJvmtiEvent kEvent>
+struct EventFnType {
+};
+
+#define EVENT_FN_TYPE(name, enum_name) \
+template <> \
+struct EventFnType<enum_name> { \
+ using type = decltype(jvmtiEventCallbacks().name); \
+};
+
+FORALL_EVENT_TYPES(EVENT_FN_TYPE)
-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!";
+#undef EVENT_FN_TYPE
+
+template <ArtJvmtiEvent kEvent>
+ALWAYS_INLINE inline typename EventFnType<kEvent>::type GetCallback(ArtJvmTiEnv* env);
+
+#define GET_CALLBACK(name, enum_name) \
+template <> \
+ALWAYS_INLINE inline EventFnType<enum_name>::type GetCallback<enum_name>( \
+ ArtJvmTiEnv* env) { \
+ if (env->event_callbacks == nullptr) { \
+ return nullptr; \
+ } \
+ return env->event_callbacks->name; \
}
+FORALL_EVENT_TYPES(GET_CALLBACK)
+
+#undef GET_CALLBACK
+
+#undef FORALL_EVENT_TYPES
+
+} // namespace impl
+
+// C++ does not allow partial template function specialization. The dispatch for our separated
+// ClassFileLoadHook event types is the same, so use this helper for code deduplication.
// TODO Locking of some type!
-template <>
+template <ArtJvmtiEvent kEvent>
inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread,
- ArtJvmtiEvent event,
JNIEnv* jnienv,
jclass class_being_redefined,
jobject loader,
@@ -136,26 +124,16 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread,
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 */);
+ static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
+ kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable, "Unsupported event");
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);
+ if (ShouldDispatch<kEvent>(env, thread)) {
+ jint new_len = 0;
+ unsigned char* new_data = nullptr;
+ auto callback = impl::GetCallback<kEvent>(env);
callback(env,
jnienv,
class_being_redefined,
@@ -186,28 +164,16 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread,
}
}
-template <typename ...Args>
+// Our goal for DispatchEvent: Do not allow implicit type conversion. Types of ...args must match
+// exactly the argument types of the corresponding Jvmti kEvent function pointer.
+
+template <ArtJvmtiEvent kEvent, 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)) {
- FnType* callback = GetCallback<FnType>(env, event);
+ if (ShouldDispatch<kEvent>(env, thread)) {
+ FnType* callback = impl::GetCallback<kEvent>(env);
if (callback != nullptr) {
(*callback)(env, args...);
}
@@ -215,14 +181,66 @@ inline void EventHandler::GenericDispatchEvent(art::Thread* thread,
}
}
-inline bool EventHandler::ShouldDispatch(ArtJvmtiEvent event,
- ArtJvmTiEnv* env,
+// C++ does not allow partial template function specialization. The dispatch for our separated
+// ClassFileLoadHook event types is the same, and in the DispatchClassFileLoadHookEvent helper.
+// The following two DispatchEvent specializations dispatch to it.
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
+ art::Thread* thread,
+ 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 {
+ return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
+ thread,
+ jnienv,
+ class_being_redefined,
+ loader,
+ name,
+ protection_domain,
+ class_data_len,
+ class_data,
+ new_class_data_len,
+ new_class_data);
+}
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
+ art::Thread* thread,
+ 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 {
+ return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
+ thread,
+ jnienv,
+ class_being_redefined,
+ loader,
+ name,
+ protection_domain,
+ class_data_len,
+ class_data,
+ new_class_data_len,
+ new_class_data);
+}
+
+template <ArtJvmtiEvent kEvent>
+inline bool EventHandler::ShouldDispatch(ArtJvmTiEnv* env,
art::Thread* thread) {
- bool dispatch = env->event_masks.global_event_mask.Test(event);
+ bool dispatch = env->event_masks.global_event_mask.Test(kEvent);
- if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(event)) {
+ if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(kEvent)) {
EventMask* mask = env->event_masks.GetEventMaskOrNull(thread);
- dispatch = mask != nullptr && mask->Test(event);
+ dispatch = mask != nullptr && mask->Test(kEvent);
}
return dispatch;
}
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index d3f8001d69..34492a91fd 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -206,13 +206,12 @@ class JvmtiAllocationListener : public art::gc::AllocationListener {
ScopedLocalRef<jclass> klass(
jni_env, jni_env->AddLocalReference<jclass>(obj->Ptr()->GetClass()));
- handler_->DispatchEvent(self,
- ArtJvmtiEvent::kVmObjectAlloc,
- jni_env,
- thread.get(),
- object.get(),
- klass.get(),
- static_cast<jlong>(byte_count));
+ handler_->DispatchEvent<ArtJvmtiEvent::kVmObjectAlloc>(self,
+ reinterpret_cast<JNIEnv*>(jni_env),
+ thread.get(),
+ object.get(),
+ klass.get(),
+ static_cast<jlong>(byte_count));
}
}
@@ -241,11 +240,11 @@ class JvmtiGcPauseListener : public art::gc::GcPauseListener {
finish_enabled_(false) {}
void StartPause() OVERRIDE {
- handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kGarbageCollectionStart);
+ handler_->DispatchEvent<ArtJvmtiEvent::kGarbageCollectionStart>(nullptr);
}
void EndPause() OVERRIDE {
- handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kGarbageCollectionFinish);
+ handler_->DispatchEvent<ArtJvmtiEvent::kGarbageCollectionFinish>(nullptr);
}
bool IsEnabled() {
@@ -303,6 +302,64 @@ void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) {
}
}
+// Checks to see if the env has the capabilities associated with the given event.
+static bool HasAssociatedCapability(ArtJvmTiEnv* env,
+ ArtJvmtiEvent event) {
+ jvmtiCapabilities caps = env->capabilities;
+ switch (event) {
+ case ArtJvmtiEvent::kBreakpoint:
+ return caps.can_generate_breakpoint_events == 1;
+
+ case ArtJvmtiEvent::kCompiledMethodLoad:
+ case ArtJvmtiEvent::kCompiledMethodUnload:
+ return caps.can_generate_compiled_method_load_events == 1;
+
+ case ArtJvmtiEvent::kException:
+ case ArtJvmtiEvent::kExceptionCatch:
+ return caps.can_generate_exception_events == 1;
+
+ case ArtJvmtiEvent::kFieldAccess:
+ return caps.can_generate_field_access_events == 1;
+
+ case ArtJvmtiEvent::kFieldModification:
+ return caps.can_generate_field_modification_events == 1;
+
+ case ArtJvmtiEvent::kFramePop:
+ return caps.can_generate_frame_pop_events == 1;
+
+ case ArtJvmtiEvent::kGarbageCollectionStart:
+ case ArtJvmtiEvent::kGarbageCollectionFinish:
+ return caps.can_generate_garbage_collection_events == 1;
+
+ case ArtJvmtiEvent::kMethodEntry:
+ return caps.can_generate_method_entry_events == 1;
+
+ case ArtJvmtiEvent::kMethodExit:
+ return caps.can_generate_method_exit_events == 1;
+
+ case ArtJvmtiEvent::kMonitorContendedEnter:
+ case ArtJvmtiEvent::kMonitorContendedEntered:
+ case ArtJvmtiEvent::kMonitorWait:
+ case ArtJvmtiEvent::kMonitorWaited:
+ return caps.can_generate_monitor_events == 1;
+
+ case ArtJvmtiEvent::kNativeMethodBind:
+ return caps.can_generate_native_method_bind_events == 1;
+
+ case ArtJvmtiEvent::kObjectFree:
+ return caps.can_generate_object_free_events == 1;
+
+ case ArtJvmtiEvent::kSingleStep:
+ return caps.can_generate_single_step_events == 1;
+
+ case ArtJvmtiEvent::kVmObjectAlloc:
+ return caps.can_generate_vm_object_alloc_events == 1;
+
+ default:
+ return true;
+ }
+}
+
jvmtiError EventHandler::SetEvent(ArtJvmTiEnv* env,
art::Thread* thread,
ArtJvmtiEvent event,
@@ -319,8 +376,6 @@ jvmtiError EventHandler::SetEvent(ArtJvmTiEnv* env,
}
}
- // TODO: Capability check.
-
if (mode != JVMTI_ENABLE && mode != JVMTI_DISABLE) {
return ERR(ILLEGAL_ARGUMENT);
}
@@ -329,6 +384,10 @@ jvmtiError EventHandler::SetEvent(ArtJvmTiEnv* env,
return ERR(INVALID_EVENT_TYPE);
}
+ if (!HasAssociatedCapability(env, event)) {
+ return ERR(MUST_POSSESS_CAPABILITY);
+ }
+
bool old_state = global_mask.Test(event);
if (mode == JVMTI_ENABLE) {
diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h
index 8e246ded8e..4e20d1776b 100644
--- a/runtime/openjdkjvmti/events.h
+++ b/runtime/openjdkjvmti/events.h
@@ -156,9 +156,9 @@ class EventHandler {
ArtJvmtiEvent event,
jvmtiEventMode mode);
- template <typename ...Args>
+ template <ArtJvmtiEvent kEvent, typename ...Args>
ALWAYS_INLINE
- inline void DispatchEvent(art::Thread* thread, ArtJvmtiEvent event, Args... args) const;
+ inline void DispatchEvent(art::Thread* thread, Args... args) const;
// Tell the event handler capabilities were added/lost so it can adjust the sent events.If
// caps_added is true then caps is all the newly set capabilities of the jvmtiEnv. If it is false
@@ -169,8 +169,9 @@ class EventHandler {
bool added);
private:
+ template <ArtJvmtiEvent kEvent>
ALWAYS_INLINE
- static inline bool ShouldDispatch(ArtJvmtiEvent event, ArtJvmTiEnv* env, art::Thread* thread);
+ static inline bool ShouldDispatch(ArtJvmTiEnv* env, art::Thread* thread);
ALWAYS_INLINE
inline bool NeedsEventUpdate(ArtJvmTiEnv* env,
@@ -181,14 +182,17 @@ class EventHandler {
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>
+ template <ArtJvmtiEvent kEvent>
ALWAYS_INLINE inline void DispatchClassFileLoadHookEvent(art::Thread* thread,
- ArtJvmtiEvent event,
- Args... args) const;
+ 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;
void HandleEventType(ArtJvmtiEvent event, bool enable);
diff --git a/runtime/openjdkjvmti/object_tagging.cc b/runtime/openjdkjvmti/object_tagging.cc
index 94cb46a428..b27c2a3834 100644
--- a/runtime/openjdkjvmti/object_tagging.cc
+++ b/runtime/openjdkjvmti/object_tagging.cc
@@ -207,7 +207,7 @@ void ObjectTagTable::SweepImpl(art::IsMarkedVisitor* visitor) {
}
void ObjectTagTable::HandleNullSweep(jlong tag) {
- event_handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kObjectFree, tag);
+ event_handler_->DispatchEvent<ArtJvmtiEvent::kObjectFree>(nullptr, tag);
}
template <typename T, ObjectTagTable::TableUpdateNullTarget kTargetNull>
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index 450f75a813..c14fd84264 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -31,6 +31,8 @@
#include "ti_class.h"
+#include "android-base/stringprintf.h"
+
#include <mutex>
#include <unordered_set>
@@ -38,34 +40,226 @@
#include "base/macros.h"
#include "class_table-inl.h"
#include "class_linker.h"
+#include "common_throws.h"
#include "events-inl.h"
#include "handle.h"
#include "jni_env_ext-inl.h"
#include "jni_internal.h"
+#include "mirror/array-inl.h"
+#include "mirror/class-inl.h"
+#include "mirror/class_ext.h"
#include "runtime.h"
#include "runtime_callbacks.h"
#include "ScopedLocalRef.h"
#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"
namespace openjdkjvmti {
+using android::base::StringPrintf;
+
+static std::unique_ptr<const art::DexFile> MakeSingleDexFile(art::Thread* self,
+ const char* descriptor,
+ const std::string& orig_location,
+ jint final_len,
+ const unsigned char* final_dex_data)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ // Make the mmap
+ std::string error_msg;
+ std::unique_ptr<art::MemMap> map(Redefiner::MoveDataToMemMap(orig_location,
+ final_len,
+ final_dex_data,
+ &error_msg));
+ if (map.get() == nullptr) {
+ LOG(WARNING) << "Unable to allocate mmap for redefined dex file! Error was: " << error_msg;
+ self->ThrowOutOfMemoryError(StringPrintf(
+ "Unable to allocate dex file for transformation of %s", descriptor).c_str());
+ return nullptr;
+ }
+
+ // Make a dex-file
+ if (map->Size() < sizeof(art::DexFile::Header)) {
+ LOG(WARNING) << "Could not read dex file header because dex_data was too short";
+ art::ThrowClassFormatError(nullptr,
+ "Unable to read transformed dex file of %s",
+ descriptor);
+ return nullptr;
+ }
+ uint32_t checksum = reinterpret_cast<const art::DexFile::Header*>(map->Begin())->checksum_;
+ std::unique_ptr<const art::DexFile> dex_file(art::DexFile::Open(map->GetName(),
+ checksum,
+ std::move(map),
+ /*verify*/true,
+ /*verify_checksum*/true,
+ &error_msg));
+ if (dex_file.get() == nullptr) {
+ LOG(WARNING) << "Unable to load modified dex file for " << descriptor << ": " << error_msg;
+ art::ThrowClassFormatError(nullptr,
+ "Unable to read transformed dex file of %s because %s",
+ descriptor,
+ error_msg.c_str());
+ return nullptr;
+ }
+ if (dex_file->NumClassDefs() != 1) {
+ LOG(WARNING) << "Dex file contains more than 1 class_def. Ignoring.";
+ // TODO Throw some other sort of error here maybe?
+ art::ThrowClassFormatError(
+ nullptr,
+ "Unable to use transformed dex file of %s because it contained too many classes",
+ descriptor);
+ return nullptr;
+ }
+ return dex_file;
+}
+
struct ClassCallback : public art::ClassLoadCallback {
+ void ClassPreDefine(const char* descriptor,
+ art::Handle<art::mirror::Class> klass,
+ art::Handle<art::mirror::ClassLoader> class_loader,
+ const art::DexFile& initial_dex_file,
+ const art::DexFile::ClassDef& initial_class_def ATTRIBUTE_UNUSED,
+ /*out*/art::DexFile const** final_dex_file,
+ /*out*/art::DexFile::ClassDef const** final_class_def)
+ OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ bool is_enabled =
+ event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassFileLoadHookRetransformable) ||
+ event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassFileLoadHookNonRetransformable);
+ if (!is_enabled) {
+ return;
+ }
+ if (descriptor[0] != 'L') {
+ // It is a primitive or array. Just return
+ return;
+ }
+ std::string name(std::string(descriptor).substr(1, strlen(descriptor) - 2));
+
+ art::Thread* self = art::Thread::Current();
+ art::JNIEnvExt* env = self->GetJniEnv();
+ ScopedLocalRef<jobject> loader(
+ env, class_loader.IsNull() ? nullptr : env->AddLocalReference<jobject>(class_loader.Get()));
+ // Go back to native.
+ art::ScopedThreadSuspension sts(self, art::ThreadState::kNative);
+ // Call all Non-retransformable agents.
+ jint post_no_redefine_len = 0;
+ unsigned char* post_no_redefine_dex_data = nullptr;
+ std::unique_ptr<const unsigned char> post_no_redefine_unique_ptr(nullptr);
+ event_handler->DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetransformable>(
+ self,
+ static_cast<JNIEnv*>(env),
+ static_cast<jclass>(nullptr), // The class doesn't really exist yet so send null.
+ loader.get(),
+ name.c_str(),
+ static_cast<jobject>(nullptr), // Android doesn't seem to have protection domains
+ static_cast<jint>(initial_dex_file.Size()),
+ static_cast<const unsigned char*>(initial_dex_file.Begin()),
+ static_cast<jint*>(&post_no_redefine_len),
+ static_cast<unsigned char**>(&post_no_redefine_dex_data));
+ if (post_no_redefine_dex_data == nullptr) {
+ DCHECK_EQ(post_no_redefine_len, 0);
+ post_no_redefine_dex_data = const_cast<unsigned char*>(initial_dex_file.Begin());
+ post_no_redefine_len = initial_dex_file.Size();
+ } else {
+ post_no_redefine_unique_ptr = std::unique_ptr<const unsigned char>(post_no_redefine_dex_data);
+ DCHECK_GT(post_no_redefine_len, 0);
+ }
+ // Call all retransformable agents.
+ jint final_len = 0;
+ unsigned char* final_dex_data = nullptr;
+ std::unique_ptr<const unsigned char> final_dex_unique_ptr(nullptr);
+ event_handler->DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
+ self,
+ static_cast<JNIEnv*>(env),
+ static_cast<jclass>(nullptr), // The class doesn't really exist yet so send null.
+ loader.get(),
+ name.c_str(),
+ static_cast<jobject>(nullptr), // Android doesn't seem to have protection domains
+ static_cast<jint>(post_no_redefine_len),
+ static_cast<const unsigned char*>(post_no_redefine_dex_data),
+ static_cast<jint*>(&final_len),
+ static_cast<unsigned char**>(&final_dex_data));
+ if (final_dex_data == nullptr) {
+ DCHECK_EQ(final_len, 0);
+ final_dex_data = post_no_redefine_dex_data;
+ final_len = post_no_redefine_len;
+ } else {
+ final_dex_unique_ptr = std::unique_ptr<const unsigned char>(final_dex_data);
+ DCHECK_GT(final_len, 0);
+ }
+
+ if (final_dex_data != initial_dex_file.Begin()) {
+ LOG(WARNING) << "Changing class " << descriptor;
+ art::ScopedObjectAccess soa(self);
+ art::StackHandleScope<2> hs(self);
+ // Save the results of all the non-retransformable agents.
+ // First allocate the ClassExt
+ art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->EnsureExtDataPresent(self)));
+ // Make sure we have a ClassExt. This is fine even though we are a temporary since it will
+ // get copied.
+ if (ext.IsNull()) {
+ // We will just return failure if we fail to allocate
+ LOG(WARNING) << "Could not allocate ext-data for class '" << descriptor << "'. "
+ << "Aborting transformation since we will be unable to store it.";
+ self->AssertPendingOOMException();
+ return;
+ }
+
+ // Allocate the byte array to store the dex file bytes in.
+ art::Handle<art::mirror::ByteArray> arr(hs.NewHandle(
+ art::mirror::ByteArray::AllocateAndFill(
+ self,
+ reinterpret_cast<const signed char*>(post_no_redefine_dex_data),
+ post_no_redefine_len)));
+ if (arr.IsNull()) {
+ LOG(WARNING) << "Unable to allocate byte array for initial dex-file bytes. Aborting "
+ << "transformation";
+ self->AssertPendingOOMException();
+ return;
+ }
+
+ std::unique_ptr<const art::DexFile> dex_file(MakeSingleDexFile(self,
+ descriptor,
+ initial_dex_file.GetLocation(),
+ final_len,
+ final_dex_data));
+ if (dex_file.get() == nullptr) {
+ return;
+ }
+
+ // 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.";
+
+ 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
+ *final_class_def = &dex_file->GetClassDef(0);
+ *final_dex_file = dex_file.release();
+ }
+ }
+
void ClassLoad(art::Handle<art::mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) {
if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassLoad)) {
art::Thread* thread = art::Thread::Current();
ScopedLocalRef<jclass> jklass(thread->GetJniEnv(),
thread->GetJniEnv()->AddLocalReference<jclass>(klass.Get()));
- ScopedLocalRef<jclass> jthread(
- thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jclass>(thread->GetPeer()));
+ ScopedLocalRef<jthread> thread_jni(
+ thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jthread>(thread->GetPeer()));
{
art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
- event_handler->DispatchEvent(thread,
- ArtJvmtiEvent::kClassLoad,
- reinterpret_cast<JNIEnv*>(thread->GetJniEnv()),
- jthread.get(),
- jklass.get());
+ event_handler->DispatchEvent<ArtJvmtiEvent::kClassLoad>(
+ thread,
+ static_cast<JNIEnv*>(thread->GetJniEnv()),
+ thread_jni.get(),
+ jklass.get());
}
AddTempClass(thread, jklass.get());
}
@@ -78,14 +272,14 @@ struct ClassCallback : public art::ClassLoadCallback {
art::Thread* thread = art::Thread::Current();
ScopedLocalRef<jclass> jklass(thread->GetJniEnv(),
thread->GetJniEnv()->AddLocalReference<jclass>(klass.Get()));
- ScopedLocalRef<jclass> jthread(
- thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jclass>(thread->GetPeer()));
+ ScopedLocalRef<jthread> thread_jni(
+ thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jthread>(thread->GetPeer()));
art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
- event_handler->DispatchEvent(thread,
- ArtJvmtiEvent::kClassPrepare,
- reinterpret_cast<JNIEnv*>(thread->GetJniEnv()),
- jthread.get(),
- jklass.get());
+ event_handler->DispatchEvent<ArtJvmtiEvent::kClassPrepare>(
+ thread,
+ static_cast<JNIEnv*>(thread->GetJniEnv()),
+ thread_jni.get(),
+ jklass.get());
}
}
diff --git a/runtime/openjdkjvmti/ti_class_loader.cc b/runtime/openjdkjvmti/ti_class_loader.cc
new file mode 100644
index 0000000000..afec0bfac0
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_class_loader.cc
@@ -0,0 +1,211 @@
+/* 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::ScopedObjectAccessUnchecked soa(self);
+ art::StackHandleScope<3> hs(self);
+ if (art::ClassLinker::IsBootClassLoader(soa, loader.Get())) {
+ art::Runtime::Current()->GetClassLinker()->AppendToBootClassPath(self, *dex_file);
+ return true;
+ }
+ 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> old_cookie(hs.NewHandle(GetDexFileCookie(java_dex_file_obj)));
+ art::Handle<art::mirror::LongArray> cookie(hs.NewHandle(
+ AllocateNewDexFileCookie(self, old_cookie, 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);
+ }
+}
+
+art::ObjPtr<art::mirror::LongArray> ClassLoaderHelper::GetDexFileCookie(
+ art::Handle<art::mirror::Object> java_dex_file_obj) {
+ // 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);
+ return internal_cookie_field->GetObject(java_dex_file_obj.Get())->AsLongArray();
+}
+
+// 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::LongArray> cookie,
+ const art::DexFile* dex_file) {
+ art::StackHandleScope<1> hs(self);
+ 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));
+ // This must match the casts in runtime/native/dalvik_system_DexFile.cc:ConvertDexFilesToJavaArray
+ new_cookie->SetWithoutChecks<false>(
+ 1, static_cast<int64_t>(reinterpret_cast<uintptr_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..1ac49886cb
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_class_loader.h
@@ -0,0 +1,99 @@
+/* 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> GetDexFileCookie(
+ art::Handle<art::mirror::Object> java_dex_file) REQUIRES_SHARED(art::Locks::mutator_lock_);
+
+ static art::ObjPtr<art::mirror::LongArray> AllocateNewDexFileCookie(
+ art::Thread* self,
+ art::Handle<art::mirror::LongArray> old_dex_file_cookie,
+ 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_dump.cc b/runtime/openjdkjvmti/ti_dump.cc
index 2ee5c409f6..d9e3ef1bcf 100644
--- a/runtime/openjdkjvmti/ti_dump.cc
+++ b/runtime/openjdkjvmti/ti_dump.cc
@@ -48,7 +48,7 @@ struct DumpCallback : public art::RuntimeSigQuitCallback {
void SigQuit() OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
art::Thread* thread = art::Thread::Current();
art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
- event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kDataDumpRequest);
+ event_handler->DispatchEvent<ArtJvmtiEvent::kDataDumpRequest>(nullptr);
}
EventHandler* event_handler = nullptr;
diff --git a/runtime/openjdkjvmti/ti_heap.cc b/runtime/openjdkjvmti/ti_heap.cc
index 7b2521d63a..fe3e52b0c1 100644
--- a/runtime/openjdkjvmti/ti_heap.cc
+++ b/runtime/openjdkjvmti/ti_heap.cc
@@ -303,11 +303,11 @@ class FollowReferencesHelper FINAL {
art::Thread* thread = FindThread(info);
if (thread != nullptr) {
- art::mirror::Object* thread_obj = thread->GetPeer();
+ art::mirror::Object* thread_obj;
if (thread->IsStillStarting()) {
thread_obj = nullptr;
} else {
- thread_obj = thread->GetPeer();
+ thread_obj = thread->GetPeerFromOtherThread();
}
if (thread_obj != nullptr) {
ref_info->jni_local.thread_tag = tag_table_->GetTagOrZero(thread_obj);
@@ -333,11 +333,11 @@ class FollowReferencesHelper FINAL {
art::Thread* thread = FindThread(info);
if (thread != nullptr) {
- art::mirror::Object* thread_obj = thread->GetPeer();
+ art::mirror::Object* thread_obj;
if (thread->IsStillStarting()) {
thread_obj = nullptr;
} else {
- thread_obj = thread->GetPeer();
+ thread_obj = thread->GetPeerFromOtherThread();
}
if (thread_obj != nullptr) {
ref_info->stack_local.thread_tag = tag_table_->GetTagOrZero(thread_obj);
diff --git a/runtime/openjdkjvmti/ti_monitor.cc b/runtime/openjdkjvmti/ti_monitor.cc
index b82768397b..645faea41b 100644
--- a/runtime/openjdkjvmti/ti_monitor.cc
+++ b/runtime/openjdkjvmti/ti_monitor.cc
@@ -54,7 +54,7 @@ class JvmtiMonitor {
JvmtiMonitor() : owner_(nullptr), count_(0) {
}
- static bool Destroy(art::Thread* self, JvmtiMonitor* monitor) {
+ static bool Destroy(art::Thread* self, JvmtiMonitor* monitor) NO_THREAD_SAFETY_ANALYSIS {
// Check whether this thread holds the monitor, or nobody does.
art::Thread* owner_thread = monitor->owner_.load(std::memory_order_relaxed);
if (owner_thread != nullptr && self != owner_thread) {
@@ -71,7 +71,7 @@ class JvmtiMonitor {
return true;
}
- void MonitorEnter(art::Thread* self) {
+ void MonitorEnter(art::Thread* self) NO_THREAD_SAFETY_ANALYSIS {
// Check for recursive enter.
if (IsOwner(self)) {
count_++;
@@ -86,7 +86,7 @@ class JvmtiMonitor {
count_ = 1;
}
- bool MonitorExit(art::Thread* self) {
+ bool MonitorExit(art::Thread* self) NO_THREAD_SAFETY_ANALYSIS {
if (!IsOwner(self)) {
return false;
}
diff --git a/runtime/openjdkjvmti/ti_phase.cc b/runtime/openjdkjvmti/ti_phase.cc
index 154406a5db..60371cfafe 100644
--- a/runtime/openjdkjvmti/ti_phase.cc
+++ b/runtime/openjdkjvmti/ti_phase.cc
@@ -64,7 +64,7 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback {
case RuntimePhase::kStart:
{
art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative);
- event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kVmStart, GetJniEnv());
+ event_handler->DispatchEvent<ArtJvmtiEvent::kVmStart>(nullptr, GetJniEnv());
PhaseUtil::current_phase_ = JVMTI_PHASE_START;
}
break;
@@ -72,17 +72,14 @@ struct PhaseUtil::PhaseCallback : public art::RuntimePhaseCallback {
{
ScopedLocalRef<jthread> thread(GetJniEnv(), GetCurrentJThread());
art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative);
- event_handler->DispatchEvent(nullptr,
- ArtJvmtiEvent::kVmInit,
- GetJniEnv(),
- thread.get());
+ event_handler->DispatchEvent<ArtJvmtiEvent::kVmInit>(nullptr, GetJniEnv(), thread.get());
PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE;
}
break;
case RuntimePhase::kDeath:
{
art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative);
- event_handler->DispatchEvent(nullptr, ArtJvmtiEvent::kVmDeath, GetJniEnv());
+ event_handler->DispatchEvent<ArtJvmtiEvent::kVmDeath>(nullptr, GetJniEnv());
PhaseUtil::current_phase_ = JVMTI_PHASE_DEAD;
}
// TODO: Block events now.
@@ -139,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 34efc502e1..058b93a042 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -48,12 +48,13 @@
#include "jit/jit_code_cache.h"
#include "jni_env_ext-inl.h"
#include "jvmti_allocator.h"
-#include "mirror/class.h"
+#include "mirror/class-inl.h"
#include "mirror/class_ext.h"
#include "mirror/object.h"
#include "object_lock.h"
#include "runtime.h"
#include "ScopedLocalRef.h"
+#include "ti_class_loader.h"
#include "transform.h"
namespace openjdkjvmti {
@@ -74,9 +75,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor {
StackVisitor::StackWalkKind::kIncludeInlinedFrames),
allocator_(allocator),
obsoleted_methods_(obsoleted_methods),
- obsolete_maps_(obsolete_maps),
- is_runtime_frame_(false) {
- }
+ obsolete_maps_(obsolete_maps) { }
~ObsoleteMethodStackVisitor() OVERRIDE {}
@@ -99,21 +98,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor {
bool VisitFrame() OVERRIDE REQUIRES(art::Locks::mutator_lock_) {
art::ArtMethod* old_method = GetMethod();
- // TODO REMOVE once either current_method doesn't stick around through suspend points or deopt
- // works through runtime methods.
- bool prev_was_runtime_frame_ = is_runtime_frame_;
- is_runtime_frame_ = old_method->IsRuntimeMethod();
if (obsoleted_methods_.find(old_method) != obsoleted_methods_.end()) {
- // The check below works since when we deoptimize we set shadow frames for all frames until a
- // native/runtime transition and for those set the return PC to a function that will complete
- // the deoptimization. This does leave us with the unfortunate side-effect that frames just
- // below runtime frames cannot be deoptimized at the moment.
- // TODO REMOVE once either current_method doesn't stick around through suspend points or deopt
- // works through runtime methods.
- // TODO b/33616143
- if (!IsShadowFrame() && prev_was_runtime_frame_) {
- LOG(FATAL) << "Deoptimization failed due to runtime method in stack. See b/33616143";
- }
// We cannot ensure that the right dex file is used in inlined frames so we don't support
// redefining them.
DCHECK(!IsInInlinedFrame()) << "Inlined frames are not supported when using redefinition";
@@ -136,6 +121,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor {
new_obsolete_method->CopyFrom(old_method, ptr_size);
DCHECK_EQ(new_obsolete_method->GetDeclaringClass(), old_method->GetDeclaringClass());
new_obsolete_method->SetIsObsolete();
+ new_obsolete_method->SetDontCompile();
obsolete_maps_->insert({old_method, new_obsolete_method});
// Update JIT Data structures to point to the new method.
art::jit::Jit* jit = art::Runtime::Current()->GetJit();
@@ -162,9 +148,6 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor {
// values in this map must be added to the obsolete_methods_ (and obsolete_dex_caches_) fields of
// the redefined classes ClassExt by the caller.
std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps_;
- // TODO REMOVE once either current_method doesn't stick around through suspend points or deopt
- // works through runtime methods.
- bool is_runtime_frame_;
};
jvmtiError Redefiner::IsModifiableClass(jvmtiEnv* env ATTRIBUTE_UNUSED,
@@ -386,85 +369,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 +382,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) {
@@ -520,28 +391,13 @@ void Redefiner::RecordFailure(jvmtiError result,
result_ = result;
}
-// Allocates a ByteArray big enough to store the given number of bytes and copies them from the
-// bytes pointer.
-static art::mirror::ByteArray* AllocateAndFillBytes(art::Thread* self,
- const uint8_t* bytes,
- int32_t num_bytes)
- REQUIRES_SHARED(art::Locks::mutator_lock_) {
- art::StackHandleScope<1> hs(self);
- art::Handle<art::mirror::ByteArray> arr(hs.NewHandle(
- art::mirror::ByteArray::Alloc(self, num_bytes)));
- if (!arr.IsNull()) {
- // Copy it in. Just skip if it's null
- memcpy(arr->GetData(), bytes, num_bytes);
- }
- return arr.Get();
-}
-
art::mirror::ByteArray* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFileBytes() {
// If we have been specifically given a new set of bytes use that
if (original_dex_file_.size() != 0) {
- return AllocateAndFillBytes(driver_->self_,
- &original_dex_file_.At(0),
- original_dex_file_.size());
+ return art::mirror::ByteArray::AllocateAndFill(
+ driver_->self_,
+ reinterpret_cast<const signed char*>(&original_dex_file_.At(0)),
+ original_dex_file_.size());
}
// See if we already have one set.
@@ -561,7 +417,10 @@ art::mirror::ByteArray* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFi
LOG(WARNING) << "Current dex file has more than one class in it. Calling RetransformClasses "
<< "on this class might fail if no transformations are applied to it!";
}
- return AllocateAndFillBytes(driver_->self_, current_dex_file.Begin(), current_dex_file.Size());
+ return art::mirror::ByteArray::AllocateAndFill(
+ driver_->self_,
+ reinterpret_cast<const signed char*>(current_dex_file.Begin()),
+ current_dex_file.Size());
}
struct CallbackCtx {
@@ -587,7 +446,8 @@ void Redefiner::ClassRedefinition::FindAndAllocateObsoleteMethods(art::mirror::C
art::ScopedAssertNoThreadSuspension ns("No thread suspension during thread stack walking");
art::mirror::ClassExt* ext = art_klass->GetExtData();
CHECK(ext->GetObsoleteMethods() != nullptr);
- CallbackCtx ctx(art_klass->GetClassLoader()->GetAllocator());
+ art::ClassLinker* linker = driver_->runtime_->GetClassLinker();
+ CallbackCtx ctx(linker->GetAllocatorForClassLoader(art_klass->GetClassLoader()));
// Add all the declared methods to the map
for (auto& m : art_klass->GetDeclaredMethods(art::kRuntimePointerSize)) {
ctx.obsolete_methods.insert(&m);
@@ -631,16 +491,141 @@ void Redefiner::ClassRedefinition::FillObsoleteMethodMap(
}
}
-// TODO It should be possible to only deoptimize the specific obsolete methods.
-// TODO ReJitEverything can (sort of) fail. In certain cases it will skip deoptimizing some frames.
-// If one of these frames is an obsolete method we have a problem. b/33616143
-// TODO This shouldn't be necessary once we can ensure that the current method is not kept in
-// registers across suspend points.
-// TODO Pending b/33630159
-void Redefiner::EnsureObsoleteMethodsAreDeoptimized() {
- art::ScopedAssertNoThreadSuspension nts("Deoptimizing everything!");
- art::instrumentation::Instrumentation* i = runtime_->GetInstrumentation();
- i->ReJitEverything("libOpenJkdJvmti - Class Redefinition");
+// Try and get the declared method. First try to get a virtual method then a direct method if that's
+// not found.
+static art::ArtMethod* FindMethod(art::Handle<art::mirror::Class> klass,
+ const char* name,
+ art::Signature sig) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::ArtMethod* m = klass->FindDeclaredVirtualMethod(name, sig, art::kRuntimePointerSize);
+ if (m == nullptr) {
+ m = klass->FindDeclaredDirectMethod(name, sig, art::kRuntimePointerSize);
+ }
+ return m;
+}
+
+bool Redefiner::ClassRedefinition::CheckSameMethods() {
+ art::StackHandleScope<1> hs(driver_->self_);
+ art::Handle<art::mirror::Class> h_klass(hs.NewHandle(GetMirrorClass()));
+ DCHECK_EQ(dex_file_->NumClassDefs(), 1u);
+
+ art::ClassDataItemIterator new_iter(*dex_file_,
+ dex_file_->GetClassData(dex_file_->GetClassDef(0)));
+
+ // Make sure we have the same number of methods.
+ uint32_t num_new_method = new_iter.NumVirtualMethods() + new_iter.NumDirectMethods();
+ uint32_t num_old_method = h_klass->GetDeclaredMethodsSlice(art::kRuntimePointerSize).size();
+ if (num_new_method != num_old_method) {
+ bool bigger = num_new_method > num_old_method;
+ RecordFailure(bigger ? ERR(UNSUPPORTED_REDEFINITION_METHOD_ADDED)
+ : ERR(UNSUPPORTED_REDEFINITION_METHOD_DELETED),
+ StringPrintf("Total number of declared methods changed from %d to %d",
+ num_old_method, num_new_method));
+ return false;
+ }
+
+ // Skip all of the fields. We should have already checked this.
+ while (new_iter.HasNextStaticField() || new_iter.HasNextInstanceField()) {
+ new_iter.Next();
+ }
+ // Check each of the methods. NB we don't need to specifically check for removals since the 2 dex
+ // files have the same number of methods, which means there must be an equal amount of additions
+ // and removals.
+ for (; new_iter.HasNextVirtualMethod() || new_iter.HasNextDirectMethod(); new_iter.Next()) {
+ // Get the data on the method we are searching for
+ const art::DexFile::MethodId& new_method_id = dex_file_->GetMethodId(new_iter.GetMemberIndex());
+ const char* new_method_name = dex_file_->GetMethodName(new_method_id);
+ art::Signature new_method_signature = dex_file_->GetMethodSignature(new_method_id);
+ art::ArtMethod* old_method = FindMethod(h_klass, new_method_name, new_method_signature);
+ // If we got past the check for the same number of methods above that means there must be at
+ // least one added and one removed method. We will return the ADDED failure message since it is
+ // easier to get a useful error report for it.
+ if (old_method == nullptr) {
+ RecordFailure(ERR(UNSUPPORTED_REDEFINITION_METHOD_ADDED),
+ StringPrintf("Unknown method '%s' (sig: %s) was added!",
+ new_method_name,
+ new_method_signature.ToString().c_str()));
+ return false;
+ }
+ // Since direct methods have different flags than virtual ones (specifically direct methods must
+ // have kAccPrivate or kAccStatic or kAccConstructor flags) we can tell if a method changes from
+ // virtual to direct.
+ uint32_t new_flags = new_iter.GetMethodAccessFlags();
+ if (new_flags != (old_method->GetAccessFlags() & art::kAccValidMethodFlags)) {
+ RecordFailure(ERR(UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED),
+ StringPrintf("method '%s' (sig: %s) had different access flags",
+ new_method_name,
+ new_method_signature.ToString().c_str()));
+ return false;
+ }
+ }
+ return true;
+}
+
+bool Redefiner::ClassRedefinition::CheckSameFields() {
+ art::StackHandleScope<1> hs(driver_->self_);
+ art::Handle<art::mirror::Class> h_klass(hs.NewHandle(GetMirrorClass()));
+ DCHECK_EQ(dex_file_->NumClassDefs(), 1u);
+ art::ClassDataItemIterator new_iter(*dex_file_,
+ dex_file_->GetClassData(dex_file_->GetClassDef(0)));
+ const art::DexFile& old_dex_file = h_klass->GetDexFile();
+ art::ClassDataItemIterator old_iter(old_dex_file,
+ old_dex_file.GetClassData(*h_klass->GetClassDef()));
+ // Instance and static fields can be differentiated by their flags so no need to check them
+ // separately.
+ while (new_iter.HasNextInstanceField() || new_iter.HasNextStaticField()) {
+ // Get the data on the method we are searching for
+ const art::DexFile::FieldId& new_field_id = dex_file_->GetFieldId(new_iter.GetMemberIndex());
+ const char* new_field_name = dex_file_->GetFieldName(new_field_id);
+ const char* new_field_type = dex_file_->GetFieldTypeDescriptor(new_field_id);
+
+ if (!(old_iter.HasNextInstanceField() || old_iter.HasNextStaticField())) {
+ // We are missing the old version of this method!
+ RecordFailure(ERR(UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED),
+ StringPrintf("Unknown field '%s' (type: %s) added!",
+ new_field_name,
+ new_field_type));
+ return false;
+ }
+
+ const art::DexFile::FieldId& old_field_id = old_dex_file.GetFieldId(old_iter.GetMemberIndex());
+ const char* old_field_name = old_dex_file.GetFieldName(old_field_id);
+ const char* old_field_type = old_dex_file.GetFieldTypeDescriptor(old_field_id);
+
+ // Check name and type.
+ if (strcmp(old_field_name, new_field_name) != 0 ||
+ strcmp(old_field_type, new_field_type) != 0) {
+ RecordFailure(ERR(UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED),
+ StringPrintf("Field changed from '%s' (sig: %s) to '%s' (sig: %s)!",
+ old_field_name,
+ old_field_type,
+ new_field_name,
+ new_field_type));
+ return false;
+ }
+
+ // Since static fields have different flags than instance ones (specifically static fields must
+ // have the kAccStatic flag) we can tell if a field changes from static to instance.
+ if (new_iter.GetFieldAccessFlags() != old_iter.GetFieldAccessFlags()) {
+ RecordFailure(ERR(UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED),
+ StringPrintf("Field '%s' (sig: %s) had different access flags",
+ new_field_name,
+ new_field_type));
+ return false;
+ }
+
+ new_iter.Next();
+ old_iter.Next();
+ }
+ if (old_iter.HasNextInstanceField() || old_iter.HasNextStaticField()) {
+ RecordFailure(ERR(UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED),
+ StringPrintf("field '%s' (sig: %s) is missing!",
+ old_dex_file.GetFieldName(old_dex_file.GetFieldId(
+ old_iter.GetMemberIndex())),
+ old_dex_file.GetFieldTypeDescriptor(old_dex_file.GetFieldId(
+ old_iter.GetMemberIndex()))));
+ return false;
+ }
+ return true;
}
bool Redefiner::ClassRedefinition::CheckClass() {
@@ -854,31 +839,85 @@ class RedefinitionDataHolder {
DISALLOW_COPY_AND_ASSIGN(RedefinitionDataHolder);
};
+// Looks through the previously allocated cookies to see if we need to update them with another new
+// dexfile. This is so that even if multiple classes with the same classloader are redefined at
+// once they are all added to the classloader.
+bool Redefiner::ClassRedefinition::AllocateAndRememberNewDexFileCookie(
+ int32_t klass_index,
+ art::Handle<art::mirror::ClassLoader> source_class_loader,
+ art::Handle<art::mirror::Object> dex_file_obj,
+ /*out*/RedefinitionDataHolder* holder) {
+ art::StackHandleScope<2> hs(driver_->self_);
+ art::MutableHandle<art::mirror::LongArray> old_cookie(
+ hs.NewHandle<art::mirror::LongArray>(nullptr));
+ bool has_older_cookie = false;
+ // See if we already have a cookie that a previous redefinition got from the same classloader.
+ for (int32_t i = 0; i < klass_index; i++) {
+ if (holder->GetSourceClassLoader(i) == source_class_loader.Get()) {
+ // Since every instance of this classloader should have the same cookie associated with it we
+ // can stop looking here.
+ has_older_cookie = true;
+ old_cookie.Assign(holder->GetNewDexFileCookie(i));
+ break;
+ }
+ }
+ if (old_cookie.IsNull()) {
+ // No older cookie. Get it directly from the dex_file_obj
+ // We should not have seen this classloader elsewhere.
+ CHECK(!has_older_cookie);
+ old_cookie.Assign(ClassLoaderHelper::GetDexFileCookie(dex_file_obj));
+ }
+ // Use the old cookie to generate the new one with the new DexFile* added in.
+ art::Handle<art::mirror::LongArray>
+ new_cookie(hs.NewHandle(ClassLoaderHelper::AllocateNewDexFileCookie(driver_->self_,
+ old_cookie,
+ dex_file_.get())));
+ // Make sure the allocation worked.
+ if (new_cookie.IsNull()) {
+ return false;
+ }
+
+ // Save the cookie.
+ holder->SetNewDexFileCookie(klass_index, new_cookie.Get());
+ // If there are other copies of this same classloader we need to make sure that we all have the
+ // same cookie.
+ if (has_older_cookie) {
+ for (int32_t i = 0; i < klass_index; i++) {
+ // We will let the GC take care of the cookie we allocated for this one.
+ if (holder->GetSourceClassLoader(i) == source_class_loader.Get()) {
+ holder->SetNewDexFileCookie(i, new_cookie.Get());
+ }
+ }
+ }
+
+ return true;
+}
+
bool Redefiner::ClassRedefinition::FinishRemainingAllocations(
int32_t klass_index, /*out*/RedefinitionDataHolder* holder) {
+ art::ScopedObjectAccessUnchecked soa(driver_->self_);
art::StackHandleScope<2> hs(driver_->self_);
holder->SetMirrorClass(klass_index, GetMirrorClass());
// This shouldn't allocate
art::Handle<art::mirror::ClassLoader> loader(hs.NewHandle(GetClassLoader()));
- holder->SetSourceClassLoader(klass_index, loader.Get());
- if (loader.Get() == nullptr) {
- // TODO Better error msg.
- RecordFailure(ERR(INTERNAL), "Unable to find class loader!");
- return false;
- }
- art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle(FindSourceDexFileObject(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));
- if (holder->GetNewDexFileCookie(klass_index) == nullptr) {
- driver_->self_->AssertPendingOOMException();
- driver_->self_->ClearException();
- RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate dex file array for class loader");
- return false;
+ // The bootclasspath is handled specially so it doesn't have a j.l.DexFile.
+ if (!art::ClassLinker::IsBootClassLoader(soa, loader.Get())) {
+ holder->SetSourceClassLoader(klass_index, loader.Get());
+ 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 dex file!");
+ return false;
+ }
+ // Allocate the new dex file cookie.
+ if (!AllocateAndRememberNewDexFileCookie(klass_index, loader, dex_file_obj, holder)) {
+ driver_->self_->AssertPendingOOMException();
+ driver_->self_->ClearException();
+ RecordFailure(ERR(OUT_OF_MEMORY), "Unable to allocate dex file array for class loader");
+ return false;
+ }
}
holder->SetNewDexCache(klass_index, CreateNewDexCache(loader));
if (holder->GetNewDexCache(klass_index) == nullptr) {
@@ -965,6 +1004,13 @@ jvmtiError Redefiner::Run() {
// cleaned up by the GC eventually.
return result_;
}
+ int32_t counter = 0;
+ for (Redefiner::ClassRedefinition& redef : redefinitions_) {
+ if (holder.GetSourceClassLoader(counter) == nullptr) {
+ runtime_->GetClassLinker()->AppendToBootClassPath(self_, redef.GetDexFile());
+ }
+ counter++;
+ }
// Disable GC and wait for it to be done if we are a moving GC. This is fine since we are done
// allocating so no deadlocks.
art::gc::Heap* heap = runtime_->GetHeap();
@@ -983,24 +1029,20 @@ jvmtiError Redefiner::Run() {
// TODO We need to update all debugger MethodIDs so they note the method they point to is
// obsolete or implement some other well defined semantics.
// TODO We need to decide on & implement semantics for JNI jmethodids when we redefine methods.
- int32_t cnt = 0;
+ counter = 0;
for (Redefiner::ClassRedefinition& redef : redefinitions_) {
- art::mirror::Class* klass = holder.GetMirrorClass(cnt);
- redef.UpdateJavaDexFile(holder.GetJavaDexFile(cnt), holder.GetNewDexFileCookie(cnt));
+ art::ScopedAssertNoThreadSuspension nts("Updating runtime objects for redefinition");
+ if (holder.GetSourceClassLoader(counter) != nullptr) {
+ ClassLoaderHelper::UpdateJavaDexFile(holder.GetJavaDexFile(counter),
+ holder.GetNewDexFileCookie(counter));
+ }
+ art::mirror::Class* klass = holder.GetMirrorClass(counter);
// 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));
- cnt++;
+ redef.UpdateClass(klass, holder.GetNewDexCache(counter),
+ holder.GetOriginalDexFileBytes(counter));
+ counter++;
}
- // Ensure that obsolete methods are deoptimized. This is needed since optimized methods may have
- // pointers to their ArtMethod's stashed in registers that they then use to attempt to hit the
- // DexCache. (b/33630159)
- // TODO This can fail (leave some methods optimized) near runtime methods (including
- // quick-to-interpreter transition function).
- // TODO We probably don't need this at all once we have a way to ensure that the
- // current_art_method is never stashed in a (physical) register by the JIT and lost to the
- // stack-walker.
- EnsureObsoleteMethodsAreDeoptimized();
// TODO Verify the new Class.
// TODO Shrink the obsolete method maps if possible?
// TODO find appropriate class loader.
@@ -1102,24 +1144,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 29a7e1f3ac..3209abbe64 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -96,6 +96,11 @@ class Redefiner {
static jvmtiError IsModifiableClass(jvmtiEnv* env, jclass klass, jboolean* is_redefinable);
+ static std::unique_ptr<art::MemMap> MoveDataToMemMap(const std::string& original_location,
+ jint data_len,
+ const unsigned char* dex_data,
+ std::string* error_msg);
+
private:
class ClassRedefinition {
public:
@@ -122,18 +127,13 @@ 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_);
+ const art::DexFile& GetDexFile() {
+ return *dex_file_;
+ }
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_);
@@ -145,6 +145,13 @@ class Redefiner {
bool FinishRemainingAllocations(int32_t klass_index, /*out*/RedefinitionDataHolder* holder)
REQUIRES_SHARED(art::Locks::mutator_lock_);
+ bool AllocateAndRememberNewDexFileCookie(
+ int32_t klass_index,
+ art::Handle<art::mirror::ClassLoader> source_class_loader,
+ art::Handle<art::mirror::Object> dex_file_obj,
+ /*out*/RedefinitionDataHolder* holder)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+
void FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass)
REQUIRES(art::Locks::mutator_lock_);
@@ -170,17 +177,11 @@ class Redefiner {
// 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 add/remove methods, or change their modifiers or types.
+ bool CheckSameMethods() REQUIRES_SHARED(art::Locks::mutator_lock_);
- // 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 does not modify fields types or modifiers.
+ bool CheckSameFields() REQUIRES_SHARED(art::Locks::mutator_lock_);
void UpdateJavaDexFile(art::ObjPtr<art::mirror::Object> java_dex_file,
art::ObjPtr<art::mirror::LongArray> new_cookie)
@@ -234,11 +235,6 @@ class Redefiner {
/*out*/std::string* error_msg)
REQUIRES_SHARED(art::Locks::mutator_lock_);
- static std::unique_ptr<art::MemMap> MoveDataToMemMap(const std::string& original_location,
- jint data_len,
- const unsigned char* dex_data,
- std::string* error_msg);
-
// TODO Put on all the lock qualifiers.
jvmtiError Run() REQUIRES_SHARED(art::Locks::mutator_lock_);
@@ -248,14 +244,6 @@ class Redefiner {
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
- // DexCache.
- void EnsureObsoleteMethodsAreDeoptimized()
- REQUIRES(art::Locks::mutator_lock_)
- REQUIRES(!art::Locks::thread_list_lock_,
- !art::Locks::classlinker_classes_lock_);
-
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);
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);
diff --git a/runtime/openjdkjvmti/ti_stack.cc b/runtime/openjdkjvmti/ti_stack.cc
index 4cf55a6a98..b5a6c6e1ee 100644
--- a/runtime/openjdkjvmti/ti_stack.cc
+++ b/runtime/openjdkjvmti/ti_stack.cc
@@ -377,7 +377,8 @@ jvmtiError StackUtil::GetAllStackTraces(jvmtiEnv* env,
jvmtiStackInfo& old_stack_info = stack_info_array.get()[i];
jvmtiStackInfo& new_stack_info = stack_info[i];
- jthread thread_peer = current->GetJniEnv()->AddLocalReference<jthread>(threads[i]->GetPeer());
+ jthread thread_peer = current->GetJniEnv()->AddLocalReference<jthread>(
+ threads[i]->GetPeerFromOtherThread());
new_stack_info.thread = thread_peer;
if (old_stack_info.frame_count > 0) {
@@ -453,7 +454,7 @@ jvmtiError StackUtil::GetThreadListStackTraces(jvmtiEnv* env,
}
// Get the peer, and check whether we know it.
- art::ObjPtr<art::mirror::Object> peer = thread->GetPeer();
+ art::ObjPtr<art::mirror::Object> peer = thread->GetPeerFromOtherThread();
for (size_t index = 0; index != handles.size(); ++index) {
if (peer == handles[index].Get()) {
// Found the thread.
diff --git a/runtime/openjdkjvmti/ti_thread.cc b/runtime/openjdkjvmti/ti_thread.cc
index 9f81d6ba97..00d4144415 100644
--- a/runtime/openjdkjvmti/ti_thread.cc
+++ b/runtime/openjdkjvmti/ti_thread.cc
@@ -61,11 +61,14 @@ struct ThreadCallback : public art::ThreadLifecycleCallback, public art::Runtime
}
return self->GetJniEnv()->AddLocalReference<jthread>(self->GetPeer());
}
- void Post(art::Thread* self, ArtJvmtiEvent type) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ template <ArtJvmtiEvent kEvent>
+ void Post(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_) {
DCHECK_EQ(self, art::Thread::Current());
ScopedLocalRef<jthread> thread(self->GetJniEnv(), GetThreadObject(self));
art::ScopedThreadSuspension sts(self, art::ThreadState::kNative);
- event_handler->DispatchEvent(self, type, self->GetJniEnv(), thread.get());
+ event_handler->DispatchEvent<kEvent>(self,
+ reinterpret_cast<JNIEnv*>(self->GetJniEnv()),
+ thread.get());
}
void ThreadStart(art::Thread* self) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
@@ -81,11 +84,11 @@ struct ThreadCallback : public art::ThreadLifecycleCallback, public art::Runtime
}
return;
}
- Post(self, ArtJvmtiEvent::kThreadStart);
+ Post<ArtJvmtiEvent::kThreadStart>(self);
}
void ThreadDeath(art::Thread* self) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
- Post(self, ArtJvmtiEvent::kThreadEnd);
+ Post<ArtJvmtiEvent::kThreadEnd>(self);
}
void NextRuntimePhase(RuntimePhase phase) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
@@ -93,7 +96,7 @@ struct ThreadCallback : public art::ThreadLifecycleCallback, public art::Runtime
// We moved to VMInit. Report the main thread as started (it was attached early, and must
// not be reported until Init.
started = true;
- Post(art::Thread::Current(), ArtJvmtiEvent::kThreadStart);
+ Post<ArtJvmtiEvent::kThreadStart>(art::Thread::Current());
}
}
@@ -197,7 +200,7 @@ jvmtiError ThreadUtil::GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadI
info_ptr->is_daemon = self->IsDaemon();
- art::ObjPtr<art::mirror::Object> peer = self->GetPeer();
+ art::ObjPtr<art::mirror::Object> peer = self->GetPeerFromOtherThread();
// ThreadGroup.
if (peer != nullptr) {
@@ -455,7 +458,7 @@ jvmtiError ThreadUtil::GetAllThreads(jvmtiEnv* env,
continue;
}
- art::ObjPtr<art::mirror::Object> peer = thread->GetPeer();
+ art::ObjPtr<art::mirror::Object> peer = thread->GetPeerFromOtherThread();
if (peer != nullptr) {
peers.push_back(peer);
}
diff --git a/runtime/openjdkjvmti/ti_threadgroup.cc b/runtime/openjdkjvmti/ti_threadgroup.cc
index 35b1bfd920..e63ce6576a 100644
--- a/runtime/openjdkjvmti/ti_threadgroup.cc
+++ b/runtime/openjdkjvmti/ti_threadgroup.cc
@@ -174,7 +174,7 @@ static void GetThreads(art::Handle<art::mirror::Object> thread_group,
if (t->IsStillStarting()) {
continue;
}
- art::ObjPtr<art::mirror::Object> peer = t->GetPeer();
+ art::ObjPtr<art::mirror::Object> peer = t->GetPeerFromOtherThread();
if (peer == nullptr) {
continue;
}
diff --git a/runtime/openjdkjvmti/transform.cc b/runtime/openjdkjvmti/transform.cc
index af4fb7187a..2fec631c00 100644
--- a/runtime/openjdkjvmti/transform.cc
+++ b/runtime/openjdkjvmti/transform.cc
@@ -68,19 +68,17 @@ jvmtiError Transformer::RetransformClassesDirect(
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));
+ gEventHandler.DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
+ self,
+ GetJniEnv(env),
+ def.klass,
+ def.loader,
+ def.name.c_str(),
+ def.protection_domain,
+ def.dex_len,
+ static_cast<const unsigned char*>(def.dex_data.get()),
+ &new_len,
+ &new_data);
def.SetNewDexData(env, new_len, new_data);
}
return OK;
@@ -139,20 +137,6 @@ jvmtiError GetClassLocation(ArtJvmTiEnv* env, jclass klass, /*out*/std::string*
return OK;
}
-static jvmtiError CopyDataIntoJvmtiBuffer(ArtJvmTiEnv* env,
- const unsigned char* source,
- jint len,
- /*out*/unsigned char** dest) {
- jvmtiError res = env->Allocate(len, dest);
- if (res != OK) {
- return res;
- }
- memcpy(reinterpret_cast<void*>(*dest),
- reinterpret_cast<const void*>(source),
- len);
- return OK;
-}
-
jvmtiError Transformer::GetDexDataForRetransformation(ArtJvmTiEnv* env,
art::Handle<art::mirror::Class> klass,
/*out*/jint* dex_data_len,
@@ -195,7 +179,9 @@ jvmtiError Transformer::FillInTransformationData(ArtJvmTiEnv* env,
}
def->klass = klass;
def->loader = soa.AddLocalReference<jobject>(hs_klass->GetClassLoader());
- def->name = art::mirror::Class::ComputeName(hs_klass)->ToModifiedUtf8();
+ std::string descriptor_store;
+ std::string descriptor(hs_klass->GetDescriptor(&descriptor_store));
+ def->name = descriptor.substr(1, descriptor.size() - 2);
// TODO is this always null?
def->protection_domain = nullptr;
if (def->dex_data.get() == nullptr) {