diff options
Diffstat (limited to 'tools/ahat/src/heapdump/AhatClassInstance.java')
-rw-r--r-- | tools/ahat/src/heapdump/AhatClassInstance.java | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/tools/ahat/src/heapdump/AhatClassInstance.java b/tools/ahat/src/heapdump/AhatClassInstance.java new file mode 100644 index 0000000000..273530af64 --- /dev/null +++ b/tools/ahat/src/heapdump/AhatClassInstance.java @@ -0,0 +1,224 @@ +/* + * 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; + } +} |