Home | History | Annotate | Download | only in heapdump
      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 
     28   public AhatClassInstance(long id) {
     29     super(id);
     30   }
     31 
     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 
     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 
     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    */
     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    */
     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    */
    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    */
    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 
    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 
    139   @Override public AhatInstance getReferent() {
    140     if (isInstanceOfClass("java.lang.ref.Reference")) {
    141       return getRefField("referent");
    142     }
    143     return null;
    144   }
    145 
    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 
    156   @Override public AhatInstance getAssociatedBitmapInstance() {
    157     if (isInstanceOfClass("android.graphics.Bitmap")) {
    158       return this;
    159     }
    160     return null;
    161   }
    162 
    163   @Override public boolean isClassInstance() {
    164     return true;
    165   }
    166 
    167   @Override public AhatClassInstance asClassInstance() {
    168     return this;
    169   }
    170 
    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    */
    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 
    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