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