1 /* 2 * Copyright (C) 2016 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 17 package com.android.ahat.heapdump; 18 19 import com.android.tools.perflib.heap.ClassInstance; 20 import com.android.tools.perflib.heap.Instance; 21 import java.awt.image.BufferedImage; 22 import java.util.Arrays; 23 import java.util.List; 24 25 public class AhatClassInstance extends AhatInstance { 26 private FieldValue[] mFieldValues; 27 AhatClassInstance(long id)28 public AhatClassInstance(long id) { 29 super(id); 30 } 31 initialize(AhatSnapshot snapshot, Instance inst)32 @Override void initialize(AhatSnapshot snapshot, Instance inst) { 33 super.initialize(snapshot, inst); 34 35 ClassInstance classInst = (ClassInstance)inst; 36 List<ClassInstance.FieldValue> fieldValues = classInst.getValues(); 37 mFieldValues = new FieldValue[fieldValues.size()]; 38 for (int i = 0; i < mFieldValues.length; i++) { 39 ClassInstance.FieldValue field = fieldValues.get(i); 40 String name = field.getField().getName(); 41 String type = field.getField().getType().toString(); 42 Value value = snapshot.getValue(field.getValue()); 43 44 mFieldValues[i] = new FieldValue(name, type, value); 45 46 if (field.getValue() instanceof Instance) { 47 Instance ref = (Instance)field.getValue(); 48 if (ref.getNextInstanceToGcRoot() == inst) { 49 value.asAhatInstance().setNextInstanceToGcRoot(this, "." + name); 50 } 51 } 52 } 53 } 54 getField(String fieldName)55 @Override public Value getField(String fieldName) { 56 for (FieldValue field : mFieldValues) { 57 if (fieldName.equals(field.getName())) { 58 return field.getValue(); 59 } 60 } 61 return null; 62 } 63 getRefField(String fieldName)64 @Override public AhatInstance getRefField(String fieldName) { 65 Value value = getField(fieldName); 66 return value == null ? null : value.asAhatInstance(); 67 } 68 69 /** 70 * Read an int field of an instance. 71 * The field is assumed to be an int type. 72 * Returns <code>def</code> if the field value is not an int or could not be 73 * read. 74 */ getIntField(String fieldName, Integer def)75 private Integer getIntField(String fieldName, Integer def) { 76 Value value = getField(fieldName); 77 if (value == null || !value.isInteger()) { 78 return def; 79 } 80 return value.asInteger(); 81 } 82 83 /** 84 * Read a long field of this instance. 85 * The field is assumed to be a long type. 86 * Returns <code>def</code> if the field value is not an long or could not 87 * be read. 88 */ getLongField(String fieldName, Long def)89 private Long getLongField(String fieldName, Long def) { 90 Value value = getField(fieldName); 91 if (value == null || !value.isLong()) { 92 return def; 93 } 94 return value.asLong(); 95 } 96 97 /** 98 * Returns the list of class instance fields for this instance. 99 */ getInstanceFields()100 public List<FieldValue> getInstanceFields() { 101 return Arrays.asList(mFieldValues); 102 } 103 104 /** 105 * Returns true if this is an instance of a class with the given name. 106 */ isInstanceOfClass(String className)107 private boolean isInstanceOfClass(String className) { 108 AhatClassObj cls = getClassObj(); 109 while (cls != null) { 110 if (className.equals(cls.getName())) { 111 return true; 112 } 113 cls = cls.getSuperClassObj(); 114 } 115 return false; 116 } 117 asString(int maxChars)118 @Override public String asString(int maxChars) { 119 if (!isInstanceOfClass("java.lang.String")) { 120 return null; 121 } 122 123 Value value = getField("value"); 124 if (!value.isAhatInstance()) { 125 return null; 126 } 127 128 AhatInstance inst = value.asAhatInstance(); 129 if (inst.isArrayInstance()) { 130 AhatArrayInstance chars = inst.asArrayInstance(); 131 int numChars = chars.getLength(); 132 int count = getIntField("count", numChars); 133 int offset = getIntField("offset", 0); 134 return chars.asMaybeCompressedString(offset, count, maxChars); 135 } 136 return null; 137 } 138 getReferent()139 @Override public AhatInstance getReferent() { 140 if (isInstanceOfClass("java.lang.ref.Reference")) { 141 return getRefField("referent"); 142 } 143 return null; 144 } 145 getDexCacheLocation(int maxChars)146 @Override public String getDexCacheLocation(int maxChars) { 147 if (isInstanceOfClass("java.lang.DexCache")) { 148 AhatInstance location = getRefField("location"); 149 if (location != null) { 150 return location.asString(maxChars); 151 } 152 } 153 return null; 154 } 155 getAssociatedBitmapInstance()156 @Override public AhatInstance getAssociatedBitmapInstance() { 157 if (isInstanceOfClass("android.graphics.Bitmap")) { 158 return this; 159 } 160 return null; 161 } 162 isClassInstance()163 @Override public boolean isClassInstance() { 164 return true; 165 } 166 asClassInstance()167 @Override public AhatClassInstance asClassInstance() { 168 return this; 169 } 170 toString()171 @Override public String toString() { 172 return String.format("%s@%08x", getClassName(), getId()); 173 } 174 175 /** 176 * Read the given field from the given instance. 177 * The field is assumed to be a byte[] field. 178 * Returns null if the field value is null, not a byte[] or could not be read. 179 */ getByteArrayField(String fieldName)180 private byte[] getByteArrayField(String fieldName) { 181 Value value = getField(fieldName); 182 if (!value.isAhatInstance()) { 183 return null; 184 } 185 return value.asAhatInstance().asByteArray(); 186 } 187 asBitmap()188 public BufferedImage asBitmap() { 189 if (!isInstanceOfClass("android.graphics.Bitmap")) { 190 return null; 191 } 192 193 Integer width = getIntField("mWidth", null); 194 if (width == null) { 195 return null; 196 } 197 198 Integer height = getIntField("mHeight", null); 199 if (height == null) { 200 return null; 201 } 202 203 byte[] buffer = getByteArrayField("mBuffer"); 204 if (buffer == null) { 205 return null; 206 } 207 208 // Convert the raw data to an image 209 // Convert BGRA to ABGR 210 int[] abgr = new int[height * width]; 211 for (int i = 0; i < abgr.length; i++) { 212 abgr[i] = ( 213 (((int) buffer[i * 4 + 3] & 0xFF) << 24) 214 + (((int) buffer[i * 4 + 0] & 0xFF) << 16) 215 + (((int) buffer[i * 4 + 1] & 0xFF) << 8) 216 + ((int) buffer[i * 4 + 2] & 0xFF)); 217 } 218 219 BufferedImage bitmap = new BufferedImage( 220 width, height, BufferedImage.TYPE_4BYTE_ABGR); 221 bitmap.setRGB(0, 0, width, height, abgr, 0, width); 222 return bitmap; 223 } 224 } 225