diff options
| author | 2018-01-12 22:28:24 +0000 | |
|---|---|---|
| committer | 2018-01-12 22:28:24 +0000 | |
| commit | 017adc29ab0384fa042fdea21cdbf6cfaf4019f2 (patch) | |
| tree | 81ec402eb034df9b6a72f7a6a7946e635322c948 | |
| parent | 5a2492b8c0b8903ac4fe5bbb518824a91de4da47 (diff) | |
| parent | 94730ef9ca432b5ede81e928cffc4006911aa650 (diff) | |
Merge changes from topic "userdebug-jdwp"
* changes:
Ensure that methods requiring interpreter entrypoint always have it.
Support using adbconnection and openjdkjvmti without JAVA_DEBUGGABLE
| -rw-r--r-- | adbconnection/adbconnection.cc | 9 | ||||
| -rw-r--r-- | openjdkjvmti/OpenjdkJvmTi.cc | 179 | ||||
| -rw-r--r-- | openjdkjvmti/art_jvmti.h | 68 | ||||
| -rw-r--r-- | openjdkjvmti/deopt_manager.cc | 31 | ||||
| -rw-r--r-- | openjdkjvmti/deopt_manager.h | 7 | ||||
| -rw-r--r-- | runtime/art_method.cc | 15 | ||||
| -rw-r--r-- | runtime/class_linker.cc | 54 | ||||
| -rw-r--r-- | runtime/class_linker.h | 5 | ||||
| -rw-r--r-- | runtime/entrypoints/quick/quick_trampoline_entrypoints.cc | 6 | ||||
| -rw-r--r-- | runtime/instrumentation.cc | 8 | ||||
| -rw-r--r-- | runtime/oat_file_manager.cc | 11 | ||||
| -rw-r--r-- | runtime/runtime.cc | 14 | ||||
| -rw-r--r-- | runtime/runtime.h | 5 | ||||
| -rwxr-xr-x | test/etc/run-test-jar | 5 |
14 files changed, 276 insertions, 141 deletions
diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc index a5c885a933..80cfc83d3c 100644 --- a/adbconnection/adbconnection.cc +++ b/adbconnection/adbconnection.cc @@ -63,9 +63,7 @@ static constexpr int kControlSockSendTimeout = 10; static AdbConnectionState* gState; static bool IsDebuggingPossible() { - // TODO We need to do this on IsJdwpAllowed not IsDebuggable in order to support userdebug - // workloads. For now we will only allow it when we are debuggable so that testing is easier. - return art::Runtime::Current()->IsJavaDebuggable() && art::Dbg::IsJdwpAllowed(); + return art::Dbg::IsJdwpAllowed(); } // Begin running the debugger. @@ -581,11 +579,14 @@ void AdbConnectionState::RunPollLoop(art::Thread* self) { DCHECK(!agent_has_socket_); if (!agent_loaded_) { DCHECK(!agent_listening_); + // TODO Should we check in some other way if we are userdebug/eng? + CHECK(art::Dbg::IsJdwpAllowed()); // Load the agent now! self->AssertNoPendingException(); art::Runtime::Current()->AttachAgent(/* JNIEnv* */ nullptr, MakeAgentArg(), - /* classloader */ nullptr); + /* classloader */ nullptr, + /*allow_non_debuggable_tooling*/ true); if (self->IsExceptionPending()) { LOG(ERROR) << "Failed to load agent " << agent_name_; art::ScopedObjectAccess soa(self); diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc index aae805569f..027635bbb5 100644 --- a/openjdkjvmti/OpenjdkJvmTi.cc +++ b/openjdkjvmti/OpenjdkJvmTi.cc @@ -83,6 +83,12 @@ DeoptManager gDeoptManager; } \ } while (false) +// Returns whether we are able to use all jvmti features. +static bool IsFullJvmtiAvailable() { + art::Runtime* runtime = art::Runtime::Current(); + return runtime->GetInstrumentation()->IsForcedInterpretOnly() || runtime->IsJavaDebuggable(); +} + class JvmtiFunctions { private: static jvmtiError getEnvironmentError(jvmtiEnv* env) { @@ -1092,10 +1098,64 @@ class JvmtiFunctions { &gEventHandler); } +#define FOR_ALL_CAPABILITIES(FUN) \ + FUN(can_tag_objects) \ + FUN(can_generate_field_modification_events) \ + FUN(can_generate_field_access_events) \ + FUN(can_get_bytecodes) \ + FUN(can_get_synthetic_attribute) \ + FUN(can_get_owned_monitor_info) \ + FUN(can_get_current_contended_monitor) \ + FUN(can_get_monitor_info) \ + FUN(can_pop_frame) \ + FUN(can_redefine_classes) \ + FUN(can_signal_thread) \ + FUN(can_get_source_file_name) \ + FUN(can_get_line_numbers) \ + FUN(can_get_source_debug_extension) \ + FUN(can_access_local_variables) \ + FUN(can_maintain_original_method_order) \ + FUN(can_generate_single_step_events) \ + FUN(can_generate_exception_events) \ + FUN(can_generate_frame_pop_events) \ + FUN(can_generate_breakpoint_events) \ + FUN(can_suspend) \ + FUN(can_redefine_any_class) \ + FUN(can_get_current_thread_cpu_time) \ + FUN(can_get_thread_cpu_time) \ + FUN(can_generate_method_entry_events) \ + FUN(can_generate_method_exit_events) \ + FUN(can_generate_all_class_hook_events) \ + FUN(can_generate_compiled_method_load_events) \ + FUN(can_generate_monitor_events) \ + FUN(can_generate_vm_object_alloc_events) \ + FUN(can_generate_native_method_bind_events) \ + FUN(can_generate_garbage_collection_events) \ + FUN(can_generate_object_free_events) \ + FUN(can_force_early_return) \ + FUN(can_get_owned_monitor_stack_depth_info) \ + FUN(can_get_constant_pool) \ + FUN(can_set_native_method_prefix) \ + FUN(can_retransform_classes) \ + FUN(can_retransform_any_class) \ + FUN(can_generate_resource_exhaustion_heap_events) \ + FUN(can_generate_resource_exhaustion_threads_events) + static jvmtiError GetPotentialCapabilities(jvmtiEnv* env, jvmtiCapabilities* capabilities_ptr) { ENSURE_VALID_ENV(env); ENSURE_NON_NULL(capabilities_ptr); *capabilities_ptr = kPotentialCapabilities; + if (UNLIKELY(!IsFullJvmtiAvailable())) { +#define REMOVE_NONDEBUGGABLE_UNSUPPORTED(e) \ + do { \ + if (kNonDebuggableUnsupportedCapabilities.e == 1) { \ + capabilities_ptr->e = 0; \ + } \ + } while (false); + + FOR_ALL_CAPABILITIES(REMOVE_NONDEBUGGABLE_UNSUPPORTED); +#undef REMOVE_NONDEBUGGABLE_UNSUPPORTED + } return OK; } @@ -1122,49 +1182,9 @@ class JvmtiFunctions { ret = ERR(NOT_AVAILABLE); \ } \ } \ - } while (false) - - ADD_CAPABILITY(can_tag_objects); - ADD_CAPABILITY(can_generate_field_modification_events); - ADD_CAPABILITY(can_generate_field_access_events); - ADD_CAPABILITY(can_get_bytecodes); - ADD_CAPABILITY(can_get_synthetic_attribute); - ADD_CAPABILITY(can_get_owned_monitor_info); - ADD_CAPABILITY(can_get_current_contended_monitor); - ADD_CAPABILITY(can_get_monitor_info); - ADD_CAPABILITY(can_pop_frame); - ADD_CAPABILITY(can_redefine_classes); - ADD_CAPABILITY(can_signal_thread); - ADD_CAPABILITY(can_get_source_file_name); - ADD_CAPABILITY(can_get_line_numbers); - ADD_CAPABILITY(can_get_source_debug_extension); - ADD_CAPABILITY(can_access_local_variables); - ADD_CAPABILITY(can_maintain_original_method_order); - ADD_CAPABILITY(can_generate_single_step_events); - ADD_CAPABILITY(can_generate_exception_events); - ADD_CAPABILITY(can_generate_frame_pop_events); - ADD_CAPABILITY(can_generate_breakpoint_events); - ADD_CAPABILITY(can_suspend); - ADD_CAPABILITY(can_redefine_any_class); - ADD_CAPABILITY(can_get_current_thread_cpu_time); - ADD_CAPABILITY(can_get_thread_cpu_time); - ADD_CAPABILITY(can_generate_method_entry_events); - ADD_CAPABILITY(can_generate_method_exit_events); - ADD_CAPABILITY(can_generate_all_class_hook_events); - ADD_CAPABILITY(can_generate_compiled_method_load_events); - ADD_CAPABILITY(can_generate_monitor_events); - ADD_CAPABILITY(can_generate_vm_object_alloc_events); - ADD_CAPABILITY(can_generate_native_method_bind_events); - ADD_CAPABILITY(can_generate_garbage_collection_events); - ADD_CAPABILITY(can_generate_object_free_events); - ADD_CAPABILITY(can_force_early_return); - ADD_CAPABILITY(can_get_owned_monitor_stack_depth_info); - ADD_CAPABILITY(can_get_constant_pool); - ADD_CAPABILITY(can_set_native_method_prefix); - ADD_CAPABILITY(can_retransform_classes); - ADD_CAPABILITY(can_retransform_any_class); - ADD_CAPABILITY(can_generate_resource_exhaustion_heap_events); - ADD_CAPABILITY(can_generate_resource_exhaustion_threads_events); + } while (false); + + FOR_ALL_CAPABILITIES(ADD_CAPABILITY); #undef ADD_CAPABILITY gEventHandler.HandleChangedCapabilities(ArtJvmTiEnv::AsArtJvmTiEnv(env), changed, @@ -1186,49 +1206,9 @@ class JvmtiFunctions { changed.e = 1; \ } \ } \ - } while (false) - - DEL_CAPABILITY(can_tag_objects); - DEL_CAPABILITY(can_generate_field_modification_events); - DEL_CAPABILITY(can_generate_field_access_events); - DEL_CAPABILITY(can_get_bytecodes); - DEL_CAPABILITY(can_get_synthetic_attribute); - DEL_CAPABILITY(can_get_owned_monitor_info); - DEL_CAPABILITY(can_get_current_contended_monitor); - DEL_CAPABILITY(can_get_monitor_info); - DEL_CAPABILITY(can_pop_frame); - DEL_CAPABILITY(can_redefine_classes); - DEL_CAPABILITY(can_signal_thread); - DEL_CAPABILITY(can_get_source_file_name); - DEL_CAPABILITY(can_get_line_numbers); - DEL_CAPABILITY(can_get_source_debug_extension); - DEL_CAPABILITY(can_access_local_variables); - DEL_CAPABILITY(can_maintain_original_method_order); - DEL_CAPABILITY(can_generate_single_step_events); - DEL_CAPABILITY(can_generate_exception_events); - DEL_CAPABILITY(can_generate_frame_pop_events); - DEL_CAPABILITY(can_generate_breakpoint_events); - DEL_CAPABILITY(can_suspend); - DEL_CAPABILITY(can_redefine_any_class); - DEL_CAPABILITY(can_get_current_thread_cpu_time); - DEL_CAPABILITY(can_get_thread_cpu_time); - DEL_CAPABILITY(can_generate_method_entry_events); - DEL_CAPABILITY(can_generate_method_exit_events); - DEL_CAPABILITY(can_generate_all_class_hook_events); - DEL_CAPABILITY(can_generate_compiled_method_load_events); - DEL_CAPABILITY(can_generate_monitor_events); - DEL_CAPABILITY(can_generate_vm_object_alloc_events); - DEL_CAPABILITY(can_generate_native_method_bind_events); - DEL_CAPABILITY(can_generate_garbage_collection_events); - DEL_CAPABILITY(can_generate_object_free_events); - DEL_CAPABILITY(can_force_early_return); - DEL_CAPABILITY(can_get_owned_monitor_stack_depth_info); - DEL_CAPABILITY(can_get_constant_pool); - DEL_CAPABILITY(can_set_native_method_prefix); - DEL_CAPABILITY(can_retransform_classes); - DEL_CAPABILITY(can_retransform_any_class); - DEL_CAPABILITY(can_generate_resource_exhaustion_heap_events); - DEL_CAPABILITY(can_generate_resource_exhaustion_threads_events); + } while (false); + + FOR_ALL_CAPABILITIES(DEL_CAPABILITY); #undef DEL_CAPABILITY gEventHandler.HandleChangedCapabilities(ArtJvmTiEnv::AsArtJvmTiEnv(env), changed, @@ -1236,6 +1216,8 @@ class JvmtiFunctions { return OK; } +#undef FOR_ALL_CAPABILITIES + static jvmtiError GetCapabilities(jvmtiEnv* env, jvmtiCapabilities* capabilities_ptr) { ENSURE_VALID_ENV(env); ENSURE_NON_NULL(capabilities_ptr); @@ -1341,7 +1323,7 @@ class JvmtiFunctions { static jvmtiError GetVersionNumber(jvmtiEnv* env, jint* version_ptr) { ENSURE_VALID_ENV(env); - *version_ptr = JVMTI_VERSION; + *version_ptr = ArtJvmTiEnv::AsArtJvmTiEnv(env)->ti_version; return OK; } @@ -1495,9 +1477,10 @@ static bool IsJvmtiVersion(jint version) { extern const jvmtiInterface_1 gJvmtiInterface; -ArtJvmTiEnv::ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler) +ArtJvmTiEnv::ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler, jint version) : art_vm(runtime), local_data(nullptr), + ti_version(version), capabilities(), event_info_mutex_("jvmtiEnv_EventInfoMutex") { object_tag_table = std::unique_ptr<ObjectTagTable>(new ObjectTagTable(event_handler, this)); @@ -1506,8 +1489,8 @@ ArtJvmTiEnv::ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler) // Creates a jvmtiEnv and returns it with the art::ti::Env that is associated with it. new_art_ti // is a pointer to the uninitialized memory for an art::ti::Env. -static void CreateArtJvmTiEnv(art::JavaVMExt* vm, /*out*/void** new_jvmtiEnv) { - struct ArtJvmTiEnv* env = new ArtJvmTiEnv(vm, &gEventHandler); +static void CreateArtJvmTiEnv(art::JavaVMExt* vm, jint version, /*out*/void** new_jvmtiEnv) { + struct ArtJvmTiEnv* env = new ArtJvmTiEnv(vm, &gEventHandler, version); *new_jvmtiEnv = env; gEventHandler.RegisterArtJvmTiEnv(env); @@ -1520,8 +1503,14 @@ static void CreateArtJvmTiEnv(art::JavaVMExt* vm, /*out*/void** new_jvmtiEnv) { // places the return value in 'env' if this library can handle the GetEnv request. Otherwise // returns false and does not modify the 'env' pointer. static jint GetEnvHandler(art::JavaVMExt* vm, /*out*/void** env, jint version) { - if (IsJvmtiVersion(version)) { - CreateArtJvmTiEnv(vm, env); + // JavaDebuggable will either be set by the runtime as it is starting up or the plugin if it's + // loaded early enough. If this is false we cannot guarantee conformance to all JVMTI behaviors + // due to optimizations. We will only allow agents to get ArtTiEnvs using the kArtTiVersion. + if (IsFullJvmtiAvailable() && IsJvmtiVersion(version)) { + CreateArtJvmTiEnv(vm, JVMTI_VERSION, env); + return JNI_OK; + } else if (version == kArtTiVersion) { + CreateArtJvmTiEnv(vm, kArtTiVersion, env); return JNI_OK; } else { printf("version 0x%x is not valid!", version); @@ -1547,6 +1536,12 @@ extern "C" bool ArtPlugin_Initialize() { SearchUtil::Register(); HeapUtil::Register(); + { + // Make sure we can deopt anything we need to. + art::ScopedObjectAccess soa(art::Thread::Current()); + gDeoptManager.FinishSetup(); + } + runtime->GetJavaVM()->AddEnvironmentHook(GetEnvHandler); return true; diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h index 2a8c2e91df..73cc601e3e 100644 --- a/openjdkjvmti/art_jvmti.h +++ b/openjdkjvmti/art_jvmti.h @@ -62,10 +62,22 @@ namespace openjdkjvmti { class ObjectTagTable; +// A special version that we use to identify special tooling interface versions which mostly matches +// the jvmti spec but everything is best effort. This is used to implement the userdebug +// 'debug-anything' behavior. +// +// This is the value 0x70010200. +static constexpr jint kArtTiVersion = JVMTI_VERSION_1_2 | 0x40000000; + // A structure that is a jvmtiEnv with additional information for the runtime. struct ArtJvmTiEnv : public jvmtiEnv { art::JavaVMExt* art_vm; void* local_data; + + // The ti_version we are compatible with. This is only for giving the correct value for GetVersion + // when running on a userdebug/eng device. + jint ti_version; + jvmtiCapabilities capabilities; EventMasks event_masks; @@ -90,7 +102,7 @@ struct ArtJvmTiEnv : public jvmtiEnv { // RW lock to protect access to all of the event data. art::ReaderWriterMutex event_info_mutex_ DEFAULT_MUTEX_ACQUIRED_AFTER; - ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler); + ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler, jint ti_version); static ArtJvmTiEnv* AsArtJvmTiEnv(jvmtiEnv* env) { return art::down_cast<ArtJvmTiEnv*>(env); @@ -272,6 +284,60 @@ const jvmtiCapabilities kPotentialCapabilities = { .can_generate_resource_exhaustion_threads_events = 0, }; +// These are capabilities that are disabled if we were loaded without being debuggable. +// +// This includes the following capabilities: +// can_retransform_any_class: +// can_retransform_classes: +// can_redefine_any_class: +// can_redefine_classes: +// We need to ensure that inlined code is either not present or can always be deoptimized. This +// is not guaranteed for non-debuggable processes since we might have inlined bootclasspath code +// on a threads stack. +const jvmtiCapabilities kNonDebuggableUnsupportedCapabilities = { + .can_tag_objects = 0, + .can_generate_field_modification_events = 0, + .can_generate_field_access_events = 0, + .can_get_bytecodes = 0, + .can_get_synthetic_attribute = 0, + .can_get_owned_monitor_info = 0, + .can_get_current_contended_monitor = 0, + .can_get_monitor_info = 0, + .can_pop_frame = 0, + .can_redefine_classes = 1, + .can_signal_thread = 0, + .can_get_source_file_name = 0, + .can_get_line_numbers = 0, + .can_get_source_debug_extension = 0, + .can_access_local_variables = 0, + .can_maintain_original_method_order = 0, + .can_generate_single_step_events = 0, + .can_generate_exception_events = 0, + .can_generate_frame_pop_events = 0, + .can_generate_breakpoint_events = 0, + .can_suspend = 0, + .can_redefine_any_class = 1, + .can_get_current_thread_cpu_time = 0, + .can_get_thread_cpu_time = 0, + .can_generate_method_entry_events = 0, + .can_generate_method_exit_events = 0, + .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_native_method_bind_events = 0, + .can_generate_garbage_collection_events = 0, + .can_generate_object_free_events = 0, + .can_force_early_return = 0, + .can_get_owned_monitor_stack_depth_info = 0, + .can_get_constant_pool = 0, + .can_set_native_method_prefix = 0, + .can_retransform_classes = 1, + .can_retransform_any_class = 1, + .can_generate_resource_exhaustion_heap_events = 0, + .can_generate_resource_exhaustion_threads_events = 0, +}; + } // namespace openjdkjvmti #endif // ART_OPENJDKJVMTI_ART_JVMTI_H_ diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc index aced769cb5..53d84836fc 100644 --- a/openjdkjvmti/deopt_manager.cc +++ b/openjdkjvmti/deopt_manager.cc @@ -68,7 +68,9 @@ bool JvmtiMethodInspectionCallback::IsMethodSafeToJit(art::ArtMethod* method) { } DeoptManager::DeoptManager() - : deoptimization_status_lock_("JVMTI_DeoptimizationStatusLock"), + : deoptimization_status_lock_("JVMTI_DeoptimizationStatusLock", + static_cast<art::LockLevel>( + art::LockLevel::kClassLinkerClassesLock + 1)), deoptimization_condition_("JVMTI_DeoptimizationCondition", deoptimization_status_lock_), performing_deoptimization_(false), global_deopt_count_(0), @@ -91,6 +93,33 @@ void DeoptManager::Shutdown() { callbacks->RemoveMethodInspectionCallback(&inspection_callback_); } +void DeoptManager::FinishSetup() { + art::Thread* self = art::Thread::Current(); + art::MutexLock mu(self, deoptimization_status_lock_); + + art::Runtime* runtime = art::Runtime::Current(); + // See if we need to do anything. + if (!runtime->IsJavaDebuggable()) { + // See if we can enable all JVMTI functions. If this is false, only kArtTiVersion agents can be + // retrieved and they will all be best-effort. + if (PhaseUtil::GetPhaseUnchecked() == JVMTI_PHASE_ONLOAD) { + // We are still early enough to change the compiler options and get full JVMTI support. + LOG(INFO) << "Openjdkjvmti plugin loaded on a non-debuggable runtime. Changing runtime to " + << "debuggable state. Please pass '--debuggable' to dex2oat and " + << "'-Xcompiler-option --debuggable' to dalvikvm in the future."; + DCHECK(runtime->GetJit() == nullptr) << "Jit should not be running yet!"; + runtime->AddCompilerOption("--debuggable"); + runtime->SetJavaDebuggable(true); + } else { + LOG(WARNING) << "Openjdkjvmti plugin was loaded on a non-debuggable Runtime. Plugin was " + << "loaded too late to change runtime state to DEBUGGABLE. Only kArtTiVersion " + << "(0x" << std::hex << kArtTiVersion << ") environments are available. Some " + << "functionality might not work properly."; + } + runtime->DeoptimizeBootImage(); + } +} + bool DeoptManager::MethodHasBreakpoints(art::ArtMethod* method) { art::MutexLock lk(art::Thread::Current(), deoptimization_status_lock_); return MethodHasBreakpointsLocked(method); diff --git a/openjdkjvmti/deopt_manager.h b/openjdkjvmti/deopt_manager.h index b265fa8ec2..a495b6835c 100644 --- a/openjdkjvmti/deopt_manager.h +++ b/openjdkjvmti/deopt_manager.h @@ -101,6 +101,10 @@ class DeoptManager { void DeoptimizeThread(art::Thread* target) REQUIRES_SHARED(art::Locks::mutator_lock_); void DeoptimizeAllThreads() REQUIRES_SHARED(art::Locks::mutator_lock_); + void FinishSetup() + REQUIRES(!deoptimization_status_lock_, !art::Roles::uninterruptible_) + REQUIRES_SHARED(art::Locks::mutator_lock_); + static DeoptManager* Get(); private: @@ -141,9 +145,8 @@ class DeoptManager { REQUIRES(!art::Roles::uninterruptible_, !art::Locks::mutator_lock_); static constexpr const char* kDeoptManagerInstrumentationKey = "JVMTI_DeoptManager"; - // static constexpr const char* kDeoptManagerThreadName = "JVMTI_DeoptManagerWorkerThread"; - art::Mutex deoptimization_status_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + art::Mutex deoptimization_status_lock_ ACQUIRED_BEFORE(art::Locks::classlinker_classes_lock_); art::ConditionVariable deoptimization_condition_ GUARDED_BY(deoptimization_status_lock_); bool performing_deoptimization_ GUARDED_BY(deoptimization_status_lock_); diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 44a5dde485..f9eedae23e 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -319,6 +319,21 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* self->AssertThreadSuspensionIsAllowable(); CHECK_EQ(kRunnable, self->GetState()); CHECK_STREQ(GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty(), shorty); + + if (!IsNative() && + !IsObsolete() && + !IsProxyMethod() && + IsInvokable() && + ClassLinker::ShouldUseInterpreterEntrypoint(this, GetEntryPointFromQuickCompiledCode())) { + ClassLinker* cl = Runtime::Current()->GetClassLinker(); + const void* entry_point = GetEntryPointFromQuickCompiledCode(); + DCHECK(cl->IsQuickToInterpreterBridge(entry_point) || + cl->IsQuickResolutionStub(entry_point) || + entry_point == GetQuickInstrumentationEntryPoint()) + << PrettyMethod() << " is expected to be interpreted but has an unexpected entrypoint." + << " The entrypoint is " << entry_point << " (incorrect) oat entrypoint would be " + << GetOatMethodQuickCode(cl->GetImagePointerSize()); + } } // Push a transition back into managed code onto the linked list in thread. diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 877654247c..c487808161 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1296,22 +1296,32 @@ void AppImageClassLoadersAndDexCachesHelper::Update( } for (ArtMethod& m : klass->GetDirectMethods(kRuntimePointerSize)) { const void* code = m.GetEntryPointFromQuickCompiledCode(); - const void* oat_code = m.IsInvokable() ? class_linker->GetQuickOatCodeFor(&m) : code; - if (!class_linker->IsQuickResolutionStub(code) && - !class_linker->IsQuickGenericJniStub(code) && + if (!m.IsProxyMethod() && + !m.IsNative() && + !class_linker->IsQuickResolutionStub(code) && !class_linker->IsQuickToInterpreterBridge(code) && - !m.IsNative()) { - DCHECK_EQ(code, oat_code) << m.PrettyMethod(); + m.IsInvokable()) { + // Since this is just a sanity check it's okay to get the oat code here regardless + // of whether it's usable. + const void* oat_code = m.GetOatMethodQuickCode(class_linker->GetImagePointerSize()); + if (oat_code != nullptr) { + DCHECK_EQ(code, oat_code) << m.PrettyMethod(); + } } } for (ArtMethod& m : klass->GetVirtualMethods(kRuntimePointerSize)) { const void* code = m.GetEntryPointFromQuickCompiledCode(); - const void* oat_code = m.IsInvokable() ? class_linker->GetQuickOatCodeFor(&m) : code; - if (!class_linker->IsQuickResolutionStub(code) && - !class_linker->IsQuickGenericJniStub(code) && + if (!m.IsProxyMethod() && + !m.IsNative() && + !class_linker->IsQuickResolutionStub(code) && !class_linker->IsQuickToInterpreterBridge(code) && - !m.IsNative()) { - DCHECK_EQ(code, oat_code) << m.PrettyMethod(); + m.IsInvokable()) { + // Since this is just a sanity check it's okay to get the oat code here regardless + // of whether it's usable. + const void* oat_code = m.GetOatMethodQuickCode(class_linker->GetImagePointerSize()); + if (oat_code != nullptr) { + DCHECK_EQ(code, oat_code) << m.PrettyMethod(); + } } } } @@ -2899,21 +2909,25 @@ uint32_t ClassLinker::SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file, image_pointer_size_); } -// Special case to get oat code without overwriting a trampoline. -const void* ClassLinker::GetQuickOatCodeFor(ArtMethod* method) { +const void* ClassLinker::GetQuickEntrypointFor(ArtMethod* method) { CHECK(method->IsInvokable()) << method->PrettyMethod(); if (method->IsProxyMethod()) { return GetQuickProxyInvokeHandler(); } - auto* code = method->GetOatMethodQuickCode(GetImagePointerSize()); - if (code != nullptr) { - return code; - } - if (method->IsNative()) { - // No code and native? Use generic trampoline. - return GetQuickGenericJniStub(); + const void* oat_code = method->GetOatMethodQuickCode(GetImagePointerSize()); + if (oat_code == nullptr) { + // We need either the generic jni or interpreter bridge. + if (method->IsNative()) { + return GetQuickGenericJniStub(); + } else { + return GetQuickToInterpreterBridge(); + } + } else if (ClassLinker::ShouldUseInterpreterEntrypoint(method, oat_code)) { + // We have oat code but we cannot use it for some reason. + return GetQuickToInterpreterBridge(); + } else { + return oat_code; } - return GetQuickToInterpreterBridge(); } bool ClassLinker::ShouldUseInterpreterEntrypoint(ArtMethod* method, const void* quick_code) { diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 3e3425f5ac..62804e79c5 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -498,9 +498,8 @@ class ClassLinker { std::string GetDescriptorForProxy(ObjPtr<mirror::Class> proxy_class) REQUIRES_SHARED(Locks::mutator_lock_); - // Get the oat code for a method when its class isn't yet initialized. - const void* GetQuickOatCodeFor(ArtMethod* method) - REQUIRES_SHARED(Locks::mutator_lock_); + // Get the correct entrypoint for a method as far as the class-linker is concerned. + const void* GetQuickEntrypointFor(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); pid_t GetClassesLockOwner(); // For SignalCatcher. pid_t GetDexLockOwner(); // For SignalCatcher. diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index f727690c11..a32c717ffe 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -1294,9 +1294,9 @@ extern "C" const void* artQuickResolutionTrampoline( // with the interpreter. code = GetQuickToInterpreterBridge(); } else if (invoke_type == kStatic) { - // Class is still initializing, go to oat and grab code (trampoline must be left in place - // until class is initialized to stop races between threads). - code = linker->GetQuickOatCodeFor(called); + // Class is still initializing. The entrypoint contains the trampoline, so we cannot return + // it. Instead, ask the class linker what is the actual code that needs to be invoked. + code = linker->GetQuickEntrypointFor(called); } else { // No trampoline for non-static methods. code = called->GetEntryPointFromQuickCompiledCode(); diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 4524448916..2a1f219fae 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -167,7 +167,7 @@ void Instrumentation::InstallStubsForMethod(ArtMethod* method) { if (NeedDebugVersionFor(method)) { new_quick_code = GetQuickToInterpreterBridge(); } else { - new_quick_code = class_linker->GetQuickOatCodeFor(method); + new_quick_code = class_linker->GetQuickEntrypointFor(method); } } else { new_quick_code = GetQuickResolutionStub(); @@ -188,7 +188,7 @@ void Instrumentation::InstallStubsForMethod(ArtMethod* method) { } else if (entry_exit_stubs_installed_) { new_quick_code = GetQuickInstrumentationEntryPoint(); } else { - new_quick_code = class_linker->GetQuickOatCodeFor(method); + new_quick_code = class_linker->GetQuickEntrypointFor(method); } } else { new_quick_code = GetQuickResolutionStub(); @@ -877,7 +877,7 @@ void Instrumentation::Undeoptimize(ArtMethod* method) { } else { const void* quick_code = NeedDebugVersionFor(method) ? GetQuickToInterpreterBridge() - : class_linker->GetQuickOatCodeFor(method); + : class_linker->GetQuickEntrypointFor(method); UpdateEntrypoints(method, quick_code); } @@ -971,7 +971,7 @@ const void* Instrumentation::GetQuickCodeFor(ArtMethod* method, PointerSize poin return code; } } - return class_linker->GetQuickOatCodeFor(method); + return class_linker->GetQuickEntrypointFor(method); } void Instrumentation::MethodEnterEventImpl(Thread* thread, diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index e126c16cc9..9503360167 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -41,6 +41,7 @@ #include "jni_internal.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" +#include "oat_file.h" #include "oat_file_assistant.h" #include "obj_ptr-inl.h" #include "scoped_thread_state_change-inl.h" @@ -528,8 +529,14 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( if (source_oat_file != nullptr) { bool added_image_space = false; if (source_oat_file->IsExecutable()) { - std::unique_ptr<gc::space::ImageSpace> image_space = - kEnableAppImage ? oat_file_assistant.OpenImageSpace(source_oat_file) : nullptr; + // We need to throw away the image space if we are debuggable but the oat-file source of the + // image is not otherwise we might get classes with inlined methods or other such things. + std::unique_ptr<gc::space::ImageSpace> image_space; + if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) { + image_space = oat_file_assistant.OpenImageSpace(source_oat_file); + } else { + image_space = nullptr; + } if (image_space != nullptr) { ScopedObjectAccess soa(self); StackHandleScope<1> hs(self); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index b4284bb270..377e0a3fca 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1553,6 +1553,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { } static bool EnsureJvmtiPlugin(Runtime* runtime, + bool allow_non_debuggable_tooling, std::vector<Plugin>* plugins, std::string* error_msg) { constexpr const char* plugin_name = kIsDebugBuild ? "libopenjdkjvmtid.so" : "libopenjdkjvmti.so"; @@ -1564,9 +1565,9 @@ static bool EnsureJvmtiPlugin(Runtime* runtime, } } - // Is the process debuggable? Otherwise, do not attempt to load the plugin. - // TODO Support a crimped jvmti for non-debuggable runtimes. - if (!runtime->IsJavaDebuggable()) { + // Is the process debuggable? Otherwise, do not attempt to load the plugin unless we are + // specifically allowed. + if (!allow_non_debuggable_tooling && !runtime->IsJavaDebuggable()) { *error_msg = "Process is not debuggable."; return false; } @@ -1587,9 +1588,12 @@ static bool EnsureJvmtiPlugin(Runtime* runtime, // revisit this and make sure we're doing this on the right thread // (and we synchronize access to any shared data structures like "agents_") // -void Runtime::AttachAgent(JNIEnv* env, const std::string& agent_arg, jobject class_loader) { +void Runtime::AttachAgent(JNIEnv* env, + const std::string& agent_arg, + jobject class_loader, + bool allow_non_debuggable_tooling) { std::string error_msg; - if (!EnsureJvmtiPlugin(this, &plugins_, &error_msg)) { + if (!EnsureJvmtiPlugin(this, allow_non_debuggable_tooling, &plugins_, &error_msg)) { LOG(WARNING) << "Could not load plugin: " << error_msg; ScopedObjectAccess soa(Thread::Current()); ThrowIOException("%s", error_msg.c_str()); diff --git a/runtime/runtime.h b/runtime/runtime.h index c8edabce09..3e055c3f84 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -661,7 +661,10 @@ class Runtime { void AddSystemWeakHolder(gc::AbstractSystemWeakHolder* holder); void RemoveSystemWeakHolder(gc::AbstractSystemWeakHolder* holder); - void AttachAgent(JNIEnv* env, const std::string& agent_arg, jobject class_loader); + void AttachAgent(JNIEnv* env, + const std::string& agent_arg, + jobject class_loader, + bool allow_non_debuggable_tooling = false); const std::list<std::unique_ptr<ti::Agent>>& GetAgents() const { return agents_; diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 132099a45d..631b14afc8 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -466,10 +466,9 @@ if [ "$USE_JVMTI" = "y" ]; then if [[ "$TEST_IS_NDEBUG" = "y" ]]; then plugin=libopenjdkjvmti.so fi + # We used to add flags here that made the runtime debuggable but that is not + # needed anymore since the plugin can do it for us now. FLAGS="${FLAGS} -Xplugin:${plugin}" - FLAGS="${FLAGS} -Xcompiler-option --debuggable" - # Always make the compilation be debuggable. - COMPILE_FLAGS="${COMPILE_FLAGS} --debuggable" fi fi |