OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 /// Utility to display [GlobalResult] as a table on the command line. |
| 6 library compiler.tool.stats.print_summary; |
| 7 |
| 8 import 'dart:io'; |
| 9 import 'dart:convert'; |
| 10 import 'dart:math' show max; |
| 11 import 'package:compiler/src/stats/stats.dart'; |
| 12 |
| 13 main(args) { |
| 14 var file = args.length > 0 ? args[0] : 'out.js.stats.json'; |
| 15 var results = GlobalResult.fromJson( |
| 16 JSON.decode(new File(file).readAsStringSync())); |
| 17 print(formatAsTable(results)); |
| 18 } |
| 19 |
| 20 /// Formats [results] as a table. |
| 21 String formatAsTable(GlobalResult results) { |
| 22 var visitor = new _Counter(); |
| 23 results.accept(visitor); |
| 24 var table = new _Table(); |
| 25 table.declareColumn('bundle'); |
| 26 |
| 27 int colorIndex = 0; |
| 28 visitAllMetrics((m, _) { |
| 29 if (m is GroupedMetric) colorIndex = (colorIndex + 1) % _groupColors.length; |
| 30 table.declareColumn(m.name, |
| 31 abbreviate: true, color: _groupColors[colorIndex]); |
| 32 }); |
| 33 table.addHeader(); |
| 34 appendCount(n) => table.addEntry(n == null ? 0 : n); |
| 35 |
| 36 for (var bundle in visitor.bundleTotals.keys) { |
| 37 table.addEntry(bundle); |
| 38 visitAllMetrics( |
| 39 (m, _) => appendCount(visitor.bundleTotals[bundle].counters[m])); |
| 40 } |
| 41 table.addEmptyRow(); |
| 42 table.addHeader(); |
| 43 table.addEntry('total'); |
| 44 visitAllMetrics((m, _) => appendCount(visitor.totals.counters[m])); |
| 45 |
| 46 appendPercent(count, total) { |
| 47 if (count == null) count = 0; |
| 48 if (total == null) total = 0; |
| 49 var percent = count * 100 / total; |
| 50 table.addEntry(percent == 100 ? 100 : percent.toStringAsFixed(2)); |
| 51 } |
| 52 |
| 53 table.addEntry('%'); |
| 54 visitAllMetrics((metric, parents) { |
| 55 if (parents == null || parents.isEmpty) { |
| 56 table.addEntry(100); |
| 57 } else { |
| 58 appendPercent(visitor.totals.counters[metric], |
| 59 visitor.totals.counters[parents[0]]); |
| 60 } |
| 61 }); |
| 62 |
| 63 return table.toString(); |
| 64 } |
| 65 |
| 66 /// Visitor that adds up results for all functions in libraries, and all |
| 67 /// libraries in a bundle. |
| 68 class _Counter extends RecursiveResultVisitor { |
| 69 Map<String, Measurements> bundleTotals = {}; |
| 70 Measurements currentBundleTotals; |
| 71 Measurements totals = new Measurements(); |
| 72 |
| 73 visitBundle(BundleResult bundle) { |
| 74 currentBundleTotals = |
| 75 bundleTotals.putIfAbsent(bundle.name, () => new Measurements()); |
| 76 super.visitBundle(bundle); |
| 77 totals.addFrom(currentBundleTotals); |
| 78 } |
| 79 |
| 80 visitFunction(FunctionResult function) { |
| 81 currentBundleTotals.addFrom(function.measurements); |
| 82 } |
| 83 } |
| 84 |
| 85 /// Helper class to combine all the information in table form. |
| 86 class _Table { |
| 87 int _totalColumns = 0; |
| 88 int get totalColumns => _totalColumns; |
| 89 |
| 90 /// Abbreviations, used to make headers shorter. |
| 91 Map<String, String> abbreviations = {}; |
| 92 |
| 93 /// Width of each column. |
| 94 List<int> widths = <int>[]; |
| 95 |
| 96 /// The header for each column (`header.length == totalColumns`). |
| 97 List header = []; |
| 98 |
| 99 /// The color for each column (`color.length == totalColumns`). |
| 100 List colors = []; |
| 101 |
| 102 /// Each row on the table. Note that all rows have the same size |
| 103 /// (`rows[*].length == totalColumns`). |
| 104 List<List> rows = []; |
| 105 |
| 106 /// Columns to skip, for example, if they are all zero entries. |
| 107 List<bool> _skipped = <bool>[]; |
| 108 |
| 109 /// Whether we started adding entries. Indicates that no more columns can be |
| 110 /// added. |
| 111 bool _sealed = false; |
| 112 |
| 113 /// Current row being built by [addEntry]. |
| 114 List _currentRow; |
| 115 |
| 116 /// Add a column with the given [name]. |
| 117 void declareColumn(String name, |
| 118 {bool abbreviate: false, String color: _NO_COLOR}) { |
| 119 assert(!_sealed); |
| 120 var headerName = name; |
| 121 if (abbreviate) { |
| 122 // abbreviate the header by using only the initials of each word |
| 123 headerName = name.split(' ') |
| 124 .map((s) => s.substring(0, 1).toUpperCase()).join(''); |
| 125 while (abbreviations[headerName] != null) headerName = "$headerName'"; |
| 126 abbreviations[headerName] = name; |
| 127 } |
| 128 widths.add(max(5, headerName.length + 1)); |
| 129 header.add(headerName); |
| 130 colors.add(color); |
| 131 _skipped.add(_totalColumns > 0); |
| 132 _totalColumns++; |
| 133 } |
| 134 |
| 135 /// Add an entry in the table, creating a new row each time [totalColumns] |
| 136 /// entries are added. |
| 137 void addEntry(entry) { |
| 138 if (_currentRow == null) { |
| 139 _sealed = true; |
| 140 _currentRow = []; |
| 141 } |
| 142 int pos = _currentRow.length; |
| 143 assert(pos < _totalColumns); |
| 144 |
| 145 widths[pos] = max(widths[pos], '$entry'.length + 1); |
| 146 _currentRow.add('$entry'); |
| 147 if (entry is int && entry != 0) { |
| 148 _skipped[pos] = false; |
| 149 } |
| 150 |
| 151 if (pos + 1 == _totalColumns) { |
| 152 rows.add(_currentRow); |
| 153 _currentRow = []; |
| 154 } |
| 155 } |
| 156 |
| 157 /// Add an empty row to divide sections of the table. |
| 158 void addEmptyRow() { |
| 159 var emptyRow = []; |
| 160 for (int i = 0; i < _totalColumns; i++) { |
| 161 emptyRow.add('-' * widths[i]); |
| 162 } |
| 163 rows.add(emptyRow); |
| 164 } |
| 165 |
| 166 /// Enter the header titles. OK to do so more than once in long tables. |
| 167 void addHeader() { |
| 168 rows.add(header); |
| 169 } |
| 170 |
| 171 /// Generates a string representation of the table to print on a terminal. |
| 172 // TODO(sigmund): add also a .csv format |
| 173 String toString() { |
| 174 var sb = new StringBuffer(); |
| 175 sb.write('\n'); |
| 176 for (var row in rows) { |
| 177 var lastColor = _NO_COLOR; |
| 178 for (int i = 0; i < _totalColumns; i++) { |
| 179 if (_skipped[i]) continue; |
| 180 var entry = row[i]; |
| 181 var color = colors[i]; |
| 182 if (lastColor != color) { |
| 183 sb.write(color); |
| 184 lastColor = color; |
| 185 } |
| 186 // Align first column to the left, everything else to the right. |
| 187 sb.write( |
| 188 i == 0 ? entry.padRight(widths[i]) : entry.padLeft(widths[i] + 1)); |
| 189 } |
| 190 if (lastColor != _NO_COLOR) sb.write(_NO_COLOR); |
| 191 sb.write('\n'); |
| 192 } |
| 193 sb.write('\nWhere:\n'); |
| 194 for (var id in abbreviations.keys) { |
| 195 sb.write(' $id:'.padRight(7)); |
| 196 sb.write(' ${abbreviations[id]}\n'); |
| 197 } |
| 198 return sb.toString(); |
| 199 } |
| 200 } |
| 201 |
| 202 const _groupColors = const [ |
| 203 _YELLOW_COLOR, |
| 204 _NO_COLOR, |
| 205 ]; |
| 206 |
| 207 const _NO_COLOR = "\x1b[0m"; |
| 208 const _GREEN_COLOR = "\x1b[32m"; |
| 209 const _YELLOW_COLOR = "\x1b[33m"; |
| 210 const _WHITE_COLOR = "\x1b[37m"; |
OLD | NEW |