Merge "ART: Mips64 runtime support"
diff --git a/runtime/Android.mk b/runtime/Android.mk
index fdc3e7c..d104077 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -294,6 +294,7 @@
   base/unix_file/fd_file.h \
   dex_file.h \
   dex_instruction.h \
+  gc_root.h \
   gc/allocator/rosalloc.h \
   gc/collector/gc_type.h \
   gc/allocator_type.h \
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 377a3c3..05b6b1d 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1748,23 +1748,23 @@
   WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
   if ((flags & kVisitRootFlagAllRoots) != 0) {
     for (GcRoot<mirror::Class>& root : class_table_) {
-      root.VisitRoot(callback, arg, 0, kRootStickyClass);
+      root.VisitRoot(callback, arg, RootInfo(kRootStickyClass));
     }
     for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) {
-      root.VisitRoot(callback, arg, 0, kRootStickyClass);
+      root.VisitRoot(callback, arg, RootInfo(kRootStickyClass));
     }
   } else if ((flags & kVisitRootFlagNewRoots) != 0) {
     for (auto& root : new_class_roots_) {
       mirror::Class* old_ref = root.Read<kWithoutReadBarrier>();
-      root.VisitRoot(callback, arg, 0, kRootStickyClass);
+      root.VisitRoot(callback, arg, RootInfo(kRootStickyClass));
       mirror::Class* new_ref = root.Read<kWithoutReadBarrier>();
       if (UNLIKELY(new_ref != old_ref)) {
         // Uh ohes, GC moved a root in the log. Need to search the class_table and update the
         // corresponding object. This is slow, but luckily for us, this may only happen with a
         // concurrent moving GC.
         auto it = class_table_.Find(GcRoot<mirror::Class>(old_ref));
-        class_table_.Erase(it);
-        class_table_.Insert(GcRoot<mirror::Class>(new_ref));
+        DCHECK(it != class_table_.end());
+        *it = GcRoot<mirror::Class>(new_ref);
       }
     }
   }
@@ -1784,17 +1784,17 @@
 // reinit references to when reinitializing a ClassLinker from a
 // mapped image.
 void ClassLinker::VisitRoots(RootCallback* callback, void* arg, VisitRootFlags flags) {
-  class_roots_.VisitRoot(callback, arg, 0, kRootVMInternal);
+  class_roots_.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
   Thread* self = Thread::Current();
   {
     ReaderMutexLock mu(self, dex_lock_);
     if ((flags & kVisitRootFlagAllRoots) != 0) {
       for (GcRoot<mirror::DexCache>& dex_cache : dex_caches_) {
-        dex_cache.VisitRoot(callback, arg, 0, kRootVMInternal);
+        dex_cache.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
       }
     } else if ((flags & kVisitRootFlagNewRoots) != 0) {
       for (size_t index : new_dex_cache_roots_) {
-        dex_caches_[index].VisitRoot(callback, arg, 0, kRootVMInternal);
+        dex_caches_[index].VisitRoot(callback, arg, RootInfo(kRootVMInternal));
       }
     }
     if ((flags & kVisitRootFlagClearRootLog) != 0) {
@@ -1807,12 +1807,10 @@
     }
   }
   VisitClassRoots(callback, arg, flags);
-  array_iftable_.VisitRoot(callback, arg, 0, kRootVMInternal);
+  array_iftable_.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
   DCHECK(!array_iftable_.IsNull());
   for (size_t i = 0; i < kFindArrayCacheSize; ++i) {
-    if (!find_array_class_cache_[i].IsNull()) {
-      find_array_class_cache_[i].VisitRoot(callback, arg, 0, kRootVMInternal);
-    }
+    find_array_class_cache_[i].VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
   }
 }
 
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 6c7c1e2..64e129c 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -365,7 +365,7 @@
     }
   }
 
-  static void TestRootVisitor(mirror::Object** root, void*, uint32_t, RootType) {
+  static void TestRootVisitor(mirror::Object** root, void*, const RootInfo&) {
     EXPECT_TRUE(*root != nullptr);
   }
 };
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 229a1af..c595de7 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -342,19 +342,18 @@
 // Breakpoints.
 static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_);
 
-void DebugInvokeReq::VisitRoots(RootCallback* callback, void* arg, uint32_t tid,
-                                RootType root_type) {
+void DebugInvokeReq::VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info) {
   if (receiver != nullptr) {
-    callback(&receiver, arg, tid, root_type);
+    callback(&receiver, arg, root_info);
   }
   if (thread != nullptr) {
-    callback(&thread, arg, tid, root_type);
+    callback(&thread, arg, root_info);
   }
   if (klass != nullptr) {
-    callback(reinterpret_cast<mirror::Object**>(&klass), arg, tid, root_type);
+    callback(reinterpret_cast<mirror::Object**>(&klass), arg, root_info);
   }
   if (method != nullptr) {
-    callback(reinterpret_cast<mirror::Object**>(&method), arg, tid, root_type);
+    callback(reinterpret_cast<mirror::Object**>(&method), arg, root_info);
   }
 }
 
@@ -366,10 +365,9 @@
   method = nullptr;
 }
 
-void SingleStepControl::VisitRoots(RootCallback* callback, void* arg, uint32_t tid,
-                                   RootType root_type) {
+void SingleStepControl::VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info) {
   if (method != nullptr) {
-    callback(reinterpret_cast<mirror::Object**>(&method), arg, tid, root_type);
+    callback(reinterpret_cast<mirror::Object**>(&method), arg, root_info);
   }
 }
 
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 8f0db76..e79e8e4 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -28,6 +28,7 @@
 #include <string>
 #include <vector>
 
+#include "gc_root.h"
 #include "jdwp/jdwp.h"
 #include "jni.h"
 #include "jvalue.h"
@@ -87,7 +88,7 @@
   Mutex lock DEFAULT_MUTEX_ACQUIRED_AFTER;
   ConditionVariable cond GUARDED_BY(lock);
 
-  void VisitRoots(RootCallback* callback, void* arg, uint32_t tid, RootType root_type)
+  void VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void Clear();
@@ -122,7 +123,7 @@
   // single-step depth.
   int stack_depth;
 
-  void VisitRoots(RootCallback* callback, void* arg, uint32_t tid, RootType root_type)
+  void VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   bool ContainsDexPc(uint32_t dex_pc) const;
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index b2482ac..18af005 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -300,22 +300,20 @@
 }
 
 void MarkCompact::MarkHeapReferenceCallback(mirror::HeapReference<mirror::Object>* obj_ptr,
-                                          void* arg) {
+                                            void* arg) {
   reinterpret_cast<MarkCompact*>(arg)->MarkObject(obj_ptr->AsMirrorPtr());
 }
 
 void MarkCompact::DelayReferenceReferentCallback(mirror::Class* klass, mirror::Reference* ref,
-                                               void* arg) {
+                                                 void* arg) {
   reinterpret_cast<MarkCompact*>(arg)->DelayReferenceReferent(klass, ref);
 }
 
