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.ArrayInstance;
     20 import com.android.tools.perflib.heap.Instance;
     21 import java.nio.charset.StandardCharsets;
     22 import java.util.AbstractList;
     23 import java.util.List;
     24 
     25 public class AhatArrayInstance extends AhatInstance {
     26   // To save space, we store byte, character, and object arrays directly as
     27   // byte, character, and AhatInstance arrays respectively. This is especially
     28   // important for large byte arrays, such as bitmaps. All other array types
     29   // are stored as an array of objects, though we could potentially save space
     30   // by specializing those too. mValues is a list view of the underlying
     31   // array.
     32   private List<Value> mValues;
     33   private byte[] mByteArray;    // null if not a byte array.
     34   private char[] mCharArray;    // null if not a char array.
     35 
     36   public AhatArrayInstance(long id) {
     37     super(id);
     38   }
     39 
     40   @Override void initialize(AhatSnapshot snapshot, Instance inst) {
     41     super.initialize(snapshot, inst);
     42 
     43     ArrayInstance array = (ArrayInstance)inst;
     44     switch (array.getArrayType()) {
     45       case OBJECT:
     46         Object[] objects = array.getValues();
     47         final AhatInstance[] insts = new AhatInstance[objects.length];
     48         for (int i = 0; i < objects.length; i++) {
     49           if (objects[i] != null) {
     50             Instance ref = (Instance)objects[i];
     51             insts[i] = snapshot.findInstance(ref.getId());
     52             if (ref.getNextInstanceToGcRoot() == inst) {
     53               String field = "[" + Integer.toString(i) + "]";
     54               insts[i].setNextInstanceToGcRoot(this, field);
     55             }
     56           }
     57         }
     58         mValues = new AbstractList<Value>() {
     59           @Override public int size() {
     60             return insts.length;
     61           }
     62 
     63           @Override public Value get(int index) {
     64             AhatInstance obj = insts[index];
     65             return obj == null ? null : new Value(insts[index]);
     66           }
     67         };
     68         break;
     69 
     70       case CHAR:
     71         final char[] chars = array.asCharArray(0, array.getLength());
     72         mCharArray = chars;
     73         mValues = new AbstractList<Value>() {
     74           @Override public int size() {
     75             return chars.length;
     76           }
     77 
     78           @Override public Value get(int index) {
     79             return new Value(chars[index]);
     80           }
     81         };
     82         break;
     83 
     84       case BYTE:
     85         final byte[] bytes = array.asRawByteArray(0, array.getLength());
     86         mByteArray = bytes;
     87         mValues = new AbstractList<Value>() {
     88           @Override public int size() {
     89             return bytes.length;
     90           }
     91 
     92           @Override public Value get(int index) {
     93             return new Value(bytes[index]);
     94           }
     95         };
     96         break;
     97 
     98       default:
     99         final Object[] values = array.getValues();
    100         mValues = new AbstractList<Value>() {
    101           @Override public int size() {
    102             return values.length;
    103           }
    104 
    105           @Override public Value get(int index) {
    106             Object obj = values[index];
    107             return obj == null ? null : new Value(obj);
    108           }
    109         };
    110         break;
    111     }
    112   }
    113 
    114   /**
    115    * Returns the length of the array.
    116    */
    117   public int getLength() {
    118     return mValues.size();
    119   }
    120 
    121   /**
    122    * Returns the array's values.
    123    */
    124   public List<Value> getValues() {
    125     return mValues;
    126   }
    127 
    128   /**
    129    * Returns the object at the given index of this array.
    130    */
    131   public Value getValue(int index) {
    132     return mValues.get(index);
    133   }
    134 
    135   @Override public boolean isArrayInstance() {
    136     return true;
    137   }
    138 
    139   @Override public AhatArrayInstance asArrayInstance() {
    140     return this;
    141   }
    142 
    143   @Override public String asString(int maxChars) {
    144     return asString(0, getLength(), maxChars);
    145   }
    146 
    147   /**
    148    * Returns the String value associated with this array.
    149    * Only char arrays are considered as having an associated String value.
    150    */
    151   String asString(int offset, int count, int maxChars) {
    152     if (mCharArray == null) {
    153       return null;
    154     }
    155 
    156     if (count == 0) {
    157       return "";
    158     }
    159     int numChars = mCharArray.length;
    160     if (0 <= maxChars && maxChars < count) {
    161       count = maxChars;
    162     }
    163 
    164     int end = offset + count - 1;
    165     if (offset >= 0 && offset < numChars && end >= 0 && end < numChars) {
    166       return new String(mCharArray, offset, count);
    167     }
    168     return null;
    169   }
    170 
    171   /**
    172    * Returns the ascii String value associated with this array.
    173    * Only byte arrays are considered as having an associated ascii String value.
    174    */
    175   String asAsciiString(int offset, int count, int maxChars) {
    176     if (mByteArray == null) {
    177       return null;
    178     }
    179 
    180     if (count == 0) {
    181       return "";
    182     }
    183     int numChars = mByteArray.length;
    184     if (0 <= maxChars && maxChars < count) {
    185       count = maxChars;
    186     }
    187 
    188     int end = offset + count - 1;
    189     if (offset >= 0 && offset < numChars && end >= 0 && end < numChars) {
    190       return new String(mByteArray, offset, count, StandardCharsets.US_ASCII);
    191     }
    192     return null;
    193   }
    194 
    195   /**
    196    * Returns the String value associated with this array. Byte arrays are
    197    * considered as ascii encoded strings.
    198    */
    199   String asMaybeCompressedString(int offset, int count, int maxChars) {
    200     String str = asString(offset, count, maxChars);
    201     if (str == null) {
    202       str = asAsciiString(offset, count, maxChars);
    203     }
    204     return str;
    205   }
    206 
    207   @Override public AhatInstance getAssociatedBitmapInstance() {
    208     if (mByteArray != null) {
    209       List<AhatInstance> refs = getHardReverseReferences();
    210       if (refs.size() == 1) {
    211         AhatInstance ref = refs.get(0);
    212         return ref.getAssociatedBitmapInstance();
    213       }
    214     }
    215     return null;
    216   }
    217 
    218   @Override public String toString() {
    219     String className = getClassName();
    220     if (className.endsWith("[]")) {
    221       className = className.substring(0, className.length() - 2);
    222     }
    223     return String.format("%s[%d]@%08x", className, mValues.size(), getId());
    224   }
    225 
    226   byte[] asByteArray() {
    227     return mByteArray;
    228   }
    229 }
    230