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 17package com.android.ahat.heapdump; 18 19import com.android.tools.perflib.heap.ArrayInstance; 20import com.android.tools.perflib.heap.Instance; 21import java.nio.charset.StandardCharsets; 22import java.util.AbstractList; 23import java.util.List; 24 25public 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