blob: 19f85a8ff6a35cbdbdcb5b1d5f2fd0084b699259 [file] [log] [blame]
// Copyright (c) 2020, 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.
/// This tool generates a summary report from a binary size reports produced by
/// the AOT compiler's --print-instructions-sizes-to and
/// --write-v8-snapshot-profile-to flags.
library vm_snapshot_analysis.summary;
import 'dart:async';
import 'dart:io';
import 'package:args/command_runner.dart';
import 'package:vm_snapshot_analysis/ascii_table.dart';
import 'package:vm_snapshot_analysis/program_info.dart';
import 'package:vm_snapshot_analysis/utils.dart';
import 'package:vm_snapshot_analysis/v8_profile.dart';
class SummaryCommand extends Command<void> {
@override
final name = 'summary';
@override
final description = '''
Generate a summary report from a AOT compilers binary size dumps.
This tool can process snapshot size reports produced by
--print-instructions-sizes-to=symbol-sizes.json and
--write-v8-snapshot-profile-to=profile.heapsnapshot flags.
''';
SummaryCommand() {
argParser
..addOption('column-width',
help: 'Truncate column content to the given width'
' (${AsciiTable.unlimitedWidth} means do not truncate).',
defaultsTo: AsciiTable.unlimitedWidth.toString())
..addOption('by',
abbr: 'b',
help: 'Choose breakdown rule of the output.',
allowed: ['method', 'class', 'library', 'package'],
defaultsTo: 'method')
..addOption(
'where',
abbr: 'w',
help: 'Filter output using the given glob.',
)
..addFlag('collapse-anonymous-closures', help: '''
Collapse all anonymous closures from the same scope into a single entry.
When comparing size of AOT snapshots for two different versions of a
program there is no reliable way to precisely establish which two anonymous
closures are the same and should be compared in size - so
comparison might produce a noisy output. This option reduces confusion
by collapsing different anonymous closures within the same scope into a
single entry. Note that when comparing the same application compiled
with two different versions of an AOT compiler closures can be distinguished
precisely based on their source position (which is included in their name).
''');
}
@override
String get invocation =>
super.invocation.replaceAll('[arguments]', '<sizes.json>');
@override
Future<void> run() async {
if (argResults.rest.length != 1) {
usageException('Need to specify input JSON.');
}
final input = File(argResults.rest[0]);
if (!input.existsSync()) {
usageException('Input file ${input.path} does not exist!');
}
final columnWidth = argResults['column-width'];
final maxWidth = int.tryParse(columnWidth);
if (maxWidth == null) {
usageException(
'Specified column width (${columnWidth}) is not an integer');
}
await outputSummary(input,
maxWidth: maxWidth,
granularity: _parseHistogramType(argResults['by']),
collapseAnonymousClosures: argResults['collapse-anonymous-closures'],
filter: argResults['where']);
}
static HistogramType _parseHistogramType(String value) {
switch (value) {
case 'method':
return HistogramType.bySymbol;
case 'class':
return HistogramType.byClass;
case 'library':
return HistogramType.byLibrary;
case 'package':
return HistogramType.byPackage;
}
return null;
}
}
void outputSummary(File input,
{int maxWidth = 0,
bool collapseAnonymousClosures = false,
HistogramType granularity = HistogramType.bySymbol,
String filter}) async {
final info = await loadProgramInfo(input);
// Compute histogram.
final histogram = computeHistogram(info, granularity, filter: filter);
// Now produce the report table.
const topToReport = 30;
printHistogram(info, histogram,
prefix: histogram.bySize.take(topToReport), maxWidth: maxWidth);
if (info.snapshotInfo != null) {
print('\nBreakdown by object type:');
final typeHistogram =
computeHistogram(info, HistogramType.byNodeType, filter: filter);
printHistogram(info, typeHistogram,
prefix: typeHistogram.bySize, maxWidth: maxWidth);
print(bucketLegend);
}
}