summaryrefslogtreecommitdiff
path: root/tools/ahat/src/heapdump/AhatArrayInstance.java
diff options
context:
space:
mode:
Diffstat (limited to 'tools/ahat/src/heapdump/AhatArrayInstance.java')
-rw-r--r--tools/ahat/src/heapdump/AhatArrayInstance.java229
1 files changed, 229 insertions, 0 deletions
diff --git a/tools/ahat/src/heapdump/AhatArrayInstance.java b/tools/ahat/src/heapdump/AhatArrayInstance.java
new file mode 100644
index 0000000000..d88cf94075
--- /dev/null
+++ b/tools/ahat/src/heapdump/AhatArrayInstance.java
@@ -0,0 +1,229 @@
+/*
+ * 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.ArrayInstance;
+import com.android.tools.perflib.heap.Instance;
+import java.nio.charset.StandardCharsets;
+import java.util.AbstractList;
+import java.util.List;
+
+public class AhatArrayInstance extends AhatInstance {
+ // To save space, we store byte, character, and object arrays directly as
+ // byte, character, and AhatInstance arrays respectively. This is especially
+ // important for large byte arrays, such as bitmaps. All other array types
+ // are stored as an array of objects, though we could potentially save space
+ // by specializing those too. mValues is a list view of the underlying
+ // array.
+ private List<Value> mValues;
+ private byte[] mByteArray; // null if not a byte array.
+ private char[] mCharArray; // null if not a char array.
+
+ public AhatArrayInstance(long id) {
+ super(id);
+ }
+
+ @Override void initialize(AhatSnapshot snapshot, Instance inst) {
+ super.initialize(snapshot, inst);
+
+ ArrayInstance array = (ArrayInstance)inst;
+ switch (array.getArrayType()) {
+ case OBJECT:
+ Object[] objects = array.getValues();
+ final AhatInstance[] insts = new AhatInstance[objects.length];
+ for (int i = 0; i < objects.length; i++) {
+ if (objects[i] != null) {
+ Instance ref = (Instance)objects[i];
+ insts[i] = snapshot.findInstance(ref.getId());
+ if (ref.getNextInstanceToGcRoot() == inst) {
+ String field = "[" + Integer.toString(i) + "]";
+ insts[i].setNextInstanceToGcRoot(this, field);
+ }
+ }
+ }
+ mValues = new AbstractList<Value>() {
+ @Override public int size() {
+ return insts.length;
+ }
+
+ @Override public Value get(int index) {
+ AhatInstance obj = insts[index];
+ return obj == null ? null : new Value(insts[index]);
+ }
+ };
+ break;
+
+ case CHAR:
+ final char[] chars = array.asCharArray(0, array.getLength());
+ mCharArray = chars;
+ mValues = new AbstractList<Value>() {
+ @Override public int size() {
+ return chars.length;
+ }
+
+ @Override public Value get(int index) {
+ return new Value(chars[index]);
+ }
+ };
+ break;
+
+ case BYTE:
+ final byte[] bytes = array.asRawByteArray(0, array.getLength());
+ mByteArray = bytes;
+ mValues = new AbstractList<Value>() {
+ @Override public int size() {
+ return bytes.length;
+ }
+
+ @Override public Value get(int index) {
+ return new Value(bytes[index]);
+ }
+ };
+ break;
+
+ default:
+ final Object[] values = array.getValues();
+ mValues = new AbstractList<Value>() {
+ @Override public int size() {
+ return values.length;
+ }
+
+ @Override public Value get(int index) {
+ Object obj = values[index];
+ return obj == null ? null : new Value(obj);
+ }
+ };
+ break;
+ }
+ }
+
+ /**
+ * Returns the length of the array.
+ */
+ public int getLength() {
+ return mValues.size();
+ }
+
+ /**
+ * Returns the array's values.
+ */
+ public List<Value> getValues() {
+ return mValues;
+ }
+
+ /**
+ * Returns the object at the given index of this array.
+ */
+ public Value getValue(int index) {
+ return mValues.get(index);
+ }
+
+ @Override public boolean isArrayInstance() {
+ return true;
+ }
+
+ @Override public AhatArrayInstance asArrayInstance() {
+ return this;
+ }
+
+ @Override public String asString(int maxChars) {
+ return asString(0, getLength(), maxChars);
+ }
+
+ /**
+ * Returns the String value associated with this array.
+ * Only char arrays are considered as having an associated String value.
+ */
+ String asString(int offset, int count, int maxChars) {
+ if (mCharArray == null) {
+ return null;
+ }
+
+ if (count == 0) {
+ return "";
+ }
+ int numChars = mCharArray.length;
+ if (0 <= maxChars && maxChars < count) {
+ count = maxChars;
+ }
+
+ int end = offset + count - 1;
+ if (offset >= 0 && offset < numChars && end >= 0 && end < numChars) {
+ return new String(mCharArray, offset, count);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the ascii String value associated with this array.
+ * Only byte arrays are considered as having an associated ascii String value.
+ */
+ String asAsciiString(int offset, int count, int maxChars) {
+ if (mByteArray == null) {
+ return null;
+ }
+
+ if (count == 0) {
+ return "";
+ }
+ int numChars = mByteArray.length;
+ if (0 <= maxChars && maxChars < count) {
+ count = maxChars;
+ }
+
+ int end = offset + count - 1;
+ if (offset >= 0 && offset < numChars && end >= 0 && end < numChars) {
+ return new String(mByteArray, offset, count, StandardCharsets.US_ASCII);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the String value associated with this array. Byte arrays are
+ * considered as ascii encoded strings.
+ */
+ String asMaybeCompressedString(int offset, int count, int maxChars) {
+ String str = asString(offset, count, maxChars);
+ if (str == null) {
+ str = asAsciiString(offset, count, maxChars);
+ }
+ return str;
+ }
+
+ @Override public AhatInstance getAssociatedBitmapInstance() {
+ if (mByteArray != null) {
+ List<AhatInstance> refs = getHardReverseReferences();
+ if (refs.size() == 1) {
+ AhatInstance ref = refs.get(0);
+ return ref.getAssociatedBitmapInstance();
+ }
+ }
+ return null;
+ }
+
+ @Override public String toString() {
+ String className = getClassName();
+ if (className.endsWith("[]")) {
+ className = className.substring(0, className.length() - 2);
+ }
+ return String.format("%s[%d]@%08x", className, mValues.size(), getId());
+ }
+
+ byte[] asByteArray() {
+ return mByteArray;
+ }
+}