blob: 3cdb40cc6d35e0a128dc4ed1ed19b1be105524da [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 Uhler1a5baaa2015-12-21 12:47:26 -080022import com.android.tools.perflib.heap.Heap;
Gus Smithe6a63872016-06-09 16:50:44 -070023import com.android.tools.perflib.heap.Instance;
Richard Uhlerb730b782015-07-15 16:01:58 -070024import com.android.tools.perflib.heap.Type;
Gus Smithe6a63872016-06-09 16:50:44 -070025
Richard Uhlerb730b782015-07-15 16:01:58 -070026import java.awt.image.BufferedImage;
27
28/**
29 * Utilities for extracting information from hprof instances.
30 */
31class InstanceUtils {
32 /**
33 * Returns true if the given instance is an instance of a class with the
34 * given name.
35 */
Richard Uhler1a5baaa2015-12-21 12:47:26 -080036 private static boolean isInstanceOfClass(Instance inst, String className) {
Richard Uhler35244722015-09-10 16:45:54 -070037 ClassObj cls = (inst == null) ? null : inst.getClassObj();
Richard Uhlerb730b782015-07-15 16:01:58 -070038 return (cls != null && className.equals(cls.getClassName()));
39 }
40
41 /**
Richard Uhlerb730b782015-07-15 16:01:58 -070042 * Read the byte[] value from an hprof Instance.
43 * Returns null if the instance is not a byte array.
44 */
45 private static byte[] asByteArray(Instance inst) {
Gus Smithe6a63872016-06-09 16:50:44 -070046 if (!(inst instanceof ArrayInstance)) {
Richard Uhlerb730b782015-07-15 16:01:58 -070047 return null;
48 }
49
Gus Smithe6a63872016-06-09 16:50:44 -070050 ArrayInstance array = (ArrayInstance) inst;
Richard Uhlerb730b782015-07-15 16:01:58 -070051 if (array.getArrayType() != Type.BYTE) {
52 return null;
53 }
54
55 Object[] objs = array.getValues();
56 byte[] bytes = new byte[objs.length];
57 for (int i = 0; i < objs.length; i++) {
Gus Smithe6a63872016-06-09 16:50:44 -070058 Byte b = (Byte) objs[i];
Richard Uhlerb730b782015-07-15 16:01:58 -070059 bytes[i] = b.byteValue();
60 }
61 return bytes;
62 }
63
64
Richard Uhler77ff54b2015-08-31 10:22:56 -070065 /**
66 * Read the string value from an hprof Instance.
67 * Returns null if the object can't be interpreted as a string.
68 */
Richard Uhlerb730b782015-07-15 16:01:58 -070069 public static String asString(Instance inst) {
Richard Uhler77ff54b2015-08-31 10:22:56 -070070 return asString(inst, -1);
71 }
72
73 /**
74 * Read the string value from an hprof Instance.
75 * Returns null if the object can't be interpreted as a string.
76 * The returned string is truncated to maxChars characters.
77 * If maxChars is negative, the returned string is not truncated.
78 */
79 public static String asString(Instance inst, int maxChars) {
Richard Uhlerc7f77122016-02-11 08:48:24 -080080 // The inst object could either be a java.lang.String or a char[]. If it
81 // is a char[], use that directly as the value, otherwise use the value
82 // field of the string object. The field accesses for count and offset
83 // later on will work okay regardless of what type the inst object is.
84 Object value = inst;
85 if (isInstanceOfClass(inst, "java.lang.String")) {
86 value = getField(inst, "value");
Richard Uhlerb730b782015-07-15 16:01:58 -070087 }
Richard Uhler788b21e2015-08-31 10:33:56 -070088
Richard Uhler788b21e2015-08-31 10:33:56 -070089 if (!(value instanceof ArrayInstance)) {
90 return null;
91 }
92
93 ArrayInstance chars = (ArrayInstance) value;
94 if (chars.getArrayType() != Type.CHAR) {
95 return null;
96 }
97
98 // TODO: When perflib provides a better way to get the length of the
99 // array, we should use that here.
100 int numChars = chars.getValues().length;
101 int count = getIntField(inst, "count", numChars);
Richard Uhler788b21e2015-08-31 10:33:56 -0700102 if (count == 0) {
103 return "";
104 }
Richard Uhler77ff54b2015-08-31 10:22:56 -0700105 if (0 <= maxChars && maxChars < count) {
106 count = maxChars;
107 }
Richard Uhler788b21e2015-08-31 10:33:56 -0700108
Richard Uhler77ff54b2015-08-31 10:22:56 -0700109 int offset = getIntField(inst, "offset", 0);
110 int end = offset + count - 1;
Richard Uhler788b21e2015-08-31 10:33:56 -0700111 if (offset >= 0 && offset < numChars && end >= 0 && end < numChars) {
112 return new String(chars.asCharArray(offset, count));
113 }
114 return null;
Richard Uhlerb730b782015-07-15 16:01:58 -0700115 }
116
117 /**
118 * Read the bitmap data for the given android.graphics.Bitmap object.
119 * Returns null if the object isn't for android.graphics.Bitmap or the
120 * bitmap data couldn't be read.
121 */
122 public static BufferedImage asBitmap(Instance inst) {
123 if (!isInstanceOfClass(inst, "android.graphics.Bitmap")) {
124 return null;
125 }
126
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800127 Integer width = getIntField(inst, "mWidth", null);
Richard Uhlerb730b782015-07-15 16:01:58 -0700128 if (width == null) {
129 return null;
130 }
131
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800132 Integer height = getIntField(inst, "mHeight", null);
Richard Uhlerb730b782015-07-15 16:01:58 -0700133 if (height == null) {
134 return null;
135 }
136
137 byte[] buffer = getByteArrayField(inst, "mBuffer");
138 if (buffer == null) {
139 return null;
140 }
141
142 // Convert the raw data to an image
143 // Convert BGRA to ABGR
144 int[] abgr = new int[height * width];
145 for (int i = 0; i < abgr.length; i++) {
146 abgr[i] = (
Gus Smithe6a63872016-06-09 16:50:44 -0700147 (((int) buffer[i * 4 + 3] & 0xFF) << 24)
148 + (((int) buffer[i * 4 + 0] & 0xFF) << 16)
149 + (((int) buffer[i * 4 + 1] & 0xFF) << 8)
150 + ((int) buffer[i * 4 + 2] & 0xFF));
Richard Uhlerb730b782015-07-15 16:01:58 -0700151 }
152
153 BufferedImage bitmap = new BufferedImage(
154 width, height, BufferedImage.TYPE_4BYTE_ABGR);
155 bitmap.setRGB(0, 0, width, height, abgr, 0, width);
156 return bitmap;
157 }
158
159 /**
160 * Read a field of an instance.
161 * Returns null if the field value is null or if the field couldn't be read.
162 */
Richard Uhler35244722015-09-10 16:45:54 -0700163 public static Object getField(Instance inst, String fieldName) {
Richard Uhlerb730b782015-07-15 16:01:58 -0700164 if (!(inst instanceof ClassInstance)) {
165 return null;
166 }
167
168 ClassInstance clsinst = (ClassInstance) inst;
169 Object value = null;
170 int count = 0;
171 for (ClassInstance.FieldValue field : clsinst.getValues()) {
172 if (fieldName.equals(field.getField().getName())) {
173 value = field.getValue();
174 count++;
175 }
176 }
177 return count == 1 ? value : null;
178 }
179
180 /**
181 * Read a reference field of an instance.
182 * Returns null if the field value is null, or if the field couldn't be read.
183 */
184 private static Instance getRefField(Instance inst, String fieldName) {
185 Object value = getField(inst, fieldName);
186 if (!(value instanceof Instance)) {
187 return null;
188 }
Gus Smithe6a63872016-06-09 16:50:44 -0700189 return (Instance) value;
Richard Uhlerb730b782015-07-15 16:01:58 -0700190 }
191
192 /**
193 * Read an int field of an instance.
194 * The field is assumed to be an int type.
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800195 * Returns <code>def</code> if the field value is not an int or could not be
196 * read.
Richard Uhlerb730b782015-07-15 16:01:58 -0700197 */
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800198 private static Integer getIntField(Instance inst, String fieldName, Integer def) {
Richard Uhlerb730b782015-07-15 16:01:58 -0700199 Object value = getField(inst, fieldName);
200 if (!(value instanceof Integer)) {
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800201 return def;
Richard Uhlerb730b782015-07-15 16:01:58 -0700202 }
Gus Smithe6a63872016-06-09 16:50:44 -0700203 return (Integer) value;
Richard Uhlerb730b782015-07-15 16:01:58 -0700204 }
205
206 /**
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800207 * Read a long field of an instance.
208 * The field is assumed to be a long type.
209 * Returns <code>def</code> if the field value is not an long or could not
210 * be read.
Richard Uhler788b21e2015-08-31 10:33:56 -0700211 */
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800212 private static Long getLongField(Instance inst, String fieldName, Long def) {
213 Object value = getField(inst, fieldName);
214 if (!(value instanceof Long)) {
215 return def;
216 }
Gus Smithe6a63872016-06-09 16:50:44 -0700217 return (Long) value;
Richard Uhler788b21e2015-08-31 10:33:56 -0700218 }
219
220 /**
Richard Uhlerb730b782015-07-15 16:01:58 -0700221 * Read the given field from the given instance.
222 * The field is assumed to be a byte[] field.
223 * Returns null if the field value is null, not a byte[] or could not be read.
224 */
225 private static byte[] getByteArrayField(Instance inst, String fieldName) {
226 Object value = getField(inst, fieldName);
227 if (!(value instanceof Instance)) {
228 return null;
229 }
Gus Smithe6a63872016-06-09 16:50:44 -0700230 return asByteArray((Instance) value);
Richard Uhlerb730b782015-07-15 16:01:58 -0700231 }
232
Richard Uhlerb730b782015-07-15 16:01:58 -0700233 // Return the bitmap instance associated with this object, or null if there
234 // is none. This works for android.graphics.Bitmap instances and their
235 // underlying Byte[] instances.
236 public static Instance getAssociatedBitmapInstance(Instance inst) {
237 ClassObj cls = inst.getClassObj();
238 if (cls == null) {
239 return null;
240 }
241
242 if ("android.graphics.Bitmap".equals(cls.getClassName())) {
243 return inst;
244 }
245
246 if (inst instanceof ArrayInstance) {
Gus Smithe6a63872016-06-09 16:50:44 -0700247 ArrayInstance array = (ArrayInstance) inst;
Richard Uhler6919a012015-12-15 09:15:00 -0800248 if (array.getArrayType() == Type.BYTE && inst.getHardReverseReferences().size() == 1) {
249 Instance ref = inst.getHardReverseReferences().get(0);
Richard Uhlerb730b782015-07-15 16:01:58 -0700250 ClassObj clsref = ref.getClassObj();
251 if (clsref != null && "android.graphics.Bitmap".equals(clsref.getClassName())) {
252 return ref;
253 }
254 }
255 }
256 return null;
257 }
258
Richard Uhlerb3577302015-10-29 13:02:42 -0700259 private static boolean isJavaLangRefReference(Instance inst) {
260 ClassObj cls = (inst == null) ? null : inst.getClassObj();
261 while (cls != null) {
262 if ("java.lang.ref.Reference".equals(cls.getClassName())) {
263 return true;
264 }
265 cls = cls.getSuperClassObj();
266 }
267 return false;
268 }
269
270 public static Instance getReferent(Instance inst) {
271 if (isJavaLangRefReference(inst)) {
272 return getRefField(inst, "referent");
273 }
274 return null;
275 }
276
Richard Uhlerb730b782015-07-15 16:01:58 -0700277 /**
278 * Assuming inst represents a DexCache object, return the dex location for
279 * that dex cache. Returns null if the given instance doesn't represent a
280 * DexCache object or the location could not be found.
Richard Uhler77ff54b2015-08-31 10:22:56 -0700281 * If maxChars is non-negative, the returned location is truncated to
282 * maxChars in length.
Richard Uhlerb730b782015-07-15 16:01:58 -0700283 */
Richard Uhler77ff54b2015-08-31 10:22:56 -0700284 public static String getDexCacheLocation(Instance inst, int maxChars) {
Richard Uhlerb730b782015-07-15 16:01:58 -0700285 if (isInstanceOfClass(inst, "java.lang.DexCache")) {
286 Instance location = getRefField(inst, "location");
287 if (location != null) {
Richard Uhler77ff54b2015-08-31 10:22:56 -0700288 return asString(location, maxChars);
Richard Uhlerb730b782015-07-15 16:01:58 -0700289 }
290 }
291 return null;
292 }
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800293
294 public static class NativeAllocation {
295 public long size;
296 public Heap heap;
297 public long pointer;
298 public Instance referent;
299
300 public NativeAllocation(long size, Heap heap, long pointer, Instance referent) {
301 this.size = size;
302 this.heap = heap;
303 this.pointer = pointer;
304 this.referent = referent;
305 }
306 }
307
308 /**
309 * Assuming inst represents a NativeAllocation, return information about the
310 * native allocation. Returns null if the given instance doesn't represent a
311 * native allocation.
312 */
313 public static NativeAllocation getNativeAllocation(Instance inst) {
314 if (!isInstanceOfClass(inst, "libcore.util.NativeAllocationRegistry$CleanerThunk")) {
315 return null;
316 }
317
318 Long pointer = InstanceUtils.getLongField(inst, "nativePtr", null);
319 if (pointer == null) {
320 return null;
321 }
322
323 // Search for the registry field of inst.
324 // Note: We know inst as an instance of ClassInstance because we already
325 // read the nativePtr field from it.
326 Instance registry = null;
Gus Smithe6a63872016-06-09 16:50:44 -0700327 for (ClassInstance.FieldValue field : ((ClassInstance) inst).getValues()) {
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800328 Object fieldValue = field.getValue();
329 if (fieldValue instanceof Instance) {
Gus Smithe6a63872016-06-09 16:50:44 -0700330 Instance fieldInst = (Instance) fieldValue;
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800331 if (isInstanceOfClass(fieldInst, "libcore.util.NativeAllocationRegistry")) {
332 registry = fieldInst;
333 break;
334 }
335 }
336 }
337
338 if (registry == null) {
339 return null;
340 }
341
342 Long size = InstanceUtils.getLongField(registry, "size", null);
343 if (size == null) {
344 return null;
345 }
346
347 Instance referent = null;
Richard Uhler6919a012015-12-15 09:15:00 -0800348 for (Instance ref : inst.getHardReverseReferences()) {
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800349 if (isInstanceOfClass(ref, "sun.misc.Cleaner")) {
350 referent = InstanceUtils.getReferent(ref);
351 if (referent != null) {
352 break;
353 }
354 }
355 }
356
357 if (referent == null) {
358 return null;
359 }
360 return new NativeAllocation(size, inst.getHeap(), pointer, referent);
361 }
Richard Uhlerb730b782015-07-15 16:01:58 -0700362}