blob: 8769d115ffcfdf68ab76b2f4e9790b67adaf577b [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
Richard Uhler702e6412016-08-03 09:26:35 -070098 int numChars = chars.getLength();
Richard Uhler788b21e2015-08-31 10:33:56 -070099 int count = getIntField(inst, "count", numChars);
Richard Uhler788b21e2015-08-31 10:33:56 -0700100 if (count == 0) {
101 return "";
102 }
Richard Uhler77ff54b2015-08-31 10:22:56 -0700103 if (0 <= maxChars && maxChars < count) {
104 count = maxChars;
105 }
Richard Uhler788b21e2015-08-31 10:33:56 -0700106
Richard Uhler77ff54b2015-08-31 10:22:56 -0700107 int offset = getIntField(inst, "offset", 0);
108 int end = offset + count - 1;
Richard Uhler788b21e2015-08-31 10:33:56 -0700109 if (offset >= 0 && offset < numChars && end >= 0 && end < numChars) {
110 return new String(chars.asCharArray(offset, count));
111 }
112 return null;
Richard Uhlerb730b782015-07-15 16:01:58 -0700113 }
114
115 /**
116 * Read the bitmap data for the given android.graphics.Bitmap object.
117 * Returns null if the object isn't for android.graphics.Bitmap or the
118 * bitmap data couldn't be read.
119 */
120 public static BufferedImage asBitmap(Instance inst) {
121 if (!isInstanceOfClass(inst, "android.graphics.Bitmap")) {
122 return null;
123 }
124
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800125 Integer width = getIntField(inst, "mWidth", null);
Richard Uhlerb730b782015-07-15 16:01:58 -0700126 if (width == null) {
127 return null;
128 }
129
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800130 Integer height = getIntField(inst, "mHeight", null);
Richard Uhlerb730b782015-07-15 16:01:58 -0700131 if (height == null) {
132 return null;
133 }
134
135 byte[] buffer = getByteArrayField(inst, "mBuffer");
136 if (buffer == null) {
137 return null;
138 }
139
140 // Convert the raw data to an image
141 // Convert BGRA to ABGR
142 int[] abgr = new int[height * width];
143 for (int i = 0; i < abgr.length; i++) {
144 abgr[i] = (
Gus Smithe6a63872016-06-09 16:50:44 -0700145 (((int) buffer[i * 4 + 3] & 0xFF) << 24)
146 + (((int) buffer[i * 4 + 0] & 0xFF) << 16)
147 + (((int) buffer[i * 4 + 1] & 0xFF) << 8)
148 + ((int) buffer[i * 4 + 2] & 0xFF));
Richard Uhlerb730b782015-07-15 16:01:58 -0700149 }
150
151 BufferedImage bitmap = new BufferedImage(
152 width, height, BufferedImage.TYPE_4BYTE_ABGR);
153 bitmap.setRGB(0, 0, width, height, abgr, 0, width);
154 return bitmap;
155 }
156
157 /**
158 * Read a field of an instance.
159 * Returns null if the field value is null or if the field couldn't be read.
160 */
Richard Uhler35244722015-09-10 16:45:54 -0700161 public static Object getField(Instance inst, String fieldName) {
Richard Uhlerb730b782015-07-15 16:01:58 -0700162 if (!(inst instanceof ClassInstance)) {
163 return null;
164 }
165
166 ClassInstance clsinst = (ClassInstance) inst;
167 Object value = null;
168 int count = 0;
169 for (ClassInstance.FieldValue field : clsinst.getValues()) {
170 if (fieldName.equals(field.getField().getName())) {
171 value = field.getValue();
172 count++;
173 }
174 }
175 return count == 1 ? value : null;
176 }
177
178 /**
179 * Read a reference field of an instance.
180 * Returns null if the field value is null, or if the field couldn't be read.
181 */
182 private static Instance getRefField(Instance inst, String fieldName) {
183 Object value = getField(inst, fieldName);
184 if (!(value instanceof Instance)) {
185 return null;
186 }
Gus Smithe6a63872016-06-09 16:50:44 -0700187 return (Instance) value;
Richard Uhlerb730b782015-07-15 16:01:58 -0700188 }
189
190 /**
191 * Read an int field of an instance.
192 * The field is assumed to be an int type.
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800193 * Returns <code>def</code> if the field value is not an int or could not be
194 * read.
Richard Uhlerb730b782015-07-15 16:01:58 -0700195 */
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800196 private static Integer getIntField(Instance inst, String fieldName, Integer def) {
Richard Uhlerb730b782015-07-15 16:01:58 -0700197 Object value = getField(inst, fieldName);
198 if (!(value instanceof Integer)) {
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800199 return def;
Richard Uhlerb730b782015-07-15 16:01:58 -0700200 }
Gus Smithe6a63872016-06-09 16:50:44 -0700201 return (Integer) value;
Richard Uhlerb730b782015-07-15 16:01:58 -0700202 }
203
204 /**
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800205 * Read a long field of an instance.
206 * The field is assumed to be a long type.
207 * Returns <code>def</code> if the field value is not an long or could not
208 * be read.
Richard Uhler788b21e2015-08-31 10:33:56 -0700209 */
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800210 private static Long getLongField(Instance inst, String fieldName, Long def) {
211 Object value = getField(inst, fieldName);
212 if (!(value instanceof Long)) {
213 return def;
214 }
Gus Smithe6a63872016-06-09 16:50:44 -0700215 return (Long) value;
Richard Uhler788b21e2015-08-31 10:33:56 -0700216 }
217
218 /**
Richard Uhlerb730b782015-07-15 16:01:58 -0700219 * Read the given field from the given instance.
220 * The field is assumed to be a byte[] field.
221 * Returns null if the field value is null, not a byte[] or could not be read.
222 */
223 private static byte[] getByteArrayField(Instance inst, String fieldName) {
224 Object value = getField(inst, fieldName);
225 if (!(value instanceof Instance)) {
226 return null;
227 }
Gus Smithe6a63872016-06-09 16:50:44 -0700228 return asByteArray((Instance) value);
Richard Uhlerb730b782015-07-15 16:01:58 -0700229 }
230
Richard Uhlerb730b782015-07-15 16:01:58 -0700231 // Return the bitmap instance associated with this object, or null if there
232 // is none. This works for android.graphics.Bitmap instances and their
233 // underlying Byte[] instances.
234 public static Instance getAssociatedBitmapInstance(Instance inst) {
235 ClassObj cls = inst.getClassObj();
236 if (cls == null) {
237 return null;
238 }
239
240 if ("android.graphics.Bitmap".equals(cls.getClassName())) {
241 return inst;
242 }
243
244 if (inst instanceof ArrayInstance) {
Gus Smithe6a63872016-06-09 16:50:44 -0700245 ArrayInstance array = (ArrayInstance) inst;
Richard Uhler6919a012015-12-15 09:15:00 -0800246 if (array.getArrayType() == Type.BYTE && inst.getHardReverseReferences().size() == 1) {
247 Instance ref = inst.getHardReverseReferences().get(0);
Richard Uhlerb730b782015-07-15 16:01:58 -0700248 ClassObj clsref = ref.getClassObj();
249 if (clsref != null && "android.graphics.Bitmap".equals(clsref.getClassName())) {
250 return ref;
251 }
252 }
253 }
254 return null;
255 }
256
Richard Uhlerb3577302015-10-29 13:02:42 -0700257 private static boolean isJavaLangRefReference(Instance inst) {
258 ClassObj cls = (inst == null) ? null : inst.getClassObj();
259 while (cls != null) {
260 if ("java.lang.ref.Reference".equals(cls.getClassName())) {
261 return true;
262 }
263 cls = cls.getSuperClassObj();
264 }
265 return false;
266 }
267
268 public static Instance getReferent(Instance inst) {
269 if (isJavaLangRefReference(inst)) {
270 return getRefField(inst, "referent");
271 }
272 return null;
273 }
274
Richard Uhlerb730b782015-07-15 16:01:58 -0700275 /**
276 * Assuming inst represents a DexCache object, return the dex location for
277 * that dex cache. Returns null if the given instance doesn't represent a
278 * DexCache object or the location could not be found.
Richard Uhler77ff54b2015-08-31 10:22:56 -0700279 * If maxChars is non-negative, the returned location is truncated to
280 * maxChars in length.
Richard Uhlerb730b782015-07-15 16:01:58 -0700281 */
Richard Uhler77ff54b2015-08-31 10:22:56 -0700282 public static String getDexCacheLocation(Instance inst, int maxChars) {
Richard Uhlerb730b782015-07-15 16:01:58 -0700283 if (isInstanceOfClass(inst, "java.lang.DexCache")) {
284 Instance location = getRefField(inst, "location");
285 if (location != null) {
Richard Uhler77ff54b2015-08-31 10:22:56 -0700286 return asString(location, maxChars);
Richard Uhlerb730b782015-07-15 16:01:58 -0700287 }
288 }
289 return null;
290 }
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800291
292 public static class NativeAllocation {
293 public long size;
294 public Heap heap;
295 public long pointer;
296 public Instance referent;
297
298 public NativeAllocation(long size, Heap heap, long pointer, Instance referent) {
299 this.size = size;
300 this.heap = heap;
301 this.pointer = pointer;
302 this.referent = referent;
303 }
304 }
305
306 /**
307 * Assuming inst represents a NativeAllocation, return information about the
308 * native allocation. Returns null if the given instance doesn't represent a
309 * native allocation.
310 */
311 public static NativeAllocation getNativeAllocation(Instance inst) {
312 if (!isInstanceOfClass(inst, "libcore.util.NativeAllocationRegistry$CleanerThunk")) {
313 return null;
314 }
315
316 Long pointer = InstanceUtils.getLongField(inst, "nativePtr", null);
317 if (pointer == null) {
318 return null;
319 }
320
321 // Search for the registry field of inst.
322 // Note: We know inst as an instance of ClassInstance because we already
323 // read the nativePtr field from it.
324 Instance registry = null;
Gus Smithe6a63872016-06-09 16:50:44 -0700325 for (ClassInstance.FieldValue field : ((ClassInstance) inst).getValues()) {
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800326 Object fieldValue = field.getValue();
327 if (fieldValue instanceof Instance) {
Gus Smithe6a63872016-06-09 16:50:44 -0700328 Instance fieldInst = (Instance) fieldValue;
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800329 if (isInstanceOfClass(fieldInst, "libcore.util.NativeAllocationRegistry")) {
330 registry = fieldInst;
331 break;
332 }
333 }
334 }
335
336 if (registry == null) {
337 return null;
338 }
339
340 Long size = InstanceUtils.getLongField(registry, "size", null);
341 if (size == null) {
342 return null;
343 }
344
345 Instance referent = null;
Richard Uhler6919a012015-12-15 09:15:00 -0800346 for (Instance ref : inst.getHardReverseReferences()) {
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800347 if (isInstanceOfClass(ref, "sun.misc.Cleaner")) {
348 referent = InstanceUtils.getReferent(ref);
349 if (referent != null) {
350 break;
351 }
352 }
353 }
354
355 if (referent == null) {
356 return null;
357 }
358 return new NativeAllocation(size, inst.getHeap(), pointer, referent);
359 }
Richard Uhlerb730b782015-07-15 16:01:58 -0700360}