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
diff --git a/test/924-threads/expected.txt b/test/924-threads/expected.txt
index 67d20eb..4c0f4ea 100644
--- a/test/924-threads/expected.txt
+++ b/test/924-threads/expected.txt
@@ -19,6 +19,11 @@
 true
 java.lang.ThreadGroup[name=main,maxpri=10]
 class dalvik.system.PathClassLoader
+Subclass
+5
+false
+java.lang.ThreadGroup[name=main,maxpri=10]
+class dalvik.system.PathClassLoader
 5
 5
 0 = NEW
diff --git a/test/924-threads/src/Main.java b/test/924-threads/src/Main.java
index 716f59e..7328560 100644
--- a/test/924-threads/src/Main.java
+++ b/test/924-threads/src/Main.java
@@ -52,6 +52,11 @@
     // Thread has died, check that we can still get info.
     printThreadInfo(t3);
 
+    // Try a subclass of thread.
+    Thread t4 = new Thread("Subclass") {
+    };
+    printThreadInfo(t4);
+
     doStateTests();
 
     doAllThreadsTests();