ART: Add marking to thread peer gathering in TI

When CC is marking, we may get stale references due to a missing
mark.

Bug: 34760612
Test: ART_TEST_JIT=true ART_TEST_GC_STRESS=true test-art-host-run-test-924-threads
Test: ART_TEST_JIT=true ART_TEST_GC_STRESS=true test-art-host-run-test-925-threadgroups
Change-Id: I1becfc188b59a3c99cc7eea07c63abaaf108fd15
diff --git a/runtime/openjdkjvmti/ti_heap.cc b/runtime/openjdkjvmti/ti_heap.cc
index 7b2521d..fe3e52b 100644
--- a/runtime/openjdkjvmti/ti_heap.cc
+++ b/runtime/openjdkjvmti/ti_heap.cc
@@ -303,11 +303,11 @@
 
           art::Thread* thread = FindThread(info);
           if (thread != nullptr) {
-            art::mirror::Object* thread_obj = thread->GetPeer();
+            art::mirror::Object* thread_obj;
             if (thread->IsStillStarting()) {
               thread_obj = nullptr;
             } else {
-              thread_obj = thread->GetPeer();
+              thread_obj = thread->GetPeerFromOtherThread();
             }
             if (thread_obj != nullptr) {
               ref_info->jni_local.thread_tag = tag_table_->GetTagOrZero(thread_obj);
@@ -333,11 +333,11 @@
 
           art::Thread* thread = FindThread(info);
           if (thread != nullptr) {
-            art::mirror::Object* thread_obj = thread->GetPeer();
+            art::mirror::Object* thread_obj;
             if (thread->IsStillStarting()) {
               thread_obj = nullptr;
             } else {
-              thread_obj = thread->GetPeer();
+              thread_obj = thread->GetPeerFromOtherThread();
             }
             if (thread_obj != nullptr) {
               ref_info->stack_local.thread_tag = tag_table_->GetTagOrZero(thread_obj);
diff --git a/runtime/openjdkjvmti/ti_stack.cc b/runtime/openjdkjvmti/ti_stack.cc
index 4cf55a6..b5a6c6e 100644
--- a/runtime/openjdkjvmti/ti_stack.cc
+++ b/runtime/openjdkjvmti/ti_stack.cc
@@ -377,7 +377,8 @@
     jvmtiStackInfo& old_stack_info = stack_info_array.get()[i];
     jvmtiStackInfo& new_stack_info = stack_info[i];
 
-    jthread thread_peer = current->GetJniEnv()->AddLocalReference<jthread>(threads[i]->GetPeer());
+    jthread thread_peer = current->GetJniEnv()->AddLocalReference<jthread>(
+        threads[i]->GetPeerFromOtherThread());
     new_stack_info.thread = thread_peer;
 
     if (old_stack_info.frame_count > 0) {
@@ -453,7 +454,7 @@
         }
 
         // Get the peer, and check whether we know it.
-        art::ObjPtr<art::mirror::Object> peer = thread->GetPeer();
+        art::ObjPtr<art::mirror::Object> peer = thread->GetPeerFromOtherThread();
         for (size_t index = 0; index != handles.size(); ++index) {
           if (peer == handles[index].Get()) {
             // Found the thread.
diff --git a/runtime/openjdkjvmti/ti_thread.cc b/runtime/openjdkjvmti/ti_thread.cc
index b18a5cd..00d4144 100644
--- a/runtime/openjdkjvmti/ti_thread.cc
+++ b/runtime/openjdkjvmti/ti_thread.cc
@@ -200,7 +200,7 @@
 
     info_ptr->is_daemon = self->IsDaemon();
 
-    art::ObjPtr<art::mirror::Object> peer = self->GetPeer();
+    art::ObjPtr<art::mirror::Object> peer = self->GetPeerFromOtherThread();
 
     // ThreadGroup.
     if (peer != nullptr) {
@@ -458,7 +458,7 @@
       continue;
     }
 
-    art::ObjPtr<art::mirror::Object> peer = thread->GetPeer();
+    art::ObjPtr<art::mirror::Object> peer = thread->GetPeerFromOtherThread();
     if (peer != nullptr) {
       peers.push_back(peer);
     }
diff --git a/runtime/openjdkjvmti/ti_threadgroup.cc b/runtime/openjdkjvmti/ti_threadgroup.cc
index 35b1bfd..e63ce65 100644
--- a/runtime/openjdkjvmti/ti_threadgroup.cc
+++ b/runtime/openjdkjvmti/ti_threadgroup.cc
@@ -174,7 +174,7 @@
     if (t->IsStillStarting()) {
       continue;
     }
-    art::ObjPtr<art::mirror::Object> peer = t->GetPeer();
+    art::ObjPtr<art::mirror::Object> peer = t->GetPeerFromOtherThread();
     if (peer == nullptr) {
       continue;
     }
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 632a380..0ea7c5a 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -65,6 +65,7 @@
 #include "object_lock.h"
 #include "quick_exception_handler.h"
 #include "quick/quick_method_frame_info.h"
+#include "read_barrier-inl.h"
 #include "reflection.h"
 #include "runtime.h"
 #include "runtime_callbacks.h"
@@ -1845,6 +1846,7 @@
     : tls32_(daemon),
       wait_monitor_(nullptr),
       interrupted_(false),
+      custom_tls_(nullptr),
       can_call_into_java_(true) {
   wait_mutex_ = new Mutex("a thread wait mutex");
   wait_cond_ = new ConditionVariable("a thread wait condition variable", *wait_mutex_);
@@ -3457,4 +3459,15 @@
   return Runtime::Current()->IsAotCompiler();
 }
 
+mirror::Object* Thread::GetPeerFromOtherThread() const {
+  mirror::Object* peer = GetPeer();
+  if (kUseReadBarrier && Current()->GetIsGcMarking()) {
+    // We may call Thread::Dump() in the middle of the CC thread flip and this thread's stack
+    // may have not been flipped yet and peer may be a from-space (stale) ref. So explicitly
+    // mark/forward it here.
+    peer = art::ReadBarrier::Mark(peer);
+  }
+  return peer;
+}
+
 }  // namespace art
diff --git a/runtime/thread.h b/runtime/thread.h
index b59eac6..8300a01 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -359,6 +359,10 @@
     CHECK(tlsPtr_.jpeer == nullptr);
     return tlsPtr_.opeer;
   }
+  // GetPeer is not safe if called on another thread in the middle of the CC thread flip and
+  // the thread's stack may have not been flipped yet and peer may be a from-space (stale) ref.
+  // This function will explicitly mark/forward it.
+  mirror::Object* GetPeerFromOtherThread() const REQUIRES_SHARED(Locks::mutator_lock_);
 
   bool HasPeer() const {
     return tlsPtr_.jpeer != nullptr || tlsPtr_.opeer != nullptr;