• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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;
18 
19 import com.android.ahat.heapdump.AhatArrayInstance;
20 import com.android.ahat.heapdump.AhatClassInstance;
21 import com.android.ahat.heapdump.AhatClassObj;
22 import com.android.ahat.heapdump.AhatHeap;
23 import com.android.ahat.heapdump.AhatInstance;
24 import com.android.ahat.heapdump.AhatSnapshot;
25 import com.android.ahat.heapdump.Diff;
26 import com.android.ahat.heapdump.FieldValue;
27 import com.android.ahat.heapdump.PathElement;
28 import com.android.ahat.heapdump.Site;
29 import com.android.ahat.heapdump.Value;
30 import java.io.IOException;
31 import java.util.Collection;
32 import java.util.Collections;
33 import java.util.List;
34 import java.util.Objects;
35 
36 
37 class ObjectHandler implements AhatHandler {
38 
39   private static final String ARRAY_ELEMENTS_ID = "elements";
40   private static final String DOMINATOR_PATH_ID = "dompath";
41   private static final String ALLOCATION_SITE_ID = "frames";
42   private static final String DOMINATED_OBJECTS_ID = "dominated";
43   private static final String INSTANCE_FIELDS_ID = "ifields";
44   private static final String STATIC_FIELDS_ID = "sfields";
45   private static final String HARD_REFS_ID = "refs";
46   private static final String SOFT_REFS_ID = "srefs";
47 
48   private AhatSnapshot mSnapshot;
49 
ObjectHandler(AhatSnapshot snapshot)50   public ObjectHandler(AhatSnapshot snapshot) {
51     mSnapshot = snapshot;
52   }
53 
54   @Override
handle(Doc doc, Query query)55   public void handle(Doc doc, Query query) throws IOException {
56     long id = query.getLong("id", 0);
57     AhatInstance inst = mSnapshot.findInstance(id);
58     if (inst == null) {
59       doc.println(DocString.format("No object with id %08xl", id));
60       return;
61     }
62     AhatInstance base = inst.getBaseline();
63 
64     doc.title("Object %08x", inst.getId());
65     doc.big(Summarizer.summarize(inst));
66 
67     printAllocationSite(doc, query, inst);
68     printGcRootPath(doc, query, inst);
69 
70     doc.section("Object Info");
71     AhatClassObj cls = inst.getClassObj();
72     doc.descriptions();
73     doc.description(DocString.text("Class"), Summarizer.summarize(cls));
74 
75     DocString sizeDescription = DocString.format("%,14d ", inst.getSize());
76     sizeDescription.appendDelta(false, base.isPlaceHolder(),
77         inst.getSize(), base.getSize());
78     doc.description(DocString.text("Size"), sizeDescription);
79 
80     DocString rsizeDescription = DocString.format("%,14d ", inst.getTotalRetainedSize());
81     rsizeDescription.appendDelta(false, base.isPlaceHolder(),
82         inst.getTotalRetainedSize(), base.getTotalRetainedSize());
83     doc.description(DocString.text("Retained Size"), rsizeDescription);
84 
85     doc.description(DocString.text("Heap"), DocString.text(inst.getHeap().getName()));
86 
87     Collection<String> rootTypes = inst.getRootTypes();
88     if (rootTypes != null) {
89       DocString types = new DocString();
90       String comma = "";
91       for (String type : rootTypes) {
92         types.append(comma);
93         types.append(type);
94         comma = ", ";
95       }
96       doc.description(DocString.text("Root Types"), types);
97     }
98 
99     doc.end();
100 
101     printBitmap(doc, inst);
102     if (inst.isClassInstance()) {
103       printClassInstanceFields(doc, query, inst.asClassInstance());
104     } else if (inst.isArrayInstance()) {
105       printArrayElements(doc, query, inst.asArrayInstance());
106     } else if (inst.isClassObj()) {
107       printClassInfo(doc, query, inst.asClassObj());
108     }
109     printReferences(doc, query, inst);
110     printDominatedObjects(doc, query, inst);
111   }
112 
printClassInstanceFields(Doc doc, Query query, AhatClassInstance inst)113   private static void printClassInstanceFields(Doc doc, Query query, AhatClassInstance inst) {
114     doc.section("Fields");
115     AhatInstance base = inst.getBaseline();
116     List<FieldValue> fields = inst.getInstanceFields();
117     if (!base.isPlaceHolder()) {
118       Diff.fields(fields, base.asClassInstance().getInstanceFields());
119     }
120     SubsetSelector<FieldValue> selector = new SubsetSelector(query, INSTANCE_FIELDS_ID, fields);
121     printFields(doc, inst != base && !base.isPlaceHolder(), selector.selected());
122     selector.render(doc);
123   }
124 
printArrayElements(Doc doc, Query query, AhatArrayInstance array)125   private static void printArrayElements(Doc doc, Query query, AhatArrayInstance array) {
126     doc.section("Array Elements");
127     AhatInstance base = array.getBaseline();
128     boolean diff = array.getBaseline() != array && !base.isPlaceHolder();
129     doc.table(
130         new Column("Index", Column.Align.RIGHT),
131         new Column("Value"),
132         new Column("Δ", Column.Align.LEFT, diff));
133 
134     List<Value> elements = array.getValues();
135     SubsetSelector<Value> selector = new SubsetSelector(query, ARRAY_ELEMENTS_ID, elements);
136     int i = 0;
137     for (Value current : selector.selected()) {
138       DocString delta = new DocString();
139       if (diff) {
140         Value previous = Value.getBaseline(base.asArrayInstance().getValue(i));
141         if (!Objects.equals(current, previous)) {
142           delta.append("was ");
143           delta.append(Summarizer.summarize(previous));
144         }
145       }
146       doc.row(DocString.format("%d", i), Summarizer.summarize(current), delta);
147       i++;
148     }
149     doc.end();
150     selector.render(doc);
151   }
152 
printFields(Doc doc, boolean diff, List<FieldValue> fields)153   private static void printFields(Doc doc, boolean diff, List<FieldValue> fields) {
154     doc.table(
155         new Column("Type"),
156         new Column("Name"),
157         new Column("Value"),
158         new Column("Δ", Column.Align.LEFT, diff));
159 
160     for (FieldValue field : fields) {
161       Value current = field.getValue();
162       DocString value;
163       if (field.isPlaceHolder()) {
164         value = DocString.removed("del");
165       } else {
166         value = Summarizer.summarize(current);
167       }
168 
169       DocString delta = new DocString();
170       FieldValue basefield = field.getBaseline();
171       if (basefield.isPlaceHolder()) {
172         delta.append(DocString.added("new"));
173       } else {
174         Value previous = Value.getBaseline(basefield.getValue());
175         if (!Objects.equals(current, previous)) {
176           delta.append("was ");
177           delta.append(Summarizer.summarize(previous));
178         }
179       }
180       doc.row(DocString.text(field.getType()), DocString.text(field.getName()), value, delta);
181     }
182     doc.end();
183   }
184 
printClassInfo(Doc doc, Query query, AhatClassObj clsobj)185   private static void printClassInfo(Doc doc, Query query, AhatClassObj clsobj) {
186     doc.section("Class Info");
187     doc.descriptions();
188     doc.description(DocString.text("Super Class"),
189         Summarizer.summarize(clsobj.getSuperClassObj()));
190     doc.description(DocString.text("Class Loader"),
191         Summarizer.summarize(clsobj.getClassLoader()));
192     doc.end();
193 
194     doc.section("Static Fields");
195     AhatInstance base = clsobj.getBaseline();
196     List<FieldValue> fields = clsobj.getStaticFieldValues();
197     if (!base.isPlaceHolder()) {
198       Diff.fields(fields, base.asClassObj().getStaticFieldValues());
199     }
200     SubsetSelector<FieldValue> selector = new SubsetSelector(query, STATIC_FIELDS_ID, fields);
201     printFields(doc, clsobj != base && !base.isPlaceHolder(), selector.selected());
202     selector.render(doc);
203   }
204 
printReferences(Doc doc, Query query, AhatInstance inst)205   private static void printReferences(Doc doc, Query query, AhatInstance inst) {
206     doc.section("Objects with References to this Object");
207     if (inst.getHardReverseReferences().isEmpty()) {
208       doc.println(DocString.text("(none)"));
209     } else {
210       doc.table(new Column("Object"));
211       List<AhatInstance> references = inst.getHardReverseReferences();
212       SubsetSelector<AhatInstance> selector = new SubsetSelector(query, HARD_REFS_ID, references);
213       for (AhatInstance ref : selector.selected()) {
214         doc.row(Summarizer.summarize(ref));
215       }
216       doc.end();
217       selector.render(doc);
218     }
219 
220     if (!inst.getSoftReverseReferences().isEmpty()) {
221       doc.section("Objects with Soft References to this Object");
222       doc.table(new Column("Object"));
223       List<AhatInstance> references = inst.getSoftReverseReferences();
224       SubsetSelector<AhatInstance> selector = new SubsetSelector(query, SOFT_REFS_ID, references);
225       for (AhatInstance ref : selector.selected()) {
226         doc.row(Summarizer.summarize(ref));
227       }
228       doc.end();
229       selector.render(doc);
230     }
231   }
232 
printAllocationSite(Doc doc, Query query, AhatInstance inst)233   private void printAllocationSite(Doc doc, Query query, AhatInstance inst) {
234     doc.section("Allocation Site");
235     Site site = inst.getSite();
236     SitePrinter.printSite(mSnapshot, doc, query, ALLOCATION_SITE_ID, site);
237   }
238 
239   // Draw the bitmap corresponding to this instance if there is one.
printBitmap(Doc doc, AhatInstance inst)240   private static void printBitmap(Doc doc, AhatInstance inst) {
241     AhatInstance bitmap = inst.getAssociatedBitmapInstance();
242     if (bitmap != null) {
243       doc.section("Bitmap Image");
244       doc.println(DocString.image(
245             DocString.formattedUri("bitmap?id=%d", bitmap.getId()), "bitmap image"));
246     }
247   }
248 
printGcRootPath(Doc doc, Query query, AhatInstance inst)249   private void printGcRootPath(Doc doc, Query query, AhatInstance inst) {
250     doc.section("Sample Path from GC Root");
251     List<PathElement> path = inst.getPathFromGcRoot();
252 
253     // Add a dummy PathElement as a marker for the root.
254     final PathElement root = new PathElement(null, null);
255     path.add(0, root);
256 
257     HeapTable.TableConfig<PathElement> table = new HeapTable.TableConfig<PathElement>() {
258       public String getHeapsDescription() {
259         return "Bytes Retained by Heap (Dominators Only)";
260       }
261 
262       public long getSize(PathElement element, AhatHeap heap) {
263         if (element == root) {
264           return heap.getSize();
265         }
266         if (element.isDominator) {
267           return element.instance.getRetainedSize(heap);
268         }
269         return 0;
270       }
271 
272       public List<HeapTable.ValueConfig<PathElement>> getValueConfigs() {
273         HeapTable.ValueConfig<PathElement> value = new HeapTable.ValueConfig<PathElement>() {
274           public String getDescription() {
275             return "Path Element";
276           }
277 
278           public DocString render(PathElement element) {
279             if (element == root) {
280               return DocString.link(DocString.uri("rooted"), DocString.text("ROOT"));
281             } else {
282               DocString label = DocString.text("→ ");
283               label.append(Summarizer.summarize(element.instance));
284               label.append(element.field);
285               return label;
286             }
287           }
288         };
289         return Collections.singletonList(value);
290       }
291     };
292     HeapTable.render(doc, query, DOMINATOR_PATH_ID, table, mSnapshot, path);
293   }
294 
printDominatedObjects(Doc doc, Query query, AhatInstance inst)295   public void printDominatedObjects(Doc doc, Query query, AhatInstance inst) {
296     doc.section("Immediately Dominated Objects");
297     List<AhatInstance> instances = inst.getDominated();
298     if (instances != null) {
299       DominatedList.render(mSnapshot, doc, query, DOMINATED_OBJECTS_ID, instances);
300     } else {
301       doc.println(DocString.text("(none)"));
302     }
303   }
304 }
305 
306