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.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 
     50   public ObjectHandler(AhatSnapshot snapshot) {
     51     mSnapshot = snapshot;
     52   }
     53 
     54   @Override
     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 
    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 
    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 
    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 
    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 
    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 
    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.
    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 
    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 
    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