| Index: pkg/compiler/tool/stats/print_summary.dart
|
| diff --git a/pkg/compiler/tool/stats/print_summary.dart b/pkg/compiler/tool/stats/print_summary.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..cae6b8f6c7e9b3531906c0586bcaa06a8b1ebfa4
|
| --- /dev/null
|
| +++ b/pkg/compiler/tool/stats/print_summary.dart
|
| @@ -0,0 +1,210 @@
|
| +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
| +// for details. All rights reserved. Use of this source code is governed by a
|
| +// BSD-style license that can be found in the LICENSE file.
|
| +
|
| +/// Utility to display [GlobalResult] as a table on the command line.
|
| +library compiler.tool.stats.print_summary;
|
| +
|
| +import 'dart:io';
|
| +import 'dart:convert';
|
| +import 'dart:math' show max;
|
| +import 'package:compiler/src/stats/stats.dart';
|
| +
|
| +main(args) {
|
| + var file = args.length > 0 ? args[0] : 'out.js.stats.json';
|
| + var results = GlobalResult.fromJson(
|
| + JSON.decode(new File(file).readAsStringSync()));
|
| + print(formatAsTable(results));
|
| +}
|
| +
|
| +/// Formats [results] as a table.
|
| +String formatAsTable(GlobalResult results) {
|
| + var visitor = new _Counter();
|
| + results.accept(visitor);
|
| + var table = new _Table();
|
| + table.declareColumn('bundle');
|
| +
|
| + int colorIndex = 0;
|
| + visitAllMetrics((m, _) {
|
| + if (m is GroupedMetric) colorIndex = (colorIndex + 1) % _groupColors.length;
|
| + table.declareColumn(m.name,
|
| + abbreviate: true, color: _groupColors[colorIndex]);
|
| + });
|
| + table.addHeader();
|
| + appendCount(n) => table.addEntry(n == null ? 0 : n);
|
| +
|
| + for (var bundle in visitor.bundleTotals.keys) {
|
| + table.addEntry(bundle);
|
| + visitAllMetrics(
|
| + (m, _) => appendCount(visitor.bundleTotals[bundle].counters[m]));
|
| + }
|
| + table.addEmptyRow();
|
| + table.addHeader();
|
| + table.addEntry('total');
|
| + visitAllMetrics((m, _) => appendCount(visitor.totals.counters[m]));
|
| +
|
| + appendPercent(count, total) {
|
| + if (count == null) count = 0;
|
| + if (total == null) total = 0;
|
| + var percent = count * 100 / total;
|
| + table.addEntry(percent == 100 ? 100 : percent.toStringAsFixed(2));
|
| + }
|
| +
|
| + table.addEntry('%');
|
| + visitAllMetrics((metric, parents) {
|
| + if (parents == null || parents.isEmpty) {
|
| + table.addEntry(100);
|
| + } else {
|
| + appendPercent(visitor.totals.counters[metric],
|
| + visitor.totals.counters[parents[0]]);
|
| + }
|
| + });
|
| +
|
| + return table.toString();
|
| +}
|
| +
|
| +/// Visitor that adds up results for all functions in libraries, and all
|
| +/// libraries in a bundle.
|
| +class _Counter extends RecursiveResultVisitor {
|
| + Map<String, Measurements> bundleTotals = {};
|
| + Measurements currentBundleTotals;
|
| + Measurements totals = new Measurements();
|
| +
|
| + visitBundle(BundleResult bundle) {
|
| + currentBundleTotals =
|
| + bundleTotals.putIfAbsent(bundle.name, () => new Measurements());
|
| + super.visitBundle(bundle);
|
| + totals.addFrom(currentBundleTotals);
|
| + }
|
| +
|
| + visitFunction(FunctionResult function) {
|
| + currentBundleTotals.addFrom(function.measurements);
|
| + }
|
| +}
|
| +
|
| +/// Helper class to combine all the information in table form.
|
| +class _Table {
|
| + int _totalColumns = 0;
|
| + int get totalColumns => _totalColumns;
|
| +
|
| + /// Abbreviations, used to make headers shorter.
|
| + Map<String, String> abbreviations = {};
|
| +
|
| + /// Width of each column.
|
| + List<int> widths = <int>[];
|
| +
|
| + /// The header for each column (`header.length == totalColumns`).
|
| + List header = [];
|
| +
|
| + /// The color for each column (`color.length == totalColumns`).
|
| + List colors = [];
|
| +
|
| + /// Each row on the table. Note that all rows have the same size
|
| + /// (`rows[*].length == totalColumns`).
|
| + List<List> rows = [];
|
| +
|
| + /// Columns to skip, for example, if they are all zero entries.
|
| + List<bool> _skipped = <bool>[];
|
| +
|
| + /// Whether we started adding entries. Indicates that no more columns can be
|
| + /// added.
|
| + bool _sealed = false;
|
| +
|
| + /// Current row being built by [addEntry].
|
| + List _currentRow;
|
| +
|
| + /// Add a column with the given [name].
|
| + void declareColumn(String name,
|
| + {bool abbreviate: false, String color: _NO_COLOR}) {
|
| + assert(!_sealed);
|
| + var headerName = name;
|
| + if (abbreviate) {
|
| + // abbreviate the header by using only the initials of each word
|
| + headerName = name.split(' ')
|
| + .map((s) => s.substring(0, 1).toUpperCase()).join('');
|
| + while (abbreviations[headerName] != null) headerName = "$headerName'";
|
| + abbreviations[headerName] = name;
|
| + }
|
| + widths.add(max(5, headerName.length + 1));
|
| + header.add(headerName);
|
| + colors.add(color);
|
| + _skipped.add(_totalColumns > 0);
|
| + _totalColumns++;
|
| + }
|
| +
|
| + /// Add an entry in the table, creating a new row each time [totalColumns]
|
| + /// entries are added.
|
| + void addEntry(entry) {
|
| + if (_currentRow == null) {
|
| + _sealed = true;
|
| + _currentRow = [];
|
| + }
|
| + int pos = _currentRow.length;
|
| + assert(pos < _totalColumns);
|
| +
|
| + widths[pos] = max(widths[pos], '$entry'.length + 1);
|
| + _currentRow.add('$entry');
|
| + if (entry is int && entry != 0) {
|
| + _skipped[pos] = false;
|
| + }
|
| +
|
| + if (pos + 1 == _totalColumns) {
|
| + rows.add(_currentRow);
|
| + _currentRow = [];
|
| + }
|
| + }
|
| +
|
| + /// Add an empty row to divide sections of the table.
|
| + void addEmptyRow() {
|
| + var emptyRow = [];
|
| + for (int i = 0; i < _totalColumns; i++) {
|
| + emptyRow.add('-' * widths[i]);
|
| + }
|
| + rows.add(emptyRow);
|
| + }
|
| +
|
| + /// Enter the header titles. OK to do so more than once in long tables.
|
| + void addHeader() {
|
| + rows.add(header);
|
| + }
|
| +
|
| + /// Generates a string representation of the table to print on a terminal.
|
| + // TODO(sigmund): add also a .csv format
|
| + String toString() {
|
| + var sb = new StringBuffer();
|
| + sb.write('\n');
|
| + for (var row in rows) {
|
| + var lastColor = _NO_COLOR;
|
| + for (int i = 0; i < _totalColumns; i++) {
|
| + if (_skipped[i]) continue;
|
| + var entry = row[i];
|
| + var color = colors[i];
|
| + if (lastColor != color) {
|
| + sb.write(color);
|
| + lastColor = color;
|
| + }
|
| + // Align first column to the left, everything else to the right.
|
| + sb.write(
|
| + i == 0 ? entry.padRight(widths[i]) : entry.padLeft(widths[i] + 1));
|
| + }
|
| + if (lastColor != _NO_COLOR) sb.write(_NO_COLOR);
|
| + sb.write('\n');
|
| + }
|
| + sb.write('\nWhere:\n');
|
| + for (var id in abbreviations.keys) {
|
| + sb.write(' $id:'.padRight(7));
|
| + sb.write(' ${abbreviations[id]}\n');
|
| + }
|
| + return sb.toString();
|
| + }
|
| +}
|
| +
|
| +const _groupColors = const [
|
| + _YELLOW_COLOR,
|
| + _NO_COLOR,
|
| +];
|
| +
|
| +const _NO_COLOR = "\x1b[0m";
|
| +const _GREEN_COLOR = "\x1b[32m";
|
| +const _YELLOW_COLOR = "\x1b[33m";
|
| +const _WHITE_COLOR = "\x1b[37m";
|
|
|