Merge "Revert "Revert "Refactor ClassLinker::LinkInterfaceMethods()."""
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index e1117e6..7b86339 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -2406,16 +2406,29 @@
   }
 }
 
-bool ConcurrentCopying::IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* field) {
+bool ConcurrentCopying::IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* field,
+                                                    bool do_atomic_update) {
   mirror::Object* from_ref = field->AsMirrorPtr();
+  if (from_ref == nullptr) {
+    return true;
+  }
   mirror::Object* to_ref = IsMarked(from_ref);
   if (to_ref == nullptr) {
     return false;
   }
   if (from_ref != to_ref) {
-    QuasiAtomic::ThreadFenceRelease();
-    field->Assign(to_ref);
-    QuasiAtomic::ThreadFenceSequentiallyConsistent();
+    if (do_atomic_update) {
+      do {
+        if (field->AsMirrorPtr() != from_ref) {
+          // Concurrently overwritten by a mutator.
+          break;
+        }
+      } while (!field->CasWeakRelaxed(from_ref, to_ref));
+    } else {
+      QuasiAtomic::ThreadFenceRelease();
+      field->Assign(to_ref);
+      QuasiAtomic::ThreadFenceSequentiallyConsistent();
+    }
   }
   return true;
 }
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index 5b8a557..844bb45 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -183,7 +183,8 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
   bool IsMarkedInUnevacFromSpace(mirror::Object* from_ref)
       REQUIRES_SHARED(Locks::mutator_lock_);
-  virtual bool IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* field) OVERRIDE
+  virtual bool IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* field,
+                                           bool do_atomic_update) OVERRIDE
       REQUIRES_SHARED(Locks::mutator_lock_);
   void SweepSystemWeaks(Thread* self)
       REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::heap_bitmap_lock_);
diff --git a/runtime/gc/collector/garbage_collector.h b/runtime/gc/collector/garbage_collector.h
index 5b51399..0177e2a 100644
--- a/runtime/gc/collector/garbage_collector.h
+++ b/runtime/gc/collector/garbage_collector.h
@@ -187,7 +187,10 @@
   // and will be used for reading system weaks while the GC is running.
   virtual mirror::Object* IsMarked(mirror::Object* obj)
       REQUIRES_SHARED(Locks::mutator_lock_) = 0;
-  virtual bool IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* obj)
+  // Returns true if the given heap reference is null or is already marked. If it's already marked,
+  // update the reference (uses a CAS if do_atomic_update is true. Otherwise, returns false.
+  virtual bool IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* obj,
+                                           bool do_atomic_update)
       REQUIRES_SHARED(Locks::mutator_lock_) = 0;
   // Used by reference processor.
   virtual void ProcessMarkStack() REQUIRES_SHARED(Locks::mutator_lock_) = 0;
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index ddcb6c0..85e6783 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -472,9 +472,15 @@
   return mark_bitmap_->Test(object) ? object : nullptr;
 }
 
-bool MarkCompact::IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* ref_ptr) {
+bool MarkCompact::IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* ref_ptr,
+                                              // MarkCompact does the GC in a pause. No CAS needed.
+                                              bool do_atomic_update ATTRIBUTE_UNUSED) {
   // Side effect free since we call this before ever moving objects.
-  return IsMarked(ref_ptr->AsMirrorPtr()) != nullptr;
+  mirror::Object* obj = ref_ptr->AsMirrorPtr();
+  if (obj == nullptr) {
+    return true;
+  }
+  return IsMarked(obj) != nullptr;
 }
 
 void MarkCompact::SweepSystemWeaks() {
diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h
index 564f85b..6d52d5d 100644
--- a/runtime/gc/collector/mark_compact.h
+++ b/runtime/gc/collector/mark_compact.h
@@ -175,7 +175,8 @@
   virtual mirror::Object* IsMarked(mirror::Object* obj) OVERRIDE
       REQUIRES_SHARED(Locks::heap_bitmap_lock_)
       REQUIRES(Locks::mutator_lock_);
-  virtual bool IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* obj) OVERRIDE
+  virtual bool IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* obj,
+                                           bool do_atomic_update) OVERRIDE
       REQUIRES_SHARED(Locks::heap_bitmap_lock_)
       REQUIRES(Locks::mutator_lock_);
   void ForwardObject(mirror::Object* obj) REQUIRES(Locks::heap_bitmap_lock_,
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 06ed029..f00da73 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -390,8 +390,13 @@
   }
 }
 
-bool MarkSweep::IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* ref) {
-  return IsMarked(ref->AsMirrorPtr());
+bool MarkSweep::IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* ref,
+                                            bool do_atomic_update ATTRIBUTE_UNUSED) {
+  mirror::Object* obj = ref->AsMirrorPtr();
+  if (obj == nullptr) {
+    return true;
+  }
+  return IsMarked(obj);
 }
 
 class MarkSweep::MarkObjectSlowPath {
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index 02cf462..a6e2d61 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -188,7 +188,8 @@
   void VerifyIsLive(const mirror::Object* obj)
       REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
 
-  virtual bool IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* ref) OVERRIDE
+  virtual bool IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* ref,
+                                           bool do_atomic_update) OVERRIDE
       REQUIRES(Locks::heap_bitmap_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index f2aa5a7..cb9e7e2 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -765,8 +765,13 @@
   return mark_bitmap_->Test(obj) ? obj : nullptr;
 }
 
