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