blob: 8b7f9ea41a2ea6c0545c3a4f3b730e296201af5c [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 Uhlerb730b782015-07-15 16:01:58 -070079 if (!isInstanceOfClass(inst, "java.lang.String")) {
80 return null;
81 }
Richard Uhler788b21e2015-08-31 10:33:56 -070082
83 Object value = getField(inst, "value");
84 if (!(value instanceof ArrayInstance)) {
85 return null;
86 }
87
88 ArrayInstance chars = (ArrayInstance) value;
89 if (chars.getArrayType() != Type.CHAR) {
90 return null;
91 }
92
93 // TODO: When perflib provides a better way to get the length of the
94 // array, we should use that here.
95 int numChars = chars.getValues().length;
96 int count = getIntField(inst, "count", numChars);
Richard Uhler788b21e2015-08-31 10:33:56 -070097 if (count == 0) {
98 return "";
99 }
Richard Uhler77ff54b2015-08-31 10:22:56 -0700100 if (0 <= maxChars && maxChars < count) {
101 count = maxChars;
102 }
Richard Uhler788b21e2015-08-31 10:33:56 -0700103
Richard Uhler77ff54b2015-08-31 10:22:56 -0700104 int offset = getIntField(inst, "offset", 0);
105 int end = offset + count - 1;
Richard Uhler788b21e2015-08-31 10:33:56 -0700106 if (offset >= 0 && offset < numChars && end >= 0 && end < numChars) {
107 return new String(chars.asCharArray(offset, count));
108 }
109 return null;
Richard Uhlerb730b782015-07-15 16:01:58 -0700110 }
111
112 /**
113 * Read the bitmap data for the given android.graphics.Bitmap object.
114 * Returns null if the object isn't for android.graphics.Bitmap or the
115 * bitmap data couldn't be read.
116 */
117 public static BufferedImage asBitmap(Instance inst) {
118 if (!isInstanceOfClass(inst, "android.graphics.Bitmap")) {
119 return null;
120 }
121
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800122 Integer width = getIntField(inst, "mWidth", null);
Richard Uhlerb730b782015-07-15 16:01:58 -0700123 if (width == null) {
124 return null;
125 }
126
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800127 Integer height = getIntField(inst, "mHeight", null);
Richard Uhlerb730b782015-07-15 16:01:58 -0700128 if (height == null) {
129 return null;
130 }
131
132 byte[] buffer = getByteArrayField(inst, "mBuffer");
133 if (buffer == null) {
134 return null;
135 }
136
137 // Convert the raw data to an image
138 // Convert BGRA to ABGR
139 int[] abgr = new int[height * width];
140 for (int i = 0; i < abgr.length; i++) {
141 abgr[i] = (
142 (((int)buffer[i * 4 + 3] & 0xFF) << 24) +
143 (((int)buffer[i * 4 + 0] & 0xFF) << 16) +
144 (((int)buffer[i * 4 + 1] & 0xFF) << 8) +
145 ((int)buffer[i * 4 + 2] & 0xFF));
146 }
147
148 BufferedImage bitmap = new BufferedImage(
149 width, height, BufferedImage.TYPE_4BYTE_ABGR);
150 bitmap.setRGB(0, 0, width, height, abgr, 0, width);
151 return bitmap;
152 }
153
154 /**
155 * Read a field of an instance.
156 * Returns null if the field value is null or if the field couldn't be read.
157 */
Richard Uhler35244722015-09-10 16:45:54 -0700158 public static Object getField(Instance inst, String fieldName) {
Richard Uhlerb730b782015-07-15 16:01:58 -0700159 if (!(inst instanceof ClassInstance)) {
160 return null;
161 }
162
163 ClassInstance clsinst = (ClassInstance) inst;
164 Object value = null;
165 int count = 0;
166 for (ClassInstance.FieldValue field : clsinst.getValues()) {
167 if (fieldName.equals(field.getField().getName())) {
168 value = field.getValue();
169 count++;
170 }
171 }
172 return count == 1 ? value : null;
173 }
174
175 /**
176 * Read a reference field of an instance.
177 * Returns null if the field value is null, or if the field couldn't be read.
178 */
179 private static Instance getRefField(Instance inst, String fieldName) {
180 Object value = getField(inst, fieldName);
181 if (!(value instanceof Instance)) {
182 return null;
183 }
184 return (Instance)value;
185 }
186
187 /**
188 * Read an int field of an instance.
189 * The field is assumed to be an int type.
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800190 * Returns <code>def</code> if the field value is not an int or could not be
191 * read.
Richard Uhlerb730b782015-07-15 16:01:58 -0700192 */
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800193 private static Integer getIntField(Instance inst, String fieldName, Integer def) {
Richard Uhlerb730b782015-07-15 16:01:58 -0700194 Object value = getField(inst, fieldName);
195 if (!(value instanceof Integer)) {
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800196 return def;
Richard Uhlerb730b782015-07-15 16:01:58 -0700197 }
198 return (Integer)value;
199 }
200
201 /**
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800202 * Read a long field of an instance.
203 * The field is assumed to be a long type.
204 * Returns <code>def</code> if the field value is not an long or could not
205 * be read.
Richard Uhler788b21e2015-08-31 10:33:56 -0700206 */
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800207 private static Long getLongField(Instance inst, String fieldName, Long def) {
208 Object value = getField(inst, fieldName);
209 if (!(value instanceof Long)) {
210 return def;
211 }
212 return (Long)value;
Richard Uhler788b21e2015-08-31 10:33:56 -0700213 }
214
215 /**
Richard Uhlerb730b782015-07-15 16:01:58 -0700216 * Read the given field from the given instance.
217 * The field is assumed to be a byte[] field.
218 * Returns null if the field value is null, not a byte[] or could not be read.
219 */
220 private static byte[] getByteArrayField(Instance inst, String fieldName) {
221 Object value = getField(inst, fieldName);
222 if (!(value instanceof Instance)) {
223 return null;
224 }
225 return asByteArray((Instance)value);
226 }
227
Richard Uhlerb730b782015-07-15 16:01:58 -0700228 // Return the bitmap instance associated with this object, or null if there
229 // is none. This works for android.graphics.Bitmap instances and their
230 // underlying Byte[] instances.
231 public static Instance getAssociatedBitmapInstance(Instance inst) {
232 ClassObj cls = inst.getClassObj();
233 if (cls == null) {
234 return null;
235 }
236
237 if ("android.graphics.Bitmap".equals(cls.getClassName())) {
238 return inst;
239 }
240
241 if (inst instanceof ArrayInstance) {
242 ArrayInstance array = (ArrayInstance)inst;
243 if (array.getArrayType() == Type.BYTE && inst.getHardReferences().size() == 1) {
244 Instance ref = inst.getHardReferences().get(0);
245 ClassObj clsref = ref.getClassObj();
246 if (clsref != null && "android.graphics.Bitmap".equals(clsref.getClassName())) {
247 return ref;
248 }
249 }
250 }
251 return null;
252 }
253
Richard Uhlerb3577302015-10-29 13:02:42 -0700254 private static boolean isJavaLangRefReference(Instance inst) {
255 ClassObj cls = (inst == null) ? null : inst.getClassObj();
256 while (cls != null) {
257 if ("java.lang.ref.Reference".equals(cls.getClassName())) {
258 return true;
259 }
260 cls = cls.getSuperClassObj();
261 }
262 return false;
263 }
264
265 public static Instance getReferent(Instance inst) {
266 if (isJavaLangRefReference(inst)) {
267 return getRefField(inst, "referent");
268 }
269 return null;
270 }
271
Richard Uhlerb730b782015-07-15 16:01:58 -0700272 /**
273 * Assuming inst represents a DexCache object, return the dex location for
274 * that dex cache. Returns null if the given instance doesn't represent a
275 * DexCache object or the location could not be found.
Richard Uhler77ff54b2015-08-31 10:22:56 -0700276 * If maxChars is non-negative, the returned location is truncated to
277 * maxChars in length.
Richard Uhlerb730b782015-07-15 16:01:58 -0700278 */
Richard Uhler77ff54b2015-08-31 10:22:56 -0700279 public static String getDexCacheLocation(Instance inst, int maxChars) {
Richard Uhlerb730b782015-07-15 16:01:58 -0700280 if (isInstanceOfClass(inst, "java.lang.DexCache")) {
281 Instance location = getRefField(inst, "location");
282 if (location != null) {
Richard Uhler77ff54b2015-08-31 10:22:56 -0700283 return asString(location, maxChars);
Richard Uhlerb730b782015-07-15 16:01:58 -0700284 }
285 }
286 return null;
287 }
Richard Uhler1a5baaa2015-12-21 12:47:26 -0800288
289 public static class NativeAllocation {
290 public long size;
291 public Heap heap;
292 public long pointer;
293 public Instance referent;
294
295 public NativeAllocation(long size, Heap heap, long pointer, Instance referent) {
296 this.size = size;
297 this.heap = heap;
298 this.pointer = pointer;
299 this.referent = referent;
300 }
301 }
302
303 /**
304 * Assuming inst represents a NativeAllocation, return information about the
305 * native allocation. Returns null if the given instance doesn't represent a
306 * native allocation.
307 */
308 public static NativeAllocation getNativeAllocation(Instance inst) {
309 if (!isInstanceOfClass(inst, "libcore.util.NativeAllocationRegistry$CleanerThunk")) {
310 return null;
311 }
312
313 Long pointer = InstanceUtils.getLongField(inst, "nativePtr", null);
314 if (pointer == null) {
315 return null;
316 }
317
318 // Search for the registry field of inst.
319 // Note: We know inst as an instance of ClassInstance because we already
320 // read the nativePtr field from it.
321 Instance registry = null;
322 for (ClassInstance.FieldValue field : ((ClassInstance)inst).getValues()) {
323 Object fieldValue = field.getValue();
324 if (fieldValue instanceof Instance) {
325 Instance fieldInst = (Instance)fieldValue;
326 if (isInstanceOfClass(fieldInst, "libcore.util.NativeAllocationRegistry")) {
327 registry = fieldInst;
328 break;
329 }
330 }
331 }
332
333 if (registry == null) {
334 return null;
335 }
336
337 Long size = InstanceUtils.getLongField(registry, "size", null);
338 if (size == null) {
339 return null;
340 }
341
342 Instance referent = null;
343 for (Instance ref : inst.getHardReferences()) {
344 if (isInstanceOfClass(ref, "sun.misc.Cleaner")) {
345 referent = InstanceUtils.getReferent(ref);
346 if (referent != null) {
347 break;
348 }
349 }
350 }
351
352 if (referent == null) {
353 return null;
354 }
355 return new NativeAllocation(size, inst.getHeap(), pointer, referent);
356 }
Richard Uhlerb730b782015-07-15 16:01:58 -0700357}