summaryrefslogtreecommitdiff
path: root/openjdkjvmti
diff options
context:
space:
mode:
Diffstat (limited to 'openjdkjvmti')
-rw-r--r--openjdkjvmti/Android.bp7
-rw-r--r--openjdkjvmti/LICENSE (renamed from openjdkjvmti/NOTICE)0
-rw-r--r--openjdkjvmti/OpenjdkJvmTi.cc22
-rw-r--r--openjdkjvmti/art_jvmti.h13
-rw-r--r--openjdkjvmti/deopt_manager.cc150
-rw-r--r--openjdkjvmti/deopt_manager.h7
-rw-r--r--openjdkjvmti/events.cc36
-rw-r--r--openjdkjvmti/events.h4
-rw-r--r--openjdkjvmti/jvmti_allocator.h24
-rw-r--r--openjdkjvmti/jvmti_weak_table-inl.h38
-rw-r--r--openjdkjvmti/jvmti_weak_table.h16
-rw-r--r--openjdkjvmti/ti_class.cc90
-rw-r--r--openjdkjvmti/ti_class_definition.cc6
-rw-r--r--openjdkjvmti/ti_class_loader-inl.h4
-rw-r--r--openjdkjvmti/ti_class_loader.cc18
-rw-r--r--openjdkjvmti/ti_extension.cc6
-rw-r--r--openjdkjvmti/ti_heap.cc4
-rw-r--r--openjdkjvmti/ti_redefine.cc115
-rw-r--r--openjdkjvmti/ti_redefine.h4
-rw-r--r--openjdkjvmti/ti_search.cc59
-rw-r--r--openjdkjvmti/ti_stack.cc15
-rw-r--r--openjdkjvmti/ti_thread.cc109
-rw-r--r--openjdkjvmti/ti_threadgroup.cc43
-rw-r--r--openjdkjvmti/transform.cc7
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 {