-void MarkCompact::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
-                                   RootType /*root_type*/) {
+void MarkCompact::MarkRootCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
   reinterpret_cast<MarkCompact*>(arg)->MarkObject(*root);
 }
 
-void MarkCompact::UpdateRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
-                                     RootType /*root_type*/) {
+void MarkCompact::UpdateRootCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
   mirror::Object* obj = *root;
   mirror::Object* new_obj = reinterpret_cast<MarkCompact*>(arg)->GetMarkedForwardAddress(obj);
   if (obj != new_obj) {
diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h
index f40e870..f6d473d 100644
--- a/runtime/gc/collector/mark_compact.h
+++ b/runtime/gc/collector/mark_compact.h
@@ -24,6 +24,7 @@
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "garbage_collector.h"
+#include "gc_root.h"
 #include "gc/accounting/heap_bitmap.h"
 #include "immune_region.h"
 #include "lock_word.h"
@@ -113,8 +114,7 @@
   void SweepSystemWeaks()
       SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
-  static void MarkRootCallback(mirror::Object** root, void* arg, uint32_t /*tid*/,
-                               RootType /*root_type*/)
+  static void MarkRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
   static mirror::Object* MarkObjectCallback(mirror::Object* root, void* arg)
@@ -180,8 +180,7 @@
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
   // Update the references of objects by using the forwarding addresses.
   void UpdateReferences() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
-  static void UpdateRootCallback(mirror::Object** root, void* arg, uint32_t /*thread_id*/,
-                                 RootType /*root_type*/)
+  static void UpdateRootCallback(mirror::Object** root, void* arg, const RootInfo& /*root_info*/)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_)
       SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
   // Move objects and restore lock words.
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index 6ad44e6..80f7968 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -460,42 +460,35 @@
   }
 }
 
-void MarkSweep::MarkRootParallelCallback(Object** root, void* arg, uint32_t /*thread_id*/,
-                                         RootType /*root_type*/) {
+void MarkSweep::MarkRootParallelCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
   reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNullParallel(*root);
 }
 
-void MarkSweep::VerifyRootMarked(Object** root, void* arg, uint32_t /*thread_id*/,
-                                 RootType /*root_type*/) {
+void MarkSweep::VerifyRootMarked(Object** root, void* arg, const RootInfo& /*root_info*/) {
   CHECK(reinterpret_cast<MarkSweep*>(arg)->IsMarked(*root));
 }
 
-void MarkSweep::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
-                                 RootType /*root_type*/) {
+void MarkSweep::MarkRootCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
   reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNull(*root);
 }
 
