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.AhatHeap;
     20 import com.android.ahat.heapdump.AhatSnapshot;
     21 import com.android.ahat.heapdump.Diffable;
     22 import java.util.ArrayList;
     23 import java.util.HashMap;
     24 import java.util.List;
     25 import java.util.Map;
     26 
     27 /**
     28  * Class for rendering a table that includes sizes of some kind for each heap.
     29  */
     30 class HeapTable {
     31   /**
     32    * Configuration for a value column of a heap table.
     33    */
     34   public interface ValueConfig<T> {
     35     String getDescription();
     36     DocString render(T element);
     37   }
     38 
     39   /**
     40    * Configuration for the HeapTable.
     41    */
     42   public interface TableConfig<T> {
     43     String getHeapsDescription();
     44     long getSize(T element, AhatHeap heap);
     45     List<ValueConfig<T>> getValueConfigs();
     46   }
     47 
     48   private static DocString sizeString(long size, boolean isPlaceHolder) {
     49     DocString string = new DocString();
     50     if (isPlaceHolder) {
     51       string.append(DocString.removed("del"));
     52     } else if (size != 0) {
     53       string.appendFormat("%,14d", size);
     54     }
     55     return string;
     56   }
     57 
     58   /**
     59    * Render the table to the given document.
     60    * @param query - The page query.
     61    * @param id - A unique identifier for the table on the page.
     62    */
     63   public static <T extends Diffable<T>> void render(Doc doc, Query query, String id,
     64       TableConfig<T> config, AhatSnapshot snapshot, List<T> elements) {
     65     // Only show the heaps that have non-zero entries.
     66     List<AhatHeap> heaps = new ArrayList<AhatHeap>();
     67     for (AhatHeap heap : snapshot.getHeaps()) {
     68       if (hasNonZeroEntry(heap, config, elements)) {
     69         heaps.add(heap);
     70       }
     71     }
     72 
     73     List<ValueConfig<T>> values = config.getValueConfigs();
     74 
     75     // Print the heap and values descriptions.
     76     List<Column> subcols = new ArrayList<Column>();
     77     for (AhatHeap heap : heaps) {
     78       subcols.add(new Column(heap.getName(), Column.Align.RIGHT));
     79       subcols.add(new Column("", Column.Align.RIGHT, snapshot.isDiffed()));
     80     }
     81     boolean showTotal = heaps.size() > 1;
     82     subcols.add(new Column("Total", Column.Align.RIGHT, showTotal));
     83     subcols.add(new Column("", Column.Align.RIGHT, showTotal && snapshot.isDiffed()));
     84     List<Column> cols = new ArrayList<Column>();
     85     for (ValueConfig value : values) {
     86       cols.add(new Column(value.getDescription()));
     87     }
     88     doc.table(DocString.text(config.getHeapsDescription()), subcols, cols);
     89 
     90     // Print the entries up to the selected limit.
     91     SubsetSelector<T> selector = new SubsetSelector(query, id, elements);
     92     ArrayList<DocString> vals = new ArrayList<DocString>();
     93     for (T elem : selector.selected()) {
     94       T base = elem.getBaseline();
     95       vals.clear();
     96       long total = 0;
     97       long basetotal = 0;
     98       for (AhatHeap heap : heaps) {
     99         long size = config.getSize(elem, heap);
    100         long basesize = config.getSize(base, heap.getBaseline());
    101         total += size;
    102         basetotal += basesize;
    103         vals.add(sizeString(size, elem.isPlaceHolder()));
    104         vals.add(DocString.delta(elem.isPlaceHolder(), base.isPlaceHolder(), size, basesize));
    105       }
    106       vals.add(sizeString(total, elem.isPlaceHolder()));
    107       vals.add(DocString.delta(elem.isPlaceHolder(), base.isPlaceHolder(), total, basetotal));
    108 
    109       for (ValueConfig<T> value : values) {
    110         vals.add(value.render(elem));
    111       }
    112       doc.row(vals.toArray(new DocString[0]));
    113     }
    114 
    115     // Print a summary of the remaining entries if there are any.
    116     List<T> remaining = selector.remaining();
    117     if (!remaining.isEmpty()) {
    118       Map<AhatHeap, Long> summary = new HashMap<AhatHeap, Long>();
    119       Map<AhatHeap, Long> basesummary = new HashMap<AhatHeap, Long>();
    120       for (AhatHeap heap : heaps) {
    121         summary.put(heap, 0L);
    122         basesummary.put(heap, 0L);
    123       }
    124 
    125       for (T elem : remaining) {
    126         for (AhatHeap heap : heaps) {
    127           long size = config.getSize(elem, heap);
    128           summary.put(heap, summary.get(heap) + size);
    129 
    130           long basesize = config.getSize(elem.getBaseline(), heap.getBaseline());
    131           basesummary.put(heap, basesummary.get(heap) + basesize);
    132         }
    133       }
    134 
    135       vals.clear();
    136       long total = 0;
    137       long basetotal = 0;
    138       for (AhatHeap heap : heaps) {
    139         long size = summary.get(heap);
    140         long basesize = basesummary.get(heap);
    141         total += size;
    142         basetotal += basesize;
    143         vals.add(sizeString(size, false));
    144         vals.add(DocString.delta(false, false, size, basesize));
    145       }
    146       vals.add(sizeString(total, false));
    147       vals.add(DocString.delta(false, false, total, basetotal));
    148 
    149       for (ValueConfig<T> value : values) {
    150         vals.add(DocString.text("..."));
    151       }
    152       doc.row(vals.toArray(new DocString[0]));
    153     }
    154     doc.end();
    155     selector.render(doc);
    156   }
    157 
    158   // Returns true if the given heap has a non-zero size entry.
    159   public static <T extends Diffable<T>> boolean hasNonZeroEntry(AhatHeap heap,
    160       TableConfig<T> config, List<T> elements) {
    161     AhatHeap baseheap = heap.getBaseline();
    162     if (heap.getSize() > 0 || baseheap.getSize() > 0) {
    163       for (T element : elements) {
    164         if (config.getSize(element, heap) > 0 ||
    165             config.getSize(element.getBaseline(), baseheap) > 0) {
    166           return true;
    167         }
    168       }
    169     }
    170     return false;
    171   }
    172 }
    173 
    174