blob: 94934a2831f841ca5f9ada2ee45ab1c56886c9ea [file] [log] [blame]
Richard Uhlerb730b782015-07-15 16:01:58 -07001/*
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.tools.perflib.heap.ArrayInstance;
20import com.android.tools.perflib.heap.ClassInstance;
21import com.android.tools.perflib.heap.ClassObj;
Richard Uhler69286492016-09-20 10:41:47 +010022import com.android.tools.perflib.heap.Field;
Richard Uhler1a5baaa2015-12-21 12:47:26 -080023import com.android.tools.perflib.heap.Heap;
Gus Smithe6a63872016-06-09 16:50:44 -070024import com.android.tools.perflib.heap.Instance;
Richard Uhler69286492016-09-20 10:41:47 +010025import com.android.tools.perflib.heap.RootObj;
Richard Uhlerb730b782015-07-15 16:01:58 -070026import com.android.tools.perflib.heap.Type;
Gus Smithe6a63872016-06-09 16:50:44 -070027
Richard Uhlerb730b782015-07-15 16:01:58 -070028import java.awt.image.BufferedImage;
Richard Uhler69286492016-09-20 10:41:47 +010029import java.util.ArrayList;
30import java.util.Collections;
31import java.util.List;
32import java.util.Map;
Richard Uhlerb730b782015-07-15 16:01:58 -070033
34/**
35 * Utilities for extracting information from hprof instances.
36 */
37class InstanceUtils {
38 /**
39 * Returns true if the given instance is an instance of a class with the
40 * given name.
41 */
Richard Uhler1a5baaa2015-12-21 12:47:26 -080042 private static boolean isInstanceOfClass(Instance inst, String className) {
Richard Uhler35244722015-09-10 16:45:54 -070043 ClassObj cls = (inst == null) ? null : inst.getClassObj();
Richard Uhlerb730b782015-07-15 16:01:58 -070044 return (cls != null && className.equals(cls.getClassName()));
45 }
46
47 /**
Richard Uhlerb730b782015-07-15 16:01:58 -070048 * Read the byte[] value from an hprof Instance.
49 * Returns null if the instance is not a byte array.
50 */
51 private static byte[] asByteArray(Instance inst) {
Gus Smithe6a63872016-06-09 16:50:44 -070052 if (!(inst instanceof ArrayInstance)) {
Richard Uhlerb730b782015-07-15 16:01:58 -070053 return null;
54 }
55
Gus Smithe6a63872016-06-09 16:50:44 -070056 ArrayInstance array = (ArrayInstance) inst;
Richard Uhlerb730b782015-07-15 16:01:58 -070057 if (array.getArrayType() != Type.BYTE) {
58 return null;
59 }
60
61 Object[] objs = array.getValues();
62 byte[] bytes = new byte[objs.length];
63 for (int i = 0; i < objs.length; i++) {
Gus Smithe6a63872016-06-09 16:50:44 -070064 Byte b = (Byte) objs[i];
Richard Uhlerb730b782015-07-15 16:01:58 -070065 bytes[i] = b.byteValue();
66 }
67 return bytes;
68 }
69
70
Richard Uhler77ff54b2015-08-31 10:22:56 -070071 /**
72 * Read the string value from an hprof Instance.
73 * Returns null if the object can't be interpreted as a string.
74 */
Richard Uhlerb730b782015-07-15 16:01:58 -070075 public static String asString(Instance inst) {
Richard Uhler77ff54b2015-08-31 10:22:56 -070076 return asString(inst, -1);
77 }
78
79 /**
80 * Read the string value from an hprof Instance.
81 * Returns null if the object can't be interpreted as a string.
82 * The returned string is truncated to maxChars characters.
83 * If maxChars is negative, the returned string is not truncated.
84 */
85 public static String asString(Instance inst, int maxChars) {
Richard Uhlerc7f77122016-02-11 08:48:24 -080086 // The inst object could either be a java.lang.String or a char[]. If it
87 // is a char[], use that directly as the value, otherwise use the value
88 // field of the string object. The field accesses for count and offset
89 // later on will work okay regardless of what type the inst object is.
90 Object value = inst;
91 if (isInstanceOfClass(inst, "java.lang.String")) {
92 value = getField(inst, "value");
Richard Uhlerb730b782015-07-15 16:01:58 -070093 }
Richard Uhler788b21e2015-08-31 10:33:56 -070094
Richard Uhler788b21e2015-08-31 10:33:56 -070095 if (!(value instanceof ArrayInstance)) {
96 return null;
97 }
98
99 ArrayInstance chars = (ArrayInstance) value;
100 if (chars.getArrayType() != Type.CHAR) {
101 return null;
102 }
103
Richard Uhler702e6412016-08-03 09:26:35 -0700104 int numChars = chars.getLength();
Richard Uhler788b21e2015-08-31 10:33:56 -0700105 int count = getIntField(inst, "count", numChars);
Richard Uhler788b21e2015-08-31 10:33:56 -0700106 if (count == 0) {
107 return "";
108 }
Richard Uhler77ff54b2015-08-31 10:22:56 -0700109 if (0 <= maxChars && maxChars < count) {
110 count = maxChars;
111 }
Richard Uhler788b21e2015-08-31 10:33:56 -0700112
Richard Uhler77ff54b2015-08-31 10:22:56 -0700113 int offset = getIntField(inst, "offset", 0);
114 int end = offset + count - 1;
Richard Uhler788b21e2015-08-31 10:33:56 -0700115 if (offset >= 0 && offset < numChars && end >= 0 && end < numChars) {
116 return new String(chars.asCharArray(offset, count));
117 }
118 return null;
Richard Uhlerb730b782015-07-15 16:01:58 -0700119 }
120
121 /**
122 * Read the bitmap data for the given android.graphics.Bitmap object.
123 * Returns null if the object isn't for android.graphics.Bitmap or the
124 * bitmap data couldn't be read.
125 */
126 public static BufferedImage asBitmap(Instance inst) {
127 if (!isInstanceOfClass(inst, "android.graphics.Bitmap")) {
128 return null;
129 }
130
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800131 Integer width = getIntField(inst, "mWidth", null);
Richard Uhlerb730b782015-07-15 16:01:58 -0700132 if (width == null) {
133 return null;
134 }
135
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800136 Integer height = getIntField(inst, "mHeight", null);
Richard Uhlerb730b782015-07-15 16:01:58 -0700137 if (height == null) {
138 return null;
139 }
140
141 byte[] buffer = getByteArrayField(inst, "mBuffer");
142 if (buffer == null) {
143 return null;
144 }
145
146 // Convert the raw data to an image
147 // Convert BGRA to ABGR
148 int[] abgr = new int[height * width];
149 for (int i = 0; i < abgr.length; i++) {
150 abgr[i] = (
Gus Smithe6a63872016-06-09 16:50:44 -0700151 (((int) buffer[i * 4 + 3] & 0xFF) << 24)
152 + (((int) buffer[i * 4 + 0] & 0xFF) << 16)
153 + (((int) buffer[i * 4 + 1] & 0xFF) << 8)
154 + ((int) buffer[i * 4 + 2] & 0xFF));
Richard Uhlerb730b782015-07-15 16:01:58 -0700155 }
156
157 BufferedImage bitmap = new BufferedImage(
158 width, height, BufferedImage.TYPE_4BYTE_ABGR);
159 bitmap.setRGB(0, 0, width, height, abgr, 0, width);
160 return bitmap;
161 }
162
163 /**
164 * Read a field of an instance.
165 * Returns null if the field value is null or if the field couldn't be read.
166 */
Richard Uhler35244722015-09-10 16:45:54 -0700167 public static Object getField(Instance inst, String fieldName) {
Richard Uhlerb730b782015-07-15 16:01:58 -0700168 if (!(inst instanceof ClassInstance)) {
169 return null;
170 }
171
172 ClassInstance clsinst = (ClassInstance) inst;
173 Object value = null;
174 int count = 0;
175 for (ClassInstance.FieldValue field : clsinst.getValues()) {
176 if (fieldName.equals(field.getField().getName())) {
177 value = field.getValue();
178 count++;
179 }
180 }
181 return count == 1 ? value : null;
182 }
183
184 /**
185 * Read a reference field of an instance.
186 * Returns null if the field value is null, or if the field couldn't be read.
187 */
Richard Uhler69286492016-09-20 10:41:47 +0100188 public static Instance getRefField(Instance inst, String fieldName) {
Richard Uhlerb730b782015-07-15 16:01:58 -0700189 Object value = getField(inst, fieldName);
190 if (!(value instanceof Instance)) {
191 return null;
192 }
Gus Smithe6a63872016-06-09 16:50:44 -0700193 return (Instance) value;
Richard Uhlerb730b782015-07-15 16:01:58 -0700194 }
195
196 /**
197 * Read an int field of an instance.
198 * The field is assumed to be an int type.
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800199 * Returns <code>def</code> if the field value is not an int or could not be
200 * read.
Richard Uhlerb730b782015-07-15 16:01:58 -0700201 */
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800202 private static Integer getIntField(Instance inst, String fieldName, Integer def) {
Richard Uhlerb730b782015-07-15 16:01:58 -0700203 Object value = getField(inst, fieldName);
204 if (!(value instanceof Integer)) {
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800205 return def;
Richard Uhlerb730b782015-07-15 16:01:58 -0700206 }
Gus Smithe6a63872016-06-09 16:50:44 -0700207 return (Integer) value;
Richard Uhlerb730b782015-07-15 16:01:58 -0700208 }
209
210 /**
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800211 * Read a long field of an instance.
212 * The field is assumed to be a long type.
213 * Returns <code>def</code> if the field value is not an long or could not
214 * be read.
Richard Uhler788b21e2015-08-31 10:33:56 -0700215 */
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800216 private static Long getLongField(Instance inst, String fieldName, Long def) {
217 Object value = getField(inst, fieldName);
218 if (!(value instanceof Long)) {
219 return def;
220 }
Gus Smithe6a63872016-06-09 16:50:44 -0700221 return (Long) value;
Richard Uhler788b21e2015-08-31 10:33:56 -0700222 }
223
224 /**
Richard Uhlerb730b782015-07-15 16:01:58 -0700225 * Read the given field from the given instance.
226 * The field is assumed to be a byte[] field.
227 * Returns null if the field value is null, not a byte[] or could not be read.
228 */
229 private static byte[] getByteArrayField(Instance inst, String fieldName) {
230 Object value = getField(inst, fieldName);
231 if (!(value instanceof Instance)) {
232 return null;
233 }
Gus Smithe6a63872016-06-09 16:50:44 -0700234 return asByteArray((Instance) value);
Richard Uhlerb730b782015-07-15 16:01:58 -0700235 }
236
Richard Uhlerb730b782015-07-15 16:01:58 -0700237 // Return the bitmap instance associated with this object, or null if there
238 // is none. This works for android.graphics.Bitmap instances and their
239 // underlying Byte[] instances.
240 public static Instance getAssociatedBitmapInstance(Instance inst) {
241 ClassObj cls = inst.getClassObj();
242 if (cls == null) {
243 return null;
244 }
245
246 if ("android.graphics.Bitmap".equals(cls.getClassName())) {
247 return inst;
248 }
249
250 if (inst instanceof ArrayInstance) {
Gus Smithe6a63872016-06-09 16:50:44 -0700251 ArrayInstance array = (ArrayInstance) inst;
Richard Uhler6919a012015-12-15 09:15:00 -0800252 if (array.getArrayType() == Type.BYTE && inst.getHardReverseReferences().size() == 1) {
253 Instance ref = inst.getHardReverseReferences().get(0);
Richard Uhlerb730b782015-07-15 16:01:58 -0700254 ClassObj clsref = ref.getClassObj();
255 if (clsref != null && "android.graphics.Bitmap".equals(clsref.getClassName())) {
256 return ref;
257 }
258 }
259 }
260 return null;
261 }
262
Richard Uhlerb3577302015-10-29 13:02:42 -0700263 private static boolean isJavaLangRefReference(Instance inst) {
264 ClassObj cls = (inst == null) ? null : inst.getClassObj();
265 while (cls != null) {
266 if ("java.lang.ref.Reference".equals(cls.getClassName())) {
267 return true;
268 }
269 cls = cls.getSuperClassObj();
270 }
271 return false;
272 }
273
274 public static Instance getReferent(Instance inst) {
275 if (isJavaLangRefReference(inst)) {
276 return getRefField(inst, "referent");
277 }
278 return null;
279 }
280
Richard Uhlerb730b782015-07-15 16:01:58 -0700281 /**
282 * Assuming inst represents a DexCache object, return the dex location for
283 * that dex cache. Returns null if the given instance doesn't represent a
284 * DexCache object or the location could not be found.
Richard Uhler77ff54b2015-08-31 10:22:56 -0700285 * If maxChars is non-negative, the returned location is truncated to
286 * maxChars in length.
Richard Uhlerb730b782015-07-15 16:01:58 -0700287 */
Richard Uhler77ff54b2015-08-31 10:22:56 -0700288 public static String getDexCacheLocation(Instance inst, int maxChars) {
Richard Uhlerb730b782015-07-15 16:01:58 -0700289 if (isInstanceOfClass(inst, "java.lang.DexCache")) {
290 Instance location = getRefField(inst, "location");
291 if (location != null) {
Richard Uhler77ff54b2015-08-31 10:22:56 -0700292 return asString(location, maxChars);
Richard Uhlerb730b782015-07-15 16:01:58 -0700293 }
294 }
295 return null;
296 }
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800297
298 public static class NativeAllocation {
299 public long size;
300 public Heap heap;
301 public long pointer;
302 public Instance referent;
303
304 public NativeAllocation(long size, Heap heap, long pointer, Instance referent) {
305 this.size = size;
306 this.heap = heap;
307 this.pointer = pointer;
308 this.referent = referent;
309 }
310 }
311
312 /**
313 * Assuming inst represents a NativeAllocation, return information about the
314 * native allocation. Returns null if the given instance doesn't represent a
315 * native allocation.
316 */
317 public static NativeAllocation getNativeAllocation(Instance inst) {
318 if (!isInstanceOfClass(inst, "libcore.util.NativeAllocationRegistry$CleanerThunk")) {
319 return null;
320 }
321
322 Long pointer = InstanceUtils.getLongField(inst, "nativePtr", null);
323 if (pointer == null) {
324 return null;
325 }
326
327 // Search for the registry field of inst.
328 // Note: We know inst as an instance of ClassInstance because we already
329 // read the nativePtr field from it.
330 Instance registry = null;
Gus Smithe6a63872016-06-09 16:50:44 -0700331 for (ClassInstance.FieldValue field : ((ClassInstance) inst).getValues()) {
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800332 Object fieldValue = field.getValue();
333 if (fieldValue instanceof Instance) {
Gus Smithe6a63872016-06-09 16:50:44 -0700334 Instance fieldInst = (Instance) fieldValue;
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800335 if (isInstanceOfClass(fieldInst, "libcore.util.NativeAllocationRegistry")) {
336 registry = fieldInst;
337 break;
338 }
339 }
340 }
341
342 if (registry == null) {
343 return null;
344 }
345
346 Long size = InstanceUtils.getLongField(registry, "size", null);
347 if (size == null) {
348 return null;
349 }
350
351 Instance referent = null;
Richard Uhler6919a012015-12-15 09:15:00 -0800352 for (Instance ref : inst.getHardReverseReferences()) {
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800353 if (isInstanceOfClass(ref, "sun.misc.Cleaner")) {
354 referent = InstanceUtils.getReferent(ref);
355 if (referent != null) {
356 break;
357 }
358 }
359 }
360
361 if (referent == null) {
362 return null;
363 }
364 return new NativeAllocation(size, inst.getHeap(), pointer, referent);
365 }
Richard Uhler69286492016-09-20 10:41:47 +0100366
367 public static class PathElement {
368 public final Instance instance;
369 public final String field;
370 public boolean isDominator;
371
372 public PathElement(Instance instance, String field) {
373 this.instance = instance;
374 this.field = field;
375 this.isDominator = false;
376 }
377 }
378
379 /**
380 * Returns a sample path from a GC root to this instance.
381 * The given instance is included as the last element of the path with an
382 * empty field description.
383 */
384 public static List<PathElement> getPathFromGcRoot(Instance inst) {
385 List<PathElement> path = new ArrayList<PathElement>();
386
387 Instance dom = inst;
388 for (PathElement elem = new PathElement(inst, ""); elem != null;
389 elem = getNextPathElementToGcRoot(elem.instance)) {
390 if (elem.instance == dom) {
391 elem.isDominator = true;
392 dom = dom.getImmediateDominator();
393 }
394 path.add(elem);
395 }
396 Collections.reverse(path);
397 return path;
398 }
399
400 /**
401 * Returns the next instance to GC root from this object and a string
402 * description of which field of that object refers to the given instance.
403 * Returns null if the given instance has no next instance to the gc root.
404 */
405 private static PathElement getNextPathElementToGcRoot(Instance inst) {
406 Instance parent = inst.getNextInstanceToGcRoot();
407 if (parent == null || parent instanceof RootObj) {
408 return null;
409 }
410
411 // Search the parent for the reference to the child.
412 // TODO: This seems terribly inefficient. Can we use data structures to
413 // help us here?
414 String description = ".???";
415 if (parent instanceof ArrayInstance) {
416 ArrayInstance array = (ArrayInstance)parent;
417 Object[] values = array.getValues();
418 for (int i = 0; i < values.length; i++) {
419 if (values[i] instanceof Instance) {
420 Instance ref = (Instance)values[i];
421 if (ref.getId() == inst.getId()) {
422 description = String.format("[%d]", i);
423 break;
424 }
425 }
426 }
427 } else if (parent instanceof ClassObj) {
428 ClassObj cls = (ClassObj)parent;
429 for (Map.Entry<Field, Object> entries : cls.getStaticFieldValues().entrySet()) {
430 if (entries.getValue() instanceof Instance) {
431 Instance ref = (Instance)entries.getValue();
432 if (ref.getId() == inst.getId()) {
433 description = "." + entries.getKey().getName();
434 break;
435 }
436 }
437 }
438 } else if (parent instanceof ClassInstance) {
439 ClassInstance obj = (ClassInstance)parent;
440 for (ClassInstance.FieldValue fields : obj.getValues()) {
441 if (fields.getValue() instanceof Instance) {
442 Instance ref = (Instance)fields.getValue();
443 if (ref.getId() == inst.getId()) {
444 description = "." + fields.getField().getName();
445 break;
446 }
447 }
448 }
449 }
450 return new PathElement(parent, description);
451 }
Richard Uhlerb730b782015-07-15 16:01:58 -0700452}