blob: 3a50150c0ec1c8a2382afd042a0bf3c6b79b81c5 [file] [log] [blame]
Richard Uhlercda4f2e2016-09-09 09:56:20 +01001/*
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
17package com.android.ahat;
18
19import com.android.ahat.heapdump.AhatClassObj;
20import com.android.ahat.heapdump.AhatHeap;
21import com.android.ahat.heapdump.AhatInstance;
22import com.android.ahat.heapdump.AhatSnapshot;
23import com.android.ahat.heapdump.PathElement;
24import com.android.ahat.heapdump.Value;
25import com.android.tools.perflib.heap.hprof.HprofClassDump;
26import com.android.tools.perflib.heap.hprof.HprofConstant;
27import com.android.tools.perflib.heap.hprof.HprofDumpRecord;
28import com.android.tools.perflib.heap.hprof.HprofHeapDump;
29import com.android.tools.perflib.heap.hprof.HprofInstanceDump;
30import com.android.tools.perflib.heap.hprof.HprofInstanceField;
31import com.android.tools.perflib.heap.hprof.HprofLoadClass;
32import com.android.tools.perflib.heap.hprof.HprofPrimitiveArrayDump;
33import com.android.tools.perflib.heap.hprof.HprofRecord;
34import com.android.tools.perflib.heap.hprof.HprofRootDebugger;
35import com.android.tools.perflib.heap.hprof.HprofStaticField;
36import com.android.tools.perflib.heap.hprof.HprofStringBuilder;
37import com.android.tools.perflib.heap.hprof.HprofType;
38import com.google.common.io.ByteArrayDataOutput;
39import com.google.common.io.ByteStreams;
40import java.io.IOException;
41import java.util.ArrayList;
42import java.util.List;
43import org.junit.Test;
44
45import static org.junit.Assert.assertEquals;
46import static org.junit.Assert.assertFalse;
47import static org.junit.Assert.assertNotNull;
48import static org.junit.Assert.assertNull;
49import static org.junit.Assert.assertTrue;
50
51public 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
Richard Uhlerd640e292016-12-28 15:46:03 +0000226 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
Richard Uhlercda4f2e2016-09-09 09:56:20 +0100236 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
Richard Uhlercda4f2e2016-09-09 09:56:20 +0100372 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}