Show unreachable objects in ahat.

But don't count them against heap size or instance counts.

Bug: 33828707
Test: ahat tests, including new InstanceTest.unreachableReferent.
Change-Id: Ic39b6d5569159497dcc76c342e22ed99d2a71307
diff --git a/tools/ahat/src/Summarizer.java b/tools/ahat/src/Summarizer.java
index 7f4dcbf..016eab4 100644
--- a/tools/ahat/src/Summarizer.java
+++ b/tools/ahat/src/Summarizer.java
@@ -50,6 +50,11 @@
       formatted.append(DocString.removed("del "));
     }
 
+    // Annotate unreachable objects as such.
+    if (!inst.isReachable()) {
+      formatted.append("unreachable ");
+    }
+
     // Annotate roots as roots.
     if (inst.isRoot()) {
       formatted.append("root ");
diff --git a/tools/ahat/src/heapdump/AhatInstance.java b/tools/ahat/src/heapdump/AhatInstance.java
index 24956f2..e6b9c00 100644
--- a/tools/ahat/src/heapdump/AhatInstance.java
+++ b/tools/ahat/src/heapdump/AhatInstance.java
@@ -31,6 +31,7 @@
   private long mSize;
   private long mTotalRetainedSize;
   private long mRetainedSizes[];      // Retained size indexed by heap index
+  private boolean mIsReachable;
   private AhatHeap mHeap;
   private AhatInstance mImmediateDominator;
   private AhatInstance mNextInstanceToGcRoot;
@@ -64,6 +65,7 @@
     mId = inst.getId();
     mSize = inst.getSize();
     mTotalRetainedSize = inst.getTotalRetainedSize();
+    mIsReachable = inst.isReachable();
 
     List<AhatHeap> heaps = snapshot.getHeaps();
     mRetainedSizes = new long[heaps.size()];
@@ -149,6 +151,13 @@
   }
 
   /**
+   * Returns whether this object is strongly-reachable.
+   */
+  public boolean isReachable() {
+    return mIsReachable;
+  }
+
+  /**
    * Returns the heap that this instance is allocated on.
    */
   public AhatHeap getHeap() {
diff --git a/tools/ahat/src/heapdump/AhatSnapshot.java b/tools/ahat/src/heapdump/AhatSnapshot.java
index 6b4953e..20b85da 100644
--- a/tools/ahat/src/heapdump/AhatSnapshot.java
+++ b/tools/ahat/src/heapdump/AhatSnapshot.java
@@ -106,17 +106,15 @@
       TObjectProcedure<Instance> doCreate = new TObjectProcedure<Instance>() {
         @Override
         public boolean execute(Instance inst) {
-          if (inst.isReachable()) {
-            long id = inst.getId();
-            if (inst instanceof ClassInstance) {
-              mInstances.add(new AhatClassInstance(id));
-            } else if (inst instanceof ArrayInstance) {
-              mInstances.add(new AhatArrayInstance(id));
-            } else if (inst instanceof ClassObj) {
-              AhatClassObj classObj = new AhatClassObj(id);
-              mInstances.add(classObj);
-              mClasses.put(((ClassObj)inst).getClassName(), classObj);
-            }
+          long id = inst.getId();
+          if (inst instanceof ClassInstance) {
+            mInstances.add(new AhatClassInstance(id));
+          } else if (inst instanceof ArrayInstance) {
+            mInstances.add(new AhatArrayInstance(id));
+          } else if (inst instanceof ClassObj) {
+            AhatClassObj classObj = new AhatClassObj(id);
+            mInstances.add(classObj);
+            mClasses.put(((ClassObj)inst).getClassName(), classObj);
           }
           return true;
         }
@@ -146,7 +144,9 @@
         mRooted.add(ahat);
       }
 
-      ahat.getHeap().addToSize(ahat.getSize());
+      if (inst.isReachable()) {
+        ahat.getHeap().addToSize(ahat.getSize());
+      }
 
       // Update sites.
       StackFrame[] frames = null;
diff --git a/tools/ahat/src/heapdump/Site.java b/tools/ahat/src/heapdump/Site.java
index a551901..738eaf0 100644
--- a/tools/ahat/src/heapdump/Site.java
+++ b/tools/ahat/src/heapdump/Site.java
@@ -129,19 +129,21 @@
     while (true) {
       site.mObjects.add(inst);
 
-      AhatHeap heap = inst.getHeap();
-      if (heap.getIndex() >= site.mSizesByHeap.length) {
-        long[] newSizes = new long[heap.getIndex() + 1];
-        for (int i = 0; i < site.mSizesByHeap.length; i++) {
-          newSizes[i] = site.mSizesByHeap[i];
-        }
-        site.mSizesByHeap = newSizes;
-      }
-      site.mSizesByHeap[heap.getIndex()] += inst.getSize();
-
       ObjectsInfo info = site.getObjectsInfo(inst.getHeap(), inst.getClassObj());
-      info.numInstances++;
-      info.numBytes += inst.getSize();
+      if (inst.isReachable()) {
+        AhatHeap heap = inst.getHeap();
+        if (heap.getIndex() >= site.mSizesByHeap.length) {
+          long[] newSizes = new long[heap.getIndex() + 1];
+          for (int i = 0; i < site.mSizesByHeap.length; i++) {
+            newSizes[i] = site.mSizesByHeap[i];
+          }
+          site.mSizesByHeap = newSizes;
+        }
+        site.mSizesByHeap[heap.getIndex()] += inst.getSize();
+
+        info.numInstances++;
+        info.numBytes += inst.getSize();
+      }
 
       if (depth > 0) {
         StackFrame next = frames[depth - 1];
diff --git a/tools/ahat/test-dump/Main.java b/tools/ahat/test-dump/Main.java
index 4a2234c..7a05b1c 100644
--- a/tools/ahat/test-dump/Main.java
+++ b/tools/ahat/test-dump/Main.java
@@ -18,6 +18,7 @@
 import java.io.IOException;
 import java.lang.ref.PhantomReference;
 import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
 import java.lang.ref.WeakReference;
 import org.apache.harmony.dalvik.ddmc.DdmVmInternal;
 
@@ -73,6 +74,7 @@
     public PhantomReference aPhantomReference = new PhantomReference(anObject, referenceQueue);
     public WeakReference aWeakReference = new WeakReference(anObject, referenceQueue);
     public WeakReference aNullReferentReference = new WeakReference(null, referenceQueue);
+    public SoftReference aSoftReference = new SoftReference(new Object());
     public byte[] bigArray;
     public ObjectTree[] gcPathArray = new ObjectTree[]{null, null,
       new ObjectTree(
diff --git a/tools/ahat/test/InstanceTest.java b/tools/ahat/test/InstanceTest.java
index 7173b11..3a50150 100644
--- a/tools/ahat/test/InstanceTest.java
+++ b/tools/ahat/test/InstanceTest.java
@@ -223,6 +223,16 @@
   }
 
   @Test
+  public void unreachableReferent() throws IOException {
+    // The test dump program should never be under enough GC pressure for the
+    // soft reference to be cleared. Ensure that ahat will show the soft
+    // reference as having a non-null referent.
+    TestDump dump = TestDump.getTestDump();
+    AhatInstance ref = dump.getDumpedAhatInstance("aSoftReference");
+    assertNotNull(ref.getReferent());
+  }
+
+  @Test
   public void gcRootPath() throws IOException {
     TestDump dump = TestDump.getTestDump();
 
@@ -359,14 +369,6 @@
   }
 
   @Test
-  public void reverseReferencesAreNotUnreachable() throws IOException {
-    TestDump dump = TestDump.getTestDump();
-    AhatInstance obj = dump.getDumpedAhatInstance("basicString");
-    assertEquals(2, obj.getHardReverseReferences().size());
-    assertEquals(0, obj.getSoftReverseReferences().size());
-  }
-
-  @Test
   public void asStringEmbedded() throws IOException {
     // Set up a heap dump with an instance of java.lang.String of
     // "hello" with instance id 0x42 that is backed by a char array that is