Distinguish between weakly reachable and unreachable instances.

Annotate weakly reachable instances with "weak" instead of
unreachable. Don't show a sample path from GC root for unreachable
instances.

Bug: 64785007
Test: m ahat-test
Test: Manually inspect strong, weak, and unreachable instances.
Change-Id: I1cb73d47198be847eaccd5855f6f14acf828a75a
diff --git a/tools/ahat/src/ObjectHandler.java b/tools/ahat/src/ObjectHandler.java
index 79f8b76..c24f22b 100644
--- a/tools/ahat/src/ObjectHandler.java
+++ b/tools/ahat/src/ObjectHandler.java
@@ -66,7 +66,10 @@
     doc.big(Summarizer.summarize(inst));
 
     printAllocationSite(doc, query, inst);
-    printGcRootPath(doc, query, inst);
+
+    if (!inst.isUnreachable()) {
+      printGcRootPath(doc, query, inst);
+    }
 
     doc.section("Object Info");
     AhatClassObj cls = inst.getClassObj();
diff --git a/tools/ahat/src/Summarizer.java b/tools/ahat/src/Summarizer.java
index 50b2e4b..901d5b2 100644
--- a/tools/ahat/src/Summarizer.java
+++ b/tools/ahat/src/Summarizer.java
@@ -51,7 +51,9 @@
     }
 
     // Annotate unreachable objects as such.
-    if (!inst.isReachable()) {
+    if (inst.isWeaklyReachable()) {
+      formatted.append("weak ");
+    } else if (inst.isUnreachable()) {
       formatted.append("unreachable ");
     }
 
diff --git a/tools/ahat/src/heapdump/AhatInstance.java b/tools/ahat/src/heapdump/AhatInstance.java
index c044487..cb2d738 100644
--- a/tools/ahat/src/heapdump/AhatInstance.java
+++ b/tools/ahat/src/heapdump/AhatInstance.java
@@ -136,13 +136,28 @@
   }
 
   /**
-   * Returns whether this object is strongly-reachable.
+   * Returns true if this object is strongly-reachable.
    */
-  public boolean isReachable() {
+  public boolean isStronglyReachable() {
     return mImmediateDominator != null;
   }
 
   /**
+   * Returns true if this object is reachable only through a
+   * soft/weak/phantom/finalizer reference.
+   */
+  public boolean isWeaklyReachable() {
+    return !isStronglyReachable() && mNextInstanceToGcRoot != null;
+  }
+
+  /**
+   * Returns true if this object is completely unreachable.
+   */
+  public boolean isUnreachable() {
+    return !isStronglyReachable() && !isWeaklyReachable();
+  }
+
+  /**
    * Returns the heap that this instance is allocated on.
    */
   public AhatHeap getHeap() {
@@ -499,6 +514,10 @@
         } else {
           if (ref.ref.mSoftReverseReferences == null) {
             ref.ref.mSoftReverseReferences = new ArrayList<AhatInstance>();
+            if (ref.ref.mNextInstanceToGcRoot == null) {
+              ref.ref.mNextInstanceToGcRoot = ref.src;
+              ref.ref.mNextInstanceToGcRootField = ref.field;
+            }
           }
           ref.ref.mSoftReverseReferences.add(ref.src);
         }
diff --git a/tools/ahat/src/heapdump/Site.java b/tools/ahat/src/heapdump/Site.java
index 821493f..523550a 100644
--- a/tools/ahat/src/heapdump/Site.java
+++ b/tools/ahat/src/heapdump/Site.java
@@ -186,7 +186,7 @@
 
     // Add all reachable objects allocated at this site.
     for (AhatInstance inst : mObjects) {
-      if (inst.isReachable()) {
+      if (inst.isStronglyReachable()) {
         AhatHeap heap = inst.getHeap();
         Size size = inst.getSize();
         ObjectsInfo info = getObjectsInfo(heap, inst.getClassObj());
diff --git a/tools/ahat/test/InstanceTest.java b/tools/ahat/test/InstanceTest.java
index 49a21e2..a4908fd 100644
--- a/tools/ahat/test/InstanceTest.java
+++ b/tools/ahat/test/InstanceTest.java
@@ -214,7 +214,9 @@
     // reference as having a non-null referent.
     TestDump dump = TestDump.getTestDump();
     AhatInstance ref = dump.getDumpedAhatInstance("aSoftReference");
-    assertNotNull(ref.getReferent());
+    AhatInstance referent = ref.getReferent();
+    assertNotNull(referent);
+    assertTrue(referent.isWeaklyReachable());
   }
 
   @Test