ART: Fix GetThreadInfo
Fix the retrieval of the context classloader. Make sure to have
the field from the Thread class, don't look into a subclass.
Add caching for the field. Also fix the usage restriction of the
function, which is only valid during the Live phase.
Bug: 36654185
Test: ./test.py --host -r -t 924
Change-Id: I5cce41b31c32e59c80bb7c3afa03e8b0975ec54e
diff --git a/runtime/openjdkjvmti/ti_phase.cc b/runtime/openjdkjvmti/ti_phase.cc
index e494cb6..941cf7b 100644
--- a/runtime/openjdkjvmti/ti_phase.cc
+++ b/runtime/openjdkjvmti/ti_phase.cc
@@ -40,6 +40,7 @@
#include "scoped_thread_state_change-inl.h"
#include "thread-inl.h"
#include "thread_list.h"
+#include "ti_thread.h"
namespace openjdkjvmti {
@@ -69,6 +70,7 @@
break;
case RuntimePhase::kInit:
{
+ ThreadUtil::CacheData();
ScopedLocalRef<jthread> thread(GetJniEnv(), GetCurrentJThread());
art::ScopedThreadSuspension sts(art::Thread::Current(), art::ThreadState::kNative);
event_handler->DispatchEvent<ArtJvmtiEvent::kVmInit>(nullptr, GetJniEnv(), thread.get());
@@ -105,6 +107,16 @@
return ERR(NONE);
}
+bool PhaseUtil::IsLivePhase() {
+ jvmtiPhase now = PhaseUtil::current_phase_;
+ DCHECK(now == JVMTI_PHASE_ONLOAD ||
+ now == JVMTI_PHASE_PRIMORDIAL ||
+ now == JVMTI_PHASE_START ||
+ now == JVMTI_PHASE_LIVE ||
+ now == JVMTI_PHASE_DEAD);
+ return now == JVMTI_PHASE_LIVE;
+}
+
void PhaseUtil::SetToOnLoad() {
DCHECK_EQ(0u, static_cast<size_t>(PhaseUtil::current_phase_));
PhaseUtil::current_phase_ = JVMTI_PHASE_ONLOAD;
@@ -117,6 +129,7 @@
void PhaseUtil::SetToLive() {
DCHECK_EQ(static_cast<size_t>(0), static_cast<size_t>(PhaseUtil::current_phase_));
+ ThreadUtil::CacheData();
PhaseUtil::current_phase_ = JVMTI_PHASE_LIVE;
}
diff --git a/runtime/openjdkjvmti/ti_phase.h b/runtime/openjdkjvmti/ti_phase.h
index 851fc27..a2c0d11 100644
--- a/runtime/openjdkjvmti/ti_phase.h
+++ b/runtime/openjdkjvmti/ti_phase.h
@@ -42,6 +42,7 @@
class PhaseUtil {
public:
static jvmtiError GetPhase(jvmtiEnv* env, jvmtiPhase* phase_ptr);
+ static bool IsLivePhase();
static void Register(EventHandler* event_handler);
static void Unregister();
diff --git a/runtime/openjdkjvmti/ti_thread.cc b/runtime/openjdkjvmti/ti_thread.cc
index 788ac30..e5ff090 100644
--- a/runtime/openjdkjvmti/ti_thread.cc
+++ b/runtime/openjdkjvmti/ti_thread.cc
@@ -44,6 +44,7 @@
#include "mirror/object-inl.h"
#include "mirror/string.h"
#include "obj_ptr.h"
+#include "ti_phase.h"
#include "runtime.h"
#include "runtime_callbacks.h"
#include "ScopedLocalRef.h"
@@ -54,6 +55,8 @@
namespace openjdkjvmti {
+art::ArtField* ThreadUtil::context_class_loader_ = nullptr;
+
struct ThreadCallback : public art::ThreadLifecycleCallback, public art::RuntimePhaseCallback {
jthread GetThreadObject(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_) {
if (self->GetPeer() == nullptr) {
@@ -121,6 +124,16 @@
runtime->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&gThreadCallback);
}
+void ThreadUtil::CacheData() {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ObjPtr<art::mirror::Class> thread_class =
+ soa.Decode<art::mirror::Class>(art::WellKnownClasses::java_lang_Thread);
+ CHECK(thread_class != nullptr);
+ context_class_loader_ = thread_class->FindDeclaredInstanceField("contextClassLoader",
+ "Ljava/lang/ClassLoader;");
+ CHECK(context_class_loader_ != nullptr);
+}
+
void ThreadUtil::Unregister() {
art::ScopedThreadStateChange stsc(art::Thread::Current(),
art::ThreadState::kWaitingForDebuggerToAttach);
@@ -146,22 +159,6 @@
return ERR(NONE);
}
-// Read the context classloader from a Java thread object. This is a lazy implementation
-// that assumes GetThreadInfo isn't called too often. If we instead cache the ArtField,
-// we will have to add synchronization as this can't be cached on startup (which is
-// potentially runtime startup).
-static art::ObjPtr<art::mirror::Object> GetContextClassLoader(art::ObjPtr<art::mirror::Object> peer)
- REQUIRES_SHARED(art::Locks::mutator_lock_) {
- if (peer == nullptr) {
- return nullptr;
- }
- art::ObjPtr<art::mirror::Class> klass = peer->GetClass();
- art::ArtField* cc_field = klass->FindDeclaredInstanceField("contextClassLoader",
- "Ljava/lang/ClassLoader;");
- CHECK(cc_field != nullptr);
- return cc_field->GetObject(peer);
-}
-
// Get the native thread. The spec says a null object denotes the current thread.
static art::Thread* GetNativeThread(jthread thread,
const art::ScopedObjectAccessAlreadyRunnable& soa)
@@ -178,6 +175,9 @@
if (info_ptr == nullptr) {
return ERR(NULL_POINTER);
}
+ if (!PhaseUtil::IsLivePhase()) {
+ return JVMTI_ERROR_WRONG_PHASE;
+ }
art::ScopedObjectAccess soa(art::Thread::Current());
@@ -217,7 +217,10 @@
}
// Context classloader.
- art::ObjPtr<art::mirror::Object> ccl = GetContextClassLoader(peer);
+ DCHECK(context_class_loader_ != nullptr);
+ art::ObjPtr<art::mirror::Object> ccl = peer != nullptr
+ ? context_class_loader_->GetObject(peer)
+ : nullptr;
info_ptr->context_class_loader = ccl == nullptr
? nullptr
: soa.AddLocalReference<jobject>(ccl);
@@ -272,7 +275,10 @@
}
// Context classloader.
- art::ObjPtr<art::mirror::Object> ccl = GetContextClassLoader(peer);
+ DCHECK(context_class_loader_ != nullptr);
+ art::ObjPtr<art::mirror::Object> ccl = peer != nullptr
+ ? context_class_loader_->GetObject(peer)
+ : nullptr;
info_ptr->context_class_loader = ccl == nullptr
? nullptr
: soa.AddLocalReference<jobject>(ccl);
diff --git a/runtime/openjdkjvmti/ti_thread.h b/runtime/openjdkjvmti/ti_thread.h
index f6f93ee..c7f75d8 100644
--- a/runtime/openjdkjvmti/ti_thread.h
+++ b/runtime/openjdkjvmti/ti_thread.h
@@ -35,6 +35,10 @@
#include "jni.h"
#include "jvmti.h"
+namespace art {
+class ArtField;
+}
+
namespace openjdkjvmti {
class EventHandler;
@@ -44,6 +48,9 @@
static void Register(EventHandler* event_handler);
static void Unregister();
+ // To be called when it is safe to cache data.
+ static void CacheData();
+
static jvmtiError GetAllThreads(jvmtiEnv* env, jint* threads_count_ptr, jthread** threads_ptr);
static jvmtiError GetCurrentThread(jvmtiEnv* env, jthread* thread_ptr);
@@ -60,6 +67,9 @@
jvmtiStartFunction proc,
const void* arg,
jint priority);
+
+ private:
+ static art::ArtField* context_class_loader_;
};
} // namespace openjdkjvmti