• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
asStringBasic()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
asStringNonAscii()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
asStringEmbeddedZero()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
asStringCharArray()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
asStringTruncated()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
asStringTruncatedNonAscii()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
asStringTruncatedEmbeddedZero()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
asStringCharArrayTruncated()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
asStringExactMax()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
asStringExactMaxNonAscii()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
asStringExactMaxEmbeddedZero()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
asStringCharArrayExactMax()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
asStringNotTruncated()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
asStringNotTruncatedNonAscii()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
asStringNotTruncatedEmbeddedZero()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
asStringCharArrayNotTruncated()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
asStringNegativeMax()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
asStringNegativeMaxNonAscii()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
asStringNegativeMaxEmbeddedZero()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
asStringCharArrayNegativeMax()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
asStringNull()193   public void asStringNull() throws IOException {
194     TestDump dump = TestDump.getTestDump();
195     AhatInstance obj = dump.getDumpedAhatInstance("nullString");
196     assertNull(obj);
197   }
198 
199   @Test
asStringNotString()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
basicReference()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
unreachableReferent()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
gcRootPath()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
retainedSize()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
objectNotABitmap()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
arrayNotABitmap()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
classObjNotABitmap()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
classInstanceToString()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
classObjToString()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
arrayInstanceToString()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
primArrayInstanceToString()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
isNotRoot()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
asStringEmbedded()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