blob: 7173b1127c160358759d88837cc909e0c4777107 [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
226 public void gcRootPath() throws IOException {
227 TestDump dump = TestDump.getTestDump();
228
229 AhatClassObj main = dump.getAhatSnapshot().findClass("Main");
230 AhatInstance gcPathArray = dump.getDumpedAhatInstance("gcPathArray");
231 Value value = gcPathArray.asArrayInstance().getValue(2);
232 AhatInstance base = value.asAhatInstance();
233 AhatInstance left = base.getRefField("left");
234 AhatInstance right = base.getRefField("right");
235 AhatInstance target = left.getRefField("right");
236
237 List<PathElement> path = target.getPathFromGcRoot();
238 assertEquals(6, path.size());
239
240 assertEquals(main, path.get(0).instance);
241 assertEquals(".stuff", path.get(0).field);
242 assertTrue(path.get(0).isDominator);
243
244 assertEquals(".gcPathArray", path.get(1).field);
245 assertTrue(path.get(1).isDominator);
246
247 assertEquals(gcPathArray, path.get(2).instance);
248 assertEquals("[2]", path.get(2).field);
249 assertTrue(path.get(2).isDominator);
250
251 assertEquals(base, path.get(3).instance);
252 assertTrue(path.get(3).isDominator);
253
254 // There are two possible paths. Either it can go through the 'left' node,
255 // or the 'right' node.
256 if (path.get(3).field.equals(".left")) {
257 assertEquals(".left", path.get(3).field);
258
259 assertEquals(left, path.get(4).instance);
260 assertEquals(".right", path.get(4).field);
261 assertFalse(path.get(4).isDominator);
262
263 } else {
264 assertEquals(".right", path.get(3).field);
265
266 assertEquals(right, path.get(4).instance);
267 assertEquals(".left", path.get(4).field);
268 assertFalse(path.get(4).isDominator);
269 }
270
271 assertEquals(target, path.get(5).instance);
272 assertEquals("", path.get(5).field);
273 assertTrue(path.get(5).isDominator);
274 }
275
276 @Test
277 public void retainedSize() throws IOException {
278 TestDump dump = TestDump.getTestDump();
279
280 // anObject should not be an immediate dominator of any other object. This
281 // means its retained size should be equal to its size for the heap it was
282 // allocated on, and should be 0 for all other heaps.
283 AhatInstance anObject = dump.getDumpedAhatInstance("anObject");
284 AhatSnapshot snapshot = dump.getAhatSnapshot();
285 long size = anObject.getSize();
286 assertEquals(size, anObject.getTotalRetainedSize());
287 assertEquals(size, anObject.getRetainedSize(anObject.getHeap()));
288 for (AhatHeap heap : snapshot.getHeaps()) {
289 if (!heap.equals(anObject.getHeap())) {
290 assertEquals(String.format("For heap '%s'", heap.getName()),
291 0, anObject.getRetainedSize(heap));
292 }
293 }
294 }
295
296 @Test
297 public void objectNotABitmap() throws IOException {
298 TestDump dump = TestDump.getTestDump();
299 AhatInstance obj = dump.getDumpedAhatInstance("anObject");
300 assertNull(obj.asBitmap());
301 }
302
303 @Test
304 public void arrayNotABitmap() throws IOException {
305 TestDump dump = TestDump.getTestDump();
306 AhatInstance obj = dump.getDumpedAhatInstance("gcPathArray");
307 assertNull(obj.asBitmap());
308 }
309
310 @Test
311 public void classObjNotABitmap() throws IOException {
312 TestDump dump = TestDump.getTestDump();
313 AhatInstance obj = dump.getAhatSnapshot().findClass("Main");
314 assertNull(obj.asBitmap());
315 }
316
317 @Test
318 public void classInstanceToString() throws IOException {
319 TestDump dump = TestDump.getTestDump();
320 AhatInstance obj = dump.getDumpedAhatInstance("aPhantomReference");
321 long id = obj.getId();
322 assertEquals(String.format("java.lang.ref.PhantomReference@%08x", id), obj.toString());
323 }
324
325 @Test
326 public void classObjToString() throws IOException {
327 TestDump dump = TestDump.getTestDump();
328 AhatInstance obj = dump.getAhatSnapshot().findClass("Main");
329 assertEquals("Main", obj.toString());
330 }
331
332 @Test
333 public void arrayInstanceToString() throws IOException {
334 TestDump dump = TestDump.getTestDump();
335 AhatInstance obj = dump.getDumpedAhatInstance("gcPathArray");
336 long id = obj.getId();
337
338 // There's a bug in perfib's proguard deobfuscation for arrays.
339 // To work around that bug for the time being, only test the suffix of
340 // the toString result. Ideally we test for string equality against
341 // "Main$ObjectTree[4]@%08x", id.
342 assertTrue(obj.toString().endsWith(String.format("[4]@%08x", id)));
343 }
344
345 @Test
346 public void primArrayInstanceToString() throws IOException {
347 TestDump dump = TestDump.getTestDump();
348 AhatInstance obj = dump.getDumpedAhatInstance("bigArray");
349 long id = obj.getId();
350 assertEquals(String.format("byte[1000000]@%08x", id), obj.toString());
351 }
352
353 @Test
354 public void isNotRoot() throws IOException {
355 TestDump dump = TestDump.getTestDump();
356 AhatInstance obj = dump.getDumpedAhatInstance("anObject");
357 assertFalse(obj.isRoot());
358 assertNull(obj.getRootTypes());
359 }
360
361 @Test
362 public void reverseReferencesAreNotUnreachable() throws IOException {
363 TestDump dump = TestDump.getTestDump();
364 AhatInstance obj = dump.getDumpedAhatInstance("basicString");
365 assertEquals(2, obj.getHardReverseReferences().size());
366 assertEquals(0, obj.getSoftReverseReferences().size());
367 }
368
369 @Test
370 public void asStringEmbedded() throws IOException {
371 // Set up a heap dump with an instance of java.lang.String of
372 // "hello" with instance id 0x42 that is backed by a char array that is
373 // bigger. This is how ART used to represent strings, and we should still
374 // support it in case the heap dump is from a previous platform version.
375 HprofStringBuilder strings = new HprofStringBuilder(0);
376 List<HprofRecord> records = new ArrayList<HprofRecord>();
377 List<HprofDumpRecord> dump = new ArrayList<HprofDumpRecord>();
378
379 final int stringClassObjectId = 1;
380 records.add(new HprofLoadClass(0, 0, stringClassObjectId, 0, strings.get("java.lang.String")));
381 dump.add(new HprofClassDump(stringClassObjectId, 0, 0, 0, 0, 0, 0, 0, 0,
382 new HprofConstant[0], new HprofStaticField[0],
383 new HprofInstanceField[]{
384 new HprofInstanceField(strings.get("count"), HprofType.TYPE_INT),
385 new HprofInstanceField(strings.get("hashCode"), HprofType.TYPE_INT),
386 new HprofInstanceField(strings.get("offset"), HprofType.TYPE_INT),
387 new HprofInstanceField(strings.get("value"), HprofType.TYPE_OBJECT)}));
388
389 dump.add(new HprofPrimitiveArrayDump(0x41, 0, HprofType.TYPE_CHAR,
390 new long[]{'n', 'o', 't', ' ', 'h', 'e', 'l', 'l', 'o', 'o', 'p'}));
391
392 ByteArrayDataOutput values = ByteStreams.newDataOutput();
393 values.writeInt(5); // count
394 values.writeInt(0); // hashCode
395 values.writeInt(4); // offset
396 values.writeInt(0x41); // value
397 dump.add(new HprofInstanceDump(0x42, 0, stringClassObjectId, values.toByteArray()));
398 dump.add(new HprofRootDebugger(stringClassObjectId));
399 dump.add(new HprofRootDebugger(0x42));
400
401 records.add(new HprofHeapDump(0, dump.toArray(new HprofDumpRecord[0])));
402 AhatSnapshot snapshot = SnapshotBuilder.makeSnapshot(strings, records);
403 AhatInstance chars = snapshot.findInstance(0x41);
404 assertNotNull(chars);
405 assertEquals("not helloop", chars.asString());
406
407 AhatInstance stringInstance = snapshot.findInstance(0x42);
408 assertNotNull(stringInstance);
409 assertEquals("hello", stringInstance.asString());
410 }
411}