mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 22:31:50 +00:00
Adding support for common element comparisons to dart2js_info
Common elements are determined structurally, not by size. Additionally, elements can be restricted to the package-level or sorted by name/size via flags. Change-Id: I8ee4bf70f4ccbf022cdb121f2e61f9416bb9affe Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/222000 Reviewed-by: Joshua Litt <joshualitt@google.com> Reviewed-by: Sigmund Cherem <sigmund@google.com> Commit-Queue: Mark Zhou <markzipan@google.com>
This commit is contained in:
parent
3d56526ba1
commit
5bff102009
|
@ -1,3 +1,7 @@
|
|||
## 0.6.6
|
||||
|
||||
* Added the `comm` tool for finding commonalities between info files.
|
||||
|
||||
## 0.6.5
|
||||
|
||||
* Drop unused dependencies.
|
||||
|
|
|
@ -92,7 +92,11 @@ The following tools are a available today:
|
|||
dependency between functions and fields in your program. Currently it only
|
||||
supports the `some_path` query, which shows a dependency path from one
|
||||
function to another.
|
||||
|
||||
|
||||
* [`common`][common]: a tool that reports the common elements of two info
|
||||
files. Commonality is determined by the element's name, file path, and URI
|
||||
but not code size.
|
||||
|
||||
* [`diff`][diff]: a tool that diffs two info files and reports which
|
||||
program elements have been added, removed, or changed size. This also
|
||||
tells which elements are no longer deferred or have become deferred.
|
||||
|
@ -160,6 +164,44 @@ a fully qualified element name, which includes the library and class name
|
|||
If the name of a function your are looking for is unique enough, it might be
|
||||
sufficient to just write that name as your regular expression.
|
||||
|
||||
### Common tool
|
||||
|
||||
This command-line tool shows common elements between two info files. It can be
|
||||
run as follows:
|
||||
|
||||
```console
|
||||
$ pub global activate dart2js_info # only needed once
|
||||
$ dart2js_info common old.js.info.data new.js.info.data
|
||||
```
|
||||
|
||||
The tool gives a breakdown of the common elements between the two info files,
|
||||
reporting code size discrepancies if they exist.
|
||||
Here's an example output snippet:
|
||||
|
||||
```
|
||||
COMMON ELEMENTS (455 common elements, 70334 bytes -> 70460 bytes)
|
||||
========================================================================
|
||||
dart:_foreign_helper::: 141 bytes
|
||||
dart:_foreign_helper::JS_CONST: 141 bytes
|
||||
dart:_foreign_helper::JS_CONST.code: 0 bytes
|
||||
dart:_interceptors::: 4052 -> 7968 bytes
|
||||
dart:_interceptors::ArrayIterator: 805 bytes
|
||||
dart:_interceptors::ArrayIterator.ArrayIterator: 0 bytes
|
||||
dart:_interceptors::ArrayIterator._current: 91 bytes
|
||||
dart:_interceptors::ArrayIterator._index: 0 bytes
|
||||
dart:_interceptors::ArrayIterator._iterable: 0 bytes
|
||||
dart:_interceptors::ArrayIterator._length: 0 bytes
|
||||
dart:_interceptors::ArrayIterator.current: 0 bytes
|
||||
dart:_interceptors::ArrayIterator.moveNext: 406 bytes
|
||||
dart:_interceptors::Interceptor: 198 bytes
|
||||
dart:_interceptors::Interceptor.toString: 104 -> 182 bytes
|
||||
|
||||
```
|
||||
|
||||
Common elements are sorted by name by default but can be sorted by size with
|
||||
the `--order-by-size` flag. Additionally, the tool can be restricted to
|
||||
just packages with the `--packages-only` flag.
|
||||
|
||||
### Diff tool
|
||||
|
||||
This command-line tool shows a diff between two info files. It can be run
|
||||
|
|
152
pkg/dart2js_info/bin/src/common_command.dart
Normal file
152
pkg/dart2js_info/bin/src/common_command.dart
Normal file
|
@ -0,0 +1,152 @@
|
|||
// Copyright (c) 2021, 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.
|
||||
|
||||
import 'package:args/command_runner.dart';
|
||||
|
||||
import 'package:dart2js_info/info.dart';
|
||||
import 'package:dart2js_info/src/common_element.dart';
|
||||
import 'package:dart2js_info/src/io.dart';
|
||||
import 'package:dart2js_info/src/util.dart';
|
||||
|
||||
import 'usage_exception.dart';
|
||||
|
||||
/// A command that computes the commonalities between two info files.
|
||||
class CommonCommand extends Command<void> with PrintUsageException {
|
||||
@override
|
||||
final String name = "common";
|
||||
@override
|
||||
final String description =
|
||||
"See code element commonalities between two dump-info files.";
|
||||
|
||||
CommonCommand() {
|
||||
argParser.addFlag('packages-only',
|
||||
defaultsTo: false, help: "Show only packages in common");
|
||||
argParser.addFlag('order-by-size',
|
||||
defaultsTo: false,
|
||||
help: "Show output ordered by size in bytes (decreasing). "
|
||||
"If there are size discrepancies, orders by the first "
|
||||
"dump-info file's reported size.");
|
||||
}
|
||||
|
||||
@override
|
||||
void run() async {
|
||||
var args = argResults.rest;
|
||||
if (args.length < 2) {
|
||||
usageException(
|
||||
'Missing arguments, expected two dump-info files to compare');
|
||||
return;
|
||||
}
|
||||
|
||||
var oldInfo = await infoFromFile(args[0]);
|
||||
var newInfo = await infoFromFile(args[1]);
|
||||
var packagesOnly = argResults['packages-only'];
|
||||
var orderBySize = argResults['order-by-size'];
|
||||
|
||||
var commonElements = findCommonalities(oldInfo, newInfo);
|
||||
|
||||
if (packagesOnly) {
|
||||
reportPackages(commonElements, orderBySize: orderBySize);
|
||||
} else {
|
||||
report(commonElements, orderBySize: orderBySize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void report(List<CommonElement> commonElements, {orderBySize = false}) {
|
||||
var oldSizeTotal = 0, newSizeTotal = 0;
|
||||
for (var element in commonElements) {
|
||||
// Only sum sizes from leaf elements so we don't double count.
|
||||
if (element.oldInfo.kind == InfoKind.field ||
|
||||
element.oldInfo.kind == InfoKind.function ||
|
||||
element.oldInfo.kind == InfoKind.closure ||
|
||||
element.oldInfo.kind == InfoKind.typedef) {
|
||||
oldSizeTotal += element.oldInfo.size;
|
||||
newSizeTotal += element.newInfo.size;
|
||||
}
|
||||
}
|
||||
|
||||
_section('COMMON ELEMENTS',
|
||||
elementCount: commonElements.length,
|
||||
oldSizeTotal: oldSizeTotal,
|
||||
newSizeTotal: newSizeTotal);
|
||||
|
||||
if (orderBySize) {
|
||||
commonElements.sort((a, b) => b.oldInfo.size.compareTo(a.oldInfo.size));
|
||||
} else {
|
||||
commonElements.sort((a, b) => a.name.compareTo(b.name));
|
||||
}
|
||||
|
||||
for (var element in commonElements) {
|
||||
var oldSize = element.oldInfo.size;
|
||||
var newSize = element.newInfo.size;
|
||||
if (oldSize == newSize) {
|
||||
print('${element.name}: ${element.oldInfo.size} bytes');
|
||||
} else {
|
||||
print('${element.name}: ${element.oldInfo.size} -> '
|
||||
'${element.newInfo.size} bytes');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reportPackages(List<CommonElement> commonElements, {orderBySize = false}) {
|
||||
// Maps package names to their cumulative size.
|
||||
var oldPackageInfo = <String, int>{};
|
||||
var newPackageInfo = <String, int>{};
|
||||
|
||||
for (int i = 0; i < commonElements.length; i++) {
|
||||
var element = commonElements[i];
|
||||
// Skip non-libraries to avoid double counting elements when accumulating
|
||||
// package-level information.
|
||||
if (element.oldInfo.kind != InfoKind.library) continue;
|
||||
|
||||
var package = packageName(element.oldInfo);
|
||||
if (package == null) continue;
|
||||
|
||||
var oldSize = element.oldInfo.size;
|
||||
var newSize = element.newInfo.size;
|
||||
oldPackageInfo[package] = (oldPackageInfo[package] ?? 0) + oldSize;
|
||||
newPackageInfo[package] = (newPackageInfo[package] ?? 0) + newSize;
|
||||
}
|
||||
|
||||
var oldSizeTotal = 0, newSizeTotal = 0;
|
||||
oldPackageInfo.forEach((oldPackageName, oldPackageSize) {
|
||||
var newPackageSize = newPackageInfo[oldPackageName];
|
||||
oldSizeTotal += oldPackageSize;
|
||||
newSizeTotal += newPackageSize;
|
||||
});
|
||||
|
||||
_section('COMMON ELEMENTS (PACKAGES)',
|
||||
elementCount: oldPackageInfo.keys.length,
|
||||
oldSizeTotal: oldSizeTotal,
|
||||
newSizeTotal: newSizeTotal);
|
||||
|
||||
var packageInfoEntries = oldPackageInfo.entries.toList();
|
||||
|
||||
if (orderBySize) {
|
||||
packageInfoEntries.sort((a, b) => b.value.compareTo(a.value));
|
||||
} else {
|
||||
packageInfoEntries.sort((a, b) => a.key.compareTo(b.key));
|
||||
}
|
||||
|
||||
for (var entry in packageInfoEntries) {
|
||||
var oldSize = entry.value;
|
||||
var newSize = newPackageInfo[entry.key];
|
||||
if (oldSize == newSize) {
|
||||
print('${entry.key}: $oldSize bytes');
|
||||
} else {
|
||||
print('${entry.key}: $oldSize bytes -> $newSize bytes');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _section(String title,
|
||||
{int elementCount, int oldSizeTotal, int newSizeTotal}) {
|
||||
if (oldSizeTotal == newSizeTotal) {
|
||||
print('$title ($elementCount common elements, $oldSizeTotal bytes)');
|
||||
} else {
|
||||
print('$title ($elementCount common elements, '
|
||||
'$oldSizeTotal bytes -> $newSizeTotal bytes)');
|
||||
}
|
||||
print('=' * 72);
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
import 'package:args/command_runner.dart';
|
||||
|
||||
import 'src/code_deps.dart';
|
||||
import 'src/common_command.dart';
|
||||
import 'src/coverage_log_server.dart';
|
||||
import 'src/debug_info.dart';
|
||||
import 'src/diff.dart';
|
||||
|
@ -23,6 +24,7 @@ void main(args) {
|
|||
var commandRunner = CommandRunner("dart2js_info",
|
||||
"collection of tools to digest the output of dart2js's --dump-info")
|
||||
..addCommand(CodeDepsCommand())
|
||||
..addCommand(CommonCommand())
|
||||
..addCommand(CoverageLogServerCommand())
|
||||
..addCommand(DebugCommand())
|
||||
..addCommand(DiffCommand())
|
||||
|
|
116
pkg/dart2js_info/lib/src/common_element.dart
Normal file
116
pkg/dart2js_info/lib/src/common_element.dart
Normal file
|
@ -0,0 +1,116 @@
|
|||
import 'package:dart2js_info/info.dart';
|
||||
import 'package:dart2js_info/src/util.dart';
|
||||
|
||||
class CommonElement {
|
||||
final BasicInfo oldInfo;
|
||||
final BasicInfo newInfo;
|
||||
|
||||
CommonElement(this.oldInfo, this.newInfo);
|
||||
|
||||
get name => longName(oldInfo, useLibraryUri: true);
|
||||
}
|
||||
|
||||
List<CommonElement> findCommonalities(AllInfo oldInfo, AllInfo newInfo) {
|
||||
var finder = _InfoCommonElementFinder(oldInfo, newInfo);
|
||||
finder.run();
|
||||
return finder.commonElements;
|
||||
}
|
||||
|
||||
class _InfoCommonElementFinder extends InfoVisitor<void> {
|
||||
final AllInfo _old;
|
||||
final AllInfo _new;
|
||||
|
||||
BasicInfo _other;
|
||||
|
||||
List<CommonElement> commonElements = <CommonElement>[];
|
||||
|
||||
_InfoCommonElementFinder(this._old, this._new);
|
||||
|
||||
void run() {
|
||||
_commonList(_old.libraries, _new.libraries);
|
||||
}
|
||||
|
||||
@override
|
||||
visitAll(AllInfo info) {
|
||||
throw StateError('should not run common on AllInfo');
|
||||
}
|
||||
|
||||
@override
|
||||
visitProgram(ProgramInfo info) {
|
||||
throw StateError('should not run common on ProgramInfo');
|
||||
}
|
||||
|
||||
@override
|
||||
visitOutput(OutputUnitInfo info) {
|
||||
throw StateError('should not run common on OutputUnitInfo');
|
||||
}
|
||||
|
||||
@override
|
||||
visitConstant(ConstantInfo info) {
|
||||
throw StateError('should not run common on ConstantInfo');
|
||||
}
|
||||
|
||||
@override
|
||||
visitLibrary(LibraryInfo info) {
|
||||
var other = _other as LibraryInfo;
|
||||
commonElements.add(CommonElement(info, other));
|
||||
_commonList(info.topLevelVariables, other.topLevelVariables);
|
||||
_commonList(info.topLevelFunctions, other.topLevelFunctions);
|
||||
_commonList(info.classes, other.classes);
|
||||
}
|
||||
|
||||
@override
|
||||
visitClass(ClassInfo info) {
|
||||
var other = _other as ClassInfo;
|
||||
commonElements.add(CommonElement(info, other));
|
||||
_commonList(info.fields, other.fields);
|
||||
_commonList(info.functions, other.functions);
|
||||
}
|
||||
|
||||
@override
|
||||
visitClassType(ClassTypeInfo info) {
|
||||
var other = _other as ClassInfo;
|
||||
commonElements.add(CommonElement(info, other));
|
||||
}
|
||||
|
||||
@override
|
||||
visitClosure(ClosureInfo info) {
|
||||
var other = _other as ClosureInfo;
|
||||
commonElements.add(CommonElement(info, other));
|
||||
_commonList([info.function], [other.function]);
|
||||
}
|
||||
|
||||
@override
|
||||
visitField(FieldInfo info) {
|
||||
var other = _other as FieldInfo;
|
||||
commonElements.add(CommonElement(info, other));
|
||||
_commonList(info.closures, other.closures);
|
||||
}
|
||||
|
||||
@override
|
||||
visitFunction(FunctionInfo info) {
|
||||
var other = _other as FunctionInfo;
|
||||
commonElements.add(CommonElement(info, other));
|
||||
_commonList(info.closures, other.closures);
|
||||
}
|
||||
|
||||
@override
|
||||
visitTypedef(TypedefInfo info) {
|
||||
var other = _other as ClassInfo;
|
||||
commonElements.add(CommonElement(info, other));
|
||||
}
|
||||
|
||||
void _commonList(List<BasicInfo> oldInfos, List<BasicInfo> newInfos) {
|
||||
var newNames = <String, BasicInfo>{};
|
||||
for (var newInfo in newInfos) {
|
||||
newNames[longName(newInfo, useLibraryUri: true)] = newInfo;
|
||||
}
|
||||
for (var oldInfo in oldInfos) {
|
||||
var oldName = longName(oldInfo, useLibraryUri: true);
|
||||
if (newNames.containsKey(oldName)) {
|
||||
_other = newNames[oldName];
|
||||
oldInfo.accept(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -90,6 +90,19 @@ String longName(Info info, {bool useLibraryUri = false, bool forId = false}) {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
/// Provides the package name associated with [info] or null otherwise.
|
||||
String packageName(Info info) {
|
||||
while (info.parent != null) {
|
||||
info = info.parent;
|
||||
}
|
||||
if (info is LibraryInfo) {
|
||||
if (info.uri.scheme == 'package') {
|
||||
return '${info.uri}'.split('/').first;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Produce a string containing [value] padded with white space up to [n] chars.
|
||||
pad(value, n, {bool right = false}) {
|
||||
var s = '$value';
|
||||
|
|
Loading…
Reference in a new issue