InstanceUtils.java revision b357730dd691e608f8a3d155330ab3763ce912cf
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 == null) ? null : 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 /** 64 * Read the string value from an hprof Instance. 65 * Returns null if the object can't be interpreted as a string. 66 */ 67 public static String asString(Instance inst) { 68 return asString(inst, -1); 69 } 70 71 /** 72 * Read the string value from an hprof Instance. 73 * Returns null if the object can't be interpreted as a string. 74 * The returned string is truncated to maxChars characters. 75 * If maxChars is negative, the returned string is not truncated. 76 */ 77 public static String asString(Instance inst, int maxChars) { 78 if (!isInstanceOfClass(inst, "java.lang.String")) { 79 return null; 80 } 81 82 Object value = getField(inst, "value"); 83 if (!(value instanceof ArrayInstance)) { 84 return null; 85 } 86 87 ArrayInstance chars = (ArrayInstance) value; 88 if (chars.getArrayType() != Type.CHAR) { 89 return null; 90 } 91 92 // TODO: When perflib provides a better way to get the length of the 93 // array, we should use that here. 94 int numChars = chars.getValues().length; 95 int count = getIntField(inst, "count", numChars); 96 if (count == 0) { 97 return ""; 98 } 99 if (0 <= maxChars && maxChars < count) { 100 count = maxChars; 101 } 102 103 int offset = getIntField(inst, "offset", 0); 104 int end = offset + count - 1; 105 if (offset >= 0 && offset < numChars && end >= 0 && end < numChars) { 106 return new String(chars.asCharArray(offset, count)); 107 } 108 return null; 109 } 110 111 /** 112 * Read the bitmap data for the given android.graphics.Bitmap object. 113 * Returns null if the object isn't for android.graphics.Bitmap or the 114 * bitmap data couldn't be read. 115 */ 116 public static BufferedImage asBitmap(Instance inst) { 117 if (!isInstanceOfClass(inst, "android.graphics.Bitmap")) { 118 return null; 119 } 120 121 Integer width = getIntField(inst, "mWidth"); 122 if (width == null) { 123 return null; 124 } 125 126 Integer height = getIntField(inst, "mHeight"); 127 if (height == null) { 128 return null; 129 } 130 131 byte[] buffer = getByteArrayField(inst, "mBuffer"); 132 if (buffer == null) { 133 return null; 134 } 135 136 // Convert the raw data to an image 137 // Convert BGRA to ABGR 138 int[] abgr = new int[height * width]; 139 for (int i = 0; i < abgr.length; i++) { 140 abgr[i] = ( 141 (((int)buffer[i * 4 + 3] & 0xFF) << 24) + 142 (((int)buffer[i * 4 + 0] & 0xFF) << 16) + 143 (((int)buffer[i * 4 + 1] & 0xFF) << 8) + 144 ((int)buffer[i * 4 + 2] & 0xFF)); 145 } 146 147 BufferedImage bitmap = new BufferedImage( 148 width, height, BufferedImage.TYPE_4BYTE_ABGR); 149 bitmap.setRGB(0, 0, width, height, abgr, 0, width); 150 return bitmap; 151 } 152 153 /** 154 * Read a field of an instance. 155 * Returns null if the field value is null or if the field couldn't be read. 156 */ 157 public static Object getField(Instance inst, String fieldName) { 158 if (!(inst instanceof ClassInstance)) { 159 return null; 160 } 161 162 ClassInstance clsinst = (ClassInstance) inst; 163 Object value = null; 164 int count = 0; 165 for (ClassInstance.FieldValue field : clsinst.getValues()) { 166 if (fieldName.equals(field.getField().getName())) { 167 value = field.getValue(); 168 count++; 169 } 170 } 171 return count == 1 ? value : null; 172 } 173 174 /** 175 * Read a reference field of an instance. 176 * Returns null if the field value is null, or if the field couldn't be read. 177 */ 178 private static Instance getRefField(Instance inst, String fieldName) { 179 Object value = getField(inst, fieldName); 180 if (!(value instanceof Instance)) { 181 return null; 182 } 183 return (Instance)value; 184 } 185 186 /** 187 * Read an int field of an instance. 188 * The field is assumed to be an int type. 189 * Returns null if the field value is not an int or could not be read. 190 */ 191 private static Integer getIntField(Instance inst, String fieldName) { 192 Object value = getField(inst, fieldName); 193 if (!(value instanceof Integer)) { 194 return null; 195 } 196 return (Integer)value; 197 } 198 199 /** 200 * Read an int field of an instance, returning a default value if the field 201 * was not an int or could not be read. 202 */ 203 private static int getIntField(Instance inst, String fieldName, int def) { 204 Integer value = getIntField(inst, fieldName); 205 return value == null ? def : value; 206 } 207 208 /** 209 * Read the given field from the given instance. 210 * The field is assumed to be a byte[] field. 211 * Returns null if the field value is null, not a byte[] or could not be read. 212 */ 213 private static byte[] getByteArrayField(Instance inst, String fieldName) { 214 Object value = getField(inst, fieldName); 215 if (!(value instanceof Instance)) { 216 return null; 217 } 218 return asByteArray((Instance)value); 219 } 220 221 // Return the bitmap instance associated with this object, or null if there 222 // is none. This works for android.graphics.Bitmap instances and their 223 // underlying Byte[] instances. 224 public static Instance getAssociatedBitmapInstance(Instance inst) { 225 ClassObj cls = inst.getClassObj(); 226 if (cls == null) { 227 return null; 228 } 229 230 if ("android.graphics.Bitmap".equals(cls.getClassName())) { 231 return inst; 232 } 233 234 if (inst instanceof ArrayInstance) { 235 ArrayInstance array = (ArrayInstance)inst; 236 if (array.getArrayType() == Type.BYTE && inst.getHardReferences().size() == 1) { 237 Instance ref = inst.getHardReferences().get(0); 238 ClassObj clsref = ref.getClassObj(); 239 if (clsref != null && "android.graphics.Bitmap".equals(clsref.getClassName())) { 240 return ref; 241 } 242 } 243 } 244 return null; 245 } 246 247 private static boolean isJavaLangRefReference(Instance inst) { 248 ClassObj cls = (inst == null) ? null : inst.getClassObj(); 249 while (cls != null) { 250 if ("java.lang.ref.Reference".equals(cls.getClassName())) { 251 return true; 252 } 253 cls = cls.getSuperClassObj(); 254 } 255 return false; 256 } 257 258 public static Instance getReferent(Instance inst) { 259 if (isJavaLangRefReference(inst)) { 260 return getRefField(inst, "referent"); 261 } 262 return null; 263 } 264 265 /** 266 * Assuming inst represents a DexCache object, return the dex location for 267 * that dex cache. Returns null if the given instance doesn't represent a 268 * DexCache object or the location could not be found. 269 * If maxChars is non-negative, the returned location is truncated to 270 * maxChars in length. 271 */ 272 public static String getDexCacheLocation(Instance inst, int maxChars) { 273 if (isInstanceOfClass(inst, "java.lang.DexCache")) { 274 Instance location = getRefField(inst, "location"); 275 if (location != null) { 276 return asString(location, maxChars); 277 } 278 } 279 return null; 280 } 281} 282