-bool SemiSpace::IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* object) {
+bool SemiSpace::IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* object,
+                                            // SemiSpace does the GC in a pause. No CAS needed.
+                                            bool do_atomic_update ATTRIBUTE_UNUSED) {
   mirror::Object* obj = object->AsMirrorPtr();
+  if (obj == nullptr) {
+    return true;
+  }
   mirror::Object* new_obj = IsMarked(obj);
   if (new_obj == nullptr) {
     return false;
diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h
index 4cebcc3..52b5e5f 100644
--- a/runtime/gc/collector/semi_space.h
+++ b/runtime/gc/collector/semi_space.h
@@ -166,7 +166,8 @@
       REQUIRES(Locks::mutator_lock_)
       REQUIRES_SHARED(Locks::heap_bitmap_lock_);
 
-  virtual bool IsMarkedHeapReference(mirror::HeapReference<mirror::Object>* object) OVERRIDE
+  virtual bool IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* object,
+                                           bool do_atomic_update) OVERRIDE
       REQUIRES(Locks::mutator_lock_)
       REQUIRES_SHARED(Locks::heap_bitmap_lock_);
 
diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc
index 081be96..c154836 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -203,7 +203,9 @@
   DCHECK(klass != nullptr);
   DCHECK(klass->IsTypeOfReferenceClass());
   mirror::HeapReference<mirror::Object>* referent = ref->GetReferentReferenceAddr();
-  if (referent->AsMirrorPtr() != nullptr && !collector->IsMarkedHeapReference(referent)) {
+  // do_atomic_update needs to be true because this happens outside of the reference processing
+  // phase.
+  if (!collector->IsNullOrMarkedHeapReference(referent, /*do_atomic_update*/true)) {
     Thread* self = Thread::Current();
     // TODO: Remove these locks, and use atomic stacks for storing references?
     // We need to check that the references haven't already been enqueued since we can end up
diff --git a/runtime/gc/reference_queue.cc b/runtime/gc/reference_queue.cc
index a0eb197..734caea 100644
--- a/runtime/gc/reference_queue.cc
+++ b/runtime/gc/reference_queue.cc
@@ -129,8 +129,9 @@
   while (!IsEmpty()) {
     ObjPtr<mirror::Reference> ref = DequeuePendingReference();
     mirror::HeapReference<mirror::Object>* referent_addr = ref->GetReferentReferenceAddr();
-    if (referent_addr->AsMirrorPtr() != nullptr &&
-        !collector->IsMarkedHeapReference(referent_addr)) {
+    // do_atomic_update is false because this happens during the reference processing phase where
+    // Reference.clear() would block.
+    if (!collector->IsNullOrMarkedHeapReference(referent_addr, /*do_atomic_update*/false)) {
       // Referent is white, clear it.
       if (Runtime::Current()->IsActiveTransaction()) {
         ref->ClearReferent<true>();
@@ -147,8 +148,9 @@
   while (!IsEmpty()) {
     ObjPtr<mirror::FinalizerReference> ref = DequeuePendingReference()->AsFinalizerReference();
     mirror::HeapReference<mirror::Object>* referent_addr = ref->GetReferentReferenceAddr();
-    if (referent_addr->AsMirrorPtr() != nullptr &&
-        !collector->IsMarkedHeapReference(referent_addr)) {
+    // do_atomic_update is false because this happens during the reference processing phase where
+    // Reference.clear() would block.
+    if (!collector->IsNullOrMarkedHeapReference(referent_addr, /*do_atomic_update*/false)) {
       ObjPtr<mirror::Object> forward_address = collector->MarkObject(referent_addr->AsMirrorPtr());
       // Move the updated referent to the zombie field.
       if (Runtime::Current()->IsActiveTransaction()) {
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index f80c43d..e0f28ad 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -566,7 +566,10 @@
     return nullptr;
   }
   MutexLock mu(self, *Locks::jni_weak_globals_lock_);
-  while (UNLIKELY(!MayAccessWeakGlobals(self))) {
+  // CMS needs this to block for concurrent reference processing because an object allocated during
+  // the GC won't be marked and concurrent reference processing would incorrectly clear the JNI weak
+  // ref. But CC (kUseReadBarrier == true) doesn't because of the to-space invariant.
+  while (!kUseReadBarrier && UNLIKELY(!MayAccessWeakGlobals(self))) {
     // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the
     // presence of threads blocking for weak ref access.
     self->CheckEmptyCheckpoint();
diff --git a/runtime/mirror/object_reference-inl.h b/runtime/mirror/object_reference-inl.h
index e70b936..22fb83c 100644
--- a/runtime/mirror/object_reference-inl.h
+++ b/runtime/mirror/object_reference-inl.h
@@ -34,6 +34,15 @@
   return HeapReference<MirrorType>(ptr.Ptr());
 }
 
+template<class MirrorType>
+bool HeapReference<MirrorType>::CasWeakRelaxed(MirrorType* expected_ptr, MirrorType* new_ptr) {
+  HeapReference<Object> expected_ref(HeapReference<Object>::FromMirrorPtr(expected_ptr));
+  HeapReference<Object> new_ref(HeapReference<Object>::FromMirrorPtr(new_ptr));
+  Atomic<uint32_t>* atomic_reference = reinterpret_cast<Atomic<uint32_t>*>(&this->reference_);
+  return atomic_reference->CompareExchangeWeakRelaxed(expected_ref.reference_,
+                                                      new_ref.reference_);
+}
+
 }  // namespace mirror
 }  // namespace art
 
diff --git a/runtime/mirror/object_reference.h b/runtime/mirror/object_reference.h
index 71f34c6..a96a120 100644
--- a/runtime/mirror/object_reference.h
+++ b/runtime/mirror/object_reference.h
@@ -94,6 +94,9 @@
   static HeapReference<MirrorType> FromObjPtr(ObjPtr<MirrorType> ptr)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  bool CasWeakRelaxed(MirrorType* old_ptr, MirrorType* new_ptr)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
  private:
   explicit HeapReference(MirrorType* mirror_ptr) REQUIRES_SHARED(Locks::mutator_lock_)
       : ObjectReference<kPoisonHeapReferences, MirrorType>(mirror_ptr) {}
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 893abd5..9c09275 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -1361,8 +1361,10 @@
 void MonitorList::Add(Monitor* m) {
   Thread* self = Thread::Current();
   MutexLock mu(self, monitor_list_lock_);
-  while (UNLIKELY((!kUseReadBarrier && !allow_new_monitors_) ||
-                  (kUseReadBarrier && !self->GetWeakRefAccessEnabled()))) {
+  // CMS needs this to block for concurrent reference processing because an object allocated during
+  // the GC won't be marked and concurrent reference processing would incorrectly clear the JNI weak
+  // ref. But CC (kUseReadBarrier == true) doesn't because of the to-space invariant.
+  while (!kUseReadBarrier && UNLIKELY(!allow_new_monitors_)) {
     // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the
     // presence of threads blocking for weak ref access.
     self->CheckEmptyCheckpoint();
diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc
index 36825cb..268d71a 100644
--- a/runtime/native/dalvik_system_VMStack.cc
+++ b/runtime/native/dalvik_system_VMStack.cc
@@ -17,6 +17,7 @@
 #include "dalvik_system_VMStack.h"
 
 #include "art_method-inl.h"
+#include "gc/task_processor.h"
 #include "jni_internal.h"
 #include "nth_caller_visitor.h"
 #include "mirror/class-inl.h"
@@ -31,9 +32,18 @@
 static jobject GetThreadStack(const ScopedFastNativeObjectAccess& soa, jobject peer)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   jobject trace = nullptr;
-  if (soa.Decode<mirror::Object>(peer) == soa.Self()->GetPeer()) {
+  ObjPtr<mirror::Object> decoded_peer = soa.Decode<mirror::Object>(peer);
+  if (decoded_peer == soa.Self()->GetPeer()) {
     trace = soa.Self()->CreateInternalStackTrace<false>(soa);
   } else {
+    // Never allow suspending the heap task thread since it may deadlock if allocations are
+    // required for the stack trace.
+    Thread* heap_task_thread =
+        Runtime::Current()->GetHeap()->GetTaskProcessor()->GetRunningThread();
+    // heap_task_thread could be null if the daemons aren't yet started.
+    if (heap_task_thread != nullptr && decoded_peer == heap_task_thread->GetPeer()) {
+      return nullptr;
+    }
     // Suspend thread to build stack trace.
     ScopedThreadSuspension sts(soa.Self(), kNative);
     ThreadList* thread_list = Runtime::Current()->GetThreadList();
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index b757b21..4bd21b4 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -29,6 +29,7 @@
            "ti_properties.cc",
            "ti_stack.cc",
            "ti_redefine.cc",
+           "ti_thread.cc",
            "transform.cc"],
     include_dirs: ["art/runtime"],
     shared_libs: [
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index c52dd76..2629c9f 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -55,6 +55,7 @@
 #include "ti_properties.h"
 #include "ti_redefine.h"
 #include "ti_stack.h"
+#include "ti_thread.h"
 #include "transform.h"
 
 // TODO Remove this at some point by annotating all the methods. It was put in to make the skeleton
@@ -117,11 +118,11 @@
   }
 
   static jvmtiError GetThreadState(jvmtiEnv* env, jthread thread, jint* thread_state_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return ThreadUtil::GetThreadState(env, thread, thread_state_ptr);
   }
 
   static jvmtiError GetCurrentThread(jvmtiEnv* env, jthread* thread_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return ThreadUtil::GetCurrentThread(env, thread_ptr);
   }
 
   static jvmtiError GetAllThreads(jvmtiEnv* env, jint* threads_count_ptr, jthread** threads_ptr) {
@@ -159,7 +160,7 @@
   }
 
   static jvmtiError GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadInfo* info_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return ThreadUtil::GetThreadInfo(env, thread, info_ptr);
   }
 
   static jvmtiError GetOwnedMonitorInfo(jvmtiEnv* env,
@@ -237,7 +238,7 @@
                                       jint max_frame_count,
                                       jvmtiStackInfo** stack_info_ptr,
                                       jint* thread_count_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    return StackUtil::GetAllStackTraces(env, max_frame_count, stack_info_ptr, thread_count_ptr);
   }
 
   static jvmtiError GetThreadListStackTraces(jvmtiEnv* env,
diff --git a/runtime/openjdkjvmti/ti_stack.cc b/runtime/openjdkjvmti/ti_stack.cc
index 579fb50..098cedb 100644
--- a/runtime/openjdkjvmti/ti_stack.cc
+++ b/runtime/openjdkjvmti/ti_stack.cc
@@ -31,9 +31,15 @@
 
 #include "ti_stack.h"
 
+#include <list>
+#include <unordered_map>
+#include <vector>
+
 #include "art_jvmti.h"
 #include "art_method-inl.h"
+#include "base/bit_utils.h"
 #include "base/enums.h"
+#include "base/mutex.h"
 #include "dex_file.h"
 #include "dex_file_annotations.h"
 #include "jni_env_ext.h"
@@ -41,19 +47,19 @@
 #include "mirror/class.h"
 #include "mirror/dex_cache.h"
 #include "scoped_thread_state_change-inl.h"
+#include "ScopedLocalRef.h"
 #include "stack.h"
-#include "thread.h"
+#include "thread-inl.h"
+#include "thread_list.h"
 #include "thread_pool.h"
 
 namespace openjdkjvmti {
 
 struct GetStackTraceVisitor : public art::StackVisitor {
   GetStackTraceVisitor(art::Thread* thread_in,
-                       art::ScopedObjectAccessAlreadyRunnable& soa_,
                        size_t start_,
                        size_t stop_)
       : StackVisitor(thread_in, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
-        soa(soa_),
         start(start_),
         stop(stop_) {}
 
@@ -85,7 +91,6 @@
     return true;
   }
 
-  art::ScopedObjectAccessAlreadyRunnable& soa;
   std::vector<jvmtiFrameInfo> frames;
   size_t start;
   size_t stop;
@@ -99,10 +104,8 @@
         start_result(0),
         stop_result(0) {}
 
-  void Run(art::Thread* self) OVERRIDE {
-    art::ScopedObjectAccess soa(art::Thread::Current());
-
-    GetStackTraceVisitor visitor(self, soa, start_input, stop_input);
+  void Run(art::Thread* self) OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    GetStackTraceVisitor visitor(self, start_input, stop_input);
     visitor.WalkStack(false);
 
     frames.swap(visitor.frames);
@@ -118,6 +121,44 @@
   size_t stop_result;
 };
 
+static jvmtiError TranslateFrameVector(const std::vector<jvmtiFrameInfo>& frames,
+                                       jint start_depth,
+                                       size_t start_result,
+                                       jint max_frame_count,
+                                       jvmtiFrameInfo* frame_buffer,
+                                       jint* count_ptr) {
+  size_t collected_frames = frames.size();
+
+  // Assume we're here having collected something.
+  DCHECK_GT(max_frame_count, 0);
+
+  // Frames from the top.
+  if (start_depth >= 0) {
+    if (start_result != 0) {
+      // Not enough frames.
+      return ERR(ILLEGAL_ARGUMENT);
+    }
+    DCHECK_LE(collected_frames, static_cast<size_t>(max_frame_count));
+    if (frames.size() > 0) {
+      memcpy(frame_buffer, frames.data(), collected_frames * sizeof(jvmtiFrameInfo));
+    }
+    *count_ptr = static_cast<jint>(frames.size());
+    return ERR(NONE);
+  }
+
+  // Frames from the bottom.
+  if (collected_frames < static_cast<size_t>(-start_depth)) {
+    return ERR(ILLEGAL_ARGUMENT);
+  }
+
+  size_t count = std::min(static_cast<size_t>(-start_depth), static_cast<size_t>(max_frame_count));
+  memcpy(frame_buffer,
+         &frames.data()[collected_frames + start_depth],
+         count * sizeof(jvmtiFrameInfo));
+  *count_ptr = static_cast<jint>(count);
+  return ERR(NONE);
+}
+
 jvmtiError StackUtil::GetStackTrace(jvmtiEnv* jvmti_env ATTRIBUTE_UNUSED,
                                     jthread java_thread,
                                     jint start_depth,
@@ -157,35 +198,179 @@
   }
 
   GetStackTraceClosure closure(start_depth >= 0 ? static_cast<size_t>(start_depth) : 0,
-                               start_depth >= 0 ?static_cast<size_t>(max_frame_count) : 0);
+                               start_depth >= 0 ? static_cast<size_t>(max_frame_count) : 0);
   thread->RequestSynchronousCheckpoint(&closure);
 
-  size_t collected_frames = closure.frames.size();
+  return TranslateFrameVector(closure.frames,
+                              start_depth,
+                              closure.start_result,
+                              max_frame_count,
+                              frame_buffer,
+                              count_ptr);
+}
 
-  // Frames from the top.
-  if (start_depth >= 0) {
-    if (closure.start_result != 0) {
-      // Not enough frames.
-      return ERR(ILLEGAL_ARGUMENT);
-    }
-    DCHECK_LE(collected_frames, static_cast<size_t>(max_frame_count));
-    if (closure.frames.size() > 0) {
-      memcpy(frame_buffer, closure.frames.data(), collected_frames * sizeof(jvmtiFrameInfo));
-    }
-    *count_ptr = static_cast<jint>(closure.frames.size());
-    return ERR(NONE);
+struct GetAllStackTraceClosure : public art::Closure {
+ public:
+  explicit GetAllStackTraceClosure(size_t stop)
+      : start_input(0),
+        stop_input(stop),
+        frames_lock("GetAllStackTraceGuard", art::LockLevel::kAbortLock),
+        start_result(0),
+        stop_result(0) {}
+
+  void Run(art::Thread* self)
+      OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!frames_lock) {
+    // self should be live here (so it could be suspended). No need to filter.
+
+    art::Thread* current = art::Thread::Current();
+    std::vector<jvmtiFrameInfo> self_frames;
+
+    GetStackTraceVisitor visitor(self, start_input, stop_input);
+    visitor.WalkStack(false);
+
+    self_frames.swap(visitor.frames);
+
+    art::MutexLock mu(current, frames_lock);
+    frames.emplace(self, self_frames);
   }
 
-  // Frames from the bottom.
-  if (collected_frames < static_cast<size_t>(-start_depth)) {
+  const size_t start_input;
+  const size_t stop_input;
+
+  art::Mutex frames_lock;
+  std::unordered_map<art::Thread*, std::vector<jvmtiFrameInfo>> frames GUARDED_BY(frames_lock);
+  size_t start_result;
+  size_t stop_result;
+};
+
+
+
+jvmtiError StackUtil::GetAllStackTraces(jvmtiEnv* env,
+                                        jint max_frame_count,
+                                        jvmtiStackInfo** stack_info_ptr,
+                                        jint* thread_count_ptr) {
+  if (max_frame_count < 0) {
     return ERR(ILLEGAL_ARGUMENT);
   }
+  if (stack_info_ptr == nullptr || thread_count_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
 
-  size_t count = std::min(static_cast<size_t>(-start_depth), static_cast<size_t>(max_frame_count));
-  memcpy(frame_buffer,
-         &closure.frames.data()[collected_frames + start_depth],
-         count * sizeof(jvmtiFrameInfo));
-  *count_ptr = static_cast<jint>(count);
+
+  art::Thread* current = art::Thread::Current();
+  art::ScopedObjectAccess soa(current);      // Now we know we have the shared lock.
+  art::ScopedThreadSuspension sts(current, art::kWaitingForDebuggerSuspension);
+  art::ScopedSuspendAll ssa("GetAllStackTraces");
+
+  std::vector<art::Thread*> threads;
+  std::vector<std::vector<jvmtiFrameInfo>> frames;
+  {
+    std::list<art::Thread*> thread_list;
+    {
+      art::MutexLock mu(current, *art::Locks::thread_list_lock_);
+      thread_list = art::Runtime::Current()->GetThreadList()->GetList();
+    }
+
+    for (art::Thread* thread : thread_list) {
+      // Skip threads that are still starting.
+      if (thread->IsStillStarting()) {
+        continue;
+      }
+
+      GetStackTraceClosure closure(0u, static_cast<size_t>(max_frame_count));
+      thread->RequestSynchronousCheckpoint(&closure);
+
+      threads.push_back(thread);
+      frames.emplace_back();
+      frames.back().swap(closure.frames);
+    }
+  }
+
+  // Convert the data into our output format. Note: we need to keep the threads suspended,
+  // as we need to access them for their peers.
+
+  // Note: we use an array of jvmtiStackInfo for convenience. The spec says we need to
+  //       allocate one big chunk for this and the actual frames, which means we need
+  //       to either be conservative or rearrange things later (the latter is implemented).
+  std::unique_ptr<jvmtiStackInfo[]> stack_info_array(new jvmtiStackInfo[frames.size()]);
+  std::vector<std::unique_ptr<jvmtiFrameInfo[]>> frame_infos;
+  frame_infos.reserve(frames.size());
+
+  // Now run through and add data for each thread.
+  size_t sum_frames = 0;
+  for (size_t index = 0; index < frames.size(); ++index) {
+    jvmtiStackInfo& stack_info = stack_info_array.get()[index];
+    memset(&stack_info, 0, sizeof(jvmtiStackInfo));
+
+    art::Thread* self = threads[index];
+    const std::vector<jvmtiFrameInfo>& thread_frames = frames[index];
+
+    // For the time being, set the thread to null. We don't have good ScopedLocalRef
+    // infrastructure.
+    DCHECK(self->GetPeer() != nullptr);
+    stack_info.thread = nullptr;
+    stack_info.state = JVMTI_THREAD_STATE_SUSPENDED;
+
+    size_t collected_frames = thread_frames.size();
+    if (max_frame_count == 0 || collected_frames == 0) {
+      stack_info.frame_count = 0;
+      stack_info.frame_buffer = nullptr;
+      continue;
+    }
+    DCHECK_LE(collected_frames, static_cast<size_t>(max_frame_count));
+
+    jvmtiFrameInfo* frame_info = new jvmtiFrameInfo[collected_frames];
+    frame_infos.emplace_back(frame_info);
+
+    jint count;
+    jvmtiError translate_result = TranslateFrameVector(thread_frames,
+                                                       0,
+                                                       0,
+                                                       static_cast<jint>(collected_frames),
+                                                       frame_info,
+                                                       &count);
+    DCHECK(translate_result == JVMTI_ERROR_NONE);
+    stack_info.frame_count = static_cast<jint>(collected_frames);
+    stack_info.frame_buffer = frame_info;
+    sum_frames += static_cast<size_t>(count);
+  }
+
+  // No errors, yet. Now put it all into an output buffer.
+  size_t rounded_stack_info_size = art::RoundUp(sizeof(jvmtiStackInfo) * frames.size(),
+                                                alignof(jvmtiFrameInfo));
+  size_t chunk_size = rounded_stack_info_size + sum_frames * sizeof(jvmtiFrameInfo);
+  unsigned char* chunk_data;
+  jvmtiError alloc_result = env->Allocate(chunk_size, &chunk_data);
+  if (alloc_result != ERR(NONE)) {
+    return alloc_result;
+  }
+
+  jvmtiStackInfo* stack_info = reinterpret_cast<jvmtiStackInfo*>(chunk_data);
+  // First copy in all the basic data.
+  memcpy(stack_info, stack_info_array.get(), sizeof(jvmtiStackInfo) * frames.size());
+
+  // Now copy the frames and fix up the pointers.
+  jvmtiFrameInfo* frame_info = reinterpret_cast<jvmtiFrameInfo*>(
+      chunk_data + rounded_stack_info_size);
+  for (size_t i = 0; i < frames.size(); ++i) {
+    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());
+    new_stack_info.thread = thread_peer;
+
+    if (old_stack_info.frame_count > 0) {
+      // Only copy when there's data - leave the nullptr alone.
+      size_t frames_size = static_cast<size_t>(old_stack_info.frame_count) * sizeof(jvmtiFrameInfo);
+      memcpy(frame_info, old_stack_info.frame_buffer, frames_size);
+      new_stack_info.frame_buffer = frame_info;
+      frame_info += old_stack_info.frame_count;
+    }
+  }
+
+  *stack_info_ptr = stack_info;
+  *thread_count_ptr = static_cast<jint>(frames.size());
+
   return ERR(NONE);
 }
 
diff --git a/runtime/openjdkjvmti/ti_stack.h b/runtime/openjdkjvmti/ti_stack.h
index 1931ed3..7619f98 100644
--- a/runtime/openjdkjvmti/ti_stack.h
+++ b/runtime/openjdkjvmti/ti_stack.h
@@ -32,12 +32,21 @@
 #ifndef ART_RUNTIME_OPENJDKJVMTI_TI_STACK_H_
 #define ART_RUNTIME_OPENJDKJVMTI_TI_STACK_H_
 
+#include "jni.h"
 #include "jvmti.h"
 
+#include "base/mutex.h"
+
 namespace openjdkjvmti {
 
 class StackUtil {
  public:
+  static jvmtiError GetAllStackTraces(jvmtiEnv* env,
+                                      jint max_frame_count,
+                                      jvmtiStackInfo** stack_info_ptr,
+                                      jint* thread_count_ptr)
+      REQUIRES(!art::Locks::thread_list_lock_);
+
   static jvmtiError GetStackTrace(jvmtiEnv* env,
                                   jthread thread,
                                   jint start_depth,
diff --git a/runtime/openjdkjvmti/ti_thread.cc b/runtime/openjdkjvmti/ti_thread.cc
new file mode 100644
index 0000000..e20f560
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_thread.cc
@@ -0,0 +1,357 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_thread.h"
+
+#include "art_field.h"
+#include "art_jvmti.h"
+#include "base/logging.h"
+#include "base/mutex.h"
+#include "jni_internal.h"
+#include "mirror/class.h"
+#include "mirror/object-inl.h"
+#include "mirror/string.h"
+#include "obj_ptr.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
+#include "well_known_classes.h"
+
+namespace openjdkjvmti {
+
+jvmtiError ThreadUtil::GetCurrentThread(jvmtiEnv* env ATTRIBUTE_UNUSED, jthread* thread_ptr) {
+  art::Thread* self = art::Thread::Current();
+
+  art::ScopedObjectAccess soa(self);
+
+  jthread thread_peer;
+  if (self->IsStillStarting()) {
+    thread_peer = nullptr;
+  } else {
+    thread_peer = soa.AddLocalReference<jthread>(self->GetPeer());
+  }
+
+  *thread_ptr = thread_peer;
+  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)
+    REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  if (thread == nullptr) {
+    return art::Thread::Current();
+  }
+
+  art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
+  return art::Thread::FromManagedThread(soa, thread);
+}
+
+jvmtiError ThreadUtil::GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadInfo* info_ptr) {
+  if (info_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  art::ScopedObjectAccess soa(art::Thread::Current());
+
+  art::Thread* self = GetNativeThread(thread, soa);
+  if (self == nullptr && thread == nullptr) {
+    return ERR(INVALID_THREAD);
+  }
+
+  JvmtiUniquePtr name_uptr;
+  if (self != nullptr) {
+    // Have a native thread object, this thread is alive.
+    std::string name;
+    self->GetThreadName(name);
+    jvmtiError name_result = CopyString(
+        env, name.c_str(), reinterpret_cast<unsigned char**>(&info_ptr->name));
+    if (name_result != ERR(NONE)) {
+      return name_result;
+    }
+    name_uptr = MakeJvmtiUniquePtr(env, info_ptr->name);
+
+    info_ptr->priority = self->GetNativePriority();
+
+    info_ptr->is_daemon = self->IsDaemon();
+
+    art::ObjPtr<art::mirror::Object> peer = self->GetPeer();
+
+    // ThreadGroup.
+    if (peer != nullptr) {
+      art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group);
+      CHECK(f != nullptr);
+      art::ObjPtr<art::mirror::Object> group = f->GetObject(peer);
+      info_ptr->thread_group = group == nullptr
+                                   ? nullptr
+                                   : soa.AddLocalReference<jthreadGroup>(group);
+    } else {
+      info_ptr->thread_group = nullptr;
+    }
+
+    // Context classloader.
+    art::ObjPtr<art::mirror::Object> ccl = GetContextClassLoader(peer);
+    info_ptr->context_class_loader = ccl == nullptr
+                                         ? nullptr
+                                         : soa.AddLocalReference<jobject>(ccl);
+  } else {
+    // Only the peer. This thread has either not been started, or is dead. Read things from
+    // the Java side.
+    art::ObjPtr<art::mirror::Object> peer = soa.Decode<art::mirror::Object>(thread);
+
+    // Name.
+    {
+      art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_name);
+      CHECK(f != nullptr);
+      art::ObjPtr<art::mirror::Object> name = f->GetObject(peer);
+      std::string name_cpp;
+      const char* name_cstr;
+      if (name != nullptr) {
+        name_cpp = name->AsString()->ToModifiedUtf8();
+        name_cstr = name_cpp.c_str();
+      } else {
+        name_cstr = "";
+      }
+      jvmtiError name_result = CopyString(
+          env, name_cstr, reinterpret_cast<unsigned char**>(&info_ptr->name));
+      if (name_result != ERR(NONE)) {
+        return name_result;
+      }
+      name_uptr = MakeJvmtiUniquePtr(env, info_ptr->name);
+    }
+
+    // Priority.
+    {
+      art::ArtField* f = art::jni::DecodeArtField(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);
+      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);
+      CHECK(f != nullptr);
+      art::ObjPtr<art::mirror::Object> group = f->GetObject(peer);
+      info_ptr->thread_group = group == nullptr
+                                   ? nullptr
+                                   : soa.AddLocalReference<jthreadGroup>(group);
+    }
+
+    // Context classloader.
+    art::ObjPtr<art::mirror::Object> ccl = GetContextClassLoader(peer);
+    info_ptr->context_class_loader = ccl == nullptr
+                                         ? nullptr
+                                         : soa.AddLocalReference<jobject>(ccl);
+  }
+
+  name_uptr.release();
+
+  return ERR(NONE);
+}
+
+// Return the thread's (or current thread, if null) thread state. Return kStarting in case
+// there's no native counterpart (thread hasn't been started, yet, or is dead).
+static art::ThreadState GetNativeThreadState(jthread thread,
+                                             const art::ScopedObjectAccessAlreadyRunnable& soa,
+                                             art::Thread** native_thread)
+    REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  art::Thread* self = nullptr;
+  art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
+  if (thread == nullptr) {
+    self = art::Thread::Current();
+  } else {
+    self = art::Thread::FromManagedThread(soa, thread);
+  }
+  *native_thread = self;
+  if (self == nullptr || self->IsStillStarting()) {
+    return art::ThreadState::kStarting;
+  }
+  return self->GetState();
+}
+
+static jint GetJvmtiThreadStateFromInternal(art::ThreadState internal_thread_state) {
+  jint jvmti_state = JVMTI_THREAD_STATE_ALIVE;
+
+  if (internal_thread_state == art::ThreadState::kSuspended) {
+    jvmti_state |= JVMTI_THREAD_STATE_SUSPENDED;
+    // Note: We do not have data about the previous state. Otherwise we should load the previous
+    //       state here.
+  }
+
+  if (internal_thread_state == art::ThreadState::kNative) {
+    jvmti_state |= JVMTI_THREAD_STATE_IN_NATIVE;
+  }
+
+  if (internal_thread_state == art::ThreadState::kRunnable ||
+      internal_thread_state == art::ThreadState::kWaitingWeakGcRootRead ||
+      internal_thread_state == art::ThreadState::kSuspended) {
+    jvmti_state |= JVMTI_THREAD_STATE_RUNNABLE;
+  } else if (internal_thread_state == art::ThreadState::kBlocked) {
+    jvmti_state |= JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER;
+  } else {
+    // Should be in waiting state.
+    jvmti_state |= JVMTI_THREAD_STATE_WAITING;
+
+    if (internal_thread_state == art::ThreadState::kTimedWaiting ||
+        internal_thread_state == art::ThreadState::kSleeping) {
+      jvmti_state |= JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT;
+    } else {
+      jvmti_state |= JVMTI_THREAD_STATE_WAITING_INDEFINITELY;
+    }
+
+    if (internal_thread_state == art::ThreadState::kSleeping) {
+      jvmti_state |= JVMTI_THREAD_STATE_SLEEPING;
+    }
+
+    if (internal_thread_state == art::ThreadState::kTimedWaiting ||
+        internal_thread_state == art::ThreadState::kWaiting) {
+      jvmti_state |= JVMTI_THREAD_STATE_IN_OBJECT_WAIT;
+    }
+
+    // TODO: PARKED. We'll have to inspect the stack.
+  }
+
+  return jvmti_state;
+}
+
+static jint GetJavaStateFromInternal(art::ThreadState internal_thread_state) {
+  switch (internal_thread_state) {
+    case art::ThreadState::kTerminated:
+      return JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED;
+
+    case art::ThreadState::kRunnable:
+    case art::ThreadState::kNative:
+    case art::ThreadState::kWaitingWeakGcRootRead:
+    case art::ThreadState::kSuspended:
+      return JVMTI_JAVA_LANG_THREAD_STATE_RUNNABLE;
+
+    case art::ThreadState::kTimedWaiting:
+    case art::ThreadState::kSleeping:
+      return JVMTI_JAVA_LANG_THREAD_STATE_TIMED_WAITING;
+
+    case art::ThreadState::kBlocked:
+      return JVMTI_JAVA_LANG_THREAD_STATE_BLOCKED;
+
+    case art::ThreadState::kStarting:
+      return JVMTI_JAVA_LANG_THREAD_STATE_NEW;
+
+    case art::ThreadState::kWaiting:
+    case art::ThreadState::kWaitingForGcToComplete:
+    case art::ThreadState::kWaitingPerformingGc:
+    case art::ThreadState::kWaitingForCheckPointsToRun:
+    case art::ThreadState::kWaitingForDebuggerSend:
+    case art::ThreadState::kWaitingForDebuggerToAttach:
+    case art::ThreadState::kWaitingInMainDebuggerLoop:
+    case art::ThreadState::kWaitingForDebuggerSuspension:
+    case art::ThreadState::kWaitingForDeoptimization:
+    case art::ThreadState::kWaitingForGetObjectsAllocated:
+    case art::ThreadState::kWaitingForJniOnLoad:
+    case art::ThreadState::kWaitingForSignalCatcherOutput:
+    case art::ThreadState::kWaitingInMainSignalCatcherLoop:
+    case art::ThreadState::kWaitingForMethodTracingStart:
+    case art::ThreadState::kWaitingForVisitObjects:
+    case art::ThreadState::kWaitingForGcThreadFlip:
+      return JVMTI_JAVA_LANG_THREAD_STATE_WAITING;
+  }
+  LOG(FATAL) << "Unreachable";
+  UNREACHABLE();
+}
+
+jvmtiError ThreadUtil::GetThreadState(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                      jthread thread,
+                                      jint* thread_state_ptr) {
+  if (thread_state_ptr == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  art::ScopedObjectAccess soa(art::Thread::Current());
+  art::Thread* native_thread = nullptr;
+  art::ThreadState internal_thread_state = GetNativeThreadState(thread, soa, &native_thread);
+
+  if (internal_thread_state == art::ThreadState::kStarting) {
+    if (thread == nullptr) {
+      // No native thread, and no Java thread? We must be starting up. Report as wrong phase.
+      return ERR(WRONG_PHASE);
+    }
+
+    // Need to read the Java "started" field to know whether this is starting or terminated.
+    art::ObjPtr<art::mirror::Object> peer = soa.Decode<art::mirror::Object>(thread);
+    art::ObjPtr<art::mirror::Class> klass = peer->GetClass();
+    art::ArtField* started_field = klass->FindDeclaredInstanceField("started", "Z");
+    CHECK(started_field != nullptr);
+    bool started = started_field->GetBoolean(peer) != 0;
+    constexpr jint kStartedState = JVMTI_JAVA_LANG_THREAD_STATE_NEW;
+    constexpr jint kTerminatedState = JVMTI_THREAD_STATE_TERMINATED |
+                                      JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED;
+    *thread_state_ptr = started ? kTerminatedState : kStartedState;
+    return ERR(NONE);
+  }
+  DCHECK(native_thread != nullptr);
+
+  // Translate internal thread state to JVMTI and Java state.
+  jint jvmti_state = GetJvmtiThreadStateFromInternal(internal_thread_state);
+  if (native_thread->IsInterrupted()) {
+    jvmti_state |= JVMTI_THREAD_STATE_INTERRUPTED;
+  }
+
+  // Java state is derived from nativeGetState.
+  // Note: Our implementation assigns "runnable" to suspended. As such, we will have slightly
+  //       different mask. However, this is for consistency with the Java view.
+  jint java_state = GetJavaStateFromInternal(internal_thread_state);
+
+  *thread_state_ptr = jvmti_state | java_state;
+
+  return ERR(NONE);
+}
+
+}  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_thread.h b/runtime/openjdkjvmti/ti_thread.h
new file mode 100644
index 0000000..b6ffbb5
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_thread.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_THREAD_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_THREAD_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class ThreadUtil {
+ public:
+  static jvmtiError GetCurrentThread(jvmtiEnv* env, jthread* thread_ptr);
+
+  static jvmtiError GetThreadInfo(jvmtiEnv* env, jthread thread, jvmtiThreadInfo* info_ptr);
+
+  static jvmtiError GetThreadState(jvmtiEnv* env, jthread thread, jint* thread_state_ptr);
+};
+
+}  // namespace openjdkjvmti
+
+#endif  // ART_RUNTIME_OPENJDKJVMTI_TI_THREAD_H_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 2086d70..df5fc5c 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1364,6 +1364,39 @@
   return true;
 }
 
+static bool EnsureJvmtiPlugin(Runtime* runtime,
+                              std::vector<Plugin>* plugins,
+                              std::string* error_msg) {
+  constexpr const char* plugin_name = kIsDebugBuild ? "libopenjdkjvmtid.so" : "libopenjdkjvmti.so";
+
+  // Is the plugin already loaded?
+  for (Plugin p : *plugins) {
+    if (p.GetLibrary() == plugin_name) {
+      return true;
+    }
+  }
+
+  // Is the process debuggable? Otherwise, do not attempt to load the plugin.
+  if (!runtime->IsDebuggable()) {
+    *error_msg = "Process is not debuggable.";
+    return false;
+  }
+
+  Plugin new_plugin = Plugin::Create(plugin_name);
+
+  // Suspend all threads to protect ourself somewhat.
+  Thread* self = Thread::Current();
+  ScopedObjectAccess soa(self);      // Now we know we have the shared lock.
+  ScopedThreadSuspension sts(self, art::kWaitingForDebuggerToAttach);
+  ScopedSuspendAll ssa("EnsureJvmtiPlugin");
+  if (!new_plugin.Load(error_msg)) {
+    return false;
+  }
+
+  plugins->push_back(std::move(new_plugin));
+  return true;
+}
+
 // Attach a new agent and add it to the list of runtime agents
 //
 // TODO: once we decide on the threading model for agents,
@@ -1371,18 +1404,25 @@
 //   (and we synchronize access to any shared data structures like "agents_")
 //
 void Runtime::AttachAgent(const std::string& agent_arg) {
+  std::string error_msg;
+  if (!EnsureJvmtiPlugin(this, &plugins_, &error_msg)) {
+    LOG(WARNING) << "Could not load plugin: " << error_msg;
+    ScopedObjectAccess soa(Thread::Current());
+    ThrowIOException("%s", error_msg.c_str());
+    return;
+  }
+
   ti::Agent agent(agent_arg);
 
   int res = 0;
-  std::string err;
-  ti::Agent::LoadError result = agent.Attach(&res, &err);
+  ti::Agent::LoadError result = agent.Attach(&res, &error_msg);
 
   if (result == ti::Agent::kNoError) {
     agents_.push_back(std::move(agent));
   } else {
-    LOG(ERROR) << "Agent attach failed (result=" << result << ") : " << err;
+    LOG(WARNING) << "Agent attach failed (result=" << result << ") : " << error_msg;
     ScopedObjectAccess soa(Thread::Current());
-    ThrowWrappedIOException("%s", err.c_str());
+    ThrowIOException("%s", error_msg.c_str());
   }
 }
 
diff --git a/test/129-ThreadGetId/expected.txt b/test/129-ThreadGetId/expected.txt
index 134d8d0..aadf90d 100644
--- a/test/129-ThreadGetId/expected.txt
+++ b/test/129-ThreadGetId/expected.txt
@@ -1 +1,2 @@
+HeapTaskDaemon depth 0
 Finishing
diff --git a/test/129-ThreadGetId/src/Main.java b/test/129-ThreadGetId/src/Main.java
index 9934bba..5aefd17 100644
--- a/test/129-ThreadGetId/src/Main.java
+++ b/test/129-ThreadGetId/src/Main.java
@@ -22,6 +22,7 @@
 
     public static void main(String[] args) throws Exception {
         final Thread[] threads = new Thread[numberOfThreads];
+        test_getStackTraces();
         for (int t = 0; t < threads.length; t++) {
             threads[t] = new Thread(new Main());
             threads[t].start();
@@ -32,6 +33,19 @@
         System.out.println("Finishing");
     }
 
+    static void test_getStackTraces() {
+        // Check all the current threads for positive IDs.
+        Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
+        for (Map.Entry<Thread, StackTraceElement[]> pair : map.entrySet()) {
+            Thread thread = pair.getKey();
+            // Expect empty stack trace since we do not support suspending the GC thread for
+            // obtaining stack traces. See b/28261069.
+            if (thread.getName().equals("HeapTaskDaemon")) {
+                System.out.println(thread.getName() + " depth " + pair.getValue().length); 
+            }
+        }
+    }
+
     public void test_getId() {
         if (Thread.currentThread().getId() <= 0) {
             System.out.println("current thread's ID is not positive");
diff --git a/test/909-attach-agent/expected.txt b/test/909-attach-agent/expected.txt
index eacc595..c0bccd6 100644
--- a/test/909-attach-agent/expected.txt
+++ b/test/909-attach-agent/expected.txt
@@ -1,3 +1,11 @@
 Hello, world!
 Attached Agent for test 909-attach-agent
 Goodbye!
+Hello, world!
+Attached Agent for test 909-attach-agent
+Goodbye!
+Hello, world!
+java.io.IOException: Process is not debuggable.
+	at dalvik.system.VMDebug.attachAgent(Native Method)
+	at Main.main(Main.java:27)
+Goodbye!
diff --git a/test/909-attach-agent/run b/test/909-attach-agent/run
index aed6e83..985341b 100755
--- a/test/909-attach-agent/run
+++ b/test/909-attach-agent/run
@@ -24,4 +24,14 @@
 ./default-run "$@" --experimental agents \
                    --experimental runtime-plugins \
                    --android-runtime-option -Xplugin:${plugin} \
+                   --android-runtime-option -Xfully-deoptable \
+                   --args agent:${agent}=909-attach-agent
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --android-runtime-option -Xfully-deoptable \
+                   --args agent:${agent}=909-attach-agent
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
                    --args agent:${agent}=909-attach-agent
diff --git a/test/911-get-stack-trace/expected.txt b/test/911-get-stack-trace/expected.txt
index f8c97ce..e40698a 100644
--- a/test/911-get-stack-trace/expected.txt
+++ b/test/911-get-stack-trace/expected.txt
@@ -4,72 +4,72 @@
 From top
 ---------
  getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2
- print (Ljava/lang/Thread;II)V 0 124
- printOrWait (IILMain$ControlData;)V 6 151
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- doTest ()V 38 34
- main ([Ljava/lang/String;)V 6 24
+ print (Ljava/lang/Thread;II)V 0 183
+ printOrWait (IILMain$ControlData;)V 6 246
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ doTest ()V 38 41
+ main ([Ljava/lang/String;)V 6 27
 ---------
- print (Ljava/lang/Thread;II)V 0 124
- printOrWait (IILMain$ControlData;)V 6 151
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- doTest ()V 42 35
- main ([Ljava/lang/String;)V 6 24
+ print (Ljava/lang/Thread;II)V 0 183
+ printOrWait (IILMain$ControlData;)V 6 246
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ doTest ()V 42 42
+ main ([Ljava/lang/String;)V 6 27
 ---------
  getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2
- print (Ljava/lang/Thread;II)V 0 124
- printOrWait (IILMain$ControlData;)V 6 151
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
- bar (IIILMain$ControlData;)J 0 136
+ print (Ljava/lang/Thread;II)V 0 183
+ printOrWait (IILMain$ControlData;)V 6 246
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
 ---------
- printOrWait (IILMain$ControlData;)V 6 151
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ printOrWait (IILMain$ControlData;)V 6 246
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
 From bottom
 ---------
- main ([Ljava/lang/String;)V 6 24
+ main ([Ljava/lang/String;)V 6 27
 ---------
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- doTest ()V 65 41
- main ([Ljava/lang/String;)V 6 24
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ doTest ()V 65 48
+ main ([Ljava/lang/String;)V 6 27
 ---------
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
 
 ################################
 ### Other thread (suspended) ###
@@ -77,132 +77,519 @@
 From top
 ---------
  wait ()V -1 -2
- printOrWait (IILMain$ControlData;)V 24 157
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- run ()V 4 54
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ run ()V 4 61
 ---------
- printOrWait (IILMain$ControlData;)V 24 157
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- run ()V 4 54
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ run ()V 4 61
 ---------
  wait ()V -1 -2
- printOrWait (IILMain$ControlData;)V 24 157
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
 ---------
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
 From bottom
 ---------
- run ()V 4 54
+ run ()V 4 61
 ---------
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- run ()V 4 54
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ run ()V 4 61
 ---------
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
 
 ###########################
 ### Other thread (live) ###
 ###########################
 From top
 ---------
- printOrWait (IILMain$ControlData;)V 44 164
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- run ()V 4 88
+ printOrWait (IILMain$ControlData;)V 44 259
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ run ()V 4 95
 ---------
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- run ()V 4 88
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ run ()V 4 95
 ---------
- printOrWait (IILMain$ControlData;)V 44 164
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ printOrWait (IILMain$ControlData;)V 44 259
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
 ---------
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
 From bottom
 ---------
- run ()V 4 88
+ run ()V 4 95
 ---------
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- run ()V 4 88
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ run ()V 4 95
 ---------
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
- foo (IIILMain$ControlData;)I 0 131
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
- bar (IIILMain$ControlData;)J 0 136
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+
+################################
+### Other threads (suspended) ###
+################################
+---------
+FinalizerDaemon
+<not printed>
+---------
+FinalizerWatchdogDaemon
+<not printed>
+---------
+HeapTaskDaemon
+<not printed>
+---------
+ReferenceQueueDaemon
+<not printed>
+---------
+Signal Catcher
+
+---------
+Thread-10
+
+---------
+Thread-11
+
+---------
+Thread-12
+
+---------
+Thread-13
+
+---------
+Thread-4
+
+---------
+Thread-5
+
+---------
+Thread-6
+
+---------
+Thread-7
+
+---------
+Thread-8
+
+---------
+Thread-9
+
+---------
+main
+
+---------
+FinalizerDaemon
+<not printed>
+---------
+FinalizerWatchdogDaemon
+<not printed>
+---------
+HeapTaskDaemon
+<not printed>
+---------
+ReferenceQueueDaemon
+<not printed>
+---------
+Signal Catcher
+
+---------
+Thread-10
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+
+---------
+Thread-11
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+
+---------
+Thread-12
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+
+---------
+Thread-13
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+
+---------
+Thread-4
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+
+---------
+Thread-5
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+
+---------
+Thread-6
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+
+---------
+Thread-7
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+
+---------
+Thread-8
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+
+---------
+Thread-9
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+
+---------
+main
+ getAllStackTraces (I)[[Ljava/lang/Object; -1 -2
+ printAll (I)V 0 219
+ doTestAllStackTraces ()V 107 156
+ main ([Ljava/lang/String;)V 15 31
+
+---------
+FinalizerDaemon
+<not printed>
+---------
+FinalizerWatchdogDaemon
+<not printed>
+---------
+HeapTaskDaemon
+<not printed>
+---------
+ReferenceQueueDaemon
+<not printed>
+---------
+Signal Catcher
+
+---------
+Thread-10
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ run ()V 4 144
+
+---------
+Thread-11
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ run ()V 4 144
+
+---------
+Thread-12
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ run ()V 4 144
+
+---------
+Thread-13
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ run ()V 4 144
+
+---------
+Thread-4
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ run ()V 4 144
+
+---------
+Thread-5
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ run ()V 4 144
+
+---------
+Thread-6
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ run ()V 4 144
+
+---------
+Thread-7
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ run ()V 4 144
+
+---------
+Thread-8
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ run ()V 4 144
+
+---------
+Thread-9
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 252
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 237
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 239
+ bar (IIILMain$ControlData;)J 0 231
+ foo (IIILMain$ControlData;)I 0 226
+ run ()V 4 144
+
+---------
+main
+ getAllStackTraces (I)[[Ljava/lang/Object; -1 -2
+ printAll (I)V 0 219
+ doTestAllStackTraces ()V 112 158
+ main ([Ljava/lang/String;)V 15 31
+
+Done
diff --git a/test/911-get-stack-trace/src/Main.java b/test/911-get-stack-trace/src/Main.java
index 722bee8..3479abb 100644
--- a/test/911-get-stack-trace/src/Main.java
+++ b/test/911-get-stack-trace/src/Main.java
@@ -14,7 +14,10 @@
  * limitations under the License.
  */
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
 
 public class Main {
@@ -24,6 +27,10 @@
     doTest();
     doTestOtherThreadWait();
     doTestOtherThreadBusyLoop();
+
+    doTestAllStackTraces();
+
+    System.out.println("Done");
   }
 
   public static void doTest() throws Exception {
@@ -109,6 +116,58 @@
     t.join();
   }
 
+  private final static List<Object> RETAIN = new ArrayList<Object>();
+
+  public static void doTestAllStackTraces() throws Exception {
+    System.out.println();
+    System.out.println("################################");
+    System.out.println("### Other threads (suspended) ###");
+    System.out.println("################################");
+
+    // Also create an unstarted and a dead thread.
+    RETAIN.add(new Thread());
+    Thread deadThread = new Thread();
+    RETAIN.add(deadThread);
+    deadThread.start();
+    deadThread.join();
+
+    final int N = 10;
+
+    final ControlData data = new ControlData(N);
+    data.waitFor = new Object();
+
+    Thread threads[] = new Thread[N];
+
+    for (int i = 0; i < N; i++) {
+      Thread t = new Thread() {
+        public void run() {
+          Recurse.foo(4, 0, 0, data);
+        }
+      };
+      t.start();
+      threads[i] = t;
+    }
+    data.reached.await();
+    Thread.yield();
+    Thread.sleep(500);  // A little bit of time...
+
+    printAll(0);
+
+    printAll(5);
+
+    printAll(25);
+
+    // Let the thread make progress and die.
+    synchronized(data.waitFor) {
+      data.waitFor.notifyAll();
+    }
+    for (int i = 0; i < N; i++) {
+      threads[i].join();
+    }
+
+    RETAIN.clear();
+  }
+
   public static void print(String[][] stack) {
     System.out.println("---------");
     for (String[] stackElement : stack) {
@@ -124,6 +183,42 @@
     print(getStackTrace(t, start, max));
   }
 
+  public static void printAll(Object[][] stacks) {
+    List<String> stringified = new ArrayList<String>(stacks.length);
+
+    for (Object[] stackInfo : stacks) {
+      Thread t = (Thread)stackInfo[0];
+      String name = (t != null) ? t.getName() : "null";
+      String stackSerialization;
+      if (name.contains("Daemon")) {
+        // Do not print daemon stacks, as they're non-deterministic.
+        stackSerialization = "<not printed>";
+      } else {
+        StringBuilder sb = new StringBuilder();
+        for (String[] stackElement : (String[][])stackInfo[1]) {
+          for (String part : stackElement) {
+            sb.append(' ');
+            sb.append(part);
+          }
+          sb.append('\n');
+        }
+        stackSerialization = sb.toString();
+      }
+      stringified.add(name + "\n" + stackSerialization);
+    }
+
+    Collections.sort(stringified);
+
+    for (String s : stringified) {
+      System.out.println("---------");
+      System.out.println(s);
+    }
+  }
+
+  public static void printAll(int max) {
+    printAll(getAllStackTraces(max));
+  }
+
   // Wrap generated stack traces into a class to separate them nicely.
   public static class Recurse {
 
@@ -170,10 +265,22 @@
   }
 
   public static class ControlData {
-    CountDownLatch reached = new CountDownLatch(1);
+    CountDownLatch reached;
     Object waitFor = null;
     volatile boolean stop = false;
+
+    public ControlData() {
+      this(1);
+    }
+
+    public ControlData(int latchCount) {
+      reached = new CountDownLatch(latchCount);
+    }
   }
 
   public static native String[][] getStackTrace(Thread thread, int start, int max);
+  // Get all stack traces. This will return an array with an element for each thread. The element
+  // is an array itself with the first element being the thread, and the second element a nested
+  // String array as in getStackTrace.
+  public static native Object[][] getAllStackTraces(int max);
 }
diff --git a/test/911-get-stack-trace/stack_trace.cc b/test/911-get-stack-trace/stack_trace.cc
index cca163b..57d4f6d 100644
--- a/test/911-get-stack-trace/stack_trace.cc
+++ b/test/911-get-stack-trace/stack_trace.cc
@@ -50,22 +50,9 @@
   return line_number;
 }
 
-extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getStackTrace(
-    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread, jint start, jint max) {
-  std::unique_ptr<jvmtiFrameInfo[]> frames(new jvmtiFrameInfo[max]);
-
-  jint count;
-  {
-    jvmtiError result = jvmti_env->GetStackTrace(thread, start, max, frames.get(), &count);
-    if (result != JVMTI_ERROR_NONE) {
-      char* err;
-      jvmti_env->GetErrorName(result, &err);
-      printf("Failure running GetStackTrace: %s\n", err);
-      jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
-      return nullptr;
-    }
-  }
-
+static jobjectArray TranslateJvmtiFrameInfoArray(JNIEnv* env,
+                                                 jvmtiFrameInfo* frames,
+                                                 jint count) {
   auto callback = [&](jint method_index) -> jobjectArray {
     char* name;
     char* sig;
@@ -140,5 +127,58 @@
   return CreateObjectArray(env, count, "[Ljava/lang/String;", callback);
 }
 
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getStackTrace(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread, jint start, jint max) {
+  std::unique_ptr<jvmtiFrameInfo[]> frames(new jvmtiFrameInfo[max]);
+
+  jint count;
+  {
+    jvmtiError result = jvmti_env->GetStackTrace(thread, start, max, frames.get(), &count);
+    if (result != JVMTI_ERROR_NONE) {
+      char* err;
+      jvmti_env->GetErrorName(result, &err);
+      printf("Failure running GetStackTrace: %s\n", err);
+      jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+      return nullptr;
+    }
+  }
+
+  return TranslateJvmtiFrameInfoArray(env, frames.get(), count);
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getAllStackTraces(
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jint max) {
+  std::unique_ptr<jvmtiFrameInfo[]> frames(new jvmtiFrameInfo[max]);
+
+  jint thread_count;
+  jvmtiStackInfo* stack_infos;
+  {
+    jvmtiError result = jvmti_env->GetAllStackTraces(max, &stack_infos, &thread_count);
+    if (result != JVMTI_ERROR_NONE) {
+      char* err;
+      jvmti_env->GetErrorName(result, &err);
+      printf("Failure running GetAllStackTraces: %s\n", err);
+      jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+      return nullptr;
+    }
+  }
+
+  auto callback = [&](jint thread_index) -> jobject {
+    auto inner_callback = [&](jint index) -> jobject {
+      if (index == 0) {
+        return stack_infos[thread_index].thread;
+      } else {
+        return TranslateJvmtiFrameInfoArray(env,
+                                            stack_infos[thread_index].frame_buffer,
+                                            stack_infos[thread_index].frame_count);
+      }
+    };
+    return CreateObjectArray(env, 2, "java/lang/Object", inner_callback);
+  };
+  jobjectArray ret = CreateObjectArray(env, thread_count, "[Ljava/lang/Object;", callback);
+  jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(stack_infos));
+  return ret;
+}
+
 }  // namespace Test911GetStackTrace
 }  // namespace art
diff --git a/test/924-threads/build b/test/924-threads/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/924-threads/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-build "$@" --experimental agents
diff --git a/test/924-threads/expected.txt b/test/924-threads/expected.txt
new file mode 100644
index 0000000..5406522
--- /dev/null
+++ b/test/924-threads/expected.txt
@@ -0,0 +1,30 @@
+currentThread OK
+main
+5
+false
+java.lang.ThreadGroup[name=main,maxpri=10]
+class dalvik.system.PathClassLoader
+main
+5
+false
+java.lang.ThreadGroup[name=main,maxpri=10]
+class dalvik.system.PathClassLoader
+Daemon Thread
+5
+true
+java.lang.ThreadGroup[name=main,maxpri=10]
+class dalvik.system.PathClassLoader
+Daemon Thread
+5
+true
+java.lang.ThreadGroup[name=main,maxpri=10]
+class dalvik.system.PathClassLoader
+5
+5
+0 = NEW
+191 = ALIVE|WAITING_INDEFINITELY|WAITING|IN_OBJECT_WAIT
+1a1 = ALIVE|WAITING_WITH_TIMEOUT|WAITING|IN_OBJECT_WAIT
+401 = ALIVE|BLOCKED_ON_MONITOR_ENTER
+e1 = ALIVE|WAITING_WITH_TIMEOUT|SLEEPING|WAITING
+5 = ALIVE|RUNNABLE
+2 = TERMINATED
diff --git a/test/924-threads/info.txt b/test/924-threads/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/924-threads/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/924-threads/run b/test/924-threads/run
new file mode 100755
index 0000000..4379349
--- /dev/null
+++ b/test/924-threads/run
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --jvmti
diff --git a/test/924-threads/src/Main.java b/test/924-threads/src/Main.java
new file mode 100644
index 0000000..0487666
--- /dev/null
+++ b/test/924-threads/src/Main.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[1]);
+
+    doTest();
+  }
+
+  private static void doTest() throws Exception {
+    Thread t1 = Thread.currentThread();
+    Thread t2 = getCurrentThread();
+
+    if (t1 != t2) {
+      throw new RuntimeException("Expected " + t1 + " but got " + t2);
+    }
+    System.out.println("currentThread OK");
+
+    printThreadInfo(t1);
+    printThreadInfo(null);
+
+    Thread t3 = new Thread("Daemon Thread");
+    t3.setDaemon(true);
+    // Do not start this thread, yet.
+    printThreadInfo(t3);
+    // Start, and wait for it to die.
+    t3.start();
+    t3.join();
+    Thread.sleep(500);  // Wait a little bit.
+    // Thread has died, check that we can still get info.
+    printThreadInfo(t3);
+
+    doStateTests();
+  }
+
+  private static class Holder {
+    volatile boolean flag = false;
+  }
+
+  private static void doStateTests() throws Exception {
+    System.out.println(Integer.toHexString(getThreadState(null)));
+    System.out.println(Integer.toHexString(getThreadState(Thread.currentThread())));
+
+    final CountDownLatch cdl1 = new CountDownLatch(1);
+    final CountDownLatch cdl2 = new CountDownLatch(1);
+    final CountDownLatch cdl3_1 = new CountDownLatch(1);
+    final CountDownLatch cdl3_2 = new CountDownLatch(1);
+    final CountDownLatch cdl4 = new CountDownLatch(1);
+    final CountDownLatch cdl5 = new CountDownLatch(1);
+    final Holder h = new Holder();
+    Runnable r = new Runnable() {
+      @Override
+      public void run() {
+        try {
+          cdl1.countDown();
+          synchronized(cdl1) {
+            cdl1.wait();
+          }
+
+          cdl2.countDown();
+          synchronized(cdl2) {
+            cdl2.wait(1000);  // Wait a second.
+          }
+
+          cdl3_1.await();
+          cdl3_2.countDown();
+          synchronized(cdl3_2) {
+            // Nothing, just wanted to block on cdl3.
+          }
+
+          cdl4.countDown();
+          Thread.sleep(1000);
+
+          cdl5.countDown();
+          while (!h.flag) {
+            // Busy-loop.
+          }
+        } catch (Exception e) {
+          throw new RuntimeException(e);
+        }
+      }
+    };
+
+    Thread t = new Thread(r);
+    printThreadState(t);
+    t.start();
+
+    // Waiting.
+    cdl1.await();
+    Thread.yield();
+    Thread.sleep(100);
+    printThreadState(t);
+    synchronized(cdl1) {
+      cdl1.notifyAll();
+    }
+
+    // Timed waiting.
+    cdl2.await();
+    Thread.yield();
+    Thread.sleep(100);
+    printThreadState(t);
+    synchronized(cdl2) {
+      cdl2.notifyAll();
+    }
+
+    // Blocked on monitor.
+    synchronized(cdl3_2) {
+      cdl3_1.countDown();
+      cdl3_2.await();
+      Thread.yield();
+      Thread.sleep(100);
+      printThreadState(t);
+    }
+
+    // Sleeping.
+    cdl4.await();
+    Thread.yield();
+    Thread.sleep(100);
+    printThreadState(t);
+
+    // Running.
+    cdl5.await();
+    Thread.yield();
+    Thread.sleep(100);
+    printThreadState(t);
+    h.flag = true;
+
+    // Dying.
+    t.join();
+    Thread.yield();
+    Thread.sleep(100);
+
+    printThreadState(t);
+  }
+
+  private final static Map<Integer, String> STATE_NAMES = new HashMap<Integer, String>();
+  private final static List<Integer> STATE_KEYS = new ArrayList<Integer>();
+  static {
+    STATE_NAMES.put(0x1, "ALIVE");
+    STATE_NAMES.put(0x2, "TERMINATED");
+    STATE_NAMES.put(0x4, "RUNNABLE");
+    STATE_NAMES.put(0x400, "BLOCKED_ON_MONITOR_ENTER");
+    STATE_NAMES.put(0x80, "WAITING");
+    STATE_NAMES.put(0x10, "WAITING_INDEFINITELY");
+    STATE_NAMES.put(0x20, "WAITING_WITH_TIMEOUT");
+    STATE_NAMES.put(0x40, "SLEEPING");
+    STATE_NAMES.put(0x100, "IN_OBJECT_WAIT");
+    STATE_NAMES.put(0x200, "PARKED");
+    STATE_NAMES.put(0x100000, "SUSPENDED");
+    STATE_NAMES.put(0x200000, "INTERRUPTED");
+    STATE_NAMES.put(0x400000, "IN_NATIVE");
+    STATE_KEYS.addAll(STATE_NAMES.keySet());
+    Collections.sort(STATE_KEYS);
+  }
+  
+  private static void printThreadState(Thread t) {
+    int state = getThreadState(t);
+
+    StringBuilder sb = new StringBuilder();
+
+    for (Integer i : STATE_KEYS) {
+      if ((state & i) != 0) {
+        if (sb.length()>0) {
+          sb.append('|');
+        }
+        sb.append(STATE_NAMES.get(i));
+      }
+    }
+
+    if (sb.length() == 0) {
+      sb.append("NEW");
+    }
+
+    System.out.println(Integer.toHexString(state) + " = " + sb.toString());
+  }
+
+  private static void printThreadInfo(Thread t) {
+    Object[] threadInfo = getThreadInfo(t);
+    if (threadInfo == null || threadInfo.length != 5) {
+      System.out.println(Arrays.toString(threadInfo));
+      throw new RuntimeException("threadInfo length wrong");
+    }
+
+    System.out.println(threadInfo[0]);  // Name
+    System.out.println(threadInfo[1]);  // Priority
+    System.out.println(threadInfo[2]);  // Daemon
+    System.out.println(threadInfo[3]);  // Threadgroup
+    System.out.println(threadInfo[4] == null ? "null" : threadInfo[4].getClass());  // Context CL.
+  }
+
+  private static native Thread getCurrentThread();
+  private static native Object[] getThreadInfo(Thread t);
+  private static native int getThreadState(Thread t);
+}
diff --git a/test/924-threads/threads.cc b/test/924-threads/threads.cc
new file mode 100644
index 0000000..4abf8fc
--- /dev/null
+++ b/test/924-threads/threads.cc
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+
+#include "android-base/stringprintf.h"
+#include "base/macros.h"
+#include "base/logging.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedLocalRef.h"
+
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test924Threads {
+
+// private static native Thread getCurrentThread();
+// private static native Object[] getThreadInfo(Thread t);
+
+extern "C" JNIEXPORT jthread JNICALL Java_Main_getCurrentThread(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
+  jthread thread = nullptr;
+  jvmtiError result = jvmti_env->GetCurrentThread(&thread);
+  if (JvmtiErrorToException(env, result)) {
+    return nullptr;
+  }
+  return thread;
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getThreadInfo(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread) {
+  jvmtiThreadInfo info;
+  memset(&info, 0, sizeof(jvmtiThreadInfo));
+
+  jvmtiError result = jvmti_env->GetThreadInfo(thread, &info);
+  if (JvmtiErrorToException(env, result)) {
+    return nullptr;
+  }
+
+  auto callback = [&](jint component_index) -> jobject {
+    switch (component_index) {
+      // The name.
+      case 0:
+        return (info.name == nullptr) ? nullptr : env->NewStringUTF(info.name);
+
+      // The priority. Use a string for simplicity of construction.
+      case 1:
+        return env->NewStringUTF(android::base::StringPrintf("%d", info.priority).c_str());
+
+      // Whether it's a daemon. Use a string for simplicity of construction.
+      case 2:
+        return env->NewStringUTF(info.is_daemon == JNI_TRUE ? "true" : "false");
+
+      // The thread group;
+      case 3:
+        return env->NewLocalRef(info.thread_group);
+
+      // The context classloader.
+      case 4:
+        return env->NewLocalRef(info.context_class_loader);
+    }
+    LOG(FATAL) << "Should not reach here";
+    UNREACHABLE();
+  };
+  jobjectArray ret = CreateObjectArray(env, 5, "java/lang/Object", callback);
+
+  jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(info.name));
+  if (info.thread_group != nullptr) {
+    env->DeleteLocalRef(info.thread_group);
+  }
+  if (info.context_class_loader != nullptr) {
+    env->DeleteLocalRef(info.context_class_loader);
+  }
+
+  return ret;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_getThreadState(
+    JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread) {
+  jint state;
+  jvmtiError result = jvmti_env->GetThreadState(thread, &state);
+  if (JvmtiErrorToException(env, result)) {
+    return 0;
+  }
+  return state;
+}
+
+}  // namespace Test924Threads
+}  // namespace art
diff --git a/test/Android.bp b/test/Android.bp
index a223c3a..b0f0e5a 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -264,6 +264,7 @@
         "920-objects/objects.cc",
         "922-properties/properties.cc",
         "923-monitors/monitors.cc",
+        "924-threads/threads.cc",
     ],
     shared_libs: [
         "libbase",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index fd3a897..dd7876f 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -296,6 +296,7 @@
   921-hello-failure \
   922-properties \
   923-monitors \
+  924-threads \
 
 ifneq (,$(filter target,$(TARGET_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -721,6 +722,16 @@
 
 TEST_ART_BROKEN_OPTIMIZING_HEAP_POISONING_RUN_TESTS :=
 
+# Tests that check semantics for a non-debuggable app.
+TEST_ART_BROKEN_DEBUGGABLE_RUN_TESTS := \
+  909-attach-agent \
+
+ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+    $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+    $(IMAGE_TYPES),$(PICTEST_TYPES),debuggable,$(TEST_ART_BROKEN_DEBUGGABLE_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+
+TEST_ART_BROKEN_DEBUGGABLE_RUN_TESTS :=
+
 # Tests incompatible with bisection bug search. Sorted by incompatibility reason.
 # 000 through 595 do not compile anything. 089 tests a build failure. 018 through 137
 # run dalvikvm more than once. 115 and 088 assume they are always compiled.