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