Add FirstPathFromRootSet and use it to debug reachability issues

Specifically issues where classes aren't pruned in image writer.

Added test.

Test: mm test-art-host-gtest-heap_verification_test -j32

Change-Id: Iea87309aaddf9e28f1856698699a925fb6ab92a1
diff --git a/runtime/gc/verification.cc b/runtime/gc/verification.cc
index c14f250..03b26a0 100644
--- a/runtime/gc/verification.cc
+++ b/runtime/gc/verification.cc
@@ -21,6 +21,7 @@
 
 #include "art_field-inl.h"
 #include "mirror/class-inl.h"
+#include "mirror/object-refvisitor-inl.h"
 
 namespace art {
 namespace gc {
@@ -138,5 +139,92 @@
   return k1 == k2;
 }
 
+using ObjectSet = std::set<mirror::Object*>;
+using WorkQueue = std::deque<std::pair<mirror::Object*, std::string>>;
+
+// Use for visiting the GcRoots held live by ArtFields, ArtMethods, and ClassLoaders.
+class Verification::BFSFindReachable {
+ public:
+  explicit BFSFindReachable(ObjectSet* visited) : visited_(visited) {}
+
+  void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    ArtField* field = obj->FindFieldByOffset(offset);
+    Visit(obj->GetFieldObject<mirror::Object>(offset),
+          field != nullptr ? field->GetName() : "");
+  }
+
+  void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (!root->IsNull()) {
+      VisitRoot(root);
+    }
+  }
+
+  void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    Visit(root->AsMirrorPtr(), "!nativeRoot");
+  }
+
+  void Visit(mirror::Object* ref, const std::string& field_name) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (ref != nullptr && visited_->insert(ref).second) {
+      new_visited_.emplace_back(ref, field_name);
+    }
+  }
+
+  const WorkQueue& NewlyVisited() const {
+    return new_visited_;
+  }
+
+ private:
+  ObjectSet* visited_;
+  mutable WorkQueue new_visited_;
+};
+
+class Verification::CollectRootVisitor : public SingleRootVisitor {
+ public:
+  CollectRootVisitor(ObjectSet* visited, WorkQueue* work) : visited_(visited), work_(work) {}
+
+  void VisitRoot(mirror::Object* obj, const RootInfo& info)
+      OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (obj != nullptr && visited_->insert(obj).second) {
+      std::ostringstream oss;
+      oss << info.ToString() << " = " << obj << "(" << obj->PrettyTypeOf() << ")";
+      work_->emplace_back(obj, oss.str());
+    }
+  }
+
+ private:
+  ObjectSet* const visited_;
+  WorkQueue* const work_;
+};
+
+std::string Verification::FirstPathFromRootSet(ObjPtr<mirror::Object> target) const {
+  Runtime* const runtime =  Runtime::Current();
+  std::set<mirror::Object*> visited;
+  std::deque<std::pair<mirror::Object*, std::string>> work;
+  {
+    CollectRootVisitor root_visitor(&visited, &work);
+    runtime->VisitRoots(&root_visitor, kVisitRootFlagAllRoots);
+  }
+  while (!work.empty()) {
+    auto pair = work.front();
+    work.pop_front();
+    if (pair.first == target) {
+      return pair.second;
+    }
+    BFSFindReachable visitor(&visited);
+    pair.first->VisitReferences(visitor, VoidFunctor());
+    for (auto&& pair2 : visitor.NewlyVisited()) {
+      std::ostringstream oss;
+      mirror::Object* obj = pair2.first;
+      oss << pair.second << " -> " << obj << "(" << obj->PrettyTypeOf() << ")." << pair2.second;
+      work.emplace_back(obj, oss.str());
+    }
+  }
+  return "<no path found>";
+}
+
 }  // namespace gc
 }  // namespace art