blob: 49a21e2d706da14eb769e577dbf3e0fc8cca2b07 [file] [log] [blame]
/*
* Copyright (C) 2015 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.
*/
package com.android.ahat;
import com.android.ahat.heapdump.AhatClassObj;
import com.android.ahat.heapdump.AhatHeap;
import com.android.ahat.heapdump.AhatInstance;
import com.android.ahat.heapdump.AhatSnapshot;
import com.android.ahat.heapdump.PathElement;
import com.android.ahat.heapdump.Size;
import com.android.ahat.heapdump.Value;
import java.io.IOException;
import java.util.List;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class InstanceTest {
@Test
public void asStringBasic() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance str = dump.getDumpedAhatInstance("basicString");
assertEquals("hello, world", str.asString());
}
@Test
public void asStringNonAscii() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance str = dump.getDumpedAhatInstance("nonAscii");
assertEquals("Sigma (Ʃ) is not ASCII", str.asString());
}
@Test
public void asStringEmbeddedZero() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance str = dump.getDumpedAhatInstance("embeddedZero");
assertEquals("embedded\0...", str.asString());
}
@Test
public void asStringCharArray() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance str = dump.getDumpedAhatInstance("charArray");
assertEquals("char thing", str.asString());
}
@Test
public void asStringTruncated() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance str = dump.getDumpedAhatInstance("basicString");
assertEquals("hello", str.asString(5));
}
@Test
public void asStringTruncatedNonAscii() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance str = dump.getDumpedAhatInstance("nonAscii");
assertEquals("Sigma (Ʃ)", str.asString(9));
}
@Test
public void asStringTruncatedEmbeddedZero() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance str = dump.getDumpedAhatInstance("embeddedZero");
assertEquals("embed", str.asString(5));
}
@Test
public void asStringCharArrayTruncated() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance str = dump.getDumpedAhatInstance("charArray");
assertEquals("char ", str.asString(5));
}
@Test
public void asStringExactMax() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance str = dump.getDumpedAhatInstance("basicString");
assertEquals("hello, world", str.asString(12));
}
@Test
public void asStringExactMaxNonAscii() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance str = dump.getDumpedAhatInstance("nonAscii");
assertEquals("Sigma (Ʃ) is not ASCII", str.asString(22));
}
@Test
public void asStringExactMaxEmbeddedZero() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance str = dump.getDumpedAhatInstance("embeddedZero");
assertEquals("embedded\0...", str.asString(12));
}
@Test
public void asStringCharArrayExactMax() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance str = dump.getDumpedAhatInstance("charArray");
assertEquals("char thing", str.asString(10));
}
@Test
public void asStringNotTruncated() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance str = dump.getDumpedAhatInstance("basicString");
assertEquals("hello, world", str.asString(50));
}
@Test
public void asStringNotTruncatedNonAscii() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance str = dump.getDumpedAhatInstance("nonAscii");
assertEquals("Sigma (Ʃ) is not ASCII", str.asString(50));
}
@Test
public void asStringNotTruncatedEmbeddedZero() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance str = dump.getDumpedAhatInstance("embeddedZero");
assertEquals("embedded\0...", str.asString(50));
}
@Test
public void asStringCharArrayNotTruncated() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance str = dump.getDumpedAhatInstance("charArray");
assertEquals("char thing", str.asString(50));
}
@Test
public void asStringNegativeMax() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance str = dump.getDumpedAhatInstance("basicString");
assertEquals("hello, world", str.asString(-3));
}
@Test
public void asStringNegativeMaxNonAscii() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance str = dump.getDumpedAhatInstance("nonAscii");
assertEquals("Sigma (Ʃ) is not ASCII", str.asString(-3));
}
@Test
public void asStringNegativeMaxEmbeddedZero() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance str = dump.getDumpedAhatInstance("embeddedZero");
assertEquals("embedded\0...", str.asString(-3));
}
@Test
public void asStringCharArrayNegativeMax() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance str = dump.getDumpedAhatInstance("charArray");
assertEquals("char thing", str.asString(-3));
}
@Test
public void asStringNull() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance obj = dump.getDumpedAhatInstance("nullString");
assertNull(obj);
}
@Test
public void asStringNotString() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance obj = dump.getDumpedAhatInstance("anObject");
assertNotNull(obj);
assertNull(obj.asString());
}
@Test
public void basicReference() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance pref = dump.getDumpedAhatInstance("aPhantomReference");
AhatInstance wref = dump.getDumpedAhatInstance("aWeakReference");
AhatInstance nref = dump.getDumpedAhatInstance("aNullReferentReference");
AhatInstance referent = dump.getDumpedAhatInstance("anObject");
assertNotNull(pref);
assertNotNull(wref);
assertNotNull(nref);
assertNotNull(referent);
assertEquals(referent, pref.getReferent());
assertEquals(referent, wref.getReferent());
assertNull(nref.getReferent());
assertNull(referent.getReferent());
}
@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();
AhatClassObj main = dump.findClass("Main");
AhatInstance gcPathArray = dump.getDumpedAhatInstance("gcPathArray");
Value value = gcPathArray.asArrayInstance().getValue(2);
AhatInstance base = value.asAhatInstance();
AhatInstance left = base.getRefField("left");
AhatInstance right = base.getRefField("right");
AhatInstance target = left.getRefField("right");
List<PathElement> path = target.getPathFromGcRoot();
assertEquals(6, path.size());
assertEquals(main, path.get(0).instance);
assertEquals(".stuff", path.get(0).field);
assertTrue(path.get(0).isDominator);
assertEquals(".gcPathArray", path.get(1).field);
assertTrue(path.get(1).isDominator);
assertEquals(gcPathArray, path.get(2).instance);
assertEquals("[2]", path.get(2).field);
assertTrue(path.get(2).isDominator);
assertEquals(base, path.get(3).instance);
assertTrue(path.get(3).isDominator);
// There are two possible paths. Either it can go through the 'left' node,
// or the 'right' node.
if (path.get(3).field.equals(".left")) {
assertEquals(".left", path.get(3).field);
assertEquals(left, path.get(4).instance);
assertEquals(".right", path.get(4).field);
assertFalse(path.get(4).isDominator);
} else {
assertEquals(".right", path.get(3).field);
assertEquals(right, path.get(4).instance);
assertEquals(".left", path.get(4).field);
assertFalse(path.get(4).isDominator);
}
assertEquals(target, path.get(5).instance);
assertEquals("", path.get(5).field);
assertTrue(path.get(5).isDominator);
}
@Test
public void gcRootPathNotWeak() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance strong = dump.getDumpedAhatInstance("aLongStrongPathToSamplePathObject");
AhatInstance strong2 = strong.getField("referent").asAhatInstance();
AhatInstance object = strong2.getField("referent").asAhatInstance();
List<PathElement> path = object.getPathFromGcRoot();
assertEquals(strong2, path.get(path.size() - 2).instance);
}
@Test
public void retainedSize() throws IOException {
TestDump dump = TestDump.getTestDump();
// anObject should not be an immediate dominator of any other object. This
// means its retained size should be equal to its size for the heap it was
// allocated on, and should be 0 for all other heaps.
AhatInstance anObject = dump.getDumpedAhatInstance("anObject");
AhatSnapshot snapshot = dump.getAhatSnapshot();
Size size = anObject.getSize();
assertEquals(size, anObject.getTotalRetainedSize());
assertEquals(size, anObject.getRetainedSize(anObject.getHeap()));
for (AhatHeap heap : snapshot.getHeaps()) {
if (!heap.equals(anObject.getHeap())) {
assertEquals(String.format("For heap '%s'", heap.getName()),
Size.ZERO, anObject.getRetainedSize(heap));
}
}
}
@Test
public void objectNotABitmap() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance obj = dump.getDumpedAhatInstance("anObject");
assertNull(obj.asBitmap());
}
@Test
public void arrayNotABitmap() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance obj = dump.getDumpedAhatInstance("gcPathArray");
assertNull(obj.asBitmap());
}
@Test
public void classObjNotABitmap() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance obj = dump.findClass("Main");
assertNull(obj.asBitmap());
}
@Test
public void classInstanceToString() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance obj = dump.getDumpedAhatInstance("aPhantomReference");
long id = obj.getId();
assertEquals(String.format("java.lang.ref.PhantomReference@%08x", id), obj.toString());
}
@Test
public void classObjToString() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance obj = dump.findClass("Main");
assertEquals("class Main", obj.toString());
}
@Test
public void arrayInstanceToString() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance obj = dump.getDumpedAhatInstance("gcPathArray");
long id = obj.getId();
// There's a bug in perfib's proguard deobfuscation for arrays.
// To work around that bug for the time being, only test the suffix of
// the toString result. Ideally we test for string equality against
// "Main$ObjectTree[4]@%08x", id.
assertTrue(obj.toString().endsWith(String.format("[4]@%08x", id)));
}
@Test
public void primArrayInstanceToString() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance obj = dump.getDumpedAhatInstance("bigArray");
long id = obj.getId();
assertEquals(String.format("byte[1000000]@%08x", id), obj.toString());
}
@Test
public void isNotRoot() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance obj = dump.getDumpedAhatInstance("anObject");
assertFalse(obj.isRoot());
assertNull(obj.getRootTypes());
}
@Test
public void reverseReferences() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance obj = dump.getDumpedAhatInstance("anObject");
AhatInstance ref = dump.getDumpedAhatInstance("aReference");
AhatInstance weak = dump.getDumpedAhatInstance("aWeakReference");
assertTrue(obj.getHardReverseReferences().contains(ref));
assertFalse(obj.getHardReverseReferences().contains(weak));
assertFalse(obj.getSoftReverseReferences().contains(ref));
assertTrue(obj.getSoftReverseReferences().contains(weak));
}
@Test
public void asStringEmbedded() throws IOException {
// On Android L, image strings were backed by a single big char array.
// Verify we show just the relative part of the string, not the entire
// char array.
TestDump dump = TestDump.getTestDump("L.hprof", null, null);
AhatSnapshot snapshot = dump.getAhatSnapshot();
// java.lang.String@0x6fe17050 is an image string "char" backed by a
// shared char array.
AhatInstance str = snapshot.findInstance(0x6fe17050);
assertEquals("char", str.asString());
}
@Test
public void nonDefaultHeapRoot() throws IOException {
TestDump dump = TestDump.getTestDump("O.hprof", null, null);
AhatSnapshot snapshot = dump.getAhatSnapshot();
// java.util.HashMap@6004fdb8 is marked as a VM INTERNAL root.
// Previously we had a bug where roots not on the default heap were not
// properly treated as roots (b/65356532).
AhatInstance map = snapshot.findInstance(0x6004fdb8);
assertEquals("java.util.HashMap", map.getClassName());
assertTrue(map.isRoot());
}
@Test
public void threadRoot() throws IOException {
TestDump dump = TestDump.getTestDump("O.hprof", null, null);
AhatSnapshot snapshot = dump.getAhatSnapshot();
// java.lang.Thread@12c03470 is marked as a thread root.
// Previously we had a bug where thread roots were not properly treated as
// roots (b/65356532).
AhatInstance thread = snapshot.findInstance(0x12c03470);
assertEquals("java.lang.Thread", thread.getClassName());
assertTrue(thread.isRoot());
}
@Test
public void classOfClass() throws IOException {
TestDump dump = TestDump.getTestDump();
AhatInstance obj = dump.getDumpedAhatInstance("anObject");
AhatClassObj cls = obj.getClassObj();
AhatClassObj clscls = cls.getClassObj();
assertNotNull(clscls);
assertEquals("java.lang.Class", clscls.getName());
}
@Test
public void nullValueString() throws IOException {
TestDump dump = TestDump.getTestDump("RI.hprof", null, null);
AhatSnapshot snapshot = dump.getAhatSnapshot();
// java.lang.String@500001a8 has a null 'value' field, which should not
// cause ahat to crash.
AhatInstance str = snapshot.findInstance(0x500001a8);
assertEquals("java.lang.String", str.getClassName());
assertNull(str.asString());
}
}