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 sizeString(long size, boolean isPlaceHolder)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 */ render(Doc doc, Query query, String id, TableConfig<T> config, AhatSnapshot snapshot, List<T> elements)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. hasNonZeroEntry(AhatHeap heap, TableConfig<T> config, List<T> elements)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