Home | History | Annotate | Download | only in test
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ahat;
     18 
     19 import com.android.ahat.heapdump.AhatClassObj;
     20 import com.android.ahat.heapdump.AhatHeap;
     21 import com.android.ahat.heapdump.AhatInstance;
     22 import com.android.ahat.heapdump.AhatSnapshot;
     23 import com.android.ahat.heapdump.PathElement;
     24 import com.android.ahat.heapdump.Value;
     25 import com.android.tools.perflib.heap.hprof.HprofClassDump;
     26 import com.android.tools.perflib.heap.hprof.HprofConstant;
     27 import com.android.tools.perflib.heap.hprof.HprofDumpRecord;
     28 import com.android.tools.perflib.heap.hprof.HprofHeapDump;
     29 import com.android.tools.perflib.heap.hprof.HprofInstanceDump;
     30 import com.android.tools.perflib.heap.hprof.HprofInstanceField;
     31 import com.android.tools.perflib.heap.hprof.HprofLoadClass;
     32 import com.android.tools.perflib.heap.hprof.HprofPrimitiveArrayDump;
     33 import com.android.tools.perflib.heap.hprof.HprofRecord;
     34 import com.android.tools.perflib.heap.hprof.HprofRootDebugger;
     35 import com.android.tools.perflib.heap.hprof.HprofStaticField;
     36 import com.android.tools.perflib.heap.hprof.HprofStringBuilder;
     37 import com.android.tools.perflib.heap.hprof.HprofType;
     38 import com.google.common.io.ByteArrayDataOutput;
     39 import com.google.common.io.ByteStreams;
     40 import java.io.IOException;
     41 import java.util.ArrayList;
     42 import java.util.List;
     43 import org.junit.Test;
     44 
     45 import static org.junit.Assert.assertEquals;
     46 import static org.junit.Assert.assertFalse;
     47 import static org.junit.Assert.assertNotNull;
     48 import static org.junit.Assert.assertNull;
     49 import static org.junit.Assert.assertTrue;
     50 
     51 public class InstanceTest {
     52   @Test
     53   public void asStringBasic() throws IOException {
     54     TestDump dump = TestDump.getTestDump();
     55     AhatInstance str = dump.getDumpedAhatInstance("basicString");
     56     assertEquals("hello, world", str.asString());
     57   }
     58 
     59   @Test
     60   public void asStringNonAscii() throws IOException {
     61     TestDump dump = TestDump.getTestDump();
     62     AhatInstance str = dump.getDumpedAhatInstance("nonAscii");
     63     assertEquals("Sigma () is not ASCII", str.asString());
     64   }
     65 
     66   @Test
     67   public void asStringEmbeddedZero() throws IOException {
     68     TestDump dump = TestDump.getTestDump();
     69     AhatInstance str = dump.getDumpedAhatInstance("embeddedZero");
     70     assertEquals("embedded\0...", str.asString());
     71   }
     72 
     73   @Test
     74   public void asStringCharArray() throws IOException {
     75     TestDump dump = TestDump.getTestDump();
     76     AhatInstance str = dump.getDumpedAhatInstance("charArray");
     77     assertEquals("char thing", str.asString());
     78   }
     79 
     80   @Test
     81   public void asStringTruncated() throws IOException {
     82     TestDump dump = TestDump.getTestDump();
     83     AhatInstance str = dump.getDumpedAhatInstance("basicString");
     84     assertEquals("hello", str.asString(5));
     85   }
     86 
     87   @Test
     88   public void asStringTruncatedNonAscii() throws IOException {
     89     TestDump dump = TestDump.getTestDump();
     90     AhatInstance str = dump.getDumpedAhatInstance("nonAscii");
     91     assertEquals("Sigma ()", str.asString(9));
     92   }
     93 
     94   @Test
     95   public void asStringTruncatedEmbeddedZero() throws IOException {
     96     TestDump dump = TestDump.getTestDump();
     97     AhatInstance str = dump.getDumpedAhatInstance("embeddedZero");
     98     assertEquals("embed", str.asString(5));
     99   }
    100 
    101   @Test
    102   public void asStringCharArrayTruncated() throws IOException {
    103     TestDump dump = TestDump.getTestDump();
    104     AhatInstance str = dump.getDumpedAhatInstance("charArray");
    105     assertEquals("char ", str.asString(5));
    106   }
    107 
    108   @Test
    109   public void asStringExactMax() throws IOException {
    110     TestDump dump = TestDump.getTestDump();
    111     AhatInstance str = dump.getDumpedAhatInstance("basicString");
    112     assertEquals("hello, world", str.asString(12));
    113   }
    114 
    115   @Test
    116   public void asStringExactMaxNonAscii() throws IOException {
    117     TestDump dump = TestDump.getTestDump();
    118     AhatInstance str = dump.getDumpedAhatInstance("nonAscii");
    119     assertEquals("Sigma () is not ASCII", str.asString(22));
    120   }
    121 
    122   @Test
    123   public void asStringExactMaxEmbeddedZero() throws IOException {
    124     TestDump dump = TestDump.getTestDump();
    125     AhatInstance str = dump.getDumpedAhatInstance("embeddedZero");
    126     assertEquals("embedded\0...", str.asString(12));
    127   }
    128 
    129   @Test
    130   public void asStringCharArrayExactMax() throws IOException {
    131     TestDump dump = TestDump.getTestDump();
    132     AhatInstance str = dump.getDumpedAhatInstance("charArray");
    133     assertEquals("char thing", str.asString(10));
    134   }
    135 
    136   @Test
    137   public void asStringNotTruncated() throws IOException {
    138     TestDump dump = TestDump.getTestDump();
    139     AhatInstance str = dump.getDumpedAhatInstance("basicString");
    140     assertEquals("hello, world", str.asString(50));
    141   }
    142 
    143   @Test
    144   public void asStringNotTruncatedNonAscii() throws IOException {
    145     TestDump dump = TestDump.getTestDump();
    146     AhatInstance str = dump.getDumpedAhatInstance("nonAscii");
    147     assertEquals("Sigma () is not ASCII", str.asString(50));
    148   }
    149 
    150   @Test
    151   public void asStringNotTruncatedEmbeddedZero() throws IOException {
    152     TestDump dump = TestDump.getTestDump();
    153     AhatInstance str = dump.getDumpedAhatInstance("embeddedZero");
    154     assertEquals("embedded\0...", str.asString(50));
    155   }
    156 
    157   @Test
    158   public void asStringCharArrayNotTruncated() throws IOException {
    159     TestDump dump = TestDump.getTestDump();
    160     AhatInstance str = dump.getDumpedAhatInstance("charArray");
    161     assertEquals("char thing", str.asString(50));
    162   }
    163 
    164   @Test
    165   public void asStringNegativeMax() throws IOException {
    166     TestDump dump = TestDump.getTestDump();
    167     AhatInstance str = dump.getDumpedAhatInstance("basicString");
    168     assertEquals("hello, world", str.asString(-3));
    169   }
    170 
    171   @Test
    172   public void asStringNegativeMaxNonAscii() throws IOException {
    173     TestDump dump = TestDump.getTestDump();
    174     AhatInstance str = dump.getDumpedAhatInstance("nonAscii");
    175     assertEquals("Sigma () is not ASCII", str.asString(-3));
    176   }
    177 
    178   @Test
    179   public void asStringNegativeMaxEmbeddedZero() throws IOException {
    180     TestDump dump = TestDump.getTestDump();
    181     AhatInstance str = dump.getDumpedAhatInstance("embeddedZero");
    182     assertEquals("embedded\0...", str.asString(-3));
    183   }
    184 
    185   @Test
    186   public void asStringCharArrayNegativeMax() throws IOException {
    187     TestDump dump = TestDump.getTestDump();
    188     AhatInstance str = dump.getDumpedAhatInstance("charArray");
    189     assertEquals("char thing", str.asString(-3));
    190   }
    191 
    192   @Test
    193   public void asStringNull() throws IOException {
    194     TestDump dump = TestDump.getTestDump();
    195     AhatInstance obj = dump.getDumpedAhatInstance("nullString");
    196     assertNull(obj);
    197   }
    198 
    199   @Test
    200   public void asStringNotString() throws IOException {
    201     TestDump dump = TestDump.getTestDump();
    202     AhatInstance obj = dump.getDumpedAhatInstance("anObject");
    203     assertNotNull(obj);
    204     assertNull(obj.asString());
    205   }
    206 
    207   @Test
    208   public void basicReference() throws IOException {
    209     TestDump dump = TestDump.getTestDump();
    210 
    211     AhatInstance pref = dump.getDumpedAhatInstance("aPhantomReference");
    212     AhatInstance wref = dump.getDumpedAhatInstance("aWeakReference");
    213     AhatInstance nref = dump.getDumpedAhatInstance("aNullReferentReference");
    214     AhatInstance referent = dump.getDumpedAhatInstance("anObject");
    215     assertNotNull(pref);
    216     assertNotNull(wref);
    217     assertNotNull(nref);
    218     assertNotNull(referent);
    219     assertEquals(referent, pref.getReferent());
    220     assertEquals(referent, wref.getReferent());
    221     assertNull(nref.getReferent());
    222     assertNull(referent.getReferent());
    223   }
    224 
    225   @Test
    226   public void unreachableReferent() throws IOException {
    227     // The test dump program should never be under enough GC pressure for the
    228     // soft reference to be cleared. Ensure that ahat will show the soft
    229     // reference as having a non-null referent.
    230     TestDump dump = TestDump.getTestDump();
    231     AhatInstance ref = dump.getDumpedAhatInstance("aSoftReference");
    232     assertNotNull(ref.getReferent());
    233   }
    234 
    235   @Test
    236   public void gcRootPath() throws IOException {
    237     TestDump dump = TestDump.getTestDump();
    238 
    239     AhatClassObj main = dump.getAhatSnapshot().findClass("Main");
    240     AhatInstance gcPathArray = dump.getDumpedAhatInstance("gcPathArray");
    241     Value value = gcPathArray.asArrayInstance().getValue(2);
    242     AhatInstance base = value.asAhatInstance();
    243     AhatInstance left = base.getRefField("left");
    244     AhatInstance right = base.getRefField("right");
    245     AhatInstance target = left.getRefField("right");
    246 
    247     List<PathElement> path = target.getPathFromGcRoot();
    248     assertEquals(6, path.size());
    249 
    250     assertEquals(main, path.get(0).instance);
    251     assertEquals(".stuff", path.get(0).field);
    252     assertTrue(path.get(0).isDominator);
    253 
    254     assertEquals(".gcPathArray", path.get(1).field);
    255     assertTrue(path.get(1).isDominator);
    256 
    257     assertEquals(gcPathArray, path.get(2).instance);
    258     assertEquals("[2]", path.get(2).field);
    259     assertTrue(path.get(2).isDominator);
    260 
    261     assertEquals(base, path.get(3).instance);
    262     assertTrue(path.get(3).isDominator);
    263 
    264     // There are two possible paths. Either it can go through the 'left' node,
    265     // or the 'right' node.
    266     if (path.get(3).field.equals(".left")) {
    267       assertEquals(".left", path.get(3).field);
    268 
    269       assertEquals(left, path.get(4).instance);
    270       assertEquals(".right", path.get(4).field);
    271       assertFalse(path.get(4).isDominator);
    272 
    273     } else {
    274       assertEquals(".right", path.get(3).field);
    275 
    276       assertEquals(right, path.get(4).instance);
    277       assertEquals(".left", path.get(4).field);
    278       assertFalse(path.get(4).isDominator);
    279     }
    280 
    281     assertEquals(target, path.get(5).instance);
    282     assertEquals("", path.get(5).field);
    283     assertTrue(path.get(5).isDominator);
    284   }
    285 
    286   @Test
    287   public void retainedSize() throws IOException {
    288     TestDump dump = TestDump.getTestDump();
    289 
    290     // anObject should not be an immediate dominator of any other object. This
    291     // means its retained size should be equal to its size for the heap it was
    292     // allocated on, and should be 0 for all other heaps.
    293     AhatInstance anObject = dump.getDumpedAhatInstance("anObject");
    294     AhatSnapshot snapshot = dump.getAhatSnapshot();
    295     long size = anObject.getSize();
    296     assertEquals(size, anObject.getTotalRetainedSize());
    297     assertEquals(size, anObject.getRetainedSize(anObject.getHeap()));
    298     for (AhatHeap heap : snapshot.getHeaps()) {
    299       if (!heap.equals(anObject.getHeap())) {
    300         assertEquals(String.format("For heap '%s'", heap.getName()),
    301             0, anObject.getRetainedSize(heap));
    302       }
    303     }
    304   }
    305 
    306   @Test
    307   public void objectNotABitmap() throws IOException {
    308     TestDump dump = TestDump.getTestDump();
    309     AhatInstance obj = dump.getDumpedAhatInstance("anObject");
    310     assertNull(obj.asBitmap());
    311   }
    312 
    313   @Test
    314   public void arrayNotABitmap() throws IOException {
    315     TestDump dump = TestDump.getTestDump();
    316     AhatInstance obj = dump.getDumpedAhatInstance("gcPathArray");
    317     assertNull(obj.asBitmap());
    318   }
    319 
    320   @Test
    321   public void classObjNotABitmap() throws IOException {
    322     TestDump dump = TestDump.getTestDump();
    323     AhatInstance obj = dump.getAhatSnapshot().findClass("Main");
    324     assertNull(obj.asBitmap());
    325   }
    326 
    327   @Test
    328   public void classInstanceToString() throws IOException {
    329     TestDump dump = TestDump.getTestDump();
    330     AhatInstance obj = dump.getDumpedAhatInstance("aPhantomReference");
    331     long id = obj.getId();
    332     assertEquals(String.format("java.lang.ref.PhantomReference@%08x", id), obj.toString());
    333   }
    334 
    335   @Test
    336   public void classObjToString() throws IOException {
    337     TestDump dump = TestDump.getTestDump();
    338     AhatInstance obj = dump.getAhatSnapshot().findClass("Main");
    339     assertEquals("Main", obj.toString());
    340   }
    341 
    342   @Test
    343   public void arrayInstanceToString() throws IOException {
    344     TestDump dump = TestDump.getTestDump();
    345     AhatInstance obj = dump.getDumpedAhatInstance("gcPathArray");
    346     long id = obj.getId();
    347 
    348     // There's a bug in perfib's proguard deobfuscation for arrays.
    349     // To work around that bug for the time being, only test the suffix of
    350     // the toString result. Ideally we test for string equality against
    351     // "Main$ObjectTree[4]@%08x", id.
    352     assertTrue(obj.toString().endsWith(String.format("[4]@%08x", id)));
    353   }
    354 
    355   @Test
    356   public void primArrayInstanceToString() throws IOException {
    357     TestDump dump = TestDump.getTestDump();
    358     AhatInstance obj = dump.getDumpedAhatInstance("bigArray");
    359     long id = obj.getId();
    360     assertEquals(String.format("byte[1000000]@%08x", id), obj.toString());
    361   }
    362 
    363   @Test
    364   public void isNotRoot() throws IOException {
    365     TestDump dump = TestDump.getTestDump();
    366     AhatInstance obj = dump.getDumpedAhatInstance("anObject");
    367     assertFalse(obj.isRoot());
    368     assertNull(obj.getRootTypes());
    369   }
    370 
    371   @Test
    372   public void asStringEmbedded() throws IOException {
    373     // Set up a heap dump with an instance of java.lang.String of
    374     // "hello" with instance id 0x42 that is backed by a char array that is
    375     // bigger. This is how ART used to represent strings, and we should still
    376     // support it in case the heap dump is from a previous platform version.
    377     HprofStringBuilder strings = new HprofStringBuilder(0);
    378     List<HprofRecord> records = new ArrayList<HprofRecord>();
    379     List<HprofDumpRecord> dump = new ArrayList<HprofDumpRecord>();
    380 
    381     final int stringClassObjectId = 1;
    382     records.add(new HprofLoadClass(0, 0, stringClassObjectId, 0, strings.get("java.lang.String")));
    383     dump.add(new HprofClassDump(stringClassObjectId, 0, 0, 0, 0, 0, 0, 0, 0,
    384           new HprofConstant[0], new HprofStaticField[0],
    385           new HprofInstanceField[]{
    386             new HprofInstanceField(strings.get("count"), HprofType.TYPE_INT),
    387             new HprofInstanceField(strings.get("hashCode"), HprofType.TYPE_INT),
    388             new HprofInstanceField(strings.get("offset"), HprofType.TYPE_INT),
    389             new HprofInstanceField(strings.get("value"), HprofType.TYPE_OBJECT)}));
    390 
    391     dump.add(new HprofPrimitiveArrayDump(0x41, 0, HprofType.TYPE_CHAR,
    392           new long[]{'n', 'o', 't', ' ', 'h', 'e', 'l', 'l', 'o', 'o', 'p'}));
    393 
    394     ByteArrayDataOutput values = ByteStreams.newDataOutput();
    395     values.writeInt(5);     // count
    396     values.writeInt(0);     // hashCode
    397     values.writeInt(4);     // offset
    398     values.writeInt(0x41);  // value
    399     dump.add(new HprofInstanceDump(0x42, 0, stringClassObjectId, values.toByteArray()));
    400     dump.add(new HprofRootDebugger(stringClassObjectId));
    401     dump.add(new HprofRootDebugger(0x42));
    402 
    403     records.add(new HprofHeapDump(0, dump.toArray(new HprofDumpRecord[0])));
    404     AhatSnapshot snapshot = SnapshotBuilder.makeSnapshot(strings, records);
    405     AhatInstance chars = snapshot.findInstance(0x41);
    406     assertNotNull(chars);
    407     assertEquals("not helloop", chars.asString());
    408 
    409     AhatInstance stringInstance = snapshot.findInstance(0x42);
    410     assertNotNull(stringInstance);
    411     assertEquals("hello", stringInstance.asString());
    412   }
    413 }
    414