/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.ahat.heapdump; import com.android.tools.perflib.heap.ClassInstance; import com.android.tools.perflib.heap.Instance; import java.awt.image.BufferedImage; import java.util.Arrays; import java.util.List; public class AhatClassInstance extends AhatInstance { private FieldValue[] mFieldValues; public AhatClassInstance(long id) { super(id); } @Override void initialize(AhatSnapshot snapshot, Instance inst) { super.initialize(snapshot, inst); ClassInstance classInst = (ClassInstance)inst; List<ClassInstance.FieldValue> fieldValues = classInst.getValues(); mFieldValues = new FieldValue[fieldValues.size()]; for (int i = 0; i < mFieldValues.length; i++) { ClassInstance.FieldValue field = fieldValues.get(i); String name = field.getField().getName(); String type = field.getField().getType().toString(); Value value = snapshot.getValue(field.getValue()); mFieldValues[i] = new FieldValue(name, type, value); if (field.getValue() instanceof Instance) { Instance ref = (Instance)field.getValue(); if (ref.getNextInstanceToGcRoot() == inst) { value.asAhatInstance().setNextInstanceToGcRoot(this, "." + name); } } } } @Override public Value getField(String fieldName) { for (FieldValue field : mFieldValues) { if (fieldName.equals(field.getName())) { return field.getValue(); } } return null; } @Override public AhatInstance getRefField(String fieldName) { Value value = getField(fieldName); return value == null ? null : value.asAhatInstance(); } /** * Read an int field of an instance. * The field is assumed to be an int type. * Returns <code>def</code> if the field value is not an int or could not be * read. */ private Integer getIntField(String fieldName, Integer def) { Value value = getField(fieldName); if (value == null || !value.isInteger()) { return def; } return value.asInteger(); } /** * Read a long field of this instance. * The field is assumed to be a long type. * Returns <code>def</code> if the field value is not an long or could not * be read. */ private Long getLongField(String fieldName, Long def) { Value value = getField(fieldName); if (value == null || !value.isLong()) { return def; } return value.asLong(); } /** * Returns the list of class instance fields for this instance. */ public List<FieldValue> getInstanceFields() { return Arrays.asList(mFieldValues); } /** * Returns true if this is an instance of a class with the given name. */ private boolean isInstanceOfClass(String className) { AhatClassObj cls = getClassObj(); while (cls != null) { if (className.equals(cls.getName())) { return true; } cls = cls.getSuperClassObj(); } return false; } @Override public String asString(int maxChars) { if (!isInstanceOfClass("java.lang.String")) { return null; } Value value = getField("value"); if (!value.isAhatInstance()) { return null; } AhatInstance inst = value.asAhatInstance(); if (inst.isArrayInstance()) { AhatArrayInstance chars = inst.asArrayInstance(); int numChars = chars.getLength(); int count = getIntField("count", numChars); int offset = getIntField("offset", 0); return chars.asMaybeCompressedString(offset, count, maxChars); } return null; } @Override public AhatInstance getReferent() { if (isInstanceOfClass("java.lang.ref.Reference")) { return getRefField("referent"); } return null; } @Override public String getDexCacheLocation(int maxChars) { if (isInstanceOfClass("java.lang.DexCache")) { AhatInstance location = getRefField("location"); if (location != null) { return location.asString(maxChars); } } return null; } @Override public AhatInstance getAssociatedBitmapInstance() { if (isInstanceOfClass("android.graphics.Bitmap")) { return this; } return null; } @Override public boolean isClassInstance() { return true; } @Override public AhatClassInstance asClassInstance() { return this; } @Override public String toString() { return String.format("%s@%08x", getClassName(), getId()); } /** * Read the given field from the given instance. * The field is assumed to be a byte[] field. * Returns null if the field value is null, not a byte[] or could not be read. */ private byte[] getByteArrayField(String fieldName) { Value value = getField(fieldName); if (!value.isAhatInstance()) { return null; } return value.asAhatInstance().asByteArray(); } public BufferedImage asBitmap() { if (!isInstanceOfClass("android.graphics.Bitmap")) { return null; } Integer width = getIntField("mWidth", null); if (width == null) { return null; } Integer height = getIntField("mHeight", null); if (height == null) { return null; } byte[] buffer = getByteArrayField("mBuffer"); if (buffer == null) { return null; } // Convert the raw data to an image // Convert BGRA to ABGR int[] abgr = new int[height * width]; for (int i = 0; i < abgr.length; i++) { abgr[i] = ( (((int) buffer[i * 4 + 3] & 0xFF) << 24) + (((int) buffer[i * 4 + 0] & 0xFF) << 16) + (((int) buffer[i * 4 + 1] & 0xFF) << 8) + ((int) buffer[i * 4 + 2] & 0xFF)); } BufferedImage bitmap = new BufferedImage( width, height, BufferedImage.TYPE_4BYTE_ABGR); bitmap.setRGB(0, 0, width, height, abgr, 0, width); return bitmap; } }