-void MarkSweep::VerifyRootCallback(const Object* root, void* arg, size_t vreg,
-                                   const StackVisitor* visitor, RootType root_type) {
-  reinterpret_cast<MarkSweep*>(arg)->VerifyRoot(root, vreg, visitor, root_type);
+void MarkSweep::VerifyRootCallback(Object** root, void* arg, const RootInfo& root_info) {
+  reinterpret_cast<MarkSweep*>(arg)->VerifyRoot(*root, root_info);
 }
 
-void MarkSweep::VerifyRoot(const Object* root, size_t vreg, const StackVisitor* visitor,
-                           RootType root_type) {
+void MarkSweep::VerifyRoot(const Object* root, const RootInfo& root_info) {
   // See if the root is on any space bitmap.
   if (heap_->GetLiveBitmap()->GetContinuousSpaceBitmap(root) == nullptr) {
     space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace();
     if (large_object_space != nullptr && !large_object_space->Contains(root)) {
-      LOG(ERROR) << "Found invalid root: " << root << " with type " << root_type;
-      if (visitor != NULL) {
-        LOG(ERROR) << visitor->DescribeLocation() << " in VReg: " << vreg;
-      }
+      LOG(ERROR) << "Found invalid root: " << root << " ";
+      root_info.Describe(LOG(ERROR));
     }
   }
 }
 
 void MarkSweep::VerifyRoots() {
-  Runtime::Current()->GetThreadList()->VerifyRoots(VerifyRootCallback, this);
+  Runtime::Current()->GetThreadList()->VisitRoots(VerifyRootCallback, this);
 }
 
 void MarkSweep::MarkRoots(Thread* self) {
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index 9ac110d..b787327 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -24,6 +24,7 @@
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "garbage_collector.h"
+#include "gc_root.h"
 #include "gc/accounting/heap_bitmap.h"
 #include "immune_region.h"
 #include "object_callbacks.h"
@@ -182,13 +183,11 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
-  static void MarkRootCallback(mirror::Object** root, void* arg, uint32_t thread_id,
-                               RootType root_type)
+  static void MarkRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
-  static void VerifyRootMarked(mirror::Object** root, void* arg, uint32_t /*thread_id*/,
-                               RootType /*root_type*/)
+  static void VerifyRootMarked(mirror::Object** root, void* arg, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
@@ -196,8 +195,7 @@
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  static void MarkRootParallelCallback(mirror::Object** root, void* arg, uint32_t thread_id,
-                                       RootType root_type)
+  static void MarkRootParallelCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // Marks an object.
@@ -247,11 +245,9 @@
   // whether or not we care about pauses.
   size_t GetThreadCount(bool paused) const;
 
-  static void VerifyRootCallback(const mirror::Object* root, void* arg, size_t vreg,
-                                 const StackVisitor *visitor, RootType root_type);
+  static void VerifyRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info);
 
-  void VerifyRoot(const mirror::Object* root, size_t vreg, const StackVisitor* visitor,
-                  RootType root_type) NO_THREAD_SAFETY_ANALYSIS;
+  void VerifyRoot(const mirror::Object* root, const RootInfo& root_info) NO_THREAD_SAFETY_ANALYSIS;
 
   // Push a single reference on a mark stack.
   void PushOnMarkStack(mirror::Object* obj);
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index 82d6992..fcc601f 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -600,8 +600,7 @@
   reinterpret_cast<SemiSpace*>(arg)->DelayReferenceReferent(klass, ref);
 }
 
-void SemiSpace::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id*/,
-                                 RootType /*root_type*/) {
+void SemiSpace::MarkRootCallback(Object** root, void* arg, const RootInfo& /*root_info*/) {
   auto ref = StackReference<mirror::Object>::FromMirrorPtr(*root);
   reinterpret_cast<SemiSpace*>(arg)->MarkObject(&ref);
   if (*root != ref.AsMirrorPtr()) {
diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h
index 1c4f1e4..f8fced8 100644
--- a/runtime/gc/collector/semi_space.h
+++ b/runtime/gc/collector/semi_space.h
@@ -23,6 +23,7 @@
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "garbage_collector.h"
+#include "gc_root.h"
 #include "gc/accounting/heap_bitmap.h"
 #include "immune_region.h"
 #include "mirror/object_reference.h"
@@ -132,8 +133,7 @@
   void SweepSystemWeaks()
       SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
-  static void MarkRootCallback(mirror::Object** root, void* arg, uint32_t /*tid*/,
-                               RootType /*root_type*/)
+  static void MarkRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
 
   static mirror::Object* MarkObjectCallback(mirror::Object* root, void* arg)
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 618f1cc..553a5d3 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -2236,8 +2236,8 @@
   gc_complete_cond_->Broadcast(self);
 }
 
-static void RootMatchesObjectVisitor(mirror::Object** root, void* arg, uint32_t /*thread_id*/,
-                                     RootType /*root_type*/) {
+static void RootMatchesObjectVisitor(mirror::Object** root, void* arg,
+                                     const RootInfo& /*root_info*/) {
   mirror::Object* obj = reinterpret_cast<mirror::Object*>(arg);
   if (*root == obj) {
     LOG(INFO) << "Object " << obj << " is a root";
@@ -2279,12 +2279,12 @@
     return heap_->IsLiveObjectLocked(obj, true, false, true);
   }
 
-  static void VerifyRootCallback(mirror::Object** root, void* arg, uint32_t thread_id,
-                                 RootType root_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  static void VerifyRootCallback(mirror::Object** root, void* arg, const RootInfo& root_info)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     VerifyReferenceVisitor* visitor = reinterpret_cast<VerifyReferenceVisitor*>(arg);
     if (!visitor->VerifyReference(nullptr, *root, MemberOffset(0))) {
       LOG(ERROR) << "Root " << *root << " is dead with type " << PrettyTypeOf(*root)
-          << " thread_id= " << thread_id << " root_type= " << root_type;
+          << " thread_id= " << root_info.GetThreadId() << " root_type= " << root_info.GetType();
     }
   }
 
diff --git a/runtime/gc_root.h b/runtime/gc_root.h
index aee5586..7e0be64 100644
--- a/runtime/gc_root.h
+++ b/runtime/gc_root.h
@@ -19,22 +19,75 @@
 
 #include "base/macros.h"
 #include "base/mutex.h"       // For Locks::mutator_lock_.
-#include "object_callbacks.h"
 
 namespace art {
 
+namespace mirror {
+class Object;
+}  // namespace mirror
+
+enum RootType {
+  kRootUnknown = 0,
+  kRootJNIGlobal,
+  kRootJNILocal,
+  kRootJavaFrame,
+  kRootNativeStack,
+  kRootStickyClass,
+  kRootThreadBlock,
+  kRootMonitorUsed,
+  kRootThreadObject,
+  kRootInternedString,
+  kRootDebugger,
+  kRootVMInternal,
+  kRootJNIMonitor,
+};
+std::ostream& operator<<(std::ostream& os, const RootType& root_type);
+
+class RootInfo {
+ public:
+  // Thread id 0 is for non thread roots.
+  explicit RootInfo(RootType type, uint32_t thread_id = 0)
+     : type_(type), thread_id_(thread_id) {
+  }
+  virtual ~RootInfo() {
+  }
+  RootType GetType() const {
+    return type_;
+  }
+  uint32_t GetThreadId() const {
+    return thread_id_;
+  }
+  virtual void Describe(std::ostream& os) const {
+    os << "Type=" << type_ << " thread_id=" << thread_id_;
+  }
+
+ private:
+  const RootType type_;
+  const uint32_t thread_id_;
+};
+
+// Returns the new address of the object, returns root if it has not moved. tid and root_type are
+// only used by hprof.
+typedef void (RootCallback)(mirror::Object** root, void* arg, const RootInfo& root_info);
+
 template<class MirrorType>
 class PACKED(4) GcRoot {
  public:
   template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
   ALWAYS_INLINE MirrorType* Read() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void VisitRoot(RootCallback* callback, void* arg, uint32_t thread_id, RootType root_type) const {
+  void VisitRoot(RootCallback* callback, void* arg, const RootInfo& info) const {
     DCHECK(!IsNull());
-    callback(reinterpret_cast<mirror::Object**>(&root_), arg, thread_id, root_type);
+    callback(reinterpret_cast<mirror::Object**>(&root_), arg, info);
     DCHECK(!IsNull());
   }
 
+  void VisitRootIfNonNull(RootCallback* callback, void* arg, const RootInfo& info) const {
+    if (!IsNull()) {
+      VisitRoot(callback, arg, info);
+    }
+  }
+
   // This is only used by IrtIterator.
   ALWAYS_INLINE MirrorType** AddressWithoutBarrier() {
     return &root_;
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 1716d5e..040757b 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -44,6 +44,7 @@
 #include "common_throws.h"
 #include "debugger.h"
 #include "dex_file-inl.h"
+#include "gc_root.h"
 #include "gc/accounting/heap_bitmap.h"
 #include "gc/heap.h"
 #include "gc/space/space.h"
@@ -456,13 +457,13 @@
     EndianOutput* output;
   };
 
-  static void RootVisitor(mirror::Object** obj, void* arg, uint32_t thread_id, RootType root_type)
+  static void RootVisitor(mirror::Object** obj, void* arg, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     DCHECK(arg != nullptr);
     DCHECK(obj != nullptr);
     DCHECK(*obj != nullptr);
     Env* env = reinterpret_cast<Env*>(arg);
-    env->hprof->VisitRoot(*obj, thread_id, root_type, env->output);
+    env->hprof->VisitRoot(*obj, root_info, env->output);
   }
 
   static void VisitObjectCallback(mirror::Object* obj, void* arg)
@@ -574,7 +575,7 @@
     }
   }
 
-  void VisitRoot(const mirror::Object* obj, uint32_t thread_id, RootType type, EndianOutput* output)
+  void VisitRoot(const mirror::Object* obj, const RootInfo& root_info, EndianOutput* output)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void MarkRootObject(const mirror::Object* obj, jobject jni_obj, HprofHeapTag heap_tag,
                       uint32_t thread_serial, EndianOutput* output);
@@ -1124,8 +1125,7 @@
   __ UpdateU4(size_patch_offset, output->Length() - (size_patch_offset + 4));
 }
 
-void Hprof::VisitRoot(const mirror::Object* obj, uint32_t thread_id, RootType type,
-                      EndianOutput* output) {
+void Hprof::VisitRoot(const mirror::Object* obj, const RootInfo& info, EndianOutput* output) {
   static const HprofHeapTag xlate[] = {
     HPROF_ROOT_UNKNOWN,
     HPROF_ROOT_JNI_GLOBAL,
@@ -1143,11 +1143,11 @@
     HPROF_ROOT_VM_INTERNAL,
     HPROF_ROOT_JNI_MONITOR,
   };
-  CHECK_LT(type, sizeof(xlate) / sizeof(HprofHeapTag));
+  CHECK_LT(info.GetType(), sizeof(xlate) / sizeof(HprofHeapTag));
   if (obj == nullptr) {
     return;
   }
-  MarkRootObject(obj, 0, xlate[type], thread_id, output);
+  MarkRootObject(obj, 0, xlate[info.GetType()], info.GetThreadId(), output);
 }
 
 // If "direct_to_ddms" is true, the other arguments are ignored, and data is
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index 0d84a1e..aa2a6b5 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -242,15 +242,15 @@
   madvise(release_start, release_end - release_start, MADV_DONTNEED);
 }
 
-void IndirectReferenceTable::VisitRoots(RootCallback* callback, void* arg, uint32_t tid,
-                                        RootType root_type) {
+void IndirectReferenceTable::VisitRoots(RootCallback* callback, void* arg,
+                                        const RootInfo& root_info) {
   for (auto ref : *this) {
     if (*ref == nullptr) {
       // Need to skip null entries to make it possible to do the
       // non-null check after the call back.
       continue;
     }
-    callback(ref, arg, tid, root_type);
+    callback(ref, arg, root_info);
     DCHECK(*ref != nullptr);
   }
 }
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index fbd5714..7f7870a 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -31,6 +31,8 @@
 
 namespace art {
 
+class RootInfo;
+
 namespace mirror {
 class Object;
 }  // namespace mirror
@@ -316,7 +318,7 @@
     return IrtIterator(table_, Capacity(), Capacity());
   }
 
-  void VisitRoots(RootCallback* callback, void* arg, uint32_t tid, RootType root_type)
+  void VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   uint32_t GetSegmentState() const {
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 6bc813f..1548cfd 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -1053,7 +1053,7 @@
     return;
   }
   for (auto pair : deoptimized_methods_) {
-    pair.second.VisitRoot(callback, arg, 0, kRootVMInternal);
+    pair.second.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
   }
 }
 
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index 7ecb58e..f92f209 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -60,7 +60,7 @@
   } else if ((flags & kVisitRootFlagNewRoots) != 0) {
     for (auto& root : new_strong_intern_roots_) {
       mirror::String* old_ref = root.Read<kWithoutReadBarrier>();
-      root.VisitRoot(callback, arg, 0, kRootInternedString);
+      root.VisitRoot(callback, arg, RootInfo(kRootInternedString));
       mirror::String* new_ref = root.Read<kWithoutReadBarrier>();
       if (new_ref != old_ref) {
         // The GC moved a root in the log. Need to search the strong interns and update the
@@ -329,10 +329,10 @@
 
 void InternTable::Table::VisitRoots(RootCallback* callback, void* arg) {
   for (auto& intern : pre_zygote_table_) {
-    intern.VisitRoot(callback, arg, 0, kRootInternedString);
+    intern.VisitRoot(callback, arg, RootInfo(kRootInternedString));
   }
   for (auto& intern : post_zygote_table_) {
-    intern.VisitRoot(callback, arg, 0, kRootInternedString);
+    intern.VisitRoot(callback, arg, RootInfo(kRootInternedString));
   }
 }
 
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index 7f677ab..4643d14 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -764,10 +764,8 @@
 
 void JavaVMExt::VisitRoots(RootCallback* callback, void* arg) {
   Thread* self = Thread::Current();
-  {
-    ReaderMutexLock mu(self, globals_lock_);
-    globals_.VisitRoots(callback, arg, 0, kRootJNIGlobal);
-  }
+  ReaderMutexLock mu(self, globals_lock_);
+  globals_.VisitRoots(callback, arg, RootInfo(kRootJNIGlobal));
   // The weak_globals table is visited by the GC itself (because it mutates the table).
 }
 
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 4dddd38..048d8ba 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -200,9 +200,7 @@
 
 template<class T>
 inline void PrimitiveArray<T>::VisitRoots(RootCallback* callback, void* arg) {
-  if (!array_class_.IsNull()) {
-    array_class_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  array_class_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 template<typename T>
diff --git a/runtime/mirror/art_field.cc b/runtime/mirror/art_field.cc
index 7e20076..5a4ebd1 100644
--- a/runtime/mirror/art_field.cc
+++ b/runtime/mirror/art_field.cc
@@ -56,9 +56,7 @@
 }
 
 void ArtField::VisitRoots(RootCallback* callback, void* arg) {
-  if (!java_lang_reflect_ArtField_.IsNull()) {
-    java_lang_reflect_ArtField_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  java_lang_reflect_ArtField_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 // TODO: we could speed up the search if fields are ordered by offsets.
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index ff3822a..288f6a6 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -60,9 +60,7 @@
 
 
 void ArtMethod::VisitRoots(RootCallback* callback, void* arg) {
-  if (!java_lang_reflect_ArtMethod_.IsNull()) {
-    java_lang_reflect_ArtMethod_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  java_lang_reflect_ArtMethod_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 mirror::String* ArtMethod::GetNameAsString(Thread* self) {
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index bd3bfbf..ae684b1 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -52,9 +52,7 @@
 }
 
 void Class::VisitRoots(RootCallback* callback, void* arg) {
-  if (!java_lang_Class_.IsNull()) {
-    java_lang_Class_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  java_lang_Class_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 void Class::SetStatus(Status new_status, Thread* self) {
diff --git a/runtime/mirror/reference.cc b/runtime/mirror/reference.cc
index c36bd98..35130e8 100644
--- a/runtime/mirror/reference.cc
+++ b/runtime/mirror/reference.cc
@@ -33,9 +33,7 @@
 }
 
 void Reference::VisitRoots(RootCallback* callback, void* arg) {
-  if (!java_lang_ref_Reference_.IsNull()) {
-    java_lang_ref_Reference_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  java_lang_ref_Reference_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 }  // namespace mirror
diff --git a/runtime/mirror/stack_trace_element.cc b/runtime/mirror/stack_trace_element.cc
index 1eb20f7..c2a67e8 100644
--- a/runtime/mirror/stack_trace_element.cc
+++ b/runtime/mirror/stack_trace_element.cc
@@ -68,9 +68,7 @@
 }
 
 void StackTraceElement::VisitRoots(RootCallback* callback, void* arg) {
-  if (!java_lang_StackTraceElement_.IsNull()) {
-    java_lang_StackTraceElement_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  java_lang_StackTraceElement_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc
index 01599ae..e199d0e 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -224,9 +224,7 @@
 }
 
 void String::VisitRoots(RootCallback* callback, void* arg) {
-  if (!java_lang_String_.IsNull()) {
-    java_lang_String_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  java_lang_String_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 }  // namespace mirror
diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc
index 93ed4d4..61d85e2 100644
--- a/runtime/mirror/throwable.cc
+++ b/runtime/mirror/throwable.cc
@@ -138,9 +138,7 @@
 }
 
 void Throwable::VisitRoots(RootCallback* callback, void* arg) {
-  if (!java_lang_Throwable_.IsNull()) {
-    java_lang_Throwable_.VisitRoot(callback, arg, 0, kRootStickyClass);
-  }
+  java_lang_Throwable_.VisitRootIfNonNull(callback, arg, RootInfo(kRootStickyClass));
 }
 
 }  // namespace mirror
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 471aa9c..599d97f 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -249,7 +249,7 @@
 typedef std::map<std::string, mirror::String*> StringTable;
 
 static void PreloadDexCachesStringsCallback(mirror::Object** root, void* arg,
-                                            uint32_t /*thread_id*/, RootType /*root_type*/)
+                                            const RootInfo& /*root_info*/)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   StringTable& table = *reinterpret_cast<StringTable*>(arg);
   mirror::String* string = const_cast<mirror::Object*>(*root)->AsString();
diff --git a/runtime/object_callbacks.h b/runtime/object_callbacks.h
index 592deed..cf81cc5 100644
--- a/runtime/object_callbacks.h
+++ b/runtime/object_callbacks.h
@@ -35,34 +35,10 @@
 }  // namespace mirror
 class StackVisitor;
 
-enum RootType {
-  kRootUnknown = 0,
-  kRootJNIGlobal,
-  kRootJNILocal,
-  kRootJavaFrame,
-  kRootNativeStack,
-  kRootStickyClass,
-  kRootThreadBlock,
-  kRootMonitorUsed,
-  kRootThreadObject,
-  kRootInternedString,
-  kRootDebugger,
-  kRootVMInternal,
-  kRootJNIMonitor,
-};
-std::ostream& operator<<(std::ostream& os, const RootType& root_type);
-
-// Returns the new address of the object, returns root if it has not moved. tid and root_type are
-// only used by hprof.
-typedef void (RootCallback)(mirror::Object** root, void* arg, uint32_t thread_id,
-    RootType root_type);
 // A callback for visiting an object in the heap.
 typedef void (ObjectCallback)(mirror::Object* obj, void* arg);
 // A callback used for marking an object, returns the new address of the object if the object moved.
 typedef mirror::Object* (MarkObjectCallback)(mirror::Object* obj, void* arg) WARN_UNUSED;
-// A callback for verifying roots.
-typedef void (VerifyRootCallback)(const mirror::Object* root, void* arg, size_t vreg,
-    const StackVisitor* visitor, RootType root_type);
 
 typedef void (MarkHeapReferenceCallback)(mirror::HeapReference<mirror::Object>* ref, void* arg);
 typedef void (DelayReferenceReferentCallback)(mirror::Class* klass, mirror::Reference* ref, void* arg);
diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc
index c917d84..e454b20 100644
--- a/runtime/reference_table.cc
+++ b/runtime/reference_table.cc
@@ -232,10 +232,9 @@
   DumpSummaryLine(os, prev, GetElementCount(prev), identical, equiv);
 }
 
-void ReferenceTable::VisitRoots(RootCallback* visitor, void* arg, uint32_t tid,
-                                RootType root_type) {
+void ReferenceTable::VisitRoots(RootCallback* visitor, void* arg, const RootInfo& root_info) {
   for (GcRoot<mirror::Object>& root : entries_) {
-    root.VisitRoot(visitor, arg, tid, root_type);
+    root.VisitRoot(visitor, arg, root_info);
   }
 }
 
diff --git a/runtime/reference_table.h b/runtime/reference_table.h
index 6cffa85..22cf1cd 100644
--- a/runtime/reference_table.h
+++ b/runtime/reference_table.h
@@ -49,7 +49,7 @@
 
   void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void VisitRoots(RootCallback* visitor, void* arg, uint32_t tid, RootType root_type);
+  void VisitRoots(RootCallback* visitor, void* arg, const RootInfo& root_info);
 
  private:
   typedef std::vector<GcRoot<mirror::Object>,
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index e531091..9dddf2f 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1221,33 +1221,15 @@
 
 void Runtime::VisitNonThreadRoots(RootCallback* callback, void* arg) {
   java_vm_->VisitRoots(callback, arg);
-  if (!sentinel_.IsNull()) {
-    sentinel_.VisitRoot(callback, arg, 0, kRootVMInternal);
-    DCHECK(!sentinel_.IsNull());
-  }
-  if (!pre_allocated_OutOfMemoryError_.IsNull()) {
-    pre_allocated_OutOfMemoryError_.VisitRoot(callback, arg, 0, kRootVMInternal);
-    DCHECK(!pre_allocated_OutOfMemoryError_.IsNull());
-  }
-  resolution_method_.VisitRoot(callback, arg, 0, kRootVMInternal);
-  DCHECK(!resolution_method_.IsNull());
-  if (!pre_allocated_NoClassDefFoundError_.IsNull()) {
-    pre_allocated_NoClassDefFoundError_.VisitRoot(callback, arg, 0, kRootVMInternal);
-    DCHECK(!pre_allocated_NoClassDefFoundError_.IsNull());
-  }
-  if (HasImtConflictMethod()) {
-    imt_conflict_method_.VisitRoot(callback, arg, 0, kRootVMInternal);
-  }
-  if (!imt_unimplemented_method_.IsNull()) {
-    imt_unimplemented_method_.VisitRoot(callback, arg, 0, kRootVMInternal);
-  }
-  if (HasDefaultImt()) {
-    default_imt_.VisitRoot(callback, arg, 0, kRootVMInternal);
-  }
+  sentinel_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+  pre_allocated_OutOfMemoryError_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+  resolution_method_.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
+  pre_allocated_NoClassDefFoundError_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+  imt_conflict_method_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+  imt_unimplemented_method_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
+  default_imt_.VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
   for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
-    if (!callee_save_methods_[i].IsNull()) {
-      callee_save_methods_[i].VisitRoot(callback, arg, 0, kRootVMInternal);
-    }
+    callee_save_methods_[i].VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
   }
   verifier::MethodVerifier::VisitStaticRoots(callback, arg);
   {
diff --git a/runtime/stack.cc b/runtime/stack.cc
index aaa5b89..3165898 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -614,4 +614,11 @@
   }
 }
 
+void JavaFrameRootInfo::Describe(std::ostream& os) const {
+  const StackVisitor* visitor = stack_visitor_;
+  CHECK(visitor != nullptr);
+  os << "Type=" << GetType() << " thread_id=" << GetThreadId() << " location=" <<
+      visitor->DescribeLocation() << " vreg=" << vreg_;
+}
+
 }  // namespace art
diff --git a/runtime/stack.h b/runtime/stack.h
index 15007af..233e1c3 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -22,6 +22,7 @@
 
 #include "arch/instruction_set.h"
 #include "dex_file.h"
+#include "gc_root.h"
 #include "mirror/object_reference.h"
 #include "throw_location.h"
 #include "utils.h"
@@ -314,6 +315,19 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(ShadowFrame);
 };
 
+class JavaFrameRootInfo : public RootInfo {
+ public:
+  JavaFrameRootInfo(uint32_t thread_id, const StackVisitor* stack_visitor, size_t vreg)
+     : RootInfo(kRootJavaFrame, thread_id), stack_visitor_(stack_visitor), vreg_(vreg) {
+  }
+  virtual void Describe(std::ostream& os) const OVERRIDE
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ private:
+  const StackVisitor* const stack_visitor_;
+  const size_t vreg_;
+};
+
 // The managed stack is used to record fragments of managed code stacks. Managed code stacks
 // may either be shadow frames or lists of frames using fixed frame sizes. Transition records are
 // necessary for transitions between code using different frame layouts and transitions into native
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 6a1aeb5..78a8bf8 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1151,8 +1151,7 @@
   }
 }
 
-static void MonitorExitVisitor(mirror::Object** object, void* arg, uint32_t /*thread_id*/,
-                               RootType /*root_type*/)
+static void MonitorExitVisitor(mirror::Object** object, void* arg, const RootInfo& /*root_info*/)
     NO_THREAD_SAFETY_ANALYSIS {
   Thread* self = reinterpret_cast<Thread*>(arg);
   mirror::Object* entered_monitor = *object;
@@ -1171,7 +1170,7 @@
 
   if (tlsPtr_.jni_env != nullptr) {
     // On thread detach, all monitors entered with JNI MonitorEnter are automatically exited.
-    tlsPtr_.jni_env->monitors.VisitRoots(MonitorExitVisitor, self, 0, kRootVMInternal);
+    tlsPtr_.jni_env->monitors.VisitRoots(MonitorExitVisitor, self, RootInfo(kRootVMInternal));
     // Release locally held global references which releasing may require the mutator lock.
     if (tlsPtr_.jpeer != nullptr) {
       // If pthread_create fails we don't have a jni env here.
@@ -1333,7 +1332,7 @@
       mirror::Object* object = cur->GetReference(j);
       if (object != nullptr) {
         mirror::Object* old_obj = object;
-        visitor(&object, arg, thread_id, kRootNativeStack);
+        visitor(&object, arg, RootInfo(kRootNativeStack, thread_id));
         if (old_obj != object) {
           cur->SetReference(j, object);
         }
@@ -2219,8 +2218,8 @@
   RootCallbackVisitor(RootCallback* callback, void* arg, uint32_t tid)
      : callback_(callback), arg_(arg), tid_(tid) {}
 
-  void operator()(mirror::Object** obj, size_t, const StackVisitor*) const {
-    callback_(obj, arg_, tid_, kRootJavaFrame);
+  void operator()(mirror::Object** obj, size_t vreg, const StackVisitor* stack_visitor) const {
+    callback_(obj, arg_, JavaFrameRootInfo(tid_, stack_visitor, vreg));
   }
 
  private:
@@ -2232,23 +2231,24 @@
 void Thread::VisitRoots(RootCallback* visitor, void* arg) {
   uint32_t thread_id = GetThreadId();
   if (tlsPtr_.opeer != nullptr) {
-    visitor(&tlsPtr_.opeer, arg, thread_id, kRootThreadObject);
+    visitor(&tlsPtr_.opeer, arg, RootInfo(kRootThreadObject, thread_id));
   }
   if (tlsPtr_.exception != nullptr && tlsPtr_.exception != GetDeoptimizationException()) {
-    visitor(reinterpret_cast<mirror::Object**>(&tlsPtr_.exception), arg, thread_id, kRootNativeStack);
+    visitor(reinterpret_cast<mirror::Object**>(&tlsPtr_.exception), arg,
+            RootInfo(kRootNativeStack, thread_id));
   }
   tlsPtr_.throw_location.VisitRoots(visitor, arg);
   if (tlsPtr_.monitor_enter_object != nullptr) {
-    visitor(&tlsPtr_.monitor_enter_object, arg, thread_id, kRootNativeStack);
+    visitor(&tlsPtr_.monitor_enter_object, arg, RootInfo(kRootNativeStack, thread_id));
   }
-  tlsPtr_.jni_env->locals.VisitRoots(visitor, arg, thread_id, kRootJNILocal);
-  tlsPtr_.jni_env->monitors.VisitRoots(visitor, arg, thread_id, kRootJNIMonitor);
+  tlsPtr_.jni_env->locals.VisitRoots(visitor, arg, RootInfo(kRootJNILocal, thread_id));
+  tlsPtr_.jni_env->monitors.VisitRoots(visitor, arg, RootInfo(kRootJNIMonitor, thread_id));
   HandleScopeVisitRoots(visitor, arg, thread_id);
   if (tlsPtr_.debug_invoke_req != nullptr) {
-    tlsPtr_.debug_invoke_req->VisitRoots(visitor, arg, thread_id, kRootDebugger);
+    tlsPtr_.debug_invoke_req->VisitRoots(visitor, arg, RootInfo(kRootDebugger, thread_id));
   }
   if (tlsPtr_.single_step_control != nullptr) {
-    tlsPtr_.single_step_control->VisitRoots(visitor, arg, thread_id, kRootDebugger);
+    tlsPtr_.single_step_control->VisitRoots(visitor, arg, RootInfo(kRootDebugger, thread_id));
   }
   if (tlsPtr_.deoptimization_shadow_frame != nullptr) {
     RootCallbackVisitor visitorToCallback(visitor, arg, thread_id);
@@ -2259,8 +2259,8 @@
     }
   }
   if (tlsPtr_.shadow_frame_under_construction != nullptr) {
-    RootCallbackVisitor visitorToCallback(visitor, arg, thread_id);
-    ReferenceMapVisitor<RootCallbackVisitor> mapper(this, nullptr, visitorToCallback);
+    RootCallbackVisitor visitor_to_callback(visitor, arg, thread_id);
+    ReferenceMapVisitor<RootCallbackVisitor> mapper(this, nullptr, visitor_to_callback);
     for (ShadowFrame* shadow_frame = tlsPtr_.shadow_frame_under_construction;
         shadow_frame != nullptr;
         shadow_frame = shadow_frame->GetLink()) {
@@ -2269,21 +2269,22 @@
   }
   // Visit roots on this thread's stack
   Context* context = GetLongJumpContext();
-  RootCallbackVisitor visitorToCallback(visitor, arg, thread_id);
-  ReferenceMapVisitor<RootCallbackVisitor> mapper(this, context, visitorToCallback);
+  RootCallbackVisitor visitor_to_callback(visitor, arg, thread_id);
+  ReferenceMapVisitor<RootCallbackVisitor> mapper(this, context, visitor_to_callback);
   mapper.WalkStack();
   ReleaseLongJumpContext(context);
   for (instrumentation::InstrumentationStackFrame& frame : *GetInstrumentationStack()) {
     if (frame.this_object_ != nullptr) {
-      visitor(&frame.this_object_, arg, thread_id, kRootJavaFrame);
+      visitor(&frame.this_object_, arg, RootInfo(kRootVMInternal, thread_id));
     }
     DCHECK(frame.method_ != nullptr);
-    visitor(reinterpret_cast<mirror::Object**>(&frame.method_), arg, thread_id, kRootJavaFrame);
+    visitor(reinterpret_cast<mirror::Object**>(&frame.method_), arg,
+            RootInfo(kRootVMInternal, thread_id));
   }
 }
 
-static void VerifyRoot(mirror::Object** root, void* /*arg*/, uint32_t /*thread_id*/,
-                       RootType /*root_type*/) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+static void VerifyRoot(mirror::Object** root, void* /*arg*/, const RootInfo& /*root_info*/)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   VerifyObject(*root);
 }
 
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 0f64883..6ec40d4 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -1045,28 +1045,6 @@
   }
 }
 
-class VerifyRootWrapperArg {
- public:
-  VerifyRootWrapperArg(VerifyRootCallback* callback, void* arg) : callback_(callback), arg_(arg) {
-  }
-  VerifyRootCallback* const callback_;
-  void* const arg_;
-};
-
-static void VerifyRootWrapperCallback(mirror::Object** root, void* arg, uint32_t /*thread_id*/,
-                                      RootType root_type) {
-  VerifyRootWrapperArg* wrapperArg = reinterpret_cast<VerifyRootWrapperArg*>(arg);
-  wrapperArg->callback_(*root, wrapperArg->arg_, 0, NULL, root_type);
-}
-
-void ThreadList::VerifyRoots(VerifyRootCallback* callback, void* arg) const {
-  VerifyRootWrapperArg wrapper(callback, arg);
-  MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
-  for (const auto& thread : list_) {
-    thread->VisitRoots(VerifyRootWrapperCallback, &wrapper);
-  }
-}
-
 uint32_t ThreadList::AllocThreadId(Thread* self) {
   MutexLock mu(self, *Locks::allocated_thread_ids_lock_);
   for (size_t i = 0; i < allocated_ids_.size(); ++i) {
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 43c065a..6751bf5 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -19,6 +19,7 @@
 
 #include "base/histogram.h"
 #include "base/mutex.h"
+#include "gc_root.h"
 #include "jni.h"
 #include "object_callbacks.h"
 
@@ -125,9 +126,6 @@
   void VisitRoots(RootCallback* callback, void* arg) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void VerifyRoots(VerifyRootCallback* callback, void* arg) const
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
   // Return a copy of the thread list.
   std::list<Thread*> GetList() EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_lock_) {
     return list_;
diff --git a/runtime/throw_location.cc b/runtime/throw_location.cc
index 04abe64..4d2aec0 100644
--- a/runtime/throw_location.cc
+++ b/runtime/throw_location.cc
@@ -34,11 +34,11 @@
 
 void ThrowLocation::VisitRoots(RootCallback* visitor, void* arg) {
   if (this_object_ != nullptr) {
-    visitor(&this_object_, arg, 0, kRootVMInternal);
+    visitor(&this_object_, arg, RootInfo(kRootVMInternal));
     DCHECK(this_object_ != nullptr);
   }
   if (method_ != nullptr) {
-    visitor(reinterpret_cast<mirror::Object**>(&method_), arg, 0, kRootVMInternal);
+    visitor(reinterpret_cast<mirror::Object**>(&method_), arg, RootInfo(kRootVMInternal));
     DCHECK(method_ != nullptr);
   }
 }
diff --git a/runtime/throw_location.h b/runtime/throw_location.h
index b36eb67..bec0da4 100644
--- a/runtime/throw_location.h
+++ b/runtime/throw_location.h
@@ -20,6 +20,7 @@
 #include "object_callbacks.h"
 #include "base/macros.h"
 #include "base/mutex.h"
+#include "gc_root.h"
 
 #include <stdint.h>
 #include <string>
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index 478066f..118c1a2 100644
--- a/runtime/transaction.cc
+++ b/runtime/transaction.cc
@@ -206,7 +206,7 @@
     it.second.VisitRoots(callback, arg);
     mirror::Object* old_root = it.first;
     mirror::Object* new_root = old_root;
-    callback(&new_root, arg, 0, kRootUnknown);
+    callback(&new_root, arg, RootInfo(kRootUnknown));
     if (new_root != old_root) {
       moving_roots.push_back(std::make_pair(old_root, new_root));
     }
@@ -233,7 +233,7 @@
     mirror::Array* old_root = it.first;
     CHECK(!old_root->IsObjectArray());
     mirror::Array* new_root = old_root;
-    callback(reinterpret_cast<mirror::Object**>(&new_root), arg, 0, kRootUnknown);
+    callback(reinterpret_cast<mirror::Object**>(&new_root), arg, RootInfo(kRootUnknown));
     if (new_root != old_root) {
       moving_roots.push_back(std::make_pair(old_root, new_root));
     }
@@ -396,7 +396,7 @@
       mirror::Object* obj =
           reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(field_value.value));
       if (obj != nullptr) {
-        callback(&obj, arg, 0, kRootUnknown);
+        callback(&obj, arg, RootInfo(kRootUnknown));
         field_value.value = reinterpret_cast<uintptr_t>(obj);
       }
     }
@@ -441,7 +441,7 @@
 }
 
 void Transaction::InternStringLog::VisitRoots(RootCallback* callback, void* arg) {
-  callback(reinterpret_cast<mirror::Object**>(&str_), arg, 0, kRootInternedString);
+  callback(reinterpret_cast<mirror::Object**>(&str_), arg, RootInfo(kRootInternedString));
 }
 
 void Transaction::ArrayLog::LogValue(size_t index, uint64_t value) {
diff --git a/runtime/transaction.h b/runtime/transaction.h
index 566f231..8c82847 100644
--- a/runtime/transaction.h
+++ b/runtime/transaction.h
@@ -20,6 +20,7 @@
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "base/value_object.h"
+#include "gc_root.h"
 #include "object_callbacks.h"
 #include "offsets.h"
 #include "primitive.h"
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index 41541b5..3510665 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -779,9 +779,7 @@
 }
 
 void RegType::VisitRoots(RootCallback* callback, void* arg) const {
-  if (!klass_.IsNull()) {
-    callback(reinterpret_cast<mirror::Object**>(&klass_), arg, 0, kRootUnknown);
-  }
+  klass_.VisitRootIfNonNull(callback, arg, RootInfo(kRootUnknown));
 }
 
 void UninitializedThisReferenceType::CheckInvariants() const {
diff --git a/test/114-ParallelGC/src/Main.java b/test/114-ParallelGC/src/Main.java
index 963fdac..48f9bd3 100644
--- a/test/114-ParallelGC/src/Main.java
+++ b/test/114-ParallelGC/src/Main.java
@@ -26,7 +26,7 @@
 
     // Timeout in minutes. Make it larger than the run-test timeout to get a native thread dump by
     // ART on timeout when running on the host.
-    public final static long TIMEOUT_VALUE = 7;
+    public final static long TIMEOUT_VALUE = 12;
 
     public final static long MAX_SIZE = 1000;  // Maximum size of array-list to allocate.
 
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 0916971..92b1e82 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -38,7 +38,7 @@
 SECONDARY_DEX=""
 TIME_OUT="y"
 # Value in minutes.
-TIME_OUT_VALUE=5
+TIME_OUT_VALUE=10
 USE_GDB="n"
 USE_JVM="n"
 VERIFY="y"
diff --git a/tools/checker.py b/tools/checker.py
index bca0707..bb2ac0c 100755
--- a/tools/checker.py
+++ b/tools/checker.py
@@ -77,7 +77,6 @@
 import shutil
 import sys
 import tempfile
-from subprocess import check_call
 
 class Logger(object):
 
@@ -689,11 +688,14 @@
 
 def ParseArguments():
   parser = argparse.ArgumentParser()
-  parser.add_argument("test_file", help="the source of the test with checking annotations")
+  parser.add_argument("tested_file",
+                      help="text file the checks should be verified against")
+  parser.add_argument("source_path", nargs="?",
+                      help="path to file/folder with checking annotations")
   parser.add_argument("--check-prefix", dest="check_prefix", default="CHECK", metavar="PREFIX",
-                      help="prefix of checks in the test file (default: CHECK)")
+                      help="prefix of checks in the test files (default: CHECK)")
   parser.add_argument("--list-groups", dest="list_groups", action="store_true",
-                      help="print a list of all groups found in the test output")
+                      help="print a list of all groups found in the tested file")
   parser.add_argument("--dump-group", dest="dump_group", metavar="GROUP",
                       help="print the contents of an output group")
   parser.add_argument("-q", "--quiet", action="store_true",
@@ -701,42 +703,6 @@
   return parser.parse_args()
 
 
-class cd:
-  """Helper class which temporarily changes the working directory."""
-
-  def __init__(self, newPath):
-    self.newPath = newPath
-
-  def __enter__(self):
-    self.savedPath = os.getcwd()
-    os.chdir(self.newPath)
-
-  def __exit__(self, etype, value, traceback):
-    os.chdir(self.savedPath)
-
-
-def CompileTest(inputFile, tempFolder):
-  classFolder = tempFolder + "/classes"
-  dexFile = tempFolder + "/test.dex"
-  oatFile = tempFolder + "/test.oat"
-  outputFile = tempFolder + "/test.cfg"
-  os.makedirs(classFolder)
-
-  # Build a DEX from the source file. We pass "--no-optimize" to dx to avoid
-  # interference with its optimizations.
-  check_call(["javac", "-d", classFolder, inputFile])
-  check_call(["dx", "--dex", "--no-optimize", "--output=" + dexFile, classFolder])
-
-  # Run dex2oat and export the HGraph. The output is stored into ${PWD}/art.cfg.
-  with cd(tempFolder):
-    check_call(["dex2oat", "-j1", "--dump-cfg=" + outputFile, "--compiler-backend=Optimizing",
-                "--android-root=" + os.environ["ANDROID_HOST_OUT"],
-                "--boot-image=" + os.environ["ANDROID_HOST_OUT"] + "/framework/core-optimizing.art",
-                "--runtime-arg", "-Xnorelocate", "--dex-file=" + dexFile, "--oat-file=" + oatFile])
-
-  return outputFile
-
-
 def ListGroups(outputFilename):
   outputFile = OutputFile(open(outputFilename, "r"))
   for group in outputFile.groups:
@@ -757,13 +723,30 @@
     Logger.fail("Group \"" + groupName + "\" not found in the output")
 
 
-def RunChecks(checkPrefix, checkFilename, outputFilename):
-  checkBaseName = os.path.basename(checkFilename)
-  outputBaseName = os.path.splitext(checkBaseName)[0] + ".cfg"
+def FindCheckFiles(path):
+  if not path:
+    Logger.fail("No source path provided")
+  elif os.path.isfile(path):
+    return [ path ]
+  elif os.path.isdir(path):
+    foundFiles = []
+    for root, dirs, files in os.walk(path):
+      for file in files:
+        if os.path.splitext(file)[1] == ".java":
+          foundFiles.append(os.path.join(root, file))
+    return foundFiles
+  else:
+    Logger.fail("Source path \"" + path + "\" not found")
 
-  checkFile = CheckFile(checkPrefix, open(checkFilename, "r"), checkBaseName)
+
+def RunChecks(checkPrefix, checkPath, outputFilename):
+  outputBaseName = os.path.basename(outputFilename)
   outputFile = OutputFile(open(outputFilename, "r"), outputBaseName)
-  checkFile.match(outputFile)
+
+  for checkFilename in FindCheckFiles(checkPath):
+    checkBaseName = os.path.basename(checkFilename)
+    checkFile = CheckFile(checkPrefix, open(checkFilename, "r"), checkBaseName)
+    checkFile.match(outputFile)
 
 
 if __name__ == "__main__":
@@ -773,12 +756,11 @@
 
   tempFolder = tempfile.mkdtemp()
   try:
-    outputFile = CompileTest(args.test_file, tempFolder)
     if args.list_groups:
-      ListGroups(outputFile)
+      ListGroups(args.tested_file)
     elif args.dump_group:
-      DumpGroup(outputFile, args.dump_group)
+      DumpGroup(args.tested_file, args.dump_group)
     else:
-      RunChecks(args.check_prefix, args.test_file, outputFile)
+      RunChecks(args.check_prefix, args.source_path, args.tested_file)
   finally:
     shutil.rmtree(tempFolder)