• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
AhatArrayInstance(long id)36   public AhatArrayInstance(long id) {
37     super(id);
38   }
39 
initialize(AhatSnapshot snapshot, Instance inst)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    */
getLength()117   public int getLength() {
118     return mValues.size();
119   }
120 
121   /**
122    * Returns the array's values.
123    */
getValues()124   public List<Value> getValues() {
125     return mValues;
126   }
127 
128   /**
129    * Returns the object at the given index of this array.
130    */
getValue(int index)131   public Value getValue(int index) {
132     return mValues.get(index);
133   }
134 
isArrayInstance()135   @Override public boolean isArrayInstance() {
136     return true;
137   }
138 
asArrayInstance()139   @Override public AhatArrayInstance asArrayInstance() {
140     return this;
141   }
142 
asString(int maxChars)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    */
asString(int offset, int count, int maxChars)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    */
asAsciiString(int offset, int count, int maxChars)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    */
asMaybeCompressedString(int offset, int count, int maxChars)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 
getAssociatedBitmapInstance()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 
toString()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 
asByteArray()226   byte[] asByteArray() {
227     return mByteArray;
228   }
229 }
230