blob: 10c109256f8a17a3fb443faf8e50c745748aacb2 [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;
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 /**
Richard Uhlerb730b782015-07-15 16:01:58 -070040 * 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 }
Richard Uhler788b21e2015-08-31 10:33:56 -070069
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;
Richard Uhlerb730b782015-07-15 16:01:58 -070095 }
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 /**
Richard Uhler788b21e2015-08-31 10:33:56 -0700186 * 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 /**
Richard Uhlerb730b782015-07-15 16:01:58 -0700195 * 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
Richard Uhlerb730b782015-07-15 16:01:58 -0700207 // 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}