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
17package com.android.ahat;
18
19import com.android.tools.perflib.heap.ArrayInstance;
20import com.android.tools.perflib.heap.ClassInstance;
21import com.android.tools.perflib.heap.ClassObj;
22import com.android.tools.perflib.heap.Field;
23import com.android.tools.perflib.heap.Heap;
24import com.android.tools.perflib.heap.Instance;
25import com.android.tools.perflib.heap.RootObj;
26import com.android.tools.perflib.heap.RootType;
27import java.io.IOException;
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.Collection;
31import java.util.Collections;
32import java.util.List;
33import java.util.Map;
34
35class ObjectHandler implements AhatHandler {
36
37  private static final String ARRAY_ELEMENTS_ID = "elements";
38  private static final String DOMINATOR_PATH_ID = "dompath";
39  private static final String ALLOCATION_SITE_ID = "frames";
40  private static final String DOMINATED_OBJECTS_ID = "dominated";
41  private static final String INSTANCE_FIELDS_ID = "ifields";
42  private static final String STATIC_FIELDS_ID = "sfields";
43  private static final String HARD_REFS_ID = "refs";
44  private static final String SOFT_REFS_ID = "srefs";
45
46  private AhatSnapshot mSnapshot;
47
48  public ObjectHandler(AhatSnapshot snapshot) {
49    mSnapshot = snapshot;
50  }
51
52  @Override
53  public void handle(Doc doc, Query query) throws IOException {
54    long id = query.getLong("id", 0);
55    Instance inst = mSnapshot.findInstance(id);
56    if (inst == null) {
57      doc.println(DocString.format("No object with id %08xl", id));
58      return;
59    }
60
61    doc.title("Object %08x", inst.getUniqueId());
62    doc.big(Value.render(mSnapshot, inst));
63
64    printAllocationSite(doc, query, inst);
65    printDominatorPath(doc, query, inst);
66
67    doc.section("Object Info");
68    ClassObj cls = inst.getClassObj();
69    doc.descriptions();
70    doc.description(DocString.text("Class"), Value.render(mSnapshot, cls));
71    doc.description(DocString.text("Size"), DocString.format("%d", inst.getSize()));
72    doc.description(
73        DocString.text("Retained Size"),
74        DocString.format("%d", inst.getTotalRetainedSize()));
75    doc.description(DocString.text("Heap"), DocString.text(inst.getHeap().getName()));
76
77    Collection<RootType> rootTypes = mSnapshot.getRootTypes(inst);
78    if (rootTypes != null) {
79      DocString types = new DocString();
80      String comma = "";
81      for (RootType type : rootTypes) {
82        types.append(comma);
83        types.append(type.getName());
84        comma = ", ";
85      }
86      doc.description(DocString.text("Root Types"), types);
87    }
88
89    doc.end();
90
91    printBitmap(doc, inst);
92    if (inst instanceof ClassInstance) {
93      printClassInstanceFields(doc, query, mSnapshot, (ClassInstance)inst);
94    } else if (inst instanceof ArrayInstance) {
95      printArrayElements(doc, query, mSnapshot, (ArrayInstance)inst);
96    } else if (inst instanceof ClassObj) {
97      printClassInfo(doc, query, mSnapshot, (ClassObj)inst);
98    }
99    printReferences(doc, query, mSnapshot, inst);
100    printDominatedObjects(doc, query, inst);
101  }
102
103  private static void printClassInstanceFields(
104      Doc doc, Query query, AhatSnapshot snapshot, ClassInstance inst) {
105    doc.section("Fields");
106    doc.table(new Column("Type"), new Column("Name"), new Column("Value"));
107    SubsetSelector<ClassInstance.FieldValue> selector
108      = new SubsetSelector(query, INSTANCE_FIELDS_ID, inst.getValues());
109    for (ClassInstance.FieldValue field : selector.selected()) {
110      doc.row(
111          DocString.text(field.getField().getType().toString()),
112          DocString.text(field.getField().getName()),
113          Value.render(snapshot, field.getValue()));
114    }
115    doc.end();
116    selector.render(doc);
117  }
118
119  private static void printArrayElements(
120      Doc doc, Query query, AhatSnapshot snapshot, ArrayInstance array) {
121    doc.section("Array Elements");
122    doc.table(new Column("Index", Column.Align.RIGHT), new Column("Value"));
123    List<Object> elements = Arrays.asList(array.getValues());
124    SubsetSelector<Object> selector = new SubsetSelector(query, ARRAY_ELEMENTS_ID, elements);
125    int i = 0;
126    for (Object elem : selector.selected()) {
127      doc.row(DocString.format("%d", i), Value.render(snapshot, elem));
128      i++;
129    }
130    doc.end();
131    selector.render(doc);
132  }
133
134  private static void printClassInfo(
135      Doc doc, Query query, AhatSnapshot snapshot, ClassObj clsobj) {
136    doc.section("Class Info");
137    doc.descriptions();
138    doc.description(DocString.text("Super Class"),
139        Value.render(snapshot, clsobj.getSuperClassObj()));
140    doc.description(DocString.text("Class Loader"),
141        Value.render(snapshot, clsobj.getClassLoader()));
142    doc.end();
143
144    doc.section("Static Fields");
145    doc.table(new Column("Type"), new Column("Name"), new Column("Value"));
146    List<Map.Entry<Field, Object>> fields
147      = new ArrayList<Map.Entry<Field, Object>>(clsobj.getStaticFieldValues().entrySet());
148    SubsetSelector<Map.Entry<Field, Object>> selector
149      = new SubsetSelector(query, STATIC_FIELDS_ID, fields);
150    for (Map.Entry<Field, Object> field : selector.selected()) {
151      doc.row(
152          DocString.text(field.getKey().getType().toString()),
153          DocString.text(field.getKey().getName()),
154          Value.render(snapshot, field.getValue()));
155    }
156    doc.end();
157    selector.render(doc);
158  }
159
160  private static void printReferences(
161      Doc doc, Query query, AhatSnapshot snapshot, Instance inst) {
162    doc.section("Objects with References to this Object");
163    if (inst.getHardReferences().isEmpty()) {
164      doc.println(DocString.text("(none)"));
165    } else {
166      doc.table(new Column("Object"));
167      List<Instance> references = inst.getHardReferences();
168      SubsetSelector<Instance> selector = new SubsetSelector(query, HARD_REFS_ID, references);
169      for (Instance ref : selector.selected()) {
170        doc.row(Value.render(snapshot, ref));
171      }
172      doc.end();
173      selector.render(doc);
174    }
175
176    if (inst.getSoftReferences() != null) {
177      doc.section("Objects with Soft References to this Object");
178      doc.table(new Column("Object"));
179      List<Instance> references = inst.getSoftReferences();
180      SubsetSelector<Instance> selector = new SubsetSelector(query, SOFT_REFS_ID, references);
181      for (Instance ref : selector.selected()) {
182        doc.row(Value.render(snapshot, ref));
183      }
184      doc.end();
185      selector.render(doc);
186    }
187  }
188
189  private void printAllocationSite(Doc doc, Query query, Instance inst) {
190    doc.section("Allocation Site");
191    Site site = mSnapshot.getSiteForInstance(inst);
192    SitePrinter.printSite(mSnapshot, doc, query, ALLOCATION_SITE_ID, site);
193  }
194
195  // Draw the bitmap corresponding to this instance if there is one.
196  private static void printBitmap(Doc doc, Instance inst) {
197    Instance bitmap = InstanceUtils.getAssociatedBitmapInstance(inst);
198    if (bitmap != null) {
199      doc.section("Bitmap Image");
200      doc.println(DocString.image(
201            DocString.formattedUri("bitmap?id=%d", bitmap.getId()), "bitmap image"));
202    }
203  }
204
205  private void printDominatorPath(Doc doc, Query query, Instance inst) {
206    doc.section("Dominator Path from Root");
207    List<Instance> path = new ArrayList<Instance>();
208    for (Instance parent = inst;
209        parent != null && !(parent instanceof RootObj);
210        parent = parent.getImmediateDominator()) {
211      path.add(parent);
212    }
213
214    // Add 'null' as a marker for the root.
215    path.add(null);
216    Collections.reverse(path);
217
218    HeapTable.TableConfig<Instance> table = new HeapTable.TableConfig<Instance>() {
219      public String getHeapsDescription() {
220        return "Bytes Retained by Heap";
221      }
222
223      public long getSize(Instance element, Heap heap) {
224        if (element == null) {
225          return mSnapshot.getHeapSize(heap);
226        }
227        int index = mSnapshot.getHeapIndex(heap);
228        return element.getRetainedSize(index);
229      }
230
231      public List<HeapTable.ValueConfig<Instance>> getValueConfigs() {
232        HeapTable.ValueConfig<Instance> value = new HeapTable.ValueConfig<Instance>() {
233          public String getDescription() {
234            return "Object";
235          }
236
237          public DocString render(Instance element) {
238            if (element == null) {
239              return DocString.link(DocString.uri("rooted"), DocString.text("ROOT"));
240            } else {
241              return DocString.text("→ ").append(Value.render(mSnapshot, element));
242            }
243          }
244        };
245        return Collections.singletonList(value);
246      }
247    };
248    HeapTable.render(doc, query, DOMINATOR_PATH_ID, table, mSnapshot, path);
249  }
250
251  public void printDominatedObjects(Doc doc, Query query, Instance inst) {
252    doc.section("Immediately Dominated Objects");
253    List<Instance> instances = mSnapshot.getDominated(inst);
254    if (instances != null) {
255      DominatedList.render(mSnapshot, doc, query, DOMINATED_OBJECTS_ID, instances);
256    } else {
257      doc.println(DocString.text("(none)"));
258    }
259  }
260}
261
262