InstanceUtils.java revision 788b21e81ce4a944766a973dbd38331200da1b4a
1/*
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;
23import com.android.tools.perflib.heap.Type;
24import java.awt.image.BufferedImage;
25
26/**
27 * Utilities for extracting information from hprof instances.
28 */
29class InstanceUtils {
30  /**
31   * Returns true if the given instance is an instance of a class with the
32   * given name.
33   */
34  public static boolean isInstanceOfClass(Instance inst, String className) {
35    ClassObj cls = inst.getClassObj();
36    return (cls != null && className.equals(cls.getClassName()));
37  }
38
39  /**
40   * Read the byte[] value from an hprof Instance.
41   * Returns null if the instance is not a byte array.
42   */
43  private static byte[] asByteArray(Instance inst) {
44    if (! (inst instanceof ArrayInstance)) {
45      return null;
46    }
47
48    ArrayInstance array = (ArrayInstance)inst;
49    if (array.getArrayType() != Type.BYTE) {
50      return null;
51    }
52
53    Object[] objs = array.getValues();
54    byte[] bytes = new byte[objs.length];
55    for (int i = 0; i < objs.length; i++) {
56      Byte b = (Byte)objs[i];
57      bytes[i] = b.byteValue();
58    }
59    return bytes;
60  }
61
62
63  // Read the string value from an hprof Instance.
64  // Returns null if the object can't be interpreted as a string.
65  public static String asString(Instance inst) {
66    if (!isInstanceOfClass(inst, "java.lang.String")) {
67      return null;
68    }
69
70    Object value = getField(inst, "value");
71    if (!(value instanceof ArrayInstance)) {
72      return null;
73    }
74
75    ArrayInstance chars = (ArrayInstance) value;
76    if (chars.getArrayType() != Type.CHAR) {
77      return null;
78    }
79
80    // TODO: When perflib provides a better way to get the length of the
81    // array, we should use that here.
82    int numChars = chars.getValues().length;
83    int count = getIntField(inst, "count", numChars);
84    int offset = getIntField(inst, "offset", 0);
85    int end = offset + count - 1;
86
87    if (count == 0) {
88      return "";
89    }
90
91    if (offset >= 0 && offset < numChars && end >= 0 && end < numChars) {
92      return new String(chars.asCharArray(offset, count));
93    }
94    return null;
95  }
96
97  /**
98   * Read the bitmap data for the given android.graphics.Bitmap object.
99   * Returns null if the object isn't for android.graphics.Bitmap or the
100   * bitmap data couldn't be read.
101   */
102  public static BufferedImage asBitmap(Instance inst) {
103    if (!isInstanceOfClass(inst, "android.graphics.Bitmap")) {
104      return null;
105    }
106
107    Integer width = getIntField(inst, "mWidth");
108    if (width == null) {
109      return null;
110    }
111
112    Integer height = getIntField(inst, "mHeight");
113    if (height == null) {
114      return null;
115    }
116
117    byte[] buffer = getByteArrayField(inst, "mBuffer");
118    if (buffer == null) {
119      return null;
120    }
121
122    // Convert the raw data to an image
123    // Convert BGRA to ABGR
124    int[] abgr = new int[height * width];
125    for (int i = 0; i < abgr.length; i++) {
126      abgr[i] = (
127          (((int)buffer[i * 4 + 3] & 0xFF) << 24) +
128          (((int)buffer[i * 4 + 0] & 0xFF) << 16) +
129          (((int)buffer[i * 4 + 1] & 0xFF) << 8) +
130          ((int)buffer[i * 4 + 2] & 0xFF));
131    }
132
133    BufferedImage bitmap = new BufferedImage(
134        width, height, BufferedImage.TYPE_4BYTE_ABGR);
135    bitmap.setRGB(0, 0, width, height, abgr, 0, width);
136    return bitmap;
137  }
138
139  /**
140   * Read a field of an instance.
141   * Returns null if the field value is null or if the field couldn't be read.
142   */
143  private static Object getField(Instance inst, String fieldName) {
144    if (!(inst instanceof ClassInstance)) {
145      return null;
146    }
147
148    ClassInstance clsinst = (ClassInstance) inst;
149    Object value = null;
150    int count = 0;
151    for (ClassInstance.FieldValue field : clsinst.getValues()) {
152      if (fieldName.equals(field.getField().getName())) {
153        value = field.getValue();
154        count++;
155      }
156    }
157    return count == 1 ? value : null;
158  }
159
160  /**
161   * Read a reference field of an instance.
162   * Returns null if the field value is null, or if the field couldn't be read.
163   */
164  private static Instance getRefField(Instance inst, String fieldName) {
165    Object value = getField(inst, fieldName);
166    if (!(value instanceof Instance)) {
167      return null;
168    }
169    return (Instance)value;
170  }
171
172  /**
173   * Read an int field of an instance.
174   * The field is assumed to be an int type.
175   * Returns null if the field value is not an int or could not be read.
176   */
177  private static Integer getIntField(Instance inst, String fieldName) {
178    Object value = getField(inst, fieldName);
179    if (!(value instanceof Integer)) {
180      return null;
181    }
182    return (Integer)value;
183  }
184
185  /**
186   * Read an int field of an instance, returning a default value if the field
187   * was not an int or could not be read.
188   */
189  private static int getIntField(Instance inst, String fieldName, int def) {
190    Integer value = getIntField(inst, fieldName);
191    return value == null ? def : value;
192  }
193
194  /**
195   * Read the given field from the given instance.
196   * The field is assumed to be a byte[] field.
197   * Returns null if the field value is null, not a byte[] or could not be read.
198   */
199  private static byte[] getByteArrayField(Instance inst, String fieldName) {
200    Object value = getField(inst, fieldName);
201    if (!(value instanceof Instance)) {
202      return null;
203    }
204    return asByteArray((Instance)value);
205  }
206
207  // Return the bitmap instance associated with this object, or null if there
208  // is none. This works for android.graphics.Bitmap instances and their
209  // underlying Byte[] instances.
210  public static Instance getAssociatedBitmapInstance(Instance inst) {
211    ClassObj cls = inst.getClassObj();
212    if (cls == null) {
213      return null;
214    }
215
216    if ("android.graphics.Bitmap".equals(cls.getClassName())) {
217      return inst;
218    }
219
220    if (inst instanceof ArrayInstance) {
221      ArrayInstance array = (ArrayInstance)inst;
222      if (array.getArrayType() == Type.BYTE && inst.getHardReferences().size() == 1) {
223        Instance ref = inst.getHardReferences().get(0);
224        ClassObj clsref = ref.getClassObj();
225        if (clsref != null && "android.graphics.Bitmap".equals(clsref.getClassName())) {
226          return ref;
227        }
228      }
229    }
230    return null;
231  }
232
233  /**
234   * Assuming inst represents a DexCache object, return the dex location for
235   * that dex cache. Returns null if the given instance doesn't represent a
236   * DexCache object or the location could not be found.
237   */
238  public static String getDexCacheLocation(Instance inst) {
239    if (isInstanceOfClass(inst, "java.lang.DexCache")) {
240      Instance location = getRefField(inst, "location");
241      if (location != null) {
242        return asString(location);
243      }
244    }
245    return null;
246  }
247}
248