Home | History | Annotate | Download | only in src
      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.tools.perflib.heap.ArrayInstance;
     20 import com.android.tools.perflib.heap.ClassInstance;
     21 import com.android.tools.perflib.heap.ClassObj;
     22 import com.android.tools.perflib.heap.Field;
     23 import com.android.tools.perflib.heap.Heap;
     24 import com.android.tools.perflib.heap.Instance;
     25 import com.android.tools.perflib.heap.RootObj;
     26 import com.android.tools.perflib.heap.RootType;
     27 import java.io.IOException;
     28 import java.util.ArrayList;
     29 import java.util.Arrays;
     30 import java.util.Collection;
     31 import java.util.Collections;
     32 import java.util.List;
     33 import java.util.Map;
     34 
     35 class 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