summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Richard Uhler <ruhler@google.com> 2017-05-16 13:31:01 +0100
committer Richard Uhler <ruhler@google.com> 2017-05-25 16:48:41 +0100
commit3ee4bff4eb293363b8fa5b263db55af59508efaf (patch)
treed7ab44338513d1699156c83fbc14a42c5aca6153
parent9b5b23555d9f82e98cabd75195eb95a1030fe1a6 (diff)
Show RegisteredNativeAllocation sizes in ahat.
Bug: 36459946 Test: m ahat-test Change-Id: I45f6dc19cf1e339a80e0d93b6f4bc58a93e571c7
-rw-r--r--tools/ahat/README.txt1
-rw-r--r--tools/ahat/src/DocString.java17
-rw-r--r--tools/ahat/src/DominatedList.java2
-rw-r--r--tools/ahat/src/HeapTable.java20
-rw-r--r--tools/ahat/src/ObjectHandler.java17
-rw-r--r--tools/ahat/src/ObjectsHandler.java11
-rw-r--r--tools/ahat/src/OverviewHandler.java50
-rw-r--r--tools/ahat/src/SiteHandler.java13
-rw-r--r--tools/ahat/src/SitePrinter.java2
-rw-r--r--tools/ahat/src/SizeTable.java106
-rw-r--r--tools/ahat/src/heapdump/AhatHeap.java8
-rw-r--r--tools/ahat/src/heapdump/AhatInstance.java77
-rw-r--r--tools/ahat/src/heapdump/AhatPlaceHolderClassObj.java12
-rw-r--r--tools/ahat/src/heapdump/AhatPlaceHolderInstance.java12
-rw-r--r--tools/ahat/src/heapdump/AhatSnapshot.java17
-rw-r--r--tools/ahat/src/heapdump/Perflib.java91
-rw-r--r--tools/ahat/src/heapdump/Site.java38
-rw-r--r--tools/ahat/src/heapdump/Size.java89
-rw-r--r--tools/ahat/src/heapdump/Sort.java21
-rw-r--r--tools/ahat/test-dump/Main.java6
-rw-r--r--tools/ahat/test/InstanceTest.java5
-rw-r--r--tools/ahat/test/NativeAllocationTest.java37
-rw-r--r--tools/ahat/test/Tests.java1
23 files changed, 522 insertions, 131 deletions
diff --git a/tools/ahat/README.txt b/tools/ahat/README.txt
index 3049871d4c..475b7bb574 100644
--- a/tools/ahat/README.txt
+++ b/tools/ahat/README.txt
@@ -76,6 +76,7 @@ Things to move to perflib:
Release History:
1.2 Pending
+ Show registered native sizes of objects.
Simplify presentation of sample path from gc root.
1.1 Feb 21, 2017
diff --git a/tools/ahat/src/DocString.java b/tools/ahat/src/DocString.java
index c6303c8c35..7970bf8de4 100644
--- a/tools/ahat/src/DocString.java
+++ b/tools/ahat/src/DocString.java
@@ -126,6 +126,23 @@ class DocString {
}
/**
+ * Standard formatted DocString for describing a size.
+ *
+ * Nothing is printed for a size of zero.
+ * Set isPlaceHolder to true to indicate that the size field corresponds to
+ * for a place holder object that should be annotated specially.
+ */
+ public static DocString size(long size, boolean isPlaceHolder) {
+ DocString string = new DocString();
+ if (isPlaceHolder) {
+ string.append(DocString.removed("del"));
+ } else if (size != 0) {
+ string.appendFormat("%,14d", size);
+ }
+ return string;
+ }
+
+ /**
* Standard formatted DocString for describing a change in size relative to
* a baseline.
* @param noCurrent - whether no current object exists.
diff --git a/tools/ahat/src/DominatedList.java b/tools/ahat/src/DominatedList.java
index f73e3ca027..75133b2184 100644
--- a/tools/ahat/src/DominatedList.java
+++ b/tools/ahat/src/DominatedList.java
@@ -55,7 +55,7 @@ class DominatedList {
@Override
public long getSize(AhatInstance element, AhatHeap heap) {
- return element.getRetainedSize(heap);
+ return element.getRetainedSize(heap).getSize();
}
@Override
diff --git a/tools/ahat/src/HeapTable.java b/tools/ahat/src/HeapTable.java
index 9abbe4a4ed..b04f2aebf7 100644
--- a/tools/ahat/src/HeapTable.java
+++ b/tools/ahat/src/HeapTable.java
@@ -45,16 +45,6 @@ class HeapTable {
List<ValueConfig<T>> getValueConfigs();
}
- private static DocString sizeString(long size, boolean isPlaceHolder) {
- DocString string = new DocString();
- if (isPlaceHolder) {
- string.append(DocString.removed("del"));
- } else if (size != 0) {
- string.appendFormat("%,14d", size);
- }
- return string;
- }
-
/**
* Render the table to the given document.
* @param query - The page query.
@@ -100,10 +90,10 @@ class HeapTable {
long basesize = config.getSize(base, heap.getBaseline());
total += size;
basetotal += basesize;
- vals.add(sizeString(size, elem.isPlaceHolder()));
+ vals.add(DocString.size(size, elem.isPlaceHolder()));
vals.add(DocString.delta(elem.isPlaceHolder(), base.isPlaceHolder(), size, basesize));
}
- vals.add(sizeString(total, elem.isPlaceHolder()));
+ vals.add(DocString.size(total, elem.isPlaceHolder()));
vals.add(DocString.delta(elem.isPlaceHolder(), base.isPlaceHolder(), total, basetotal));
for (ValueConfig<T> value : values) {
@@ -140,10 +130,10 @@ class HeapTable {
long basesize = basesummary.get(heap);
total += size;
basetotal += basesize;
- vals.add(sizeString(size, false));
+ vals.add(DocString.size(size, false));
vals.add(DocString.delta(false, false, size, basesize));
}
- vals.add(sizeString(total, false));
+ vals.add(DocString.size(total, false));
vals.add(DocString.delta(false, false, total, basetotal));
for (ValueConfig<T> value : values) {
@@ -159,7 +149,7 @@ class HeapTable {
public static <T extends Diffable<T>> boolean hasNonZeroEntry(AhatHeap heap,
TableConfig<T> config, List<T> elements) {
AhatHeap baseheap = heap.getBaseline();
- if (heap.getSize() > 0 || baseheap.getSize() > 0) {
+ if (!heap.getSize().isZero() || !baseheap.getSize().isZero()) {
for (T element : elements) {
if (config.getSize(element, heap) > 0 ||
config.getSize(element.getBaseline(), baseheap) > 0) {
diff --git a/tools/ahat/src/ObjectHandler.java b/tools/ahat/src/ObjectHandler.java
index b1d7904ef6..d6f1faa3c3 100644
--- a/tools/ahat/src/ObjectHandler.java
+++ b/tools/ahat/src/ObjectHandler.java
@@ -70,16 +70,6 @@ class ObjectHandler implements AhatHandler {
doc.descriptions();
doc.description(DocString.text("Class"), Summarizer.summarize(cls));
- DocString sizeDescription = DocString.format("%,14d ", inst.getSize());
- sizeDescription.appendDelta(false, base.isPlaceHolder(),
- inst.getSize(), base.getSize());
- doc.description(DocString.text("Size"), sizeDescription);
-
- DocString rsizeDescription = DocString.format("%,14d ", inst.getTotalRetainedSize());
- rsizeDescription.appendDelta(false, base.isPlaceHolder(),
- inst.getTotalRetainedSize(), base.getTotalRetainedSize());
- doc.description(DocString.text("Retained Size"), rsizeDescription);
-
doc.description(DocString.text("Heap"), DocString.text(inst.getHeap().getName()));
Collection<String> rootTypes = inst.getRootTypes();
@@ -96,6 +86,13 @@ class ObjectHandler implements AhatHandler {
doc.end();
+ doc.section("Object Size");
+ SizeTable.table(doc, new Column(""), inst != base && !base.isPlaceHolder());
+ SizeTable.row(doc, DocString.text("Shallow"), inst.getSize(), base.getSize());
+ SizeTable.row(doc, DocString.text("Retained"),
+ inst.getTotalRetainedSize(), base.getTotalRetainedSize());
+ SizeTable.end(doc);
+
printBitmap(doc, inst);
if (inst.isClassInstance()) {
printClassInstanceFields(doc, query, inst.asClassInstance());
diff --git a/tools/ahat/src/ObjectsHandler.java b/tools/ahat/src/ObjectsHandler.java
index 3062d23b53..86d48f1702 100644
--- a/tools/ahat/src/ObjectsHandler.java
+++ b/tools/ahat/src/ObjectsHandler.java
@@ -54,23 +54,18 @@ class ObjectsHandler implements AhatHandler {
doc.title("Objects");
- doc.table(
- new Column("Size", Column.Align.RIGHT),
- new Column("Δ", Column.Align.RIGHT, mSnapshot.isDiffed()),
+ SizeTable.table(doc, mSnapshot.isDiffed(),
new Column("Heap"),
new Column("Object"));
SubsetSelector<AhatInstance> selector = new SubsetSelector(query, OBJECTS_ID, insts);
for (AhatInstance inst : selector.selected()) {
AhatInstance base = inst.getBaseline();
- doc.row(
- DocString.format("%,14d", inst.getSize()),
- DocString.delta(inst.isPlaceHolder(), base.isPlaceHolder(),
- inst.getSize(), base.getSize()),
+ SizeTable.row(doc, inst.getSize(), base.getSize(),
DocString.text(inst.getHeap().getName()),
Summarizer.summarize(inst));
}
- doc.end();
+ SizeTable.end(doc);
selector.render(doc);
}
}
diff --git a/tools/ahat/src/OverviewHandler.java b/tools/ahat/src/OverviewHandler.java
index ea305c4e94..c9f84259a9 100644
--- a/tools/ahat/src/OverviewHandler.java
+++ b/tools/ahat/src/OverviewHandler.java
@@ -18,16 +18,12 @@ package com.android.ahat;
import com.android.ahat.heapdump.AhatHeap;
import com.android.ahat.heapdump.AhatSnapshot;
-import com.android.ahat.heapdump.Diffable;
+import com.android.ahat.heapdump.Size;
import java.io.File;
import java.io.IOException;
-import java.util.Collections;
-import java.util.List;
class OverviewHandler implements AhatHandler {
- private static final String OVERVIEW_ID = "overview";
-
private AhatSnapshot mSnapshot;
private File mHprof;
private File mBaseHprof;
@@ -53,39 +49,27 @@ class OverviewHandler implements AhatHandler {
}
doc.end();
- doc.section("Heap Sizes");
- printHeapSizes(doc, query);
+ doc.section("Bytes Retained by Heap");
+ printHeapSizes(doc);
doc.big(Menu.getMenu());
}
- private static class TableElem implements Diffable<TableElem> {
- @Override public TableElem getBaseline() {
- return this;
- }
-
- @Override public boolean isPlaceHolder() {
- return false;
- }
- }
-
- private void printHeapSizes(Doc doc, Query query) {
- List<TableElem> dummy = Collections.singletonList(new TableElem());
-
- HeapTable.TableConfig<TableElem> table = new HeapTable.TableConfig<TableElem>() {
- public String getHeapsDescription() {
- return "Bytes Retained by Heap";
- }
-
- public long getSize(TableElem element, AhatHeap heap) {
- return heap.getSize();
+ private void printHeapSizes(Doc doc) {
+ SizeTable.table(doc, new Column("Heap"), mSnapshot.isDiffed());
+ Size totalSize = Size.ZERO;
+ Size totalBase = Size.ZERO;
+ for (AhatHeap heap : mSnapshot.getHeaps()) {
+ Size size = heap.getSize();
+ Size base = heap.getBaseline().getSize();
+ if (!size.isZero() || !base.isZero()) {
+ SizeTable.row(doc, DocString.text(heap.getName()), size, base);
+ totalSize = totalSize.plus(size);
+ totalBase = totalBase.plus(base);
}
-
- public List<HeapTable.ValueConfig<TableElem>> getValueConfigs() {
- return Collections.emptyList();
- }
- };
- HeapTable.render(doc, query, OVERVIEW_ID, table, mSnapshot, dummy);
+ }
+ SizeTable.row(doc, DocString.text("Total"), totalSize, totalBase);
+ SizeTable.end(doc);
}
}
diff --git a/tools/ahat/src/SiteHandler.java b/tools/ahat/src/SiteHandler.java
index febf1713fb..7a831d3018 100644
--- a/tools/ahat/src/SiteHandler.java
+++ b/tools/ahat/src/SiteHandler.java
@@ -60,7 +60,7 @@ class SiteHandler implements AhatHandler {
}
public long getSize(Site element, AhatHeap heap) {
- return element.getSize(heap);
+ return element.getSize(heap).getSize();
}
public List<HeapTable.ValueConfig<Site>> getValueConfigs() {
@@ -80,10 +80,7 @@ class SiteHandler implements AhatHandler {
}
doc.section("Objects Allocated");
-
- doc.table(
- new Column("Reachable Bytes Allocated", Column.Align.RIGHT),
- new Column("Δ", Column.Align.RIGHT, mSnapshot.isDiffed()),
+ SizeTable.table(doc, mSnapshot.isDiffed(),
new Column("Instances", Column.Align.RIGHT),
new Column("Δ", Column.Align.RIGHT, mSnapshot.isDiffed()),
new Column("Heap"),
@@ -100,9 +97,7 @@ class SiteHandler implements AhatHandler {
for (Site.ObjectsInfo info : selector.selected()) {
Site.ObjectsInfo baseinfo = info.getBaseline();
String className = info.getClassName();
- doc.row(
- DocString.format("%,14d", info.numBytes),
- DocString.delta(false, false, info.numBytes, baseinfo.numBytes),
+ SizeTable.row(doc, info.numBytes, baseinfo.numBytes,
DocString.link(
DocString.formattedUri("objects?id=%d&depth=%d&heap=%s&class=%s",
site.getId(), site.getDepth(), info.heap.getName(), className),
@@ -111,7 +106,7 @@ class SiteHandler implements AhatHandler {
DocString.text(info.heap.getName()),
Summarizer.summarize(info.classObj));
}
- doc.end();
+ SizeTable.end(doc);
selector.render(doc);
}
}
diff --git a/tools/ahat/src/SitePrinter.java b/tools/ahat/src/SitePrinter.java
index 21ca2deda4..32037f4414 100644
--- a/tools/ahat/src/SitePrinter.java
+++ b/tools/ahat/src/SitePrinter.java
@@ -38,7 +38,7 @@ class SitePrinter {
}
public long getSize(Site element, AhatHeap heap) {
- return element.getSize(heap);
+ return element.getSize(heap).getSize();
}
public List<HeapTable.ValueConfig<Site>> getValueConfigs() {
diff --git a/tools/ahat/src/SizeTable.java b/tools/ahat/src/SizeTable.java
new file mode 100644
index 0000000000..46e395669f
--- /dev/null
+++ b/tools/ahat/src/SizeTable.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 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;
+
+import com.android.ahat.heapdump.Size;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Class for rendering a table that includes all categories of Size.
+ * Two table formats are supported, one where a custom left column can be
+ * added before the size columns:
+ * |left column|Java Size|Native Size|...|Total Size|custom columns...|
+ *
+ * The other without the custom left column:
+ * |Java Size|Native Size|...|Total Size|custom columns...|
+ */
+class SizeTable {
+ /**
+ * Start a size table with a custom left column.
+ *
+ * |left column|Java Size|Native Size|...|Total Size|custom columns...|
+ *
+ * This should be followed by calls to the 'row' method to fill in the table
+ * contents and the 'end' method to end the table.
+ *
+ * Set showDiff to true if size diffs should be shown.
+ */
+ static void table(Doc doc, Column left, boolean showDiff, Column... columns) {
+ List<Column> cols = new ArrayList<Column>();
+ cols.add(left);
+ cols.add(new Column("Java Size", Column.Align.RIGHT));
+ cols.add(new Column("Δ", Column.Align.RIGHT, showDiff));
+ cols.add(new Column("Registered Native Size", Column.Align.RIGHT));
+ cols.add(new Column("Δ", Column.Align.RIGHT, showDiff));
+ cols.add(new Column("Total Size", Column.Align.RIGHT));
+ cols.add(new Column("Δ", Column.Align.RIGHT, showDiff));
+ cols.addAll(Arrays.asList(columns));
+ doc.table(cols.toArray(new Column[cols.size()]));
+ }
+
+ /**
+ * Add a row to the currently active size table with custom left column.
+ * The number of values must match the number of columns provided for the
+ * currently active table.
+ */
+ static void row(Doc doc, DocString left, Size size, Size base, DocString... values) {
+ List<DocString> vals = new ArrayList<DocString>();
+ vals.add(left);
+ vals.add(DocString.size(size.getJavaSize(), false));
+ vals.add(DocString.delta(false, false, size.getJavaSize(), base.getJavaSize()));
+ vals.add(DocString.size(size.getRegisteredNativeSize(), false));
+ vals.add(DocString.delta(false, false,
+ size.getRegisteredNativeSize(), base.getRegisteredNativeSize()));
+ vals.add(DocString.size(size.getSize(), false));
+ vals.add(DocString.delta(false, false, size.getSize(), base.getSize()));
+ vals.addAll(Arrays.asList(values));
+ doc.row(vals.toArray(new DocString[vals.size()]));
+ }
+
+ /**
+ * Start a size table without a custom left column.
+ *
+ * |Java Size|Native Size|...|Total Size|custom columns...|
+ * This should be followed by calls to the 'row' method to fill in the table
+ * contents and the 'end' method to end the table.
+ *
+ * Set showDiff to true if size diffs should be shown.
+ */
+ static void table(Doc doc, boolean showDiff, Column... columns) {
+ // Re-use the code for a size table with custom left column by having an
+ // invisible custom left column.
+ table(doc, new Column("", Column.Align.LEFT, false), showDiff, columns);
+ }
+
+ /**
+ * Add a row to the currently active size table without a custom left column.
+ * The number of values must match the number of columns provided for the
+ * currently active table.
+ */
+ static void row(Doc doc, Size size, Size base, DocString... values) {
+ row(doc, new DocString(), size, base, values);
+ }
+
+ /**
+ * End the currently active table.
+ */
+ static void end(Doc doc) {
+ doc.end();
+ }
+}
diff --git a/tools/ahat/src/heapdump/AhatHeap.java b/tools/ahat/src/heapdump/AhatHeap.java
index c39adc4b41..b8897a182c 100644
--- a/tools/ahat/src/heapdump/AhatHeap.java
+++ b/tools/ahat/src/heapdump/AhatHeap.java
@@ -18,7 +18,7 @@ package com.android.ahat.heapdump;
public class AhatHeap implements Diffable<AhatHeap> {
private String mName;
- private long mSize = 0;
+ private Size mSize = Size.ZERO;
private int mIndex;
private AhatHeap mBaseline;
private boolean mIsPlaceHolder = false;
@@ -47,8 +47,8 @@ public class AhatHeap implements Diffable<AhatHeap> {
return new AhatHeap(name, baseline);
}
- void addToSize(long increment) {
- mSize += increment;
+ void addToSize(Size size) {
+ mSize = mSize.plus(size);
}
/**
@@ -69,7 +69,7 @@ public class AhatHeap implements Diffable<AhatHeap> {
/**
* Returns the total number of bytes allocated on this heap.
*/
- public long getSize() {
+ public Size getSize() {
return mSize;
}
diff --git a/tools/ahat/src/heapdump/AhatInstance.java b/tools/ahat/src/heapdump/AhatInstance.java
index e6b9c00384..af369d95d8 100644
--- a/tools/ahat/src/heapdump/AhatInstance.java
+++ b/tools/ahat/src/heapdump/AhatInstance.java
@@ -20,17 +20,18 @@ import com.android.tools.perflib.heap.ClassObj;
import com.android.tools.perflib.heap.Instance;
import com.android.tools.perflib.heap.RootObj;
import java.awt.image.BufferedImage;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Deque;
import java.util.List;
public abstract class AhatInstance implements Diffable<AhatInstance> {
private long mId;
- private long mSize;
- private long mTotalRetainedSize;
- private long mRetainedSizes[]; // Retained size indexed by heap index
+ private Size mSize;
+ private Size[] mRetainedSizes; // Retained size indexed by heap index
private boolean mIsReachable;
private AhatHeap mHeap;
private AhatInstance mImmediateDominator;
@@ -63,15 +64,10 @@ public abstract class AhatInstance implements Diffable<AhatInstance> {
*/
void initialize(AhatSnapshot snapshot, Instance inst) {
mId = inst.getId();
- mSize = inst.getSize();
- mTotalRetainedSize = inst.getTotalRetainedSize();
+ mSize = new Size(inst.getSize(), 0);
mIsReachable = inst.isReachable();
List<AhatHeap> heaps = snapshot.getHeaps();
- mRetainedSizes = new long[heaps.size()];
- for (AhatHeap heap : heaps) {
- mRetainedSizes[heap.getIndex()] = inst.getRetainedSize(heap.getIndex());
- }
mHeap = snapshot.getHeap(inst.getHeap().getName());
@@ -130,7 +126,7 @@ public abstract class AhatInstance implements Diffable<AhatInstance> {
/**
* Returns the shallow number of bytes this object takes up.
*/
- public long getSize() {
+ public Size getSize() {
return mSize;
}
@@ -138,16 +134,32 @@ public abstract class AhatInstance implements Diffable<AhatInstance> {
* Returns the number of bytes belonging to the given heap that this instance
* retains.
*/
- public long getRetainedSize(AhatHeap heap) {
+ public Size getRetainedSize(AhatHeap heap) {
int index = heap.getIndex();
- return 0 <= index && index < mRetainedSizes.length ? mRetainedSizes[heap.getIndex()] : 0;
+ if (mRetainedSizes != null && 0 <= index && index < mRetainedSizes.length) {
+ return mRetainedSizes[heap.getIndex()];
+ }
+ return Size.ZERO;
}
/**
* Returns the total number of bytes this instance retains.
*/
- public long getTotalRetainedSize() {
- return mTotalRetainedSize;
+ public Size getTotalRetainedSize() {
+ Size size = Size.ZERO;
+ if (mRetainedSizes != null) {
+ for (int i = 0; i < mRetainedSizes.length; i++) {
+ size = size.plus(mRetainedSizes[i]);
+ }
+ }
+ return size;
+ }
+
+ /**
+ * Increment the number of registered native bytes tied to this object.
+ */
+ void addRegisteredNativeSize(long size) {
+ mSize = mSize.plusRegisteredNativeSize(size);
}
/**
@@ -452,4 +464,41 @@ public abstract class AhatInstance implements Diffable<AhatInstance> {
AhatInstance newPlaceHolderInstance() {
return new AhatPlaceHolderInstance(this);
}
+
+ /**
+ * Recursively compute the retained size of the given instance and all
+ * other instances it dominates.
+ */
+ static void computeRetainedSize(AhatInstance inst, int numHeaps) {
+ // Note: We can't use a recursive implementation because it can lead to
+ // stack overflow. Use an iterative implementation instead.
+ //
+ // Objects not yet processed will have mRetainedSizes set to null.
+ // Once prepared, an object will have mRetaiedSizes set to an array of 0
+ // sizes.
+ Deque<AhatInstance> deque = new ArrayDeque<AhatInstance>();
+ deque.push(inst);
+
+ while (!deque.isEmpty()) {
+ inst = deque.pop();
+ if (inst.mRetainedSizes == null) {
+ inst.mRetainedSizes = new Size[numHeaps];
+ for (int i = 0; i < numHeaps; i++) {
+ inst.mRetainedSizes[i] = Size.ZERO;
+ }
+ inst.mRetainedSizes[inst.mHeap.getIndex()] =
+ inst.mRetainedSizes[inst.mHeap.getIndex()].plus(inst.mSize);
+ deque.push(inst);
+ for (AhatInstance dominated : inst.mDominated) {
+ deque.push(dominated);
+ }
+ } else {
+ for (AhatInstance dominated : inst.mDominated) {
+ for (int i = 0; i < numHeaps; i++) {
+ inst.mRetainedSizes[i] = inst.mRetainedSizes[i].plus(dominated.mRetainedSizes[i]);
+ }
+ }
+ }
+ }
+ }
}
diff --git a/tools/ahat/src/heapdump/AhatPlaceHolderClassObj.java b/tools/ahat/src/heapdump/AhatPlaceHolderClassObj.java
index c6ad87fda5..2b3e056a1e 100644
--- a/tools/ahat/src/heapdump/AhatPlaceHolderClassObj.java
+++ b/tools/ahat/src/heapdump/AhatPlaceHolderClassObj.java
@@ -29,16 +29,16 @@ public class AhatPlaceHolderClassObj extends AhatClassObj {
baseline.setBaseline(this);
}
- @Override public long getSize() {
- return 0;
+ @Override public Size getSize() {
+ return Size.ZERO;
}
- @Override public long getRetainedSize(AhatHeap heap) {
- return 0;
+ @Override public Size getRetainedSize(AhatHeap heap) {
+ return Size.ZERO;
}
- @Override public long getTotalRetainedSize() {
- return 0;
+ @Override public Size getTotalRetainedSize() {
+ return Size.ZERO;
}
@Override public AhatHeap getHeap() {
diff --git a/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java b/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java
index 9412eae9a1..4aac80484d 100644
--- a/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java
+++ b/tools/ahat/src/heapdump/AhatPlaceHolderInstance.java
@@ -29,16 +29,16 @@ public class AhatPlaceHolderInstance extends AhatInstance {
baseline.setBaseline(this);
}
- @Override public long getSize() {
- return 0;
+ @Override public Size getSize() {
+ return Size.ZERO;
}
- @Override public long getRetainedSize(AhatHeap heap) {
- return 0;
+ @Override public Size getRetainedSize(AhatHeap heap) {
+ return Size.ZERO;
}
- @Override public long getTotalRetainedSize() {
- return 0;
+ @Override public Size getTotalRetainedSize() {
+ return Size.ZERO;
}
@Override public AhatHeap getHeap() {
diff --git a/tools/ahat/src/heapdump/AhatSnapshot.java b/tools/ahat/src/heapdump/AhatSnapshot.java
index 20b85da763..35d6c8a315 100644
--- a/tools/ahat/src/heapdump/AhatSnapshot.java
+++ b/tools/ahat/src/heapdump/AhatSnapshot.java
@@ -82,8 +82,7 @@ public class AhatSnapshot implements Diffable<AhatSnapshot> {
Snapshot snapshot = Snapshot.createSnapshot(buffer, map);
snapshot.computeDominators();
- // Properly label the class of class objects in the perflib snapshot, and
- // count the total number of instances.
+ // Properly label the class of class objects in the perflib snapshot.
final ClassObj javaLangClass = snapshot.findClass("java.lang.Class");
if (javaLangClass != null) {
for (Heap heap : snapshot.getHeaps()) {
@@ -134,12 +133,19 @@ public class AhatSnapshot implements Diffable<AhatSnapshot> {
}
});
+ Map<Instance, Long> registeredNative = Perflib.getRegisteredNativeAllocations(snapshot);
+
// Initialize ahat snapshot and instances based on the perflib snapshot
// and instances.
for (AhatInstance ahat : mInstances) {
Instance inst = snapshot.findInstance(ahat.getId());
ahat.initialize(this, inst);
+ Long registeredNativeSize = registeredNative.get(inst);
+ if (registeredNativeSize != null) {
+ ahat.addRegisteredNativeSize(registeredNativeSize);
+ }
+
if (inst.getImmediateDominator() == Snapshot.SENTINEL_ROOT) {
mRooted.add(ahat);
}
@@ -166,6 +172,13 @@ public class AhatSnapshot implements Diffable<AhatSnapshot> {
}
}
snapshot.dispose();
+
+ // Compute the retained sizes of objects. We do this explicitly now rather
+ // than relying on the retained sizes computed by perflib so that
+ // registered native sizes are included.
+ for (AhatInstance inst : mRooted) {
+ AhatInstance.computeRetainedSize(inst, mHeaps.size());
+ }
}
/**
diff --git a/tools/ahat/src/heapdump/Perflib.java b/tools/ahat/src/heapdump/Perflib.java
new file mode 100644
index 0000000000..d0264a3b39
--- /dev/null
+++ b/tools/ahat/src/heapdump/Perflib.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 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.ClassInstance;
+import com.android.tools.perflib.heap.ClassObj;
+import com.android.tools.perflib.heap.Instance;
+import com.android.tools.perflib.heap.Snapshot;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Collection of utilities that may be suitable to have in perflib instead of
+ * ahat.
+ */
+public class Perflib {
+ /**
+ * Return a collection of instances in the given snapshot that are tied to
+ * registered native allocations and their corresponding registered native
+ * sizes.
+ */
+ public static Map<Instance, Long> getRegisteredNativeAllocations(Snapshot snapshot) {
+ Map<Instance, Long> allocs = new HashMap<Instance, Long>();
+ ClassObj cleanerClass = snapshot.findClass("sun.misc.Cleaner");
+ if (cleanerClass != null) {
+ for (Instance cleanerInst : cleanerClass.getInstancesList()) {
+ ClassInstance cleaner = (ClassInstance)cleanerInst;
+ Object referent = getField(cleaner, "referent");
+ if (referent instanceof Instance) {
+ Instance inst = (Instance)referent;
+ Object thunkValue = getField(cleaner, "thunk");
+ if (thunkValue instanceof ClassInstance) {
+ ClassInstance thunk = (ClassInstance)thunkValue;
+ ClassObj thunkClass = thunk.getClassObj();
+ String cleanerThunkClassName = "libcore.util.NativeAllocationRegistry$CleanerThunk";
+ if (thunkClass != null && cleanerThunkClassName.equals(thunkClass.getClassName())) {
+ for (ClassInstance.FieldValue thunkField : thunk.getValues()) {
+ if (thunkField.getValue() instanceof ClassInstance) {
+ ClassInstance registry = (ClassInstance)thunkField.getValue();
+ ClassObj registryClass = registry.getClassObj();
+ String registryClassName = "libcore.util.NativeAllocationRegistry";
+ if (registryClass != null
+ && registryClassName.equals(registryClass.getClassName())) {
+ Object sizeValue = getField(registry, "size");
+ if (sizeValue instanceof Long) {
+ long size = (Long)sizeValue;
+ if (size > 0) {
+ Long old = allocs.get(inst);
+ allocs.put(inst, old == null ? size : old + size);
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return allocs;
+ }
+
+ /**
+ * Helper function to read a single field from a perflib class instance.
+ * Returns null if field not found. Note there is no way to distinguish
+ * between field not found an a field value of null.
+ */
+ private static Object getField(ClassInstance cls, String name) {
+ for (ClassInstance.FieldValue field : cls.getValues()) {
+ if (name.equals(field.getField().getName())) {
+ return field.getValue();
+ }
+ }
+ return null;
+ }
+}
diff --git a/tools/ahat/src/heapdump/Site.java b/tools/ahat/src/heapdump/Site.java
index 738eaf0687..fdd4eea7b3 100644
--- a/tools/ahat/src/heapdump/Site.java
+++ b/tools/ahat/src/heapdump/Site.java
@@ -44,7 +44,7 @@ public class Site implements Diffable<Site> {
// The total size of objects allocated in this site (including child sites),
// organized by heap index. Heap indices outside the range of mSizesByHeap
// implicitly have size 0.
- private long[] mSizesByHeap;
+ private Size[] mSizesByHeap;
// List of child sites.
private List<Site> mChildren;
@@ -60,14 +60,18 @@ public class Site implements Diffable<Site> {
public AhatHeap heap;
public AhatClassObj classObj; // May be null.
public long numInstances;
- public long numBytes;
+ public Size numBytes;
private ObjectsInfo baseline;
- public ObjectsInfo(AhatHeap heap, AhatClassObj classObj, long numInstances, long numBytes) {
+ /**
+ * Construct a new, empty objects info for the given heap and class
+ * combination.
+ */
+ public ObjectsInfo(AhatHeap heap, AhatClassObj classObj) {
this.heap = heap;
this.classObj = classObj;
- this.numInstances = numInstances;
- this.numBytes = numBytes;
+ this.numInstances = 0;
+ this.numBytes = Size.ZERO;
this.baseline = this;
}
@@ -107,7 +111,7 @@ public class Site implements Diffable<Site> {
mLineNumber = line;
mId = id;
mDepth = depth;
- mSizesByHeap = new long[1];
+ mSizesByHeap = new Size[0];
mChildren = new ArrayList<Site>();
mObjects = new ArrayList<AhatInstance>();
mObjectsInfos = new ArrayList<ObjectsInfo>();
@@ -133,16 +137,20 @@ public class Site implements Diffable<Site> {
if (inst.isReachable()) {
AhatHeap heap = inst.getHeap();
if (heap.getIndex() >= site.mSizesByHeap.length) {
- long[] newSizes = new long[heap.getIndex() + 1];
+ Size[] newSizes = new Size[heap.getIndex() + 1];
for (int i = 0; i < site.mSizesByHeap.length; i++) {
newSizes[i] = site.mSizesByHeap[i];
}
+ for (int i = site.mSizesByHeap.length; i < heap.getIndex() + 1; i++) {
+ newSizes[i] = Size.ZERO;
+ }
site.mSizesByHeap = newSizes;
}
- site.mSizesByHeap[heap.getIndex()] += inst.getSize();
+ site.mSizesByHeap[heap.getIndex()]
+ = site.mSizesByHeap[heap.getIndex()].plus(inst.getSize());
info.numInstances++;
- info.numBytes += inst.getSize();
+ info.numBytes = info.numBytes.plus(inst.getSize());
}
if (depth > 0) {
@@ -172,9 +180,9 @@ public class Site implements Diffable<Site> {
}
// Get the size of a site for a specific heap.
- public long getSize(AhatHeap heap) {
+ public Size getSize(AhatHeap heap) {
int index = heap.getIndex();
- return index >= 0 && index < mSizesByHeap.length ? mSizesByHeap[index] : 0;
+ return index >= 0 && index < mSizesByHeap.length ? mSizesByHeap[index] : Size.ZERO;
}
/**
@@ -198,7 +206,7 @@ public class Site implements Diffable<Site> {
ObjectsInfo info = classToObjectsInfo.get(classObj);
if (info == null) {
- info = new ObjectsInfo(heap, classObj, 0, 0);
+ info = new ObjectsInfo(heap, classObj);
mObjectsInfos.add(info);
classToObjectsInfo.put(classObj, info);
}
@@ -210,10 +218,10 @@ public class Site implements Diffable<Site> {
}
// Get the combined size of the site for all heaps.
- public long getTotalSize() {
- long total = 0;
+ public Size getTotalSize() {
+ Size total = Size.ZERO;
for (int i = 0; i < mSizesByHeap.length; i++) {
- total += mSizesByHeap[i];
+ total = total.plus(mSizesByHeap[i]);
}
return total;
}
diff --git a/tools/ahat/src/heapdump/Size.java b/tools/ahat/src/heapdump/Size.java
new file mode 100644
index 0000000000..7c8db900df
--- /dev/null
+++ b/tools/ahat/src/heapdump/Size.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * The Size class is used to represent how much space an instance takes up.
+ *
+ * An abstraction is introduced rather than using a long directly in order to
+ * more easily keep track of the different components of the size. For
+ * example, some instances may have associated native, code, or graphics
+ * sizes.
+ *
+ * Size objects are immutable.
+ */
+public class Size {
+ private final long mJavaSize;
+ private final long mRegisteredNativeSize;
+
+ public static Size ZERO = new Size(0, 0);
+
+ public Size(long javaSize, long registeredNativeSize) {
+ mJavaSize = javaSize;
+ mRegisteredNativeSize = registeredNativeSize;
+ }
+
+ public long getSize() {
+ return mJavaSize + mRegisteredNativeSize;
+ }
+
+ public long getJavaSize() {
+ return mJavaSize;
+ }
+
+ public long getRegisteredNativeSize() {
+ return mRegisteredNativeSize;
+ }
+
+ /**
+ * Returns true if all the fields of this size object are zero.
+ */
+ public boolean isZero() {
+ return mJavaSize == 0 && mRegisteredNativeSize == 0;
+ }
+
+ /**
+ * Return a new Size object that is the sum of this size and the other.
+ */
+ public Size plus(Size other) {
+ if (isZero()) {
+ return other;
+ } else if (other.isZero()) {
+ return this;
+ } else {
+ return new Size(mJavaSize + other.mJavaSize,
+ mRegisteredNativeSize + other.mRegisteredNativeSize);
+ }
+ }
+
+ /**
+ * Return a new Size object that has 'size' more registered native size than
+ * this Size object.
+ */
+ public Size plusRegisteredNativeSize(long size) {
+ return new Size(mJavaSize, mRegisteredNativeSize + size);
+ }
+
+ @Override public boolean equals(Object other) {
+ if (other instanceof Size) {
+ Size s = (Size)other;
+ return mJavaSize == s.mJavaSize && mRegisteredNativeSize == s.mRegisteredNativeSize;
+ }
+ return false;
+ }
+}
+
diff --git a/tools/ahat/src/heapdump/Sort.java b/tools/ahat/src/heapdump/Sort.java
index 93d147a49e..0745803817 100644
--- a/tools/ahat/src/heapdump/Sort.java
+++ b/tools/ahat/src/heapdump/Sort.java
@@ -32,6 +32,17 @@ import java.util.List;
*/
public class Sort {
/**
+ * Compare sizes by their total size.
+ * This sorts sizes from smaller total size to larger total size.
+ */
+ public static final Comparator<Size> SIZE_BY_SIZE = new Comparator<Size>() {
+ @Override
+ public int compare(Size a, Size b) {
+ return Long.compare(a.getSize(), b.getSize());
+ }
+ };
+
+ /**
* Compare instances by their total retained size.
* Different instances with the same total retained size are considered
* equal for the purposes of comparison.
@@ -41,7 +52,7 @@ public class Sort {
= new Comparator<AhatInstance>() {
@Override
public int compare(AhatInstance a, AhatInstance b) {
- return Long.compare(b.getTotalRetainedSize(), a.getTotalRetainedSize());
+ return SIZE_BY_SIZE.compare(b.getTotalRetainedSize(), a.getTotalRetainedSize());
}
};
@@ -60,7 +71,7 @@ public class Sort {
@Override
public int compare(AhatInstance a, AhatInstance b) {
- return Long.compare(b.getRetainedSize(mHeap), a.getRetainedSize(mHeap));
+ return SIZE_BY_SIZE.compare(b.getRetainedSize(mHeap), a.getRetainedSize(mHeap));
}
}
@@ -119,7 +130,7 @@ public class Sort {
@Override
public int compare(Site a, Site b) {
- return Long.compare(b.getSize(mHeap), a.getSize(mHeap));
+ return SIZE_BY_SIZE.compare(b.getSize(mHeap), a.getSize(mHeap));
}
}
@@ -130,7 +141,7 @@ public class Sort {
public static final Comparator<Site> SITE_BY_TOTAL_SIZE = new Comparator<Site>() {
@Override
public int compare(Site a, Site b) {
- return Long.compare(b.getTotalSize(), a.getTotalSize());
+ return SIZE_BY_SIZE.compare(b.getTotalSize(), a.getTotalSize());
}
};
@@ -158,7 +169,7 @@ public class Sort {
= new Comparator<Site.ObjectsInfo>() {
@Override
public int compare(Site.ObjectsInfo a, Site.ObjectsInfo b) {
- return Long.compare(b.numBytes, a.numBytes);
+ return SIZE_BY_SIZE.compare(b.numBytes, a.numBytes);
}
};
diff --git a/tools/ahat/test-dump/Main.java b/tools/ahat/test-dump/Main.java
index 7a05b1cb89..3d3de78255 100644
--- a/tools/ahat/test-dump/Main.java
+++ b/tools/ahat/test-dump/Main.java
@@ -20,6 +20,7 @@ import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
+import libcore.util.NativeAllocationRegistry;
import org.apache.harmony.dalvik.ddmc.DdmVmInternal;
/**
@@ -98,6 +99,11 @@ public class Main {
bigArray[i] = (byte)((i*i) & 0xFF);
}
+ // 0x12345, 50000, and 0xABCDABCD are arbitrary values.
+ NativeAllocationRegistry registry = new NativeAllocationRegistry(
+ Main.class.getClassLoader(), 0x12345, 50000);
+ registry.registerNativeAllocation(anObject, 0xABCDABCD);
+
addedObject = baseline ? null : new AddedObject();
removedObject = baseline ? new RemovedObject() : null;
modifiedObject = new ModifiedObject();
diff --git a/tools/ahat/test/InstanceTest.java b/tools/ahat/test/InstanceTest.java
index 3a50150c0e..71b081c9a4 100644
--- a/tools/ahat/test/InstanceTest.java
+++ b/tools/ahat/test/InstanceTest.java
@@ -21,6 +21,7 @@ import com.android.ahat.heapdump.AhatHeap;
import com.android.ahat.heapdump.AhatInstance;
import com.android.ahat.heapdump.AhatSnapshot;
import com.android.ahat.heapdump.PathElement;
+import com.android.ahat.heapdump.Size;
import com.android.ahat.heapdump.Value;
import com.android.tools.perflib.heap.hprof.HprofClassDump;
import com.android.tools.perflib.heap.hprof.HprofConstant;
@@ -292,13 +293,13 @@ public class InstanceTest {
// allocated on, and should be 0 for all other heaps.
AhatInstance anObject = dump.getDumpedAhatInstance("anObject");
AhatSnapshot snapshot = dump.getAhatSnapshot();
- long size = anObject.getSize();
+ Size size = anObject.getSize();
assertEquals(size, anObject.getTotalRetainedSize());
assertEquals(size, anObject.getRetainedSize(anObject.getHeap()));
for (AhatHeap heap : snapshot.getHeaps()) {
if (!heap.equals(anObject.getHeap())) {
assertEquals(String.format("For heap '%s'", heap.getName()),
- 0, anObject.getRetainedSize(heap));
+ Size.ZERO, anObject.getRetainedSize(heap));
}
}
}
diff --git a/tools/ahat/test/NativeAllocationTest.java b/tools/ahat/test/NativeAllocationTest.java
new file mode 100644
index 0000000000..7436be8311
--- /dev/null
+++ b/tools/ahat/test/NativeAllocationTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+import com.android.ahat.heapdump.AhatInstance;
+import com.android.ahat.heapdump.AhatSnapshot;
+import java.io.IOException;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class NativeAllocationTest {
+
+ @Test
+ public void nativeAllocation() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+
+ AhatSnapshot snapshot = dump.getAhatSnapshot();
+ AhatInstance referent = dump.getDumpedAhatInstance("anObject");
+ assertEquals(50000, referent.getSize().getRegisteredNativeSize());
+ }
+}
+
diff --git a/tools/ahat/test/Tests.java b/tools/ahat/test/Tests.java
index 2fd3286172..c7e9b1811b 100644
--- a/tools/ahat/test/Tests.java
+++ b/tools/ahat/test/Tests.java
@@ -24,6 +24,7 @@ public class Tests {
args = new String[]{
"com.android.ahat.DiffTest",
"com.android.ahat.InstanceTest",
+ "com.android.ahat.NativeAllocationTest",
"com.android.ahat.ObjectHandlerTest",
"com.android.ahat.OverviewHandlerTest",
"com.android.ahat.PerformanceTest",