blob: 8defba26473747676ef2bd9d95508dd7c9dde166 [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;
22import com.android.tools.perflib.heap.Instance;
Richard Uhler1a5baaa2015-12-21 12:47:26 -080023import com.android.tools.perflib.heap.Heap;
Richard Uhlerb730b782015-07-15 16:01:58 -070024import com.android.tools.perflib.heap.Type;
25import java.awt.image.BufferedImage;
26
27/**
28 * Utilities for extracting information from hprof instances.
29 */
30class InstanceUtils {
31 /**
32 * Returns true if the given instance is an instance of a class with the
33 * given name.
34 */
Richard Uhler1a5baaa2015-12-21 12:47:26 -080035 private static boolean isInstanceOfClass(Instance inst, String className) {
Richard Uhler35244722015-09-10 16:45:54 -070036 ClassObj cls = (inst == null) ? null : inst.getClassObj();
Richard Uhlerb730b782015-07-15 16:01:58 -070037 return (cls != null && className.equals(cls.getClassName()));
38 }
39
40 /**
Richard Uhlerb730b782015-07-15 16:01:58 -070041 * Read the byte[] value from an hprof Instance.
42 * Returns null if the instance is not a byte array.
43 */
44 private static byte[] asByteArray(Instance inst) {
45 if (! (inst instanceof ArrayInstance)) {
46 return null;
47 }
48
49 ArrayInstance array = (ArrayInstance)inst;
50 if (array.getArrayType() != Type.BYTE) {
51 return null;
52 }
53
54 Object[] objs = array.getValues();
55 byte[] bytes = new byte[objs.length];
56 for (int i = 0; i < objs.length; i++) {
57 Byte b = (Byte)objs[i];
58 bytes[i] = b.byteValue();
59 }
60 return bytes;
61 }
62
63
Richard Uhler77ff54b2015-08-31 10:22:56 -070064 /**
65 * Read the string value from an hprof Instance.
66 * Returns null if the object can't be interpreted as a string.
67 */
Richard Uhlerb730b782015-07-15 16:01:58 -070068 public static String asString(Instance inst) {
Richard Uhler77ff54b2015-08-31 10:22:56 -070069 return asString(inst, -1);
70 }
71
72 /**
73 * Read the string value from an hprof Instance.
74 * Returns null if the object can't be interpreted as a string.
75 * The returned string is truncated to maxChars characters.
76 * If maxChars is negative, the returned string is not truncated.
77 */
78 public static String asString(Instance inst, int maxChars) {
Richard Uhlerc7f77122016-02-11 08:48:24 -080079 // The inst object could either be a java.lang.String or a char[]. If it
80 // is a char[], use that directly as the value, otherwise use the value
81 // field of the string object. The field accesses for count and offset
82 // later on will work okay regardless of what type the inst object is.
83 Object value = inst;
84 if (isInstanceOfClass(inst, "java.lang.String")) {
85 value = getField(inst, "value");
Richard Uhlerb730b782015-07-15 16:01:58 -070086 }
Richard Uhler788b21e2015-08-31 10:33:56 -070087
Richard Uhler788b21e2015-08-31 10:33:56 -070088 if (!(value instanceof ArrayInstance)) {
89 return null;
90 }
91
92 ArrayInstance chars = (ArrayInstance) value;
93 if (chars.getArrayType() != Type.CHAR) {
94 return null;
95 }
96
97 // TODO: When perflib provides a better way to get the length of the
98 // array, we should use that here.
99 int numChars = chars.getValues().length;
100 int count = getIntField(inst, "count", numChars);
Richard Uhler788b21e2015-08-31 10:33:56 -0700101 if (count == 0) {
102 return "";
103 }
Richard Uhler77ff54b2015-08-31 10:22:56 -0700104 if (0 <= maxChars && maxChars < count) {
105 count = maxChars;
106 }
Richard Uhler788b21e2015-08-31 10:33:56 -0700107
Richard Uhler77ff54b2015-08-31 10:22:56 -0700108 int offset = getIntField(inst, "offset", 0);
109 int end = offset + count - 1;
Richard Uhler788b21e2015-08-31 10:33:56 -0700110 if (offset >= 0 && offset < numChars && end >= 0 && end < numChars) {
111 return new String(chars.asCharArray(offset, count));
112 }
113 return null;
Richard Uhlerb730b782015-07-15 16:01:58 -0700114 }
115
116 /**
117 * Read the bitmap data for the given android.graphics.Bitmap object.
118 * Returns null if the object isn't for android.graphics.Bitmap or the
119 * bitmap data couldn't be read.
120 */
121 public static BufferedImage asBitmap(Instance inst) {
122 if (!isInstanceOfClass(inst, "android.graphics.Bitmap")) {
123 return null;
124 }
125
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800126 Integer width = getIntField(inst, "mWidth", null);
Richard Uhlerb730b782015-07-15 16:01:58 -0700127 if (width == null) {
128 return null;
129 }
130
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800131 Integer height = getIntField(inst, "mHeight", null);
Richard Uhlerb730b782015-07-15 16:01:58 -0700132 if (height == null) {
133 return null;
134 }
135
136 byte[] buffer = getByteArrayField(inst, "mBuffer");
137 if (buffer == null) {
138 return null;
139 }
140
141 // Convert the raw data to an image
142 // Convert BGRA to ABGR
143 int[] abgr = new int[height * width];
144 for (int i = 0; i < abgr.length; i++) {
145 abgr[i] = (
146 (((int)buffer[i * 4 + 3] & 0xFF) << 24) +
147 (((int)buffer[i * 4 + 0] & 0xFF) << 16) +
148 (((int)buffer[i * 4 + 1] & 0xFF) << 8) +
149 ((int)buffer[i * 4 + 2] & 0xFF));
150 }
151
152 BufferedImage bitmap = new BufferedImage(
153 width, height, BufferedImage.TYPE_4BYTE_ABGR);
154 bitmap.setRGB(0, 0, width, height, abgr, 0, width);
155 return bitmap;
156 }
157
158 /**
159 * Read a field of an instance.
160 * Returns null if the field value is null or if the field couldn't be read.
161 */
Richard Uhler35244722015-09-10 16:45:54 -0700162 public static Object getField(Instance inst, String fieldName) {
Richard Uhlerb730b782015-07-15 16:01:58 -0700163 if (!(inst instanceof ClassInstance)) {
164 return null;
165 }
166
167 ClassInstance clsinst = (ClassInstance) inst;
168 Object value = null;
169 int count = 0;
170 for (ClassInstance.FieldValue field : clsinst.getValues()) {
171 if (fieldName.equals(field.getField().getName())) {
172 value = field.getValue();
173 count++;
174 }
175 }
176 return count == 1 ? value : null;
177 }
178
179 /**
180 * Read a reference field of an instance.
181 * Returns null if the field value is null, or if the field couldn't be read.
182 */
183 private static Instance getRefField(Instance inst, String fieldName) {
184 Object value = getField(inst, fieldName);
185 if (!(value instanceof Instance)) {
186 return null;
187 }
188 return (Instance)value;
189 }
190
191 /**
192 * Read an int field of an instance.
193 * The field is assumed to be an int type.
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800194 * Returns <code>def</code> if the field value is not an int or could not be
195 * read.
Richard Uhlerb730b782015-07-15 16:01:58 -0700196 */
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800197 private static Integer getIntField(Instance inst, String fieldName, Integer def) {
Richard Uhlerb730b782015-07-15 16:01:58 -0700198 Object value = getField(inst, fieldName);
199 if (!(value instanceof Integer)) {
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800200 return def;
Richard Uhlerb730b782015-07-15 16:01:58 -0700201 }
202 return (Integer)value;
203 }
204
205 /**
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800206 * Read a long field of an instance.
207 * The field is assumed to be a long type.
208 * Returns <code>def</code> if the field value is not an long or could not
209 * be read.
Richard Uhler788b21e2015-08-31 10:33:56 -0700210 */
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800211 private static Long getLongField(Instance inst, String fieldName, Long def) {
212 Object value = getField(inst, fieldName);
213 if (!(value instanceof Long)) {
214 return def;
215 }
216 return (Long)value;
Richard Uhler788b21e2015-08-31 10:33:56 -0700217 }
218
219 /**
Richard Uhlerb730b782015-07-15 16:01:58 -0700220 * Read the given field from the given instance.
221 * The field is assumed to be a byte[] field.
222 * Returns null if the field value is null, not a byte[] or could not be read.
223 */
224 private static byte[] getByteArrayField(Instance inst, String fieldName) {
225 Object value = getField(inst, fieldName);
226 if (!(value instanceof Instance)) {
227 return null;
228 }
229 return asByteArray((Instance)value);
230 }
231
Richard Uhlerb730b782015-07-15 16:01:58 -0700232 // Return the bitmap instance associated with this object, or null if there
233 // is none. This works for android.graphics.Bitmap instances and their
234 // underlying Byte[] instances.
235 public static Instance getAssociatedBitmapInstance(Instance inst) {
236 ClassObj cls = inst.getClassObj();
237 if (cls == null) {
238 return null;
239 }
240
241 if ("android.graphics.Bitmap".equals(cls.getClassName())) {
242 return inst;
243 }
244
245 if (inst instanceof ArrayInstance) {
246 ArrayInstance array = (ArrayInstance)inst;
Richard Uhler6919a012015-12-15 09:15:00 -0800247 if (array.getArrayType() == Type.BYTE && inst.getHardReverseReferences().size() == 1) {
248 Instance ref = inst.getHardReverseReferences().get(0);
Richard Uhlerb730b782015-07-15 16:01:58 -0700249 ClassObj clsref = ref.getClassObj();
250 if (clsref != null && "android.graphics.Bitmap".equals(clsref.getClassName())) {
251 return ref;
252 }
253 }
254 }
255 return null;
256 }
257
Richard Uhlerb3577302015-10-29 13:02:42 -0700258 private static boolean isJavaLangRefReference(Instance inst) {
259 ClassObj cls = (inst == null) ? null : inst.getClassObj();
260 while (cls != null) {
261 if ("java.lang.ref.Reference".equals(cls.getClassName())) {
262 return true;
263 }
264 cls = cls.getSuperClassObj();
265 }
266 return false;
267 }
268
269 public static Instance getReferent(Instance inst) {
270 if (isJavaLangRefReference(inst)) {
271 return getRefField(inst, "referent");
272 }
273 return null;
274 }
275
Richard Uhlerb730b782015-07-15 16:01:58 -0700276 /**
277 * Assuming inst represents a DexCache object, return the dex location for
278 * that dex cache. Returns null if the given instance doesn't represent a
279 * DexCache object or the location could not be found.
Richard Uhler77ff54b2015-08-31 10:22:56 -0700280 * If maxChars is non-negative, the returned location is truncated to
281 * maxChars in length.
Richard Uhlerb730b782015-07-15 16:01:58 -0700282 */
Richard Uhler77ff54b2015-08-31 10:22:56 -0700283 public static String getDexCacheLocation(Instance inst, int maxChars) {
Richard Uhlerb730b782015-07-15 16:01:58 -0700284 if (isInstanceOfClass(inst, "java.lang.DexCache")) {
285 Instance location = getRefField(inst, "location");
286 if (location != null) {
Richard Uhler77ff54b2015-08-31 10:22:56 -0700287 return asString(location, maxChars);
Richard Uhlerb730b782015-07-15 16:01:58 -0700288 }
289 }
290 return null;
291 }
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800292
293 public static class NativeAllocation {
294 public long size;
295 public Heap heap;
296 public long pointer;
297 public Instance referent;
298
299 public NativeAllocation(long size, Heap heap, long pointer, Instance referent) {
300 this.size = size;
301 this.heap = heap;
302 this.pointer = pointer;
303 this.referent = referent;
304 }
305 }
306
307 /**
308 * Assuming inst represents a NativeAllocation, return information about the
309 * native allocation. Returns null if the given instance doesn't represent a
310 * native allocation.
311 */
312 public static NativeAllocation getNativeAllocation(Instance inst) {
313 if (!isInstanceOfClass(inst, "libcore.util.NativeAllocationRegistry$CleanerThunk")) {
314 return null;
315 }
316
317 Long pointer = InstanceUtils.getLongField(inst, "nativePtr", null);
318 if (pointer == null) {
319 return null;
320 }
321
322 // Search for the registry field of inst.
323 // Note: We know inst as an instance of ClassInstance because we already
324 // read the nativePtr field from it.
325 Instance registry = null;
326 for (ClassInstance.FieldValue field : ((ClassInstance)inst).getValues()) {
327 Object fieldValue = field.getValue();
328 if (fieldValue instanceof Instance) {
329 Instance fieldInst = (Instance)fieldValue;
330 if (isInstanceOfClass(fieldInst, "libcore.util.NativeAllocationRegistry")) {
331 registry = fieldInst;
332 break;
333 }
334 }
335 }
336
337 if (registry == null) {
338 return null;
339 }
340
341 Long size = InstanceUtils.getLongField(registry, "size", null);
342 if (size == null) {
343 return null;
344 }
345
346 Instance referent = null;
Richard Uhler6919a012015-12-15 09:15:00 -0800347 for (Instance ref : inst.getHardReverseReferences()) {
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800348 if (isInstanceOfClass(ref, "sun.misc.Cleaner")) {
349 referent = InstanceUtils.getReferent(ref);
350 if (referent != null) {
351 break;
352 }
353 }
354 }
355
356 if (referent == null) {
357 return null;
358 }
359 return new NativeAllocation(size, inst.getHeap(), pointer, referent);
360 }
Richard Uhlerb730b782015-07-15 16:01:58 -0700361}