diff options
Diffstat (limited to 'openjdkjvmti')
| -rw-r--r-- | openjdkjvmti/Android.bp | 7 | ||||
| -rw-r--r-- | openjdkjvmti/LICENSE (renamed from openjdkjvmti/NOTICE) | 0 | ||||
| -rw-r--r-- | openjdkjvmti/OpenjdkJvmTi.cc | 22 | ||||
| -rw-r--r-- | openjdkjvmti/art_jvmti.h | 13 | ||||
| -rw-r--r-- | openjdkjvmti/deopt_manager.cc | 150 | ||||
| -rw-r--r-- | openjdkjvmti/deopt_manager.h | 7 | ||||
| -rw-r--r-- | openjdkjvmti/events.cc | 36 | ||||
| -rw-r--r-- | openjdkjvmti/events.h | 4 | ||||
| -rw-r--r-- | openjdkjvmti/jvmti_allocator.h | 24 | ||||
| -rw-r--r-- | openjdkjvmti/jvmti_weak_table-inl.h | 38 | ||||
| -rw-r--r-- | openjdkjvmti/jvmti_weak_table.h | 16 | ||||
| -rw-r--r-- | openjdkjvmti/ti_class.cc | 90 | ||||
| -rw-r--r-- | openjdkjvmti/ti_class_definition.cc | 6 | ||||
| -rw-r--r-- | openjdkjvmti/ti_class_loader-inl.h | 4 | ||||
| -rw-r--r-- | openjdkjvmti/ti_class_loader.cc | 18 | ||||
| -rw-r--r-- | openjdkjvmti/ti_extension.cc | 6 | ||||
| -rw-r--r-- | openjdkjvmti/ti_heap.cc | 4 | ||||
| -rw-r--r-- | openjdkjvmti/ti_redefine.cc | 115 | ||||
| -rw-r--r-- | openjdkjvmti/ti_redefine.h | 4 | ||||
| -rw-r--r-- | openjdkjvmti/ti_search.cc | 59 | ||||
| -rw-r--r-- | openjdkjvmti/ti_stack.cc | 15 | ||||
| -rw-r--r-- | openjdkjvmti/ti_thread.cc | 109 | ||||
| -rw-r--r-- | openjdkjvmti/ti_threadgroup.cc | 43 | ||||
| -rw-r--r-- | openjdkjvmti/transform.cc | 7 |
24 files changed, 441 insertions, 356 deletions
diff --git a/openjdkjvmti/Android.bp b/openjdkjvmti/Android.bp index d01357f801..98cb4664e3 100644 --- a/openjdkjvmti/Android.bp +++ b/openjdkjvmti/Android.bp @@ -36,10 +36,10 @@ license { visibility: [":__subpackages__"], license_kinds: [ "SPDX-license-identifier-Apache-2.0", - "SPDX-license-identifier-GPL-with-classpath-exception", + "SPDX-license-identifier-GPL-2.0-with-classpath-exception", ], license_text: [ - "NOTICE", + "LICENSE", ], } @@ -111,13 +111,13 @@ art_cc_library { shared_libs: [ "libart", "libart-compiler", - "libart-dexlayout", "libdexfile", "libartbase", ], apex_available: [ "com.android.art", "com.android.art.debug", + "test_broken_com.android.art", ], } @@ -130,7 +130,6 @@ art_cc_library { shared_libs: [ "libartd", "libartd-compiler", - "libartd-dexlayout", "libdexfiled", "libartbased", ], diff --git a/openjdkjvmti/NOTICE b/openjdkjvmti/LICENSE index 6ec62cd4f1..6ec62cd4f1 100644 --- a/openjdkjvmti/NOTICE +++ b/openjdkjvmti/LICENSE diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc index 09900e1f73..276b3a813d 100644 --- a/openjdkjvmti/OpenjdkJvmTi.cc +++ b/openjdkjvmti/OpenjdkJvmTi.cc @@ -89,12 +89,6 @@ AllocationManager* gAllocManager; } \ } 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) { @@ -1474,19 +1468,21 @@ extern "C" bool ArtPlugin_Initialize() { FieldUtil::Register(gEventHandler); BreakpointUtil::Register(gEventHandler); Transformer::Register(gEventHandler); - - { - // Make sure we can deopt anything we need to. - art::ScopedSuspendAll ssa(__FUNCTION__); - gDeoptManager->FinishSetup(); - } - + gDeoptManager->FinishSetup(); runtime->GetJavaVM()->AddEnvironmentHook(GetEnvHandler); return true; } extern "C" bool ArtPlugin_Deinitialize() { + // When runtime is shutting down, it is not necessary to unregister callbacks or update + // instrumentation levels. Removing callbacks require a GC critical section in some cases and + // when runtime is shutting down we already stop GC and hence it is not safe to request to + // enter a GC critical section. + if (art::Runtime::Current()->IsShuttingDown(art::Thread::Current())) { + return true; + } + gEventHandler->Shutdown(); gDeoptManager->Shutdown(); PhaseUtil::Unregister(); diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h index 083ba6ddf1..bc965a232f 100644 --- a/openjdkjvmti/art_jvmti.h +++ b/openjdkjvmti/art_jvmti.h @@ -47,9 +47,11 @@ #include "base/strlcpy.h" #include "base/mutex.h" #include "events.h" +#include "instrumentation.h" #include "jni/java_vm_ext.h" #include "jni/jni_env_ext.h" #include "jvmti.h" +#include "runtime.h" #include "ti_breakpoint.h" namespace art { @@ -69,6 +71,13 @@ class ObjectTagTable; // This is the value 0x70010200. static constexpr jint kArtTiVersion = JVMTI_VERSION_1_2 | 0x40000000; +// Returns whether we are able to use all jvmti features. +static inline bool IsFullJvmtiAvailable() { + art::Runtime* runtime = art::Runtime::Current(); + return runtime->GetInstrumentation()->IsForcedInterpretOnly() || + runtime->IsJavaDebuggableAtInit(); +} + // A structure that is a jvmtiEnv with additional information for the runtime. struct ArtJvmTiEnv : public jvmtiEnv { art::JavaVMExt* art_vm; @@ -141,7 +150,7 @@ class JvmtiDeleter { explicit JvmtiDeleter(jvmtiEnv* env) : env_(env) {} JvmtiDeleter(JvmtiDeleter&) = default; - JvmtiDeleter(JvmtiDeleter&&) = default; + JvmtiDeleter(JvmtiDeleter&&) noexcept = default; JvmtiDeleter& operator=(const JvmtiDeleter&) = default; void operator()(T* ptr) const { @@ -161,7 +170,7 @@ class JvmtiDeleter<T[]> { explicit JvmtiDeleter(jvmtiEnv* env) : env_(env) {} JvmtiDeleter(JvmtiDeleter&) = default; - JvmtiDeleter(JvmtiDeleter&&) = default; + JvmtiDeleter(JvmtiDeleter&&) noexcept = default; JvmtiDeleter& operator=(const JvmtiDeleter&) = default; template <typename U> diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc index 312a797b9d..978843c6ea 100644 --- a/openjdkjvmti/deopt_manager.cc +++ b/openjdkjvmti/deopt_manager.cc @@ -47,10 +47,12 @@ #include "gc/scoped_gc_critical_section.h" #include "instrumentation.h" #include "jit/jit.h" +#include "jit/jit_code_cache.h" #include "jni/jni_internal.h" #include "mirror/class-inl.h" #include "mirror/object_array-inl.h" #include "nativehelper/scoped_local_ref.h" +#include "oat_file_manager.h" #include "read_barrier_config.h" #include "runtime_callbacks.h" #include "scoped_thread_state_change-inl.h" @@ -61,16 +63,15 @@ namespace openjdkjvmti { -// TODO We should make this much more selective in the future so we only return true when we +static constexpr const char* kInstrumentationKey = "JVMTI_DeoptRequester"; + +// We could make this much more selective in the future so we only return true when we // actually care about the method at this time (ie active frames had locals changed). For now we -// just assume that if anything has changed any frame's locals we care about all methods. If nothing -// has we only care about methods with active breakpoints on them. In the future we should probably -// rewrite all of this to instead do this at the ShadowFrame or thread granularity. -bool JvmtiMethodInspectionCallback::IsMethodBeingInspected(art::ArtMethod* method) { - // In non-java-debuggable runtimes the breakpoint check would miss if we have breakpoints on - // methods that are inlined. Since these features are best effort in non-java-debuggable - // runtimes it is OK to be less precise. For debuggable runtimes, inlining is disabled. - return manager_->HaveLocalsChanged() || manager_->MethodHasBreakpoints(method); +// just assume that if anything has changed any frame's locals we care about all methods. This only +// impacts whether we are able to OSR or not so maybe not really important to maintain frame +// specific information. +bool JvmtiMethodInspectionCallback::HaveLocalsChanged() { + return manager_->HaveLocalsChanged(); } DeoptManager::DeoptManager() @@ -94,14 +95,6 @@ void DeoptManager::Setup() { callbacks->AddMethodInspectionCallback(&inspection_callback_); } -void DeoptManager::Shutdown() { - art::ScopedThreadStateChange stsc(art::Thread::Current(), - art::ThreadState::kWaitingForDebuggerToAttach); - art::ScopedSuspendAll ssa("remove method Inspection Callback"); - art::RuntimeCallbacks* callbacks = art::Runtime::Current()->GetRuntimeCallbacks(); - callbacks->RemoveMethodInspectionCallback(&inspection_callback_); -} - void DeoptManager::DumpDeoptInfo(art::Thread* self, std::ostream& stream) { art::ScopedObjectAccess soa(self); art::MutexLock mutll(self, *art::Locks::thread_list_lock_); @@ -151,48 +144,59 @@ void DeoptManager::DumpDeoptInfo(art::Thread* self, std::ostream& stream) { 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."; - if (runtime->GetJit() == nullptr && - runtime->GetJITOptions()->UseJitCompilation() && - !runtime->GetInstrumentation()->IsForcedInterpretOnly()) { - // If we don't have a jit we should try to start the jit for performance reasons. We only - // need to do this for late attach on non-debuggable processes because for debuggable - // processes we already rely on jit and we cannot force this jit to start if we are still in - // OnLoad since the runtime hasn't started up sufficiently. This is only expected to happen - // on userdebug/eng builds. - LOG(INFO) << "Attempting to start jit for openjdkjvmti plugin."; - // Note: use rwx allowed = true, because if this is the system server, we will not be - // allowed to allocate any JIT code cache, anyways. - runtime->CreateJitCodeCache(/*rwx_memory_allowed=*/true); - runtime->CreateJit(); - if (runtime->GetJit() == nullptr) { - LOG(WARNING) << "Could not start jit for openjdkjvmti plugin. This process might be " - << "quite slow as it is running entirely in the interpreter. Try running " - << "'setenforce 0' and restarting this process."; - } - } - } + if (runtime->IsJavaDebuggable()) { + return; + } + + // See if we can enable all JVMTI functions. + 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!"; + art::ScopedSuspendAll ssa(__FUNCTION__); + // TODO check if we need to hold deoptimization_status_lock_ here. + art::MutexLock mu(self, deoptimization_status_lock_); + runtime->AddCompilerOption("--debuggable"); + runtime->SetRuntimeDebugState(art::Runtime::RuntimeDebugState::kJavaDebuggableAtInit); runtime->DeoptimizeBootImage(); + return; + } + + // Runtime has already started in non-debuggable mode. Only kArtTiVersion agents can be + // retrieved and they will all be best-effort. + LOG(WARNING) << "Openjdkjvmti plugin was loaded on a non-debuggable Runtime. Plugin was " + << "loaded too late to change runtime state to support all capabilities. Only " + << "kArtTiVersion (0x" << std::hex << kArtTiVersion << ") environments are " + << "available. Some functionality might not work properly."; + + // Transition the runtime to debuggable: + // 1. Wait for any background verification tasks to finish. We don't support + // background verification after moving to debuggable state. + runtime->GetOatFileManager().WaitForBackgroundVerificationTasksToFinish(); + + // Do the transition in ScopedJITSuspend, so we don't start any JIT compilations + // before the transition to debuggable is finished. + art::jit::ScopedJitSuspend suspend_jit; + art::ScopedSuspendAll ssa(__FUNCTION__); + + // 2. Discard any JITed code that was generated before, since they would be + // compiled without debug support. + art::jit::Jit* jit = runtime->GetJit(); + if (jit != nullptr) { + jit->GetCodeCache()->InvalidateAllCompiledCode(); + jit->GetCodeCache()->TransitionToDebuggable(); + jit->GetJitCompiler()->SetDebuggableCompilerOption(true); } + + // 3. Change the state to JavaDebuggable, so that debug features can be + // enabled from now on. + runtime->SetRuntimeDebugState(art::Runtime::RuntimeDebugState::kJavaDebuggable); + + // 4. Update all entrypoints to avoid using any AOT code. + runtime->GetInstrumentation()->UpdateEntrypointsForDebuggable(); } bool DeoptManager::MethodHasBreakpoints(art::ArtMethod* method) { @@ -365,6 +369,30 @@ void DeoptManager::AddDeoptimizeAllMethodsLocked(art::Thread* self) { } } +void DeoptManager::Shutdown() { + art::Thread* self = art::Thread::Current(); + art::Runtime* runtime = art::Runtime::Current(); + + // Do the transition in ScopedJITSuspend, so we don't start any JIT compilations + // before the transition to debuggable is finished. + art::jit::ScopedJitSuspend suspend_jit; + + art::ScopedThreadStateChange sts(self, art::ThreadState::kSuspended); + deoptimization_status_lock_.ExclusiveLock(self); + ScopedDeoptimizationContext sdc(self, this); + + art::RuntimeCallbacks* callbacks = runtime->GetRuntimeCallbacks(); + callbacks->RemoveMethodInspectionCallback(&inspection_callback_); + + if (runtime->IsShuttingDown(self)) { + return; + } + + runtime->GetInstrumentation()->DisableDeoptimization(kInstrumentationKey); + runtime->GetInstrumentation()->DisableDeoptimization(kDeoptManagerInstrumentationKey); + runtime->GetInstrumentation()->MaybeSwitchRuntimeDebugState(self); +} + void DeoptManager::RemoveDeoptimizeAllMethodsLocked(art::Thread* self) { DCHECK_GT(global_deopt_count_, 0u) << "Request to remove non-existent global deoptimization!"; global_deopt_count_--; @@ -438,7 +466,6 @@ jvmtiError DeoptManager::RemoveDeoptimizeThreadMethods(art::ScopedObjectAccessUn return OK; } -static constexpr const char* kInstrumentationKey = "JVMTI_DeoptRequester"; void DeoptManager::RemoveDeoptimizationRequester() { art::Thread* self = art::Thread::Current(); @@ -460,6 +487,15 @@ void DeoptManager::AddDeoptimizationRequester() { art::ScopedThreadStateChange stsc(self, art::ThreadState::kSuspended); deoptimization_status_lock_.ExclusiveLock(self); deopter_count_++; + if (deopter_count_ == 1) { + // When we add a deoptimization requester, we should enable entry / exit hooks. We only call + // this in debuggable runtimes and hence it won't be necessary to update entrypoints but we + // still need to inform instrumentation that we need to actually run entry / exit hooks. Though + // entrypoints are capable of running entry / exit hooks they won't run them unless enabled. + ScopedDeoptimizationContext sdc(self, this); + art::Runtime::Current()->GetInstrumentation()->EnableEntryExitHooks(kInstrumentationKey); + return; + } deoptimization_status_lock_.ExclusiveUnlock(self); } diff --git a/openjdkjvmti/deopt_manager.h b/openjdkjvmti/deopt_manager.h index c0a788b1b5..e9b91de78a 100644 --- a/openjdkjvmti/deopt_manager.h +++ b/openjdkjvmti/deopt_manager.h @@ -57,8 +57,7 @@ struct JvmtiMethodInspectionCallback : public art::MethodInspectionCallback { public: explicit JvmtiMethodInspectionCallback(DeoptManager* manager) : manager_(manager) {} - bool IsMethodBeingInspected(art::ArtMethod* method) - override REQUIRES_SHARED(art::Locks::mutator_lock_); + bool HaveLocalsChanged() override REQUIRES_SHARED(art::Locks::mutator_lock_); private: DeoptManager* manager_; @@ -111,9 +110,7 @@ class DeoptManager { REQUIRES_SHARED(art::Locks::mutator_lock_); void DeoptimizeAllThreads() REQUIRES_SHARED(art::Locks::mutator_lock_); - void FinishSetup() - REQUIRES(!deoptimization_status_lock_, !art::Roles::uninterruptible_) - REQUIRES(art::Locks::mutator_lock_); + void FinishSetup() REQUIRES(!deoptimization_status_lock_, !art::Roles::uninterruptible_); static DeoptManager* Get(); diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc index a6425af621..64da6ed3f6 100644 --- a/openjdkjvmti/events.cc +++ b/openjdkjvmti/events.cc @@ -29,25 +29,24 @@ * questions. */ -#include <android-base/thread_annotations.h> +#include "events.h" -#include "alloc_manager.h" -#include "base/locks.h" -#include "base/mutex.h" -#include "events-inl.h" +#include <sys/time.h> #include <array> #include <functional> -#include <sys/time.h> +#include "alloc_manager.h" +#include "android-base/thread_annotations.h" #include "arch/context.h" #include "art_field-inl.h" #include "art_jvmti.h" #include "art_method-inl.h" +#include "base/locks.h" #include "base/mutex.h" #include "deopt_manager.h" #include "dex/dex_file_types.h" -#include "events.h" +#include "events-inl.h" #include "gc/allocation_listener.h" #include "gc/gc_pause_listener.h" #include "gc/heap.h" @@ -71,8 +70,8 @@ #include "scoped_thread_state_change-inl.h" #include "scoped_thread_state_change.h" #include "stack.h" -#include "thread.h" #include "thread-inl.h" +#include "thread.h" #include "thread_list.h" #include "ti_phase.h" #include "ti_thread.h" @@ -447,9 +446,8 @@ class JvmtiParkListener : public art::ParkCallback { if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kMonitorWait)) { art::Thread* self = art::Thread::Current(); art::JNIEnvExt* jnienv = self->GetJniEnv(); - art::ArtField* parkBlockerField = art::jni::DecodeArtField( - art::WellKnownClasses::java_lang_Thread_parkBlocker); - art::ObjPtr<art::mirror::Object> blocker_obj = parkBlockerField->GetObj(self->GetPeer()); + art::ObjPtr<art::mirror::Object> blocker_obj = + art::WellKnownClasses::java_lang_Thread_parkBlocker->GetObj(self->GetPeer()); if (blocker_obj.IsNull()) { blocker_obj = self->GetPeer(); } @@ -505,9 +503,8 @@ class JvmtiParkListener : public art::ParkCallback { if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kMonitorWaited)) { art::Thread* self = art::Thread::Current(); art::JNIEnvExt* jnienv = self->GetJniEnv(); - art::ArtField* parkBlockerField = art::jni::DecodeArtField( - art::WellKnownClasses::java_lang_Thread_parkBlocker); - art::ObjPtr<art::mirror::Object> blocker_obj = parkBlockerField->GetObj(self->GetPeer()); + art::ObjPtr<art::mirror::Object> blocker_obj = + art::WellKnownClasses::java_lang_Thread_parkBlocker->GetObj(self->GetPeer()); if (blocker_obj.IsNull()) { blocker_obj = self->GetPeer(); } @@ -741,7 +738,6 @@ class JvmtiMethodTraceListener final : public art::instrumentation::Instrumentat // Call-back for when a method is popped due to an exception throw. A method will either cause a // MethodExited call-back or a MethodUnwind call-back when its activation is removed. void MethodUnwind(art::Thread* self, - art::Handle<art::mirror::Object> this_object ATTRIBUTE_UNUSED, art::ArtMethod* method, uint32_t dex_pc ATTRIBUTE_UNUSED) REQUIRES_SHARED(art::Locks::mutator_lock_) override { @@ -1133,13 +1129,11 @@ static DeoptRequirement GetDeoptRequirement(ArtJvmtiEvent event, jthread thread) switch (event) { case ArtJvmtiEvent::kBreakpoint: case ArtJvmtiEvent::kException: - return DeoptRequirement::kLimited; - // TODO MethodEntry is needed due to inconsistencies between the interpreter and the trampoline - // in how to handle exceptions. case ArtJvmtiEvent::kMethodEntry: + case ArtJvmtiEvent::kMethodExit: + return DeoptRequirement::kLimited; case ArtJvmtiEvent::kExceptionCatch: return DeoptRequirement::kFull; - case ArtJvmtiEvent::kMethodExit: case ArtJvmtiEvent::kFieldModification: case ArtJvmtiEvent::kFieldAccess: case ArtJvmtiEvent::kSingleStep: @@ -1256,10 +1250,10 @@ void EventHandler::HandleLocalAccessCapabilityAdded() { } for (auto& m : klass->GetMethods(art::kRuntimePointerSize)) { const void* code = m.GetEntryPointFromQuickCompiledCode(); - if (m.IsNative() || m.IsProxyMethod()) { + if (m.IsNative() || m.IsProxyMethod() || !m.IsInvokable()) { continue; } else if (!runtime_->GetClassLinker()->IsQuickToInterpreterBridge(code) && - !runtime_->IsAsyncDeoptimizeable(reinterpret_cast<uintptr_t>(code))) { + !runtime_->IsAsyncDeoptimizeable(&m, reinterpret_cast<uintptr_t>(code))) { runtime_->GetInstrumentation()->InitializeMethodsCode(&m, /*aot_code=*/ nullptr); } } diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h index be0839bdc5..7420296a15 100644 --- a/openjdkjvmti/events.h +++ b/openjdkjvmti/events.h @@ -21,9 +21,7 @@ #include <unordered_map> #include <vector> -#include <android-base/logging.h> -#include <android-base/thread_annotations.h> - +#include "android-base/logging.h" #include "android-base/thread_annotations.h" #include "base/macros.h" #include "base/mutex.h" diff --git a/openjdkjvmti/jvmti_allocator.h b/openjdkjvmti/jvmti_allocator.h index bd4c85bd7c..4adf769f12 100644 --- a/openjdkjvmti/jvmti_allocator.h +++ b/openjdkjvmti/jvmti_allocator.h @@ -46,13 +46,13 @@ template <typename T> class JvmtiAllocator; template <> class JvmtiAllocator<void> { public: - typedef void value_type; - typedef void* pointer; - typedef const void* const_pointer; + using value_type = void; + using pointer = void*; + using const_pointer = const void*; template <typename U> struct rebind { - typedef JvmtiAllocator<U> other; + using other = JvmtiAllocator<U>; }; explicit JvmtiAllocator(jvmtiEnv* env) : env_(env) {} @@ -79,17 +79,17 @@ class JvmtiAllocator<void> { template <typename T> class JvmtiAllocator { public: - typedef T value_type; - typedef T* pointer; - typedef T& reference; - typedef const T* const_pointer; - typedef const T& const_reference; - typedef size_t size_type; - typedef ptrdiff_t difference_type; + using value_type = T; + using pointer = T*; + using reference = T&; + using const_pointer = const T*; + using const_reference = const T&; + using size_type = size_t; + using difference_type = ptrdiff_t; template <typename U> struct rebind { - typedef JvmtiAllocator<U> other; + using other = JvmtiAllocator<U>; }; explicit JvmtiAllocator(jvmtiEnv* env) : env_(env) {} diff --git a/openjdkjvmti/jvmti_weak_table-inl.h b/openjdkjvmti/jvmti_weak_table-inl.h index 5b28e458f8..c5663e5475 100644 --- a/openjdkjvmti/jvmti_weak_table-inl.h +++ b/openjdkjvmti/jvmti_weak_table-inl.h @@ -114,7 +114,7 @@ bool JvmtiWeakTable<T>::RemoveLocked(art::Thread* self, art::ObjPtr<art::mirror: return true; } - if (art::kUseReadBarrier && self->GetIsGcMarking() && !update_since_last_sweep_) { + if (art::gUseReadBarrier && self->GetIsGcMarking() && !update_since_last_sweep_) { // Under concurrent GC, there is a window between moving objects and sweeping of system // weaks in which mutators are active. We may receive a to-space object pointer in obj, // but still have from-space pointers in the table. Explicitly update the table once. @@ -156,7 +156,7 @@ bool JvmtiWeakTable<T>::SetLocked(art::Thread* self, art::ObjPtr<art::mirror::Ob return true; } - if (art::kUseReadBarrier && self->GetIsGcMarking() && !update_since_last_sweep_) { + if (art::gUseReadBarrier && self->GetIsGcMarking() && !update_since_last_sweep_) { // Under concurrent GC, there is a window between moving objects and sweeping of system // weaks in which mutators are active. We may receive a to-space object pointer in obj, // but still have from-space pointers in the table. Explicitly update the table once. @@ -210,13 +210,13 @@ void JvmtiWeakTable<T>::SweepImpl(art::IsMarkedVisitor* visitor) { template <typename T> template <typename Updater, typename JvmtiWeakTable<T>::TableUpdateNullTarget kTargetNull> ALWAYS_INLINE inline void JvmtiWeakTable<T>::UpdateTableWith(Updater& updater) { - // We optimistically hope that elements will still be well-distributed when re-inserting them. - // So play with the map mechanics, and postpone rehashing. This avoids the need of a side - // vector and two passes. - float original_max_load_factor = tagged_objects_.max_load_factor(); - tagged_objects_.max_load_factor(std::numeric_limits<float>::max()); - // For checking that a max load-factor actually does what we expect. - size_t original_bucket_count = tagged_objects_.bucket_count(); + // We can't emplace within the map as a to-space reference could be the same as some + // from-space object reference in the map, causing correctness issues. The problem + // doesn't arise if all updated <K,V> pairs are inserted after the loop as by then such + // from-space object references would also have been taken care of. + + // Side vector to hold node handles of entries which are updated. + std::vector<typename TagMap::node_type> updated_node_handles; for (auto it = tagged_objects_.begin(); it != tagged_objects_.end();) { DCHECK(!it->first.IsNull()); @@ -226,22 +226,24 @@ ALWAYS_INLINE inline void JvmtiWeakTable<T>::UpdateTableWith(Updater& updater) { if (kTargetNull == kIgnoreNull && target_obj == nullptr) { // Ignore null target, don't do anything. } else { - T tag = it->second; - it = tagged_objects_.erase(it); + auto nh = tagged_objects_.extract(it++); + DCHECK(!nh.empty()); if (target_obj != nullptr) { - tagged_objects_.emplace(art::GcRoot<art::mirror::Object>(target_obj), tag); - DCHECK_EQ(original_bucket_count, tagged_objects_.bucket_count()); + nh.key() = art::GcRoot<art::mirror::Object>(target_obj); + updated_node_handles.push_back(std::move(nh)); } else if (kTargetNull == kCallHandleNull) { - HandleNullSweep(tag); + HandleNullSweep(nh.mapped()); } - continue; // Iterator was implicitly updated by erase. + continue; // Iterator already updated above. } } it++; } - - tagged_objects_.max_load_factor(original_max_load_factor); - // TODO: consider rehash here. + while (!updated_node_handles.empty()) { + auto ret = tagged_objects_.insert(std::move(updated_node_handles.back())); + DCHECK(ret.inserted); + updated_node_handles.pop_back(); + } } template <typename T> diff --git a/openjdkjvmti/jvmti_weak_table.h b/openjdkjvmti/jvmti_weak_table.h index ea0d023728..674b2a3d52 100644 --- a/openjdkjvmti/jvmti_weak_table.h +++ b/openjdkjvmti/jvmti_weak_table.h @@ -152,7 +152,7 @@ class JvmtiWeakTable : public art::gc::SystemWeakHolder { // Performance optimization: To avoid multiple table updates, ensure that during GC we // only update once. See the comment on the implementation of GetTagSlowPath. - if (art::kUseReadBarrier && + if (art::gUseReadBarrier && self != nullptr && self->GetIsGcMarking() && !update_since_last_sweep_) { @@ -211,13 +211,13 @@ class JvmtiWeakTable : public art::gc::SystemWeakHolder { }; using TagAllocator = JvmtiAllocator<std::pair<const art::GcRoot<art::mirror::Object>, T>>; - std::unordered_map<art::GcRoot<art::mirror::Object>, - T, - HashGcRoot, - EqGcRoot, - TagAllocator> tagged_objects_ - GUARDED_BY(allow_disallow_lock_) - GUARDED_BY(art::Locks::mutator_lock_); + using TagMap = std::unordered_map<art::GcRoot<art::mirror::Object>, + T, + HashGcRoot, + EqGcRoot, + TagAllocator>; + + TagMap tagged_objects_ GUARDED_BY(allow_disallow_lock_) GUARDED_BY(art::Locks::mutator_lock_); // To avoid repeatedly scanning the whole table, remember if we did that since the last sweep. bool update_since_last_sweep_; }; diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc index 9752fb18d9..3d44516173 100644 --- a/openjdkjvmti/ti_class.cc +++ b/openjdkjvmti/ti_class.cc @@ -80,7 +80,7 @@ #include "ti_phase.h" #include "ti_redefine.h" #include "transform.h" -#include "well_known_classes.h" +#include "well_known_classes-inl.h" namespace openjdkjvmti { @@ -113,10 +113,8 @@ static std::unique_ptr<const art::DexFile> MakeSingleDexFile(art::Thread* self, } uint32_t checksum = reinterpret_cast<const art::DexFile::Header*>(map.Begin())->checksum_; std::string map_name = map.GetName(); - const art::ArtDexFileLoader dex_file_loader; - std::unique_ptr<const art::DexFile> dex_file(dex_file_loader.Open(map_name, - checksum, - std::move(map), + art::ArtDexFileLoader dex_file_loader(std::move(map), map_name); + std::unique_ptr<const art::DexFile> dex_file(dex_file_loader.Open(checksum, /*verify=*/true, /*verify_checksum=*/true, &error_msg)); @@ -927,40 +925,42 @@ jvmtiError ClassUtil::GetClassLoaderClassDescriptors(jvmtiEnv* env, } else if (count_ptr == nullptr || classes == nullptr) { return ERR(NULL_POINTER); } - art::JNIEnvExt* jnienv = self->GetJniEnv(); - if (loader == nullptr || - jnienv->IsInstanceOf(loader, art::WellKnownClasses::java_lang_BootClassLoader)) { + std::vector<const art::DexFile*> dex_files_storage; + const std::vector<const art::DexFile*>* dex_files = nullptr; + if (loader == nullptr) { // We can just get the dex files directly for the boot class path. - return CopyClassDescriptors(env, - art::Runtime::Current()->GetClassLinker()->GetBootClassPath(), - count_ptr, - classes); - } - if (!jnienv->IsInstanceOf(loader, art::WellKnownClasses::java_lang_ClassLoader)) { - return ERR(ILLEGAL_ARGUMENT); - } else if (!jnienv->IsInstanceOf(loader, - art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) { - JVMTI_LOG(ERROR, env) << "GetClassLoaderClassDescriptors is only implemented for " - << "BootClassPath and dalvik.system.BaseDexClassLoader class loaders"; - // TODO Possibly return OK With no classes would be better since these ones cannot have any - // real classes associated with them. - return ERR(NOT_IMPLEMENTED); + dex_files = &art::Runtime::Current()->GetClassLinker()->GetBootClassPath(); + } else { + art::ScopedObjectAccess soa(self); + art::StackHandleScope<1> hs(self); + art::Handle<art::mirror::ClassLoader> class_loader( + hs.NewHandle(soa.Decode<art::mirror::ClassLoader>(loader))); + if (class_loader->InstanceOf(art::WellKnownClasses::java_lang_BootClassLoader.Get())) { + // We can just get the dex files directly for the boot class path. + dex_files = &art::Runtime::Current()->GetClassLinker()->GetBootClassPath(); + } else if (!class_loader->InstanceOf(art::WellKnownClasses::java_lang_ClassLoader.Get())) { + return ERR(ILLEGAL_ARGUMENT); + } else if (!class_loader->InstanceOf( + art::WellKnownClasses::dalvik_system_BaseDexClassLoader.Get())) { + JVMTI_LOG(ERROR, env) << "GetClassLoaderClassDescriptors is only implemented for " + << "BootClassPath and dalvik.system.BaseDexClassLoader class loaders"; + // TODO Possibly return OK With no classes would be better since these ones cannot have any + // real classes associated with them. + return ERR(NOT_IMPLEMENTED); + } else { + art::VisitClassLoaderDexFiles( + self, + class_loader, + [&](const art::DexFile* dex_file) { + dex_files_storage.push_back(dex_file); + return true; // Continue with other dex files. + }); + dex_files = &dex_files_storage; + } } - - art::ScopedObjectAccess soa(self); - art::StackHandleScope<1> hs(self); - art::Handle<art::mirror::ClassLoader> class_loader( - hs.NewHandle(soa.Decode<art::mirror::ClassLoader>(loader))); - std::vector<const art::DexFile*> dex_files; - art::VisitClassLoaderDexFiles( - soa, - class_loader, - [&](const art::DexFile* dex_file) { - dex_files.push_back(dex_file); - return true; // Continue with other dex files. - }); // We hold the loader so the dex files won't go away until after this call at worst. - return CopyClassDescriptors(env, dex_files, count_ptr, classes); + DCHECK(dex_files != nullptr); + return CopyClassDescriptors(env, *dex_files, count_ptr, classes); } jvmtiError ClassUtil::GetClassLoaderClasses(jvmtiEnv* env, @@ -973,19 +973,17 @@ jvmtiError ClassUtil::GetClassLoaderClasses(jvmtiEnv* env, return ERR(NULL_POINTER); } art::Thread* self = art::Thread::Current(); - if (!self->GetJniEnv()->IsInstanceOf(initiating_loader, - art::WellKnownClasses::java_lang_ClassLoader)) { - return ERR(ILLEGAL_ARGUMENT); - } - if (self->GetJniEnv()->IsInstanceOf(initiating_loader, - art::WellKnownClasses::java_lang_BootClassLoader)) { - // Need to use null for the BootClassLoader. - initiating_loader = nullptr; - } - art::ScopedObjectAccess soa(self); art::ObjPtr<art::mirror::ClassLoader> class_loader = soa.Decode<art::mirror::ClassLoader>(initiating_loader); + if (class_loader == nullptr) { + // Keep null, meaning the boot class loader. + } else if (!class_loader->InstanceOf(art::WellKnownClasses::java_lang_ClassLoader.Get())) { + return ERR(ILLEGAL_ARGUMENT); + } else if (class_loader->InstanceOf(art::WellKnownClasses::java_lang_BootClassLoader.Get())) { + // Need to use null for the BootClassLoader. + class_loader = nullptr; + } art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker(); diff --git a/openjdkjvmti/ti_class_definition.cc b/openjdkjvmti/ti_class_definition.cc index f1f559388d..a9e7b3191a 100644 --- a/openjdkjvmti/ti_class_definition.cc +++ b/openjdkjvmti/ti_class_definition.cc @@ -172,10 +172,8 @@ jvmtiError ArtClassDefinition::Init(const art::DexFile& dex_file) { if (dex_file.IsCompactDexFile()) { std::string error_msg; std::vector<std::unique_ptr<const art::DexFile>> dex_files; - const art::ArtDexFileLoader dex_file_loader; - if (!dex_file_loader.Open(dex_file.GetLocation().c_str(), - dex_file.GetLocation().c_str(), - /* verify= */ false, + art::ArtDexFileLoader dex_file_loader(dex_file.GetLocation()); + if (!dex_file_loader.Open(/* verify= */ false, /* verify_checksum= */ false, &error_msg, &dex_files)) { diff --git a/openjdkjvmti/ti_class_loader-inl.h b/openjdkjvmti/ti_class_loader-inl.h index 29ea684779..f6b012620b 100644 --- a/openjdkjvmti/ti_class_loader-inl.h +++ b/openjdkjvmti/ti_class_loader-inl.h @@ -48,8 +48,8 @@ inline void ClassLoaderHelper::VisitDexFileObjects(art::Thread* self, art::Handle<art::mirror::ClassLoader> loader, const Visitor& visitor) { art::StackHandleScope<1> hs(self); - art::ArtField* element_dex_file_field = art::jni::DecodeArtField( - art::WellKnownClasses::dalvik_system_DexPathList__Element_dexFile); + art::ArtField* element_dex_file_field = + art::WellKnownClasses::dalvik_system_DexPathList__Element_dexFile; art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list( hs.NewHandle(GetDexElementList(self, loader))); diff --git a/openjdkjvmti/ti_class_loader.cc b/openjdkjvmti/ti_class_loader.cc index d0a66343e7..c117937f5f 100644 --- a/openjdkjvmti/ti_class_loader.cc +++ b/openjdkjvmti/ti_class_loader.cc @@ -57,16 +57,17 @@ #include "object_lock.h" #include "runtime.h" #include "transform.h" +#include "well_known_classes-inl.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); + if (art::ClassLinker::IsBootClassLoader(loader.Get())) { + art::Runtime::Current()->AppendToBootClassPath( + dex_file->GetLocation(), dex_file->GetLocation(), {dex_file}); return true; } art::Handle<art::mirror::Object> java_dex_file_obj( @@ -139,15 +140,14 @@ art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> ClassLoaderHelper::Ge art::Handle<art::mirror::ClassLoader> loader) { art::StackHandleScope<4> hs(self); - art::Handle<art::mirror::Class> - base_dex_loader_class(hs.NewHandle(self->DecodeJObject( - art::WellKnownClasses::dalvik_system_BaseDexClassLoader)->AsClass())); + art::Handle<art::mirror::Class> base_dex_loader_class = + hs.NewHandle(art::WellKnownClasses::dalvik_system_BaseDexClassLoader.Get()); // Get all the ArtFields so we can look in the BaseDexClassLoader - art::ArtField* path_list_field = art::jni::DecodeArtField( - art::WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList); + art::ArtField* path_list_field = + art::WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList; art::ArtField* dex_path_list_element_field = - art::jni::DecodeArtField(art::WellKnownClasses::dalvik_system_DexPathList_dexElements); + art::WellKnownClasses::dalvik_system_DexPathList_dexElements; // Check if loader is a BaseDexClassLoader art::Handle<art::mirror::Class> loader_class(hs.NewHandle(loader->GetClass())); diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc index 10ea43a1ee..02dc9f19c8 100644 --- a/openjdkjvmti/ti_extension.cc +++ b/openjdkjvmti/ti_extension.cc @@ -398,8 +398,7 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, // These require index-ids and debuggable to function art::Runtime* runtime = art::Runtime::Current(); - if (runtime->GetJniIdType() == art::JniIdType::kIndices && - (runtime->GetInstrumentation()->IsForcedInterpretOnly() || runtime->IsJavaDebuggable())) { + if (runtime->GetJniIdType() == art::JniIdType::kIndices && IsFullJvmtiAvailable()) { // IsStructurallyModifiableClass error = add_extension( reinterpret_cast<jvmtiExtensionFunction>(Redefiner::IsStructurallyModifiableClass), @@ -703,8 +702,7 @@ jvmtiError ExtensionUtil::GetExtensionEvents(jvmtiEnv* env, return error; } art::Runtime* runtime = art::Runtime::Current(); - if (runtime->GetJniIdType() == art::JniIdType::kIndices && - (runtime->GetInstrumentation()->IsForcedInterpretOnly() || runtime->IsJavaDebuggable())) { + if (runtime->GetJniIdType() == art::JniIdType::kIndices && IsFullJvmtiAvailable()) { error = add_extension( ArtJvmtiEvent::kStructuralDexFileLoadHook, "com.android.art.class.structural_dex_file_load_hook", diff --git a/openjdkjvmti/ti_heap.cc b/openjdkjvmti/ti_heap.cc index 2a1d44207a..01864cd312 100644 --- a/openjdkjvmti/ti_heap.cc +++ b/openjdkjvmti/ti_heap.cc @@ -1851,7 +1851,9 @@ static void ReplaceWeakRoots(art::Thread* self, const ObjectMap& map_; }; ReplaceWeaksVisitor rwv(map); - art::Runtime::Current()->SweepSystemWeaks(&rwv); + art::Runtime* runtime = art::Runtime::Current(); + runtime->SweepSystemWeaks(&rwv); + runtime->GetThreadList()->SweepInterpreterCaches(&rwv); // Re-add the object tags. At this point all weak-references to the old_obj_ptr are gone. event_handler->ForEachEnv(self, [&](ArtJvmTiEnv* env) { // Cannot have REQUIRES(art::Locks::mutator_lock_) since ForEachEnv doesn't require it. diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc index 15cb6de647..aafca47605 100644 --- a/openjdkjvmti/ti_redefine.cc +++ b/openjdkjvmti/ti_redefine.cc @@ -89,7 +89,7 @@ #include "jni/jni_id_manager.h" #include "jvmti.h" #include "jvmti_allocator.h" -#include "linear_alloc.h" +#include "linear_alloc-inl.h" #include "mirror/array-alloc-inl.h" #include "mirror/array.h" #include "mirror/class-alloc-inl.h" @@ -130,7 +130,7 @@ #include "transform.h" #include "verifier/class_verifier.h" #include "verifier/verifier_enums.h" -#include "well_known_classes.h" +#include "well_known_classes-inl.h" #include "write_barrier.h" namespace openjdkjvmti { @@ -285,8 +285,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { art::Thread* thread, art::LinearAlloc* allocator, const std::unordered_set<art::ArtMethod*>& obsoleted_methods, - ObsoleteMap* obsolete_maps) - REQUIRES(art::Locks::mutator_lock_) { + ObsoleteMap* obsolete_maps) REQUIRES(art::Locks::mutator_lock_) { ObsoleteMethodStackVisitor visitor(thread, allocator, obsoleted_methods, @@ -310,7 +309,9 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { art::ClassLinker* cl = runtime->GetClassLinker(); auto ptr_size = cl->GetImagePointerSize(); const size_t method_size = art::ArtMethod::Size(ptr_size); - auto* method_storage = allocator_->Alloc(art::Thread::Current(), method_size); + auto* method_storage = allocator_->Alloc(art::Thread::Current(), + method_size, + art::LinearAllocKind::kArtMethod); CHECK(method_storage != nullptr) << "Unable to allocate storage for obsolete version of '" << old_method->PrettyMethod() << "'"; new_obsolete_method = new (method_storage) art::ArtMethod(); @@ -454,8 +455,7 @@ jvmtiError Redefiner::GetClassRedefinitionError(art::Handle<art::mirror::Class> } // Check Thread specifically since it's not a root but too many things reach into it with Unsafe // too allow structural redefinition. - if (klass->IsAssignableFrom( - self->DecodeJObject(art::WellKnownClasses::java_lang_Thread)->AsClass())) { + if (klass->IsAssignableFrom(art::WellKnownClasses::java_lang_Thread.Get())) { *error_msg = "java.lang.Thread has fields accessed using sun.misc.unsafe directly. It is not " "safe to structurally redefine it."; @@ -512,8 +512,15 @@ template jvmtiError Redefiner::GetClassRedefinitionError<RedefinitionType::kStru art::MemMap Redefiner::MoveDataToMemMap(const std::string& original_location, art::ArrayRef<const unsigned char> data, std::string* error_msg) { + std::string modified_location = StringPrintf("%s-transformed", original_location.c_str()); + // A dangling multi-dex location appended to bootclasspath can cause inaccuracy in oat file + // validation. For simplicity, just convert it to a normal location. + size_t pos = modified_location.find(art::DexFileLoader::kMultiDexSeparator); + if (pos != std::string::npos) { + modified_location[pos] = '-'; + } art::MemMap map = art::MemMap::MapAnonymous( - StringPrintf("%s-transformed", original_location.c_str()).c_str(), + modified_location.c_str(), data.size(), PROT_READ|PROT_WRITE, /*low_4gb=*/ false, @@ -545,6 +552,13 @@ Redefiner::ClassRedefinition::~ClassRedefinition() { if (driver_ != nullptr && lock_acquired_) { GetMirrorClass()->MonitorExit(driver_->self_); } + if (art::kIsDebugBuild) { + if (dex_file_ != nullptr) { + art::Thread* self = art::Thread::Current(); + art::ClassLinker* cl = art::Runtime::Current()->GetClassLinker(); + CHECK(!cl->IsDexFileRegistered(self, *dex_file_)); + } + } } template<RedefinitionType kType> @@ -711,10 +725,8 @@ jvmtiError Redefiner::AddRedefinition(ArtJvmTiEnv* env, const ArtClassDefinition } std::string name = map.GetName(); uint32_t checksum = reinterpret_cast<const art::DexFile::Header*>(map.Begin())->checksum_; - const art::ArtDexFileLoader dex_file_loader; - std::unique_ptr<const art::DexFile> dex_file(dex_file_loader.Open(name, - checksum, - std::move(map), + art::ArtDexFileLoader dex_file_loader(std::move(map), name); + std::unique_ptr<const art::DexFile> dex_file(dex_file_loader.Open(checksum, /*verify=*/true, /*verify_checksum=*/true, error_msg_)); @@ -1217,6 +1229,8 @@ class RedefinitionDataHolder { actually_structural_(redefinitions_->size(), false), initial_structural_(redefinitions_->size(), false) {} + ~RedefinitionDataHolder() REQUIRES_SHARED(art::Locks::mutator_lock_); + bool IsNull() const REQUIRES_SHARED(art::Locks::mutator_lock_) { return arr_.IsNull(); } @@ -1423,8 +1437,9 @@ class RedefinitionDataIter { RedefinitionDataIter(const RedefinitionDataIter&) = default; RedefinitionDataIter(RedefinitionDataIter&&) = default; - RedefinitionDataIter& operator=(const RedefinitionDataIter&) = default; - RedefinitionDataIter& operator=(RedefinitionDataIter&&) = default; + // Assignments are deleted because holder_ is a reference. + RedefinitionDataIter& operator=(const RedefinitionDataIter&) = delete; + RedefinitionDataIter& operator=(RedefinitionDataIter&&) = delete; bool operator==(const RedefinitionDataIter& other) const REQUIRES_SHARED(art::Locks::mutator_lock_) { @@ -1613,6 +1628,24 @@ RedefinitionDataIter RedefinitionDataHolder::end() { return RedefinitionDataIter(Length(), *this); } +RedefinitionDataHolder::~RedefinitionDataHolder() { + art::Thread* self = art::Thread::Current(); + art::ClassLinker* cl = art::Runtime::Current()->GetClassLinker(); + for (RedefinitionDataIter data = begin(); data != end(); ++data) { + art::ObjPtr<art::mirror::DexCache> dex_cache = data.GetNewDexCache(); + // When redefinition fails, the dex file will be deleted in the + // `ClassRedefinition` destructor. To avoid having a heap `DexCache` pointing + // to a dangling pointer, we clear the entries of those dex caches that are + // not registered in the runtime. + if (dex_cache != nullptr && + dex_cache->GetDexFile() != nullptr && + !cl->IsDexFileRegistered(self, *dex_cache->GetDexFile())) { + dex_cache->ResetNativeArrays(); + dex_cache->SetDexFile(nullptr); + } + } +} + bool Redefiner::ClassRedefinition::CheckVerification(const RedefinitionDataIter& iter) { DCHECK_EQ(dex_file_->NumClassDefs(), 1u); art::StackHandleScope<3> hs(driver_->self_); @@ -1651,10 +1684,12 @@ bool Redefiner::ClassRedefinition::AllocateAndRememberNewDexFileCookie( 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. + // See if we already have a cookie that a previous redefinition got from the same classloader + // and the same JavaDex file. for (auto old_data = cur_data->GetHolder().begin(); old_data != *cur_data; ++old_data) { - if (old_data.GetSourceClassLoader() == source_class_loader.Get()) { - // Since every instance of this classloader should have the same cookie associated with it we + if (old_data.GetSourceClassLoader() == source_class_loader.Get() && + old_data.GetJavaDexFile() == dex_file_obj.Get()) { + // Since every instance of this JavaDex file should have the same cookie associated with it we // can stop looking here. has_older_cookie = true; old_cookie.Assign(old_data.GetNewDexFileCookie()); @@ -1679,12 +1714,13 @@ bool Redefiner::ClassRedefinition::AllocateAndRememberNewDexFileCookie( // Save the cookie. cur_data->SetNewDexFileCookie(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 there are other copies of the same classloader and the same JavaDex file we need to + // make sure that we all have the same cookie. if (has_older_cookie) { for (auto old_data = cur_data->GetHolder().begin(); old_data != *cur_data; ++old_data) { // We will let the GC take care of the cookie we allocated for this one. - if (old_data.GetSourceClassLoader() == source_class_loader.Get()) { + if (old_data.GetSourceClassLoader() == source_class_loader.Get() && + old_data.GetJavaDexFile() == dex_file_obj.Get()) { old_data.SetNewDexFileCookie(new_cookie.Get()); } } @@ -1802,13 +1838,12 @@ bool Redefiner::ClassRedefinition::CollectAndCreateNewInstances( bool Redefiner::ClassRedefinition::FinishRemainingCommonAllocations( /*out*/RedefinitionDataIter* cur_data) { - art::ScopedObjectAccessUnchecked soa(driver_->self_); art::StackHandleScope<2> hs(driver_->self_); cur_data->SetMirrorClass(GetMirrorClass()); // This shouldn't allocate art::Handle<art::mirror::ClassLoader> loader(hs.NewHandle(GetClassLoader())); // The bootclasspath is handled specially so it doesn't have a j.l.DexFile. - if (!art::ClassLinker::IsBootClassLoader(soa, loader.Get())) { + if (!art::ClassLinker::IsBootClassLoader(loader.Get())) { cur_data->SetSourceClassLoader(loader.Get()); art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle( ClassLoaderHelper::FindSourceDexFileObject(driver_->self_, loader))); @@ -2218,6 +2253,11 @@ bool Redefiner::FinishAllRemainingCommonAllocations(RedefinitionDataHolder& hold } void Redefiner::ClassRedefinition::ReleaseDexFile() { + if (art::kIsDebugBuild) { + art::Thread* self = art::Thread::Current(); + art::ClassLinker* cl = art::Runtime::Current()->GetClassLinker(); + CHECK(cl->IsDexFileRegistered(self, *dex_file_)); + } dex_file_.release(); // NOLINT b/117926937 } @@ -2477,7 +2517,9 @@ jvmtiError Redefiner::Run() { art::ClassLinker* cl = runtime_->GetClassLinker(); if (data.GetSourceClassLoader() == nullptr) { // AppendToBootClassPath includes dex file registration. - cl->AppendToBootClassPath(&data.GetRedefinition().GetDexFile(), data.GetNewDexCache()); + const art::DexFile& dex_file = data.GetRedefinition().GetDexFile(); + runtime_->AppendToBootClassPath( + dex_file.GetLocation(), dex_file.GetLocation(), {{&dex_file, data.GetNewDexCache()}}); } else { cl->RegisterExistingDexCache(data.GetNewDexCache(), data.GetSourceClassLoader()); } @@ -2910,6 +2952,27 @@ void Redefiner::ClassRedefinition::UpdateClassStructurally(const RedefinitionDat // be undone. This replaces the mirror::Class in 'holder' as well. It's magic! HeapExtensions::ReplaceReferences(driver_->self_, map); + // Undo the replacement of old_class with new_class for the methods / fields on the old_class. + // It is hard to ensure that we don't replace the declaring class of the old class field / methods + // isn't impacted by ReplaceReferences. It is just simpler to undo the replacement here. + std::for_each( + old_classes_vec.cbegin(), + old_classes_vec.cend(), + [](art::ObjPtr<art::mirror::Class> orig) REQUIRES_SHARED(art::Locks::mutator_lock_) { + orig->VisitMethods( + [&](art::ArtMethod* method) REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (method->IsCopied()) { + // Copied methods have interfaces as their declaring class. + return; + } + method->SetDeclaringClass(orig); + }, + art::kRuntimePointerSize); + orig->VisitFields([&](art::ArtField* field) REQUIRES_SHARED(art::Locks::mutator_lock_) { + field->SetDeclaringClass(orig); + }); + }); + // Save the old class so that the JIT gc doesn't get confused by it being collected before the // jit code. This is also needed to keep the dex-caches of any obsolete methods live. for (auto [new_class, old_class] : @@ -3081,10 +3144,14 @@ bool Redefiner::ClassRedefinition::EnsureClassAllocationsFinished( // First save the old values of the 2 arrays that make up the obsolete methods maps. Then // allocate the 2 arrays that make up the obsolete methods map. Since the contents of the arrays // are only modified when all threads (other than the modifying one) are suspended we don't need - // to worry about missing the unsyncronized writes to the array. We do synchronize when setting + // to worry about missing the unsynchronized writes to the array. We do synchronize when setting // it however, since that can happen at any time. cur_data->SetOldObsoleteMethods(ext->GetObsoleteMethods()); cur_data->SetOldDexCaches(ext->GetObsoleteDexCaches()); + // FIXME: The `ClassExt::ExtendObsoleteArrays()` is non-atomic and does not ensure proper + // memory visibility, so it can race with `ArtMethod::GetObsoleteDexCache()`. + // We should allocate the new arrays here but record it in the redefinition data and set the + // new arrays in `ClassExt` later with all other threads suspended. if (!art::mirror::ClassExt::ExtendObsoleteArrays( ext, driver_->self_, klass->GetDeclaredMethodsSlice(art::kRuntimePointerSize).size())) { // OOM. Clear exception and return error. diff --git a/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h index 85b1070516..cdd662787c 100644 --- a/openjdkjvmti/ti_redefine.h +++ b/openjdkjvmti/ti_redefine.h @@ -126,7 +126,7 @@ class Redefiner { ~ClassRedefinition() NO_THREAD_SAFETY_ANALYSIS; // Move assignment so we can sort these in a vector. - ClassRedefinition& operator=(ClassRedefinition&& other) { + ClassRedefinition& operator=(ClassRedefinition&& other) noexcept { driver_ = other.driver_; klass_ = other.klass_; dex_file_ = std::move(other.dex_file_); @@ -138,7 +138,7 @@ class Redefiner { } // Move constructor so we can put these into a vector. - ClassRedefinition(ClassRedefinition&& other) + ClassRedefinition(ClassRedefinition&& other) noexcept : driver_(other.driver_), klass_(other.klass_), dex_file_(std::move(other.dex_file_)), diff --git a/openjdkjvmti/ti_search.cc b/openjdkjvmti/ti_search.cc index 526836ecfd..2e98e0db9f 100644 --- a/openjdkjvmti/ti_search.cc +++ b/openjdkjvmti/ti_search.cc @@ -60,7 +60,7 @@ #include "thread_list.h" #include "ti_logging.h" #include "ti_phase.h" -#include "well_known_classes.h" +#include "well_known_classes-inl.h" namespace openjdkjvmti { @@ -235,10 +235,8 @@ jvmtiError SearchUtil::AddToBootstrapClassLoaderSearch(jvmtiEnv* env, std::string error_msg; std::vector<std::unique_ptr<const art::DexFile>> dex_files; - const art::ArtDexFileLoader dex_file_loader; - if (!dex_file_loader.Open(segment, - segment, - /* verify= */ true, + art::ArtDexFileLoader dex_file_loader(segment); + if (!dex_file_loader.Open(/* verify= */ true, /* verify_checksum= */ true, &error_msg, &dex_files)) { @@ -247,12 +245,8 @@ jvmtiError SearchUtil::AddToBootstrapClassLoaderSearch(jvmtiEnv* env, return ERR(ILLEGAL_ARGUMENT); } - art::ScopedObjectAccess soa(art::Thread::Current()); - for (std::unique_ptr<const art::DexFile>& dex_file : dex_files) { - current->GetClassLinker()->AppendToBootClassPath(art::Thread::Current(), dex_file.release()); - } - - return ERR(NONE); + current->AddExtraBootDexFiles(segment, segment, std::move(dex_files)); + return OK; } jvmtiError SearchUtil::AddToDexClassLoaderInMemory(jvmtiEnv* jvmti_env, @@ -343,33 +337,33 @@ jvmtiError SearchUtil::AddToDexClassLoader(jvmtiEnv* jvmti_env, // exceptions are swallowed. art::Thread* self = art::Thread::Current(); - JNIEnv* env = self->GetJniEnv(); - if (!env->IsInstanceOf(classloader, art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) { + art::ScopedObjectAccess soa(self); + art::StackHandleScope<2u> hs(self); + art::Handle<art::mirror::ClassLoader> class_loader = + hs.NewHandle(soa.Decode<art::mirror::ClassLoader>(classloader)); + if (!class_loader->InstanceOf(art::WellKnownClasses::dalvik_system_BaseDexClassLoader.Get())) { JVMTI_LOG(ERROR, jvmti_env) << "Unable to add " << segment << " to non BaseDexClassLoader!"; return ERR(CLASS_LOADER_UNSUPPORTED); } - jmethodID add_dex_path_id = env->GetMethodID( - art::WellKnownClasses::dalvik_system_BaseDexClassLoader, - "addDexPath", - "(Ljava/lang/String;)V"); + art::ArtMethod* add_dex_path_id = + art::WellKnownClasses::dalvik_system_BaseDexClassLoader->FindClassMethod( + "addDexPath", "(Ljava/lang/String;)V", art::kRuntimePointerSize); if (add_dex_path_id == nullptr) { return ERR(INTERNAL); } - ScopedLocalRef<jstring> dex_path(env, env->NewStringUTF(segment)); - if (dex_path.get() == nullptr) { + art::Handle<art::mirror::String> dex_path = + hs.NewHandle(art::mirror::String::AllocFromModifiedUtf8(self, segment)); + if (dex_path == nullptr) { return ERR(INTERNAL); } - env->CallVoidMethod(classloader, add_dex_path_id, dex_path.get()); - if (env->ExceptionCheck()) { - { - art::ScopedObjectAccess soa(self); - JVMTI_LOG(ERROR, jvmti_env) << "Failed to add " << segment << " to classloader. Error was " - << self->GetException()->Dump(); - } - env->ExceptionClear(); + add_dex_path_id->InvokeVirtual<'V', 'L'>(self, class_loader.Get(), dex_path.Get()); + if (self->IsExceptionPending()) { + JVMTI_LOG(ERROR, jvmti_env) << "Failed to add " << segment << " to classloader. Error was " + << self->GetException()->Dump(); + self->ClearException(); return ERR(ILLEGAL_ARGUMENT); } return OK; @@ -396,10 +390,13 @@ jvmtiError SearchUtil::AddToSystemClassLoaderSearch(jvmtiEnv* jvmti_env, const c return ERR(INTERNAL); } - art::Thread* self = art::Thread::Current(); - JNIEnv* env = self->GetJniEnv(); - if (!env->IsInstanceOf(loader, art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) { - return ERR(INTERNAL); + { + art::ScopedObjectAccess soa(art::Thread::Current()); + art::ObjPtr<art::mirror::ClassLoader> class_loader = + soa.Decode<art::mirror::ClassLoader>(loader); + if (!class_loader->InstanceOf(art::WellKnownClasses::dalvik_system_BaseDexClassLoader.Get())) { + return ERR(INTERNAL); + } } return AddToDexClassLoader(jvmti_env, loader, segment); diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc index 38257f1d6a..8ee4adb853 100644 --- a/openjdkjvmti/ti_stack.cc +++ b/openjdkjvmti/ti_stack.cc @@ -41,7 +41,6 @@ #include "android-base/thread_annotations.h" #include "arch/context.h" #include "art_field-inl.h" -#include "art_method-inl.h" #include "art_jvmti.h" #include "art_method-inl.h" #include "barrier.h" @@ -74,15 +73,14 @@ #include "scoped_thread_state_change-inl.h" #include "scoped_thread_state_change.h" #include "stack.h" -#include "thread.h" -#include "thread_state.h" -#include "ti_logging.h" -#include "ti_thread.h" #include "thread-current-inl.h" +#include "thread.h" #include "thread_list.h" #include "thread_pool.h" +#include "thread_state.h" +#include "ti_logging.h" #include "ti_thread.h" -#include "well_known_classes.h" +#include "well_known_classes-inl.h" namespace openjdkjvmti { @@ -578,10 +576,11 @@ jvmtiError StackUtil::GetThreadListStackTraces(jvmtiEnv* env, if (thread_list[i] == nullptr) { return ERR(INVALID_THREAD); } - if (!soa.Env()->IsInstanceOf(thread_list[i], art::WellKnownClasses::java_lang_Thread)) { + art::ObjPtr<art::mirror::Object> thread = soa.Decode<art::mirror::Object>(thread_list[i]); + if (!thread->InstanceOf(art::WellKnownClasses::java_lang_Thread.Get())) { return ERR(INVALID_THREAD); } - data.handles.push_back(hs.NewHandle(soa.Decode<art::mirror::Object>(thread_list[i]))); + data.handles.push_back(hs.NewHandle(thread)); } RunCheckpointAndWait(&data, static_cast<size_t>(max_frame_count)); diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc index f31759ee34..b5bc35e7e6 100644 --- a/openjdkjvmti/ti_thread.cc +++ b/openjdkjvmti/ti_thread.cc @@ -59,7 +59,7 @@ #include "thread-current-inl.h" #include "thread_list.h" #include "ti_phase.h" -#include "well_known_classes.h" +#include "well_known_classes-inl.h" namespace openjdkjvmti { @@ -131,6 +131,7 @@ struct ThreadCallback : public art::ThreadLifecycleCallback { if (name != "JDWP" && name != "Signal Catcher" && name != "perfetto_hprof_listener" && name != art::metrics::MetricsReporter::kBackgroundThreadName && !android::base::StartsWith(name, "Jit thread pool") && + !android::base::StartsWith(name, "Heap thread pool worker thread") && !android::base::StartsWith(name, "Runtime worker thread")) { LOG(FATAL) << "Unexpected thread before start: " << name << " id: " << self->GetThreadId(); @@ -173,12 +174,7 @@ void ThreadUtil::VMInitEventSent() { static void WaitForSystemDaemonStart(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_) { - { - art::ScopedThreadStateChange strc(self, art::ThreadState::kNative); - JNIEnv* jni = self->GetJniEnv(); - jni->CallStaticVoidMethod(art::WellKnownClasses::java_lang_Daemons, - art::WellKnownClasses::java_lang_Daemons_waitForDaemonStart); - } + art::WellKnownClasses::java_lang_Daemons_waitForDaemonStart->InvokeStatic<'V'>(self); if (self->IsExceptionPending()) { LOG(WARNING) << "Exception occurred when waiting for system daemons to start: " << self->GetException()->Dump(); @@ -191,8 +187,7 @@ void ThreadUtil::CacheData() { gThreadCallback.started = true; art::Thread* self = art::Thread::Current(); art::ScopedObjectAccess soa(self); - art::ObjPtr<art::mirror::Class> thread_class = - soa.Decode<art::mirror::Class>(art::WellKnownClasses::java_lang_Thread); + art::ObjPtr<art::mirror::Class> thread_class = art::WellKnownClasses::java_lang_Thread.Get(); CHECK(thread_class != nullptr); context_class_loader_ = thread_class->FindDeclaredInstanceField("contextClassLoader", "Ljava/lang/ClassLoader;"); @@ -235,7 +230,9 @@ bool ThreadUtil::GetNativeThread(jthread thread, if (thread == nullptr) { *thr = art::Thread::Current(); return true; - } else if (!soa.Env()->IsInstanceOf(thread, art::WellKnownClasses::java_lang_Thread)) { + } + art::ObjPtr<art::mirror::Object> othread = soa.Decode<art::mirror::Object>(thread); + if (!othread->InstanceOf(art::WellKnownClasses::java_lang_Thread.Get())) { *err = ERR(INVALID_THREAD); return false; } else { @@ -296,7 +293,7 @@ jvmtiError ThreadUtil::GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadI // ThreadGroup. if (peer != nullptr) { - art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group); + art::ArtField* f = art::WellKnownClasses::java_lang_Thread_group; CHECK(f != nullptr); art::ObjPtr<art::mirror::Object> group = f->GetObject(peer); info_ptr->thread_group = group == nullptr @@ -321,7 +318,7 @@ jvmtiError ThreadUtil::GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadI // Name. { - art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_name); + art::ArtField* f = art::WellKnownClasses::java_lang_Thread_name; CHECK(f != nullptr); art::ObjPtr<art::mirror::Object> name = f->GetObject(peer); std::string name_cpp; @@ -342,21 +339,21 @@ jvmtiError ThreadUtil::GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadI // Priority. { - art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_priority); + art::ArtField* f = art::WellKnownClasses::java_lang_Thread_priority; CHECK(f != nullptr); info_ptr->priority = static_cast<jint>(f->GetInt(peer)); } // Daemon. { - art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_daemon); + art::ArtField* f = art::WellKnownClasses::java_lang_Thread_daemon; CHECK(f != nullptr); info_ptr->is_daemon = f->GetBoolean(peer) == 0 ? JNI_FALSE : JNI_TRUE; } // ThreadGroup. { - art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group); + art::ArtField* f = art::WellKnownClasses::java_lang_Thread_group; CHECK(f != nullptr); art::ObjPtr<art::mirror::Object> group = f->GetObject(peer); info_ptr->thread_group = group == nullptr @@ -616,8 +613,7 @@ jvmtiError ThreadUtil::GetThreadState(jvmtiEnv* env ATTRIBUTE_UNUSED, // Need to read the Java "started" field to know whether this is starting or terminated. art::Handle<art::mirror::Object> peer(hs.NewHandle(soa.Decode<art::mirror::Object>(thread))); - art::ObjPtr<art::mirror::Class> thread_klass = - soa.Decode<art::mirror::Class>(art::WellKnownClasses::java_lang_Thread); + art::ObjPtr<art::mirror::Class> thread_klass = art::WellKnownClasses::java_lang_Thread.Get(); if (!thread_klass->IsAssignableFrom(peer->GetClass())) { return ERR(INVALID_THREAD); } @@ -814,46 +810,52 @@ jvmtiError ThreadUtil::RunAgentThread(jvmtiEnv* jvmti_env, if (priority < JVMTI_THREAD_MIN_PRIORITY || priority > JVMTI_THREAD_MAX_PRIORITY) { return ERR(INVALID_PRIORITY); } - JNIEnv* env = art::Thread::Current()->GetJniEnv(); - if (thread == nullptr || !env->IsInstanceOf(thread, art::WellKnownClasses::java_lang_Thread)) { + if (thread == nullptr) { return ERR(INVALID_THREAD); } - if (proc == nullptr) { - return ERR(NULL_POINTER); - } - + art::Runtime* runtime = art::Runtime::Current(); + art::Thread* self = art::Thread::Current(); + std::unique_ptr<AgentData> data; { - art::Runtime* runtime = art::Runtime::Current(); - art::MutexLock mu(art::Thread::Current(), *art::Locks::runtime_shutdown_lock_); - if (runtime->IsShuttingDownLocked()) { - // The runtime is shutting down so we cannot create new threads. - // TODO It's not fully clear from the spec what we should do here. We aren't yet in - // JVMTI_PHASE_DEAD so we cannot return ERR(WRONG_PHASE) but creating new threads is now - // impossible. Existing agents don't seem to generally do anything with this return value so - // it doesn't matter too much. We could do something like sending a fake ThreadStart event - // even though code is never actually run. - return ERR(INTERNAL); + art::ScopedObjectAccess soa(self); + art::ObjPtr<art::mirror::Object> othread = soa.Decode<art::mirror::Object>(thread); + if (!othread->InstanceOf(art::WellKnownClasses::java_lang_Thread.Get())) { + return ERR(INVALID_THREAD); + } + if (proc == nullptr) { + return ERR(NULL_POINTER); } - runtime->StartThreadBirth(); - } - std::unique_ptr<AgentData> data(new AgentData); - data->arg = arg; - data->proc = proc; - // We need a global ref for Java objects, as local refs will be invalid. - data->thread = env->NewGlobalRef(thread); - data->java_vm = art::Runtime::Current()->GetJavaVM(); - data->jvmti_env = jvmti_env; - data->priority = priority; - ScopedLocalRef<jstring> s( - env, - reinterpret_cast<jstring>( - env->GetObjectField(thread, art::WellKnownClasses::java_lang_Thread_name))); - if (s == nullptr) { - data->name = "JVMTI Agent Thread"; - } else { - ScopedUtfChars name(env, s.get()); - data->name = name.c_str(); + { + art::MutexLock mu(soa.Self(), *art::Locks::runtime_shutdown_lock_); + if (runtime->IsShuttingDownLocked()) { + // The runtime is shutting down so we cannot create new threads. + // TODO It's not fully clear from the spec what we should do here. We aren't yet in + // JVMTI_PHASE_DEAD so we cannot return ERR(WRONG_PHASE) but creating new threads is now + // impossible. Existing agents don't seem to generally do anything with this return value so + // it doesn't matter too much. We could do something like sending a fake ThreadStart event + // even though code is never actually run. + return ERR(INTERNAL); + } + runtime->StartThreadBirth(); + } + + data.reset(new AgentData); + data->arg = arg; + data->proc = proc; + // We need a global ref for Java objects, as local refs will be invalid. + data->thread = runtime->GetJavaVM()->AddGlobalRef(soa.Self(), othread); + data->java_vm = runtime->GetJavaVM(); + data->jvmti_env = jvmti_env; + data->priority = priority; + art::ObjPtr<art::mirror::Object> name = + art::WellKnownClasses::java_lang_Thread_name->GetObject( + soa.Decode<art::mirror::Object>(thread)); + if (name == nullptr) { + data->name = "JVMTI Agent Thread"; + } else { + data->name = name->AsString()->ToModifiedUtf8(); + } } pthread_t pthread; @@ -863,8 +865,7 @@ jvmtiError ThreadUtil::RunAgentThread(jvmtiEnv* jvmti_env, reinterpret_cast<void*>(data.get())); if (pthread_create_result != 0) { // If the create succeeded the other thread will call EndThreadBirth. - art::Runtime* runtime = art::Runtime::Current(); - art::MutexLock mu(art::Thread::Current(), *art::Locks::runtime_shutdown_lock_); + art::MutexLock mu(self, *art::Locks::runtime_shutdown_lock_); runtime->EndThreadBirth(); return ERR(INTERNAL); } diff --git a/openjdkjvmti/ti_threadgroup.cc b/openjdkjvmti/ti_threadgroup.cc index bc912cf6a5..120024e8b2 100644 --- a/openjdkjvmti/ti_threadgroup.cc +++ b/openjdkjvmti/ti_threadgroup.cc @@ -47,7 +47,7 @@ #include "scoped_thread_state_change-inl.h" #include "thread-current-inl.h" #include "thread_list.h" -#include "well_known_classes.h" +#include "well_known_classes-inl.h" namespace openjdkjvmti { @@ -95,22 +95,21 @@ jvmtiError ThreadGroupUtil::GetThreadGroupInfo(jvmtiEnv* env, } art::ScopedObjectAccess soa(art::Thread::Current()); - if (soa.Env()->IsInstanceOf(group, art::WellKnownClasses::java_lang_ThreadGroup) == JNI_FALSE) { + art::StackHandleScope<2> hs(soa.Self()); + art::Handle<art::mirror::Class> tg_class = + hs.NewHandle(art::WellKnownClasses::java_lang_ThreadGroup.Get()); + art::Handle<art::mirror::Object> thread_group = + hs.NewHandle(soa.Decode<art::mirror::Object>(group)); + if (!thread_group->InstanceOf(tg_class.Get())) { return ERR(INVALID_THREAD_GROUP); } - art::StackHandleScope<2> hs(soa.Self()); - art::Handle<art::mirror::Class> tg_class( - hs.NewHandle(soa.Decode<art::mirror::Class>(art::WellKnownClasses::java_lang_ThreadGroup))); - art::Handle<art::mirror::Object> obj(hs.NewHandle(soa.Decode<art::mirror::Object>(group))); - // Do the name first. It's the only thing that can fail. { - art::ArtField* name_field = - art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_name); + art::ArtField* name_field = art::WellKnownClasses::java_lang_ThreadGroup_name; CHECK(name_field != nullptr); art::ObjPtr<art::mirror::String> name_obj = - art::ObjPtr<art::mirror::String>::DownCast(name_field->GetObject(obj.Get())); + art::ObjPtr<art::mirror::String>::DownCast(name_field->GetObject(thread_group.Get())); std::string tmp_str; const char* tmp_cstr; if (name_obj == nullptr) { @@ -129,10 +128,9 @@ jvmtiError ThreadGroupUtil::GetThreadGroupInfo(jvmtiEnv* env, // Parent. { - art::ArtField* parent_field = - art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_parent); + art::ArtField* parent_field = art::WellKnownClasses::java_lang_ThreadGroup_parent; CHECK(parent_field != nullptr); - art::ObjPtr<art::mirror::Object> parent_group = parent_field->GetObject(obj.Get()); + art::ObjPtr<art::mirror::Object> parent_group = parent_field->GetObject(thread_group.Get()); info_ptr->parent = parent_group == nullptr ? nullptr : soa.AddLocalReference<jthreadGroup>(parent_group); @@ -142,14 +140,14 @@ jvmtiError ThreadGroupUtil::GetThreadGroupInfo(jvmtiEnv* env, { art::ArtField* prio_field = tg_class->FindDeclaredInstanceField("maxPriority", "I"); CHECK(prio_field != nullptr); - info_ptr->max_priority = static_cast<jint>(prio_field->GetInt(obj.Get())); + info_ptr->max_priority = static_cast<jint>(prio_field->GetInt(thread_group.Get())); } // Daemon. { art::ArtField* daemon_field = tg_class->FindDeclaredInstanceField("daemon", "Z"); CHECK(daemon_field != nullptr); - info_ptr->is_daemon = daemon_field->GetBoolean(obj.Get()) == 0 ? JNI_FALSE : JNI_TRUE; + info_ptr->is_daemon = daemon_field->GetBoolean(thread_group.Get()) == 0 ? JNI_FALSE : JNI_TRUE; } return ERR(NONE); @@ -161,8 +159,7 @@ static bool IsInDesiredThreadGroup(art::Handle<art::mirror::Object> desired_thre REQUIRES_SHARED(art::Locks::mutator_lock_) { CHECK(desired_thread_group != nullptr); - art::ArtField* thread_group_field = - art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group); + art::ArtField* thread_group_field = art::WellKnownClasses::java_lang_Thread_group; DCHECK(thread_group_field != nullptr); art::ObjPtr<art::mirror::Object> group = thread_group_field->GetObject(peer); return (group == desired_thread_group.Get()); @@ -194,8 +191,7 @@ static void GetChildThreadGroups(art::Handle<art::mirror::Object> thread_group, CHECK(thread_group != nullptr); // Get the ThreadGroup[] "groups" out of this thread group... - art::ArtField* groups_field = - art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_groups); + art::ArtField* groups_field = art::WellKnownClasses::java_lang_ThreadGroup_groups; art::ObjPtr<art::mirror::Object> groups_array = groups_field->GetObject(thread_group.Get()); if (groups_array == nullptr) { @@ -225,14 +221,13 @@ jvmtiError ThreadGroupUtil::GetThreadGroupChildren(jvmtiEnv* env, } art::ScopedObjectAccess soa(art::Thread::Current()); - - if (!soa.Env()->IsInstanceOf(group, art::WellKnownClasses::java_lang_ThreadGroup)) { + art::StackHandleScope<1> hs(soa.Self()); + art::Handle<art::mirror::Object> thread_group = + hs.NewHandle(soa.Decode<art::mirror::Object>(group)); + if (!thread_group->InstanceOf(art::WellKnownClasses::java_lang_ThreadGroup.Get())) { return ERR(INVALID_THREAD_GROUP); } - art::StackHandleScope<1> hs(soa.Self()); - art::Handle<art::mirror::Object> thread_group = hs.NewHandle( - soa.Decode<art::mirror::Object>(group)); art::ObjectLock<art::mirror::Object> thread_group_lock(soa.Self(), thread_group); diff --git a/openjdkjvmti/transform.cc b/openjdkjvmti/transform.cc index 1f36da435a..bccdcb18af 100644 --- a/openjdkjvmti/transform.cc +++ b/openjdkjvmti/transform.cc @@ -29,14 +29,14 @@ * questions. */ +#include "transform.h" + #include <stddef.h> #include <sys/types.h> #include <unordered_map> #include <unordered_set> -#include "transform.h" - #include "art_method.h" #include "base/array_ref.h" #include "base/globals.h" @@ -64,9 +64,8 @@ #include "scoped_thread_state_change-inl.h" #include "stack.h" #include "thread_list.h" -#include "ti_redefine.h" #include "ti_logging.h" -#include "transform.h" +#include "ti_redefine.h" namespace openjdkjvmti { |