1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-08 12:06:26 +00:00

[vm/tools] Move snapshot analysis tool to its own package.

This code will be published on Pub so that Dev Tools can consume it.

Cq-Include-Trybots: luci.dart.try:pkg-linux-debug-try,pkg-linux-release-try,pkg-win-release-try,pkg-mac-release-try
Change-Id: Iddfbd3f0976af218d29ac20b452fbb139983a484
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/152008
Reviewed-by: Tess Strickland <sstrickl@google.com>
Commit-Queue: Vyacheslav Egorov <vegorov@google.com>
This commit is contained in:
Vyacheslav Egorov 2020-06-23 13:14:41 +00:00 committed by commit-bot@chromium.org
parent 2d73cb53a1
commit 117df0e98b
29 changed files with 11120 additions and 64 deletions

View File

@ -11,7 +11,7 @@
"constraint, update this by running tools/generate_package_config.dart."
],
"configVersion": 2,
"generated": "2020-06-17T09:39:51.245406",
"generated": "2020-06-23T13:49:13.378284",
"generator": "tools/generate_package_config.dart",
"packages": [
{
@ -560,7 +560,7 @@
"name": "stack_trace",
"rootUri": "../third_party/pkg/stack_trace",
"packageUri": "lib/",
"languageVersion": "1.23"
"languageVersion": "2.0"
},
{
"name": "stagehand",
@ -676,6 +676,12 @@
"packageUri": "lib/",
"languageVersion": "2.6"
},
{
"name": "vm_snapshot_analysis",
"rootUri": "../pkg/vm_snapshot_analysis",
"packageUri": "lib/",
"languageVersion": "2.8"
},
{
"name": "watcher",
"rootUri": "../third_party/pkg/watcher",

View File

@ -112,6 +112,7 @@ typed_data:third_party/pkg/typed_data/lib
usage:third_party/pkg/usage/lib
vm:pkg/vm/lib
vm_service:pkg/vm_service/lib
vm_snapshot_analysis:pkg/vm_snapshot_analysis/lib
watcher:third_party/pkg/watcher/lib
web_components:third_party/pkg/web_components/lib
web_socket_channel:third_party/pkg/web_socket_channel/lib

3
pkg/vm_snapshot_analysis/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.dart_tool/
.packages
pubspec.lock

View File

@ -0,0 +1,6 @@
# Below is a list of people and organizations that have contributed
# to the Dart project. Names should be added to the list like so:
#
# Name/Organization <email address>
Google LLC

View File

@ -0,0 +1,5 @@
# Changelog
## 0.1.0
- Initial release

View File

@ -0,0 +1,26 @@
Copyright 2020, the Dart project authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,137 @@
# vm_snapshot_analysis
This package provides libraries and a utility for analysing the size and
contents of Dart VM AOT snapshots based on the output of
`--print-instructions-sizes-to` and `--write-v8-snapshot-profile-to` VM flags.
## AOT Snapshot Basics
Dart VM AOT snapshot is simply a serialized representation of the Dart VM
heap graph. It consists of two parts: data (e.g. strings, `const` instances,
objects representing classes, libraries, functions and runtime metadata) and
executable code (machine code generated from Dart sources). Some nodes in this
graph have clean and direct relationship to the original program (e.g. objects
representing libraries, classes, functions), while other nodes don't. Bitwise
equivalent objects can be deduplicated and shared (e.g. two functions with the
same body will end up using the same machine code object). This makes
impossible to attribute of every single byte from the snapshot to a particular
place in the program with a 100% accuracy.
* `--print-instructions-sizes-to` attributes _executable code_ from the snapshot
to a particular Dart function (or internal stub) from which this code
originated (ignoring deduplication). Executable code usually constitutes around
half of the snapshot, those this varies depending on the application.
* `--write-v8-snapshot-profile-to` is a graph representation of the snapshot,
it attributes bytes written into a snapshot to a node in the heap graph. This
format covers both data and code sections of the snapshot.
## CLI
The command line interface to the tools in this package is provided by a single
entry point `bin/analyse.dart`. It consumes output of
`--print-instructions-sizes-to` and `--write-v8-snapshot-profile-to` flags and
presents it in different human readable ways.
This script can be intalled globally as `snapshot_analysis` using
```console
$ pub global activate vm_snapshot_analysis
```
`snapshot_analysis` supports the following subcommands:
### `summary`
```console
$ snapshot_analysis summary [-b granularity] [-w filter] <input.json>
```
This command shows breakdown of snapshot bytes at the given `granularity` (e.g.
`method`, `class`, `library` or `package`), filtered by the given substring
`filter`.
For example, here is a output showing how many bytes from a snapshot
can be attributed to classes in the `dart:core` library:
```console
$ pkg/vm/bin/snapshot_analysis.dart summary -b class -w dart:core profile.json
+-----------+------------------------+--------------+---------+----------+
| Library | Class | Size (Bytes) | Percent | Of total |
+-----------+------------------------+--------------+---------+----------+
| dart:core | _Uri | 43563 | 15.53% | 5.70% |
| dart:core | _StringBase | 28831 | 10.28% | 3.77% |
| dart:core | :: | 27559 | 9.83% | 3.60% |
| @other | | 25467 | 9.08% | 3.33% |
| dart:core | Uri | 14936 | 5.33% | 1.95% |
| dart:core | int | 12276 | 4.38% | 1.61% |
| dart:core | NoSuchMethodError | 12222 | 4.36% | 1.60% |
...
```
Here objects which can be attributed to `_Uri` take `5.7%` of the snapshot,
at the same time objects which can be attributed to `dart:core` library
but not to any specific class within this library take `3.33%` of the snapshot.
### `compare`
```console
$ snapshot_analysis compare [-b granularity] <old.json> <new.json>
```
This command shows comparison between two size profiles, allowing to understand
changes to which part of the program contributed most to the change in the
overall snapshot size.
```console
$ pkg/vm/bin/snapshot_analysis.dart compare -b class old.json new.json
+------------------------+--------------------------+--------------+---------+
| Library | Class | Diff (Bytes) | Percent |
+------------------------+--------------------------+--------------+---------+
| dart:core | _SimpleUri | 11519 | 22.34% |
| dart:core | _Uri | 6563 | 12.73% |
| dart:io | _RandomAccessFile | 5337 | 10.35% |
| @other | | 4009 | 7.78% |
...
```
In this example `11519` more bytes can be attributed to `_SimpleUri` class in
`new.json` compared to `old.json`.
### `treemap`
```console
$ snapshot_analysis treemap <input.json> <output-dir>
$ google-chrome <output-dir>/index.html
```
This command generates treemap representation of the information from the
profile `input.json` and stores it in `output-dir` directory. Treemap can
later be viewed by opening `<output-dir>/index.html` in the browser of your
choice.
## API
This package can also be used as a building block for other packages which
want to analyse VM AOT snapshots.
* `package:vm_snapshot_analysis/instruction_sizes.dart` provides helpers to
read output of `--print-instructions-sizes-to=...`
* `package:vm_snapshot_analysis/v8_profile.dart` provides helpers to read
output of `--write-v8-snapshot-profile-to=...`
Both formats can be converted into a `ProgramInfo` structure which attempts
to breakdown snapshot size into hierarchical representation of the program
structure which can be understood by a Dart developer, attributing bytes
to packages, libraries, classes and functions.
* `package:vm_snapshot_analysis/utils.dart` contains helper method
`loadProgramInfo` which automatically detects format of the input JSON file
and creates `ProgramInfo` in an appropriate way, allowing to write code
which works in the same way with both formats.
## Features and bugs
Please file feature requests and bugs at the [issue tracker][tracker].
[tracker]: https://github.com/dart-lang/sdk/issues

View File

@ -0,0 +1,4 @@
include: package:pedantic/analysis_options.1.8.0.yaml
analyzer:
exclude:
- lib/src/assets/**

View File

@ -1,15 +1,17 @@
#!/usr/bin/env dart
// 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.
import 'dart:io';
import 'package:args/command_runner.dart';
import 'package:vm/snapshot/commands/compare.dart';
import 'package:vm/snapshot/commands/summary.dart';
import 'package:vm/snapshot/commands/treemap.dart';
import 'package:vm_snapshot_analysis/src/commands/compare.dart';
import 'package:vm_snapshot_analysis/src/commands/summary.dart';
import 'package:vm_snapshot_analysis/src/commands/treemap.dart';
final runner = CommandRunner('snapshot_analysis.dart',
final runner = CommandRunner(Platform.script.pathSegments.last,
'Tools for binary size analysis of Dart VM AOT snapshots.')
..addCommand(TreemapCommand())
..addCommand(CompareCommand())

View File

@ -2,7 +2,7 @@
// 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.
library vm.snapshot.ascii_table;
library vm_snapshot_analysis.ascii_table;
import 'dart:math' as math;
@ -126,7 +126,7 @@ class AsciiTable {
final List<Row> rows = <Row>[];
AsciiTable({List<dynamic> header, this.maxWidth: unlimitedWidth}) {
AsciiTable({List<dynamic> header, this.maxWidth = unlimitedWidth}) {
if (header != null) {
addSeparator();
addRow(header);

View File

@ -3,10 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
/// Helper functions for parsing output of `--print-instructions-sizes-to` flag.
library vm.snapshot.instruction_sizes;
library vm_snapshot_analysis.instruction_sizes;
import 'package:vm/snapshot/name.dart';
import 'package:vm/snapshot/program_info.dart';
import 'package:vm_snapshot_analysis/name.dart';
import 'package:vm_snapshot_analysis/program_info.dart';
/// Parse the output of `--print-instructions-sizes-to` saved in the given
/// file [input].

View File

@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
/// Helpers for parsing Code object name produced by Code::QualifiedName
library vm.snapshot.name;
library vm_snapshot_analysis.name;
// Wrapper around the name of a Code object produced by Code::QualifiedName.
//

View File

@ -3,11 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
/// Classes for representing information about the program structure.
library vm.snapshot.program_info;
library vm_snapshot_analysis.program_info;
import 'package:meta/meta.dart';
import 'package:vm/snapshot/v8_profile.dart';
import 'package:vm_snapshot_analysis/v8_profile.dart';
/// Represents information about compiled program.
class ProgramInfo {

View File

@ -0,0 +1,938 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// TODO:
// 1. Visibility functions: base on boxPadding.t, not 15
// 2. Track a maxDisplayDepth that is user-settable:
// maxDepth == currentRoot.depth + maxDisplayDepth
function D3SymbolTreeMap(mapWidth, mapHeight, levelsToShow) {
this._mapContainer = undefined;
this._mapWidth = mapWidth;
this._mapHeight = mapHeight;
this.boxPadding = {'l': 5, 'r': 5, 't': 20, 'b': 5};
this.infobox = undefined;
this._maskContainer = undefined;
this._highlightContainer = undefined;
// Transition in this order:
// 1. Exiting items go away.
// 2. Updated items move.
// 3. New items enter.
this._exitDuration=500;
this._updateDuration=500;
this._enterDuration=500;
this._firstTransition=true;
this._layout = undefined;
this._currentRoot = undefined;
this._currentNodes = undefined;
this._treeData = undefined;
this._maxLevelsToShow = levelsToShow;
this._currentMaxDepth = this._maxLevelsToShow;
}
/**
* Make a number pretty, with comma separators.
*/
D3SymbolTreeMap._pretty = function(num) {
var asString = String(num);
var result = '';
var counter = 0;
for (var x = asString.length - 1; x >= 0; x--) {
counter++;
if (counter === 4) {
result = ',' + result;
counter = 1;
}
result = asString.charAt(x) + result;
}
return result;
}
/**
* Express a number in terms of KiB, MiB, GiB, etc.
* Note that these are powers of 2, not of 10.
*/
D3SymbolTreeMap._byteify = function(num) {
var suffix;
if (num >= 1024) {
if (num >= 1024 * 1024 * 1024) {
suffix = 'GiB';
num = num / (1024 * 1024 * 1024);
} else if (num >= 1024 * 1024) {
suffix = 'MiB';
num = num / (1024 * 1024);
} else if (num >= 1024) {
suffix = 'KiB'
num = num / 1024;
}
return num.toFixed(2) + ' ' + suffix;
}
return num + ' B';
}
D3SymbolTreeMap._NM_SYMBOL_TYPE_DESCRIPTIONS = {
// Definitions concisely derived from the nm 'man' page
'A': 'Global absolute (A)',
'B': 'Global uninitialized data (B)',
'b': 'Local uninitialized data (b)',
'C': 'Global uninitialized common (C)',
'D': 'Global initialized data (D)',
'd': 'Local initialized data (d)',
'G': 'Global small initialized data (G)',
'g': 'Local small initialized data (g)',
'i': 'Indirect function (i)',
'N': 'Debugging (N)',
'p': 'Stack unwind (p)',
'R': 'Global read-only data (R)',
'r': 'Local read-only data (r)',
'S': 'Global small uninitialized data (S)',
's': 'Local small uninitialized data (s)',
'T': 'Global code (T)',
't': 'Local code (t)',
'U': 'Undefined (U)',
'u': 'Unique (u)',
'V': 'Global weak object (V)',
'v': 'Local weak object (v)',
'W': 'Global weak symbol (W)',
'w': 'Local weak symbol (w)',
'@': 'Vtable entry (@)', // non-standard, hack.
'-': 'STABS debugging (-)',
'?': 'Unrecognized (?)',
};
D3SymbolTreeMap._NM_SYMBOL_TYPES = '';
for (var symbol_type in D3SymbolTreeMap._NM_SYMBOL_TYPE_DESCRIPTIONS) {
D3SymbolTreeMap._NM_SYMBOL_TYPES += symbol_type;
}
/**
* Given a symbol type code, look up and return a human-readable description
* of that symbol type. If the symbol type does not match one of the known
* types, the unrecognized description (corresponding to symbol type '?') is
* returned instead of null or undefined.
*/
D3SymbolTreeMap._getSymbolDescription = function(type) {
var result = D3SymbolTreeMap._NM_SYMBOL_TYPE_DESCRIPTIONS[type];
if (result === undefined) {
result = D3SymbolTreeMap._NM_SYMBOL_TYPE_DESCRIPTIONS['?'];
}
return result;
}
// Qualitative 12-value pastel Brewer palette.
D3SymbolTreeMap._colorArray = [
'rgb(141,211,199)',
'rgb(255,255,179)',
'rgb(190,186,218)',
'rgb(251,128,114)',
'rgb(128,177,211)',
'rgb(253,180,98)',
'rgb(179,222,105)',
'rgb(252,205,229)',
'rgb(217,217,217)',
'rgb(188,128,189)',
'rgb(204,235,197)',
'rgb(255,237,111)'];
D3SymbolTreeMap._initColorMap = function() {
var map = {};
var numColors = D3SymbolTreeMap._colorArray.length;
var count = 0;
for (var key in D3SymbolTreeMap._NM_SYMBOL_TYPE_DESCRIPTIONS) {
var index = count++ % numColors;
map[key] = d3.rgb(D3SymbolTreeMap._colorArray[index]);
}
D3SymbolTreeMap._colorMap = map;
}
D3SymbolTreeMap._initColorMap();
D3SymbolTreeMap.getColorForType = function(type) {
var result = D3SymbolTreeMap._colorMap[type];
if (result === undefined) return d3.rgb('rgb(255,255,255)');
return result;
}
D3SymbolTreeMap.prototype.init = function() {
this.infobox = this._createInfoBox();
this._mapContainer = d3.select('body').append('div')
.style('position', 'relative')
.style('width', this._mapWidth)
.style('height', this._mapHeight)
.style('padding', 0)
.style('margin', 0)
.style('box-shadow', '5px 5px 5px #888');
this._layout = this._createTreeMapLayout();
this._setData(tree_data); // TODO: Don't use global 'tree_data'
}
/**
* Sets the data displayed by the treemap and layint out the map.
*/
D3SymbolTreeMap.prototype._setData = function(data) {
this._treeData = data;
console.time('_crunchStats');
this._crunchStats(data);
console.timeEnd('_crunchStats');
this._currentRoot = this._treeData;
this._currentNodes = this._layout.nodes(this._currentRoot);
this._currentMaxDepth = this._maxLevelsToShow;
this._doLayout();
}
/**
* Recursively traverses the entire tree starting from the specified node,
* computing statistics and recording metadata as it goes. Call this method
* only once per imported tree.
*/
D3SymbolTreeMap.prototype._crunchStats = function(node) {
var stack = [];
stack.idCounter = 0;
this._crunchStatsHelper(stack, node);
}
/**
* Invoke the specified visitor function on all data elements currently shown
* in the treemap including any and all of their children, starting at the
* currently-displayed root and descending recursively. The function will be
* passed the datum element representing each node. No traversal guarantees
* are made.
*/
D3SymbolTreeMap.prototype.visitFromDisplayedRoot = function(visitor) {
this._visit(this._currentRoot, visitor);
}
/**
* Helper function for visit functions.
*/
D3SymbolTreeMap.prototype._visit = function(datum, visitor) {
visitor.call(this, datum);
if (datum.children) for (var i = 0; i < datum.children.length; i++) {
this._visit(datum.children[i], visitor);
}
}
D3SymbolTreeMap.prototype._crunchStatsHelper = function(stack, node) {
// Only overwrite the node ID if it isn't already set.
// This allows stats to be crunched multiple times on subsets of data
// without breaking the data-to-ID bindings. New nodes get new IDs.
if (node.id === undefined) node.id = stack.idCounter++;
if (node.children === undefined) {
// Leaf node (symbol); accumulate stats.
for (var i = 0; i < stack.length; i++) {
var ancestor = stack[i];
if (!ancestor.symbol_stats) ancestor.symbol_stats = {};
if (ancestor.symbol_stats[node.t] === undefined) {
// New symbol type we haven't seen before, just record.
ancestor.symbol_stats[node.t] = {'count': 1,
'size': node.value};
} else {
// Existing symbol type, increment.
ancestor.symbol_stats[node.t].count++;
ancestor.symbol_stats[node.t].size += node.value;
}
}
} else for (var i = 0; i < node.children.length; i++) {
stack.push(node);
this._crunchStatsHelper(stack, node.children[i]);
stack.pop();
}
}
D3SymbolTreeMap.prototype._createTreeMapLayout = function() {
var result = d3.layout.treemap()
.padding([this.boxPadding.t, this.boxPadding.r,
this.boxPadding.b, this.boxPadding.l])
.size([this._mapWidth, this._mapHeight]);
return result;
}
D3SymbolTreeMap.prototype.resize = function(width, height) {
this._mapWidth = width;
this._mapHeight = height;
this._mapContainer.style('width', width).style('height', height);
this._layout.size([this._mapWidth, this._mapHeight]);
this._currentNodes = this._layout.nodes(this._currentRoot);
this._doLayout();
}
D3SymbolTreeMap.prototype._zoomDatum = function(datum) {
if (this._currentRoot === datum) return; // already here
this._hideHighlight(datum);
this._hideInfoBox(datum);
this._currentRoot = datum;
this._currentNodes = this._layout.nodes(this._currentRoot);
this._currentMaxDepth = this._currentRoot.depth + this._maxLevelsToShow;
console.log('zooming into datum ' + this._currentRoot.n);
this._doLayout();
}
D3SymbolTreeMap.prototype.setMaxLevels = function(levelsToShow) {
this._maxLevelsToShow = levelsToShow;
this._currentNodes = this._layout.nodes(this._currentRoot);
this._currentMaxDepth = this._currentRoot.depth + this._maxLevelsToShow;
console.log('setting max levels to show: ' + this._maxLevelsToShow);
this._doLayout();
}
/**
* Clone the specified tree, returning an independent copy of the data.
* Only the original attributes expected to exist prior to invoking
* _crunchStatsHelper are retained, with the exception of the 'id' attribute
* (which must be retained for proper transitions).
* If the optional filter parameter is provided, it will be called with 'this'
* set to this treemap instance and passed the 'datum' object as an argument.
* When specified, the copy will retain only the data for which the filter
* function returns true.
*/
D3SymbolTreeMap.prototype._clone = function(datum, filter) {
var trackingStats = false;
if (this.__cloneState === undefined) {
console.time('_clone');
trackingStats = true;
this.__cloneState = {'accepted': 0, 'rejected': 0,
'forced': 0, 'pruned': 0};
}
// Must go depth-first. All parents of children that are accepted by the
// filter must be preserved!
var copy = {'n': datum.n, 'k': datum.k};
var childAccepted = false;
if (datum.children !== undefined) {
for (var i = 0; i < datum.children.length; i++) {
var copiedChild = this._clone(datum.children[i], filter);
if (copiedChild !== undefined) {
childAccepted = true; // parent must also be accepted.
if (copy.children === undefined) copy.children = [];
copy.children.push(copiedChild);
}
}
}
// Ignore nodes that don't match the filter, when present.
var accept = false;
if (childAccepted) {
// Parent of an accepted child must also be accepted.
this.__cloneState.forced++;
accept = true;
} else if (filter !== undefined && filter.call(this, datum) !== true) {
this.__cloneState.rejected++;
} else if (datum.children === undefined) {
// Accept leaf nodes that passed the filter
this.__cloneState.accepted++;
accept = true;
} else {
// Non-leaf node. If no children are accepted, prune it.
this.__cloneState.pruned++;
}
if (accept) {
if (datum.id !== undefined) copy.id = datum.id;
if (datum.lastPathElement !== undefined) {
copy.lastPathElement = datum.lastPathElement;
}
if (datum.t !== undefined) copy.t = datum.t;
if (datum.value !== undefined && datum.children === undefined) {
copy.value = datum.value;
}
} else {
// Discard the copy we were going to return
copy = undefined;
}
if (trackingStats === true) {
// We are the fist call in the recursive chain.
console.timeEnd('_clone');
var totalAccepted = this.__cloneState.accepted +
this.__cloneState.forced;
console.log(
totalAccepted + ' nodes retained (' +
this.__cloneState.forced + ' forced by accepted children, ' +
this.__cloneState.accepted + ' accepted on their own merits), ' +
this.__cloneState.rejected + ' nodes (and their children) ' +
'filtered out,' +
this.__cloneState.pruned + ' nodes pruned because because no ' +
'children remained.');
delete this.__cloneState;
}
return copy;
}
D3SymbolTreeMap.prototype.filter = function(filter) {
// Ensure we have a copy of the original root.
if (this._backupTree === undefined) this._backupTree = this._treeData;
this._mapContainer.selectAll('div').remove();
this._setData(this._clone(this._backupTree, filter));
}
D3SymbolTreeMap.prototype._doLayout = function() {
console.time('_doLayout');
this._handleInodes();
this._handleLeaves();
this._firstTransition = false;
console.timeEnd('_doLayout');
}
D3SymbolTreeMap.prototype._highlightElement = function(datum, selection) {
this._showHighlight(datum, selection);
}
D3SymbolTreeMap.prototype._unhighlightElement = function(datum, selection) {
this._hideHighlight(datum, selection);
}
D3SymbolTreeMap.prototype._handleInodes = function() {
console.time('_handleInodes');
var thisTreeMap = this;
var inodes = this._currentNodes.filter(function(datum){
return (datum.depth <= thisTreeMap._currentMaxDepth) &&
datum.children !== undefined;
});
var cellsEnter = this._mapContainer.selectAll('div.inode')
.data(inodes, function(datum) { return datum.id; })
.enter()
.append('div').attr('class', 'inode').attr('id', function(datum){
return 'node-' + datum.id;});
// Define enter/update/exit for inodes
cellsEnter
.append('div')
.attr('class', 'rect inode_rect_entering')
.style('z-index', function(datum) { return datum.id * 2; })
.style('position', 'absolute')
.style('left', function(datum) { return datum.x; })
.style('top', function(datum){ return datum.y; })
.style('width', function(datum){ return datum.dx; })
.style('height', function(datum){ return datum.dy; })
.style('opacity', '0')
.style('border', '1px solid black')
.style('background-image', function(datum) {
return thisTreeMap._makeSymbolBucketBackgroundImage.call(
thisTreeMap, datum);
})
.style('background-color', function(datum) {
if (datum.t === undefined) return 'rgb(220,220,220)';
return D3SymbolTreeMap.getColorForType(datum.t).toString();
})
.on('mouseover', function(datum){
thisTreeMap._highlightElement.call(
thisTreeMap, datum, d3.select(this));
thisTreeMap._showInfoBox.call(thisTreeMap, datum);
})
.on('mouseout', function(datum){
thisTreeMap._unhighlightElement.call(
thisTreeMap, datum, d3.select(this));
thisTreeMap._hideInfoBox.call(thisTreeMap, datum);
})
.on('mousemove', function(){
thisTreeMap._moveInfoBox.call(thisTreeMap, event);
})
.on('dblclick', function(datum){
if (datum !== thisTreeMap._currentRoot) {
// Zoom into the selection
thisTreeMap._zoomDatum(datum);
} else if (datum.parent) {
console.log('event.shiftKey=' + event.shiftKey);
if (event.shiftKey === true) {
// Back to root
thisTreeMap._zoomDatum(thisTreeMap._treeData);
} else {
// Zoom out of the selection
thisTreeMap._zoomDatum(datum.parent);
}
}
});
cellsEnter
.append('div')
.attr('class', 'label inode_label_entering')
.style('z-index', function(datum) { return (datum.id * 2) + 1; })
.style('position', 'absolute')
.style('left', function(datum){ return datum.x; })
.style('top', function(datum){ return datum.y; })
.style('width', function(datum) { return datum.dx; })
.style('height', function(datum) { return thisTreeMap.boxPadding.t; })
.style('opacity', '0')
.style('pointer-events', 'none')
.style('-webkit-user-select', 'none')
.style('overflow', 'hidden') // required for ellipsis
.style('white-space', 'nowrap') // required for ellipsis
.style('text-overflow', 'ellipsis')
.style('text-align', 'center')
.style('vertical-align', 'top')
.style('visibility', function(datum) {
return (datum.dx < 15 || datum.dy < 15) ? 'hidden' : 'visible';
})
.text(function(datum) {
var sizeish = ' [' + D3SymbolTreeMap._byteify(datum.value) + ']'
var text;
if (datum.k === 'b') { // bucket
if (datum === thisTreeMap._currentRoot) {
text = thisTreeMap.pathFor(datum) + ': '
+ D3SymbolTreeMap._getSymbolDescription(datum.t)
} else {
text = D3SymbolTreeMap._getSymbolDescription(datum.t);
}
} else if (datum === thisTreeMap._currentRoot) {
// The top-most level should always show the complete path
text = thisTreeMap.pathFor(datum);
} else {
// Anything that isn't a bucket or a leaf (symbol) or the
// current root should just show its name.
text = datum.n;
}
return text + sizeish;
}
);
// Complicated transition logic:
// For nodes that are entering, we want to fade them in in-place AFTER
// any adjusting nodes have resized and moved around. That way, new nodes
// seamlessly appear in the right spot after their containers have resized
// and moved around.
// To do this we do some trickery:
// 1. Define a '_entering' class on the entering elements
// 2. Use this to select only the entering elements and apply the opacity
// transition.
// 3. Use the same transition to drop the '_entering' suffix, so that they
// will correctly update in later zoom/resize/whatever operations.
// 4. The update transition is achieved by selecting the elements without
// the '_entering_' suffix and applying movement and resizing transition
// effects.
this._mapContainer.selectAll('div.inode_rect_entering').transition()
.duration(thisTreeMap._enterDuration).delay(
this._firstTransition ? 0 : thisTreeMap._exitDuration +
thisTreeMap._updateDuration)
.attr('class', 'rect inode_rect')
.style('opacity', '1')
this._mapContainer.selectAll('div.inode_label_entering').transition()
.duration(thisTreeMap._enterDuration).delay(
this._firstTransition ? 0 : thisTreeMap._exitDuration +
thisTreeMap._updateDuration)
.attr('class', 'label inode_label')
.style('opacity', '1')
this._mapContainer.selectAll('div.inode_rect').transition()
.duration(thisTreeMap._updateDuration).delay(thisTreeMap._exitDuration)
.style('opacity', '1')
.style('background-image', function(datum) {
return thisTreeMap._makeSymbolBucketBackgroundImage.call(
thisTreeMap, datum);
})
.style('left', function(datum) { return datum.x; })
.style('top', function(datum){ return datum.y; })
.style('width', function(datum){ return datum.dx; })
.style('height', function(datum){ return datum.dy; });
this._mapContainer.selectAll('div.inode_label').transition()
.duration(thisTreeMap._updateDuration).delay(thisTreeMap._exitDuration)
.style('opacity', '1')
.style('visibility', function(datum) {
return (datum.dx < 15 || datum.dy < 15) ? 'hidden' : 'visible';
})
.style('left', function(datum){ return datum.x; })
.style('top', function(datum){ return datum.y; })
.style('width', function(datum) { return datum.dx; })
.style('height', function(datum) { return thisTreeMap.boxPadding.t; })
.text(function(datum) {
var sizeish = ' [' + D3SymbolTreeMap._byteify(datum.value) + ']'
var text;
if (datum.k === 'b') {
if (datum === thisTreeMap._currentRoot) {
text = thisTreeMap.pathFor(datum) + ': ' +
D3SymbolTreeMap._getSymbolDescription(datum.t)
} else {
text = D3SymbolTreeMap._getSymbolDescription(datum.t);
}
} else if (datum === thisTreeMap._currentRoot) {
// The top-most level should always show the complete path
text = thisTreeMap.pathFor(datum);
} else {
// Anything that isn't a bucket or a leaf (symbol) or the
// current root should just show its name.
text = datum.n;
}
return text + sizeish;
});
var exit = this._mapContainer.selectAll('div.inode')
.data(inodes, function(datum) { return 'inode-' + datum.id; })
.exit();
exit.selectAll('div.inode_rect').transition().duration(
thisTreeMap._exitDuration).style('opacity', 0);
exit.selectAll('div.inode_label').transition().duration(
thisTreeMap._exitDuration).style('opacity', 0);
exit.transition().delay(thisTreeMap._exitDuration + 1).remove();
console.log(inodes.length + ' inodes layed out.');
console.timeEnd('_handleInodes');
}
D3SymbolTreeMap.prototype._handleLeaves = function() {
console.time('_handleLeaves');
var color_fn = d3.scale.category10();
var thisTreeMap = this;
var leaves = this._currentNodes.filter(function(datum){
return (datum.depth <= thisTreeMap._currentMaxDepth) &&
datum.children === undefined; });
var cellsEnter = this._mapContainer.selectAll('div.leaf')
.data(leaves, function(datum) { return datum.id; })
.enter()
.append('div').attr('class', 'leaf').attr('id', function(datum){
return 'node-' + datum.id;
});
// Define enter/update/exit for leaves
cellsEnter
.append('div')
.attr('class', 'rect leaf_rect_entering')
.style('z-index', function(datum) { return datum.id * 2; })
.style('position', 'absolute')
.style('left', function(datum){ return datum.x; })
.style('top', function(datum){ return datum.y; })
.style('width', function(datum){ return datum.dx; })
.style('height', function(datum){ return datum.dy; })
.style('opacity', '0')
.style('background-color', function(datum) {
if (datum.t === undefined) return 'rgb(220,220,220)';
return D3SymbolTreeMap.getColorForType(datum.t)
.darker(0.3).toString();
})
.style('border', '1px solid black')
.on('mouseover', function(datum){
thisTreeMap._highlightElement.call(
thisTreeMap, datum, d3.select(this));
thisTreeMap._showInfoBox.call(thisTreeMap, datum);
})
.on('mouseout', function(datum){
thisTreeMap._unhighlightElement.call(
thisTreeMap, datum, d3.select(this));
thisTreeMap._hideInfoBox.call(thisTreeMap, datum);
})
.on('mousemove', function(){ thisTreeMap._moveInfoBox.call(
thisTreeMap, event);
});
cellsEnter
.append('div')
.attr('class', 'label leaf_label_entering')
.style('z-index', function(datum) { return (datum.id * 2) + 1; })
.style('position', 'absolute')
.style('left', function(datum){ return datum.x; })
.style('top', function(datum){ return datum.y; })
.style('width', function(datum) { return datum.dx; })
.style('height', function(datum) { return datum.dy; })
.style('opacity', '0')
.style('pointer-events', 'none')
.style('-webkit-user-select', 'none')
.style('overflow', 'hidden') // required for ellipsis
.style('white-space', 'nowrap') // required for ellipsis
.style('text-overflow', 'ellipsis')
.style('text-align', 'center')
.style('vertical-align', 'middle')
.style('visibility', function(datum) {
return (datum.dx < 15 || datum.dy < 15) ? 'hidden' : 'visible';
})
.text(function(datum) { return datum.n; });
// Complicated transition logic: See note in _handleInodes()
this._mapContainer.selectAll('div.leaf_rect_entering').transition()
.duration(thisTreeMap._enterDuration).delay(
this._firstTransition ? 0 : thisTreeMap._exitDuration +
thisTreeMap._updateDuration)
.attr('class', 'rect leaf_rect')
.style('opacity', '1')
this._mapContainer.selectAll('div.leaf_label_entering').transition()
.duration(thisTreeMap._enterDuration).delay(
this._firstTransition ? 0 : thisTreeMap._exitDuration +
thisTreeMap._updateDuration)
.attr('class', 'label leaf_label')
.style('opacity', '1')
this._mapContainer.selectAll('div.leaf_rect').transition()
.duration(thisTreeMap._updateDuration).delay(thisTreeMap._exitDuration)
.style('opacity', '1')
.style('left', function(datum){ return datum.x; })
.style('top', function(datum){ return datum.y; })
.style('width', function(datum){ return datum.dx; })
.style('height', function(datum){ return datum.dy; });
this._mapContainer.selectAll('div.leaf_label').transition()
.duration(thisTreeMap._updateDuration).delay(thisTreeMap._exitDuration)
.style('opacity', '1')
.style('visibility', function(datum) {
return (datum.dx < 15 || datum.dy < 15) ? 'hidden' : 'visible';
})
.style('left', function(datum){ return datum.x; })
.style('top', function(datum){ return datum.y; })
.style('width', function(datum) { return datum.dx; })
.style('height', function(datum) { return datum.dy; });
var exit = this._mapContainer.selectAll('div.leaf')
.data(leaves, function(datum) { return 'leaf-' + datum.id; })
.exit();
exit.selectAll('div.leaf_rect').transition()
.duration(thisTreeMap._exitDuration)
.style('opacity', 0);
exit.selectAll('div.leaf_label').transition()
.duration(thisTreeMap._exitDuration)
.style('opacity', 0);
exit.transition().delay(thisTreeMap._exitDuration + 1).remove();
console.log(leaves.length + ' leaves layed out.');
console.timeEnd('_handleLeaves');
}
D3SymbolTreeMap.prototype._makeSymbolBucketBackgroundImage = function(datum) {
if (!(datum.t === undefined && datum.depth == this._currentMaxDepth)) {
return 'none';
}
var text = '';
var lastStop = 0;
for (var x = 0; x < D3SymbolTreeMap._NM_SYMBOL_TYPES.length; x++) {
symbol_type = D3SymbolTreeMap._NM_SYMBOL_TYPES.charAt(x);
var stats = datum.symbol_stats[symbol_type];
if (stats !== undefined) {
if (text.length !== 0) {
text += ', ';
}
var percent = 100 * (stats.size / datum.value);
var nowStop = lastStop + percent;
var tempcolor = D3SymbolTreeMap.getColorForType(symbol_type);
var color = d3.rgb(tempcolor).toString();
text += color + ' ' + lastStop + '%, ' + color + ' ' +
nowStop + '%';
lastStop = nowStop;
}
}
return 'linear-gradient(' + (datum.dx > datum.dy ? 'to right' :
'to bottom') + ', ' + text + ')';
}
D3SymbolTreeMap.prototype.pathFor = function(datum) {
if (datum.__path) return datum.__path;
parts=[];
node = datum;
while (node) {
if (node.k === 'p') { // path node
if(node.n !== '/') parts.unshift(node.n);
}
node = node.parent;
}
datum.__path = '/' + parts.join('/');
return datum.__path;
}
D3SymbolTreeMap.prototype._createHighlight = function(datum, selection) {
var x = parseInt(selection.style('left'));
var y = parseInt(selection.style('top'));
var w = parseInt(selection.style('width'));
var h = parseInt(selection.style('height'));
datum.highlight = this._mapContainer.append('div')
.attr('id', 'h-' + datum.id)
.attr('class', 'highlight')
.style('pointer-events', 'none')
.style('-webkit-user-select', 'none')
.style('z-index', '999999')
.style('position', 'absolute')
.style('top', y-2)
.style('left', x-2)
.style('width', w+4)
.style('height', h+4)
.style('margin', 0)
.style('padding', 0)
.style('border', '4px outset rgba(250,40,200,0.9)')
.style('box-sizing', 'border-box')
.style('opacity', 0.0);
}
D3SymbolTreeMap.prototype._showHighlight = function(datum, selection) {
if (datum === this._currentRoot) return;
if (datum.highlight === undefined) {
this._createHighlight(datum, selection);
}
datum.highlight.transition().duration(200).style('opacity', 1.0);
}
D3SymbolTreeMap.prototype._hideHighlight = function(datum, selection) {
if (datum.highlight === undefined) return;
datum.highlight.transition().duration(750)
.style('opacity', 0)
.each('end', function(){
if (datum.highlight) datum.highlight.remove();
delete datum.highlight;
});
}
D3SymbolTreeMap.prototype._createInfoBox = function() {
return d3.select('body')
.append('div')
.attr('id', 'infobox')
.style('z-index', '2147483647') // (2^31) - 1: Hopefully safe :)
.style('position', 'absolute')
.style('visibility', 'hidden')
.style('background-color', 'rgba(255,255,255, 0.9)')
.style('border', '1px solid black')
.style('padding', '10px')
.style('-webkit-user-select', 'none')
.style('box-shadow', '3px 3px rgba(70,70,70,0.5)')
.style('border-radius', '10px')
.style('white-space', 'nowrap');
}
D3SymbolTreeMap.prototype._showInfoBox = function(datum) {
this.infobox.text('');
var numSymbols = 0;
var sizeish = D3SymbolTreeMap._pretty(datum.value) + ' bytes (' +
D3SymbolTreeMap._byteify(datum.value) + ')';
if (datum.k === 'p' || datum.k === 'b') { // path or bucket
if (datum.symbol_stats) { // can be empty if filters are applied
for (var x = 0; x < D3SymbolTreeMap._NM_SYMBOL_TYPES.length; x++) {
symbol_type = D3SymbolTreeMap._NM_SYMBOL_TYPES.charAt(x);
var stats = datum.symbol_stats[symbol_type];
if (stats !== undefined) numSymbols += stats.count;
}
}
} else if (datum.k === 's') { // symbol
numSymbols = 1;
}
if (datum.k === 'p' && !datum.lastPathElement) {
this.infobox.append('div').text('Directory: ' + this.pathFor(datum))
this.infobox.append('div').text('Size: ' + sizeish);
} else {
if (datum.k === 'p') { // path
this.infobox.append('div').text('File: ' + this.pathFor(datum))
this.infobox.append('div').text('Size: ' + sizeish);
} else if (datum.k === 'b') { // bucket
this.infobox.append('div').text('Symbol Bucket: ' +
D3SymbolTreeMap._getSymbolDescription(datum.t));
this.infobox.append('div').text('Count: ' + numSymbols);
this.infobox.append('div').text('Size: ' + sizeish);
this.infobox.append('div').text('Location: ' + this.pathFor(datum))
} else if (datum.k === 's') { // symbol
this.infobox.append('div').text('Symbol: ' + datum.n);
this.infobox.append('div').text('Type: ' +
D3SymbolTreeMap._getSymbolDescription(datum.t));
this.infobox.append('div').text('Size: ' + sizeish);
this.infobox.append('div').text('Location: ' + this.pathFor(datum))
}
}
if (datum.k === 'p') {
this.infobox.append('div')
.text('Number of symbols: ' + D3SymbolTreeMap._pretty(numSymbols));
if (datum.symbol_stats) { // can be empty if filters are applied
var table = this.infobox.append('table')
.attr('border', 1).append('tbody');
var header = table.append('tr');
header.append('th').text('Type');
header.append('th').text('Count');
header.append('th')
.style('white-space', 'nowrap')
.text('Total Size (Bytes)');
for (var x = 0; x < D3SymbolTreeMap._NM_SYMBOL_TYPES.length; x++) {
symbol_type = D3SymbolTreeMap._NM_SYMBOL_TYPES.charAt(x);
var stats = datum.symbol_stats[symbol_type];
if (stats !== undefined) {
var tr = table.append('tr');
tr.append('td')
.style('white-space', 'nowrap')
.text(D3SymbolTreeMap._getSymbolDescription(
symbol_type));
tr.append('td').text(D3SymbolTreeMap._pretty(stats.count));
tr.append('td').text(D3SymbolTreeMap._pretty(stats.size));
}
}
}
}
this.infobox.style('visibility', 'visible');
}
D3SymbolTreeMap.prototype._hideInfoBox = function(datum) {
this.infobox.style('visibility', 'hidden');
}
D3SymbolTreeMap.prototype._moveInfoBox = function(event) {
var element = document.getElementById('infobox');
var w = element.offsetWidth;
var h = element.offsetHeight;
var offsetLeft = 10;
var offsetTop = 10;
var rightLimit = window.innerWidth;
var rightEdge = event.pageX + offsetLeft + w;
if (rightEdge > rightLimit) {
// Too close to screen edge, reflect around the cursor
offsetLeft = -1 * (w + offsetLeft);
}
var bottomLimit = window.innerHeight;
var bottomEdge = event.pageY + offsetTop + h;
if (bottomEdge > bottomLimit) {
// Too close ot screen edge, reflect around the cursor
offsetTop = -1 * (h + offsetTop);
}
this.infobox.style('top', (event.pageY + offsetTop) + 'px')
.style('left', (event.pageX + offsetLeft) + 'px');
}
D3SymbolTreeMap.prototype.biggestSymbols = function(maxRecords) {
var result = undefined;
var smallest = undefined;
var sortFunction = function(a,b) {
var result = b.value - a.value;
if (result !== 0) return result; // sort by size
var pathA = treemap.pathFor(a); // sort by path
var pathB = treemap.pathFor(b);
if (pathA > pathB) return 1;
if (pathB > pathA) return -1;
return a.n - b.n; // sort by symbol name
};
this.visitFromDisplayedRoot(function(datum) {
if (datum.children) return; // ignore non-leaves
if (!result) { // first element
result = [datum];
smallest = datum.value;
return;
}
if (result.length < maxRecords) { // filling the array
result.push(datum);
return;
}
if (datum.value > smallest) { // array is already full
result.push(datum);
result.sort(sortFunction);
result.pop(); // get rid of smallest element
smallest = result[maxRecords - 1].value; // new threshold for entry
}
});
result.sort(sortFunction);
return result;
}
D3SymbolTreeMap.prototype.biggestPaths = function(maxRecords) {
var result = undefined;
var smallest = undefined;
var sortFunction = function(a,b) {
var result = b.value - a.value;
if (result !== 0) return result; // sort by size
var pathA = treemap.pathFor(a); // sort by path
var pathB = treemap.pathFor(b);
if (pathA > pathB) return 1;
if (pathB > pathA) return -1;
console.log('warning, multiple entries for the same path: ' + pathA);
return 0; // should be impossible
};
this.visitFromDisplayedRoot(function(datum) {
if (!datum.lastPathElement) return; // ignore non-files
if (!result) { // first element
result = [datum];
smallest = datum.value;
return;
}
if (result.length < maxRecords) { // filling the array
result.push(datum);
return;
}
if (datum.value > smallest) { // array is already full
result.push(datum);
result.sort(sortFunction);
result.pop(); // get rid of smallest element
smallest = result[maxRecords - 1].value; // new threshold for entry
}
});
result.sort(sortFunction);
return result;
}

View File

@ -0,0 +1,31 @@
Name: d3
Short Name: d3
URL: https://github.com/mbostock/d3
Version: 3.4.4
Date: Mon Mar 24 20:45:44 2014 -0700
Revision: fa55eead411a3c1b01703cb1ddfd59ccc0b23124
License: BSD 3-Clause
License File: src/LICENSE
Security Critical: No
License Android Compatible: Yes
Description:
A JavaScript library for manipulating documents based on data.
Subject to the security patch(es) described below, you MAY include d3 in web-facing content, such
as in pages generated by bots or tools.
Local Modifications:
1. Deleted everything except for:
* d3.js the standalone non-minified library
* LICENSE the BSD-style 3-Clause license
* README.md the readme file from github, for basic information
2. Applied the following patches at the request of security:
patches/001_no_html.patch Disables the html() convenience functions, which could be used to
inject arbitrary content into the page. Instead of using html(),
programmatically create the individual nodes and/or text that you
require.
The html() methods have been modified to throw exceptions that
make it obvious that this feature is disabled for security.

View File

@ -0,0 +1 @@
A local copy of third_party/d3 from Chromium project.

View File

@ -0,0 +1,24 @@
diff --git a/third_party/d3/src/d3.js b/third_party/d3/src/d3.js
index a3e4b95..8a98c4d 100644
--- a/third_party/d3/src/d3.js
+++ b/third_party/d3/src/d3.js
@@ -713,6 +713,7 @@
}) : this.node().textContent;
};
d3_selectionPrototype.html = function(value) {
+ throw "disallowed by chromium security";
return arguments.length ? this.each(typeof value === "function" ? function() {
var v = value.apply(this, arguments);
this.innerHTML = v == null ? "" : v;
@@ -9274,9 +9275,11 @@
return JSON.parse(request.responseText);
}
d3.html = function(url, callback) {
+ throw "disallowed by chromium security";
return d3_xhr(url, "text/html", d3_html, callback);
};
function d3_html(request) {
+ throw "disallowed by chromium security";
var range = d3_document.createRange();
range.selectNode(d3_document.body);
return range.createContextualFragment(request.responseText);

View File

@ -0,0 +1,26 @@
Copyright (c) 2010-2014, Michael Bostock
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name Michael Bostock may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,9 @@
# Data-Driven Documents
<a href="http://d3js.org"><img src="http://d3js.org/logo.svg" align="left" hspace="10" vspace="6"></a>
**D3.js** is a JavaScript library for manipulating documents based on data. **D3** helps you bring data to life using HTML, SVG and CSS. D3s emphasis on web standards gives you the full capabilities of modern browsers without tying yourself to a proprietary framework, combining powerful visualization components and a data-driven approach to DOM manipulation.
Want to learn more? [See the wiki.](https://github.com/mbostock/d3/wiki)
For examples, [see the gallery](https://github.com/mbostock/d3/wiki/Gallery) and [mbostocks bl.ocks](http://bl.ocks.org/mbostock).

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,525 @@
<!--
Copyright 2014 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<html>
<head>
<title>Binary Size Analysis</title>
<script src="d3/d3.js" charset="utf-8"></script>
<script src="D3SymbolTreeMap.js" charset="utf-8"></script>
<script src="data.js" charset="utf-8"></script>
<style>
body {
margin: 0px;
padding: 5px;
}
.swatch {
border: 1px solid rgb(100,100,100);
-webkit-user-select: none;
cursor: default;
}
</style>
<script>
var treemap;
var filterChanging = false;
var savedSettings = {};
function init() {
if (window.metadata !== undefined && window.metadata.subtitle) {
document.getElementById('subtitle').innerHTML = ': ' + escape(metadata.subtitle);
}
initFilterOptions();
treemap = new D3SymbolTreeMap(
savedSettings.width,
savedSettings.height,
savedSettings.maxLevels);
treemap.init();
}
function getIdealSizes() {
var width = window.innerWidth - 20;
var height = window.innerHeight - 70;
return {'width': width, 'height': height};
}
function showReport(title, data, headers, dataFunction, styleFunction) {
var div = d3.select('body').append('div')
.style('margin', '0')
.style('padding', '5px')
.style('position', 'absolute')
.style('top', '10%')
.style('left', '10%')
.style('background-color', 'rgba(255,255,255,0.9)')
.style('width', '80%')
.style('height', '80%')
.style('z-index', '2147483647')
.style('border', '3px ridge grey')
.style('box-shadow', '10px 10px 5px rgba(80,80,80,0.7)')
.style('text-align', 'center')
.style('border-radius', '10px');
var titlebar = div.append('div')
.style('margin', '0')
.style('padding', '5px')
.style('position', 'absolute')
.style('top', '0%')
.style('left', '0%')
.style('width', '100%')
.style('height', '10%')
.style('font-size', 'x-large');
titlebar.text(title);
var controls = div.append('div')
.style('margin', '0')
.style('padding', '5px')
.style('position', 'absolute')
.style('top', '90%')
.style('left', '0%')
.style('width', '100%')
.style('height', '10%');
controls.append('input').attr('type', 'button')
.attr('value', 'Dismiss')
.on('click', function(){div.remove();});
var tableDiv = div.append('div')
.style('overflow', 'auto')
.style('position', 'absolute')
.style('top', '10%')
.style('left', '0%')
.style('width', '100%')
.style('height', '80%')
.style('border-top', '1px solid rgb(230,230,230)')
.style('border-bottom', '1px solid rgb(230,230,230)');
var table = tableDiv.append('table')
.attr('border', '1')
.attr('cellspacing', '0')
.attr('cellpadding', '2')
.style('margin-left', 'auto')
.style('margin-right', 'auto');
var header = table.append('tr');
for (var i = 0; i < headers.length; i++) {
header.append('th').text(headers[i]);
}
for (var i = 0; i < data.length; i++) {
var row = table.append('tr');
for (j = 0; j < headers.length; j++) {
var td = row.append('td');
if (styleFunction) {
styleFunction.call(this, td, j);
}
dataFunction.call(this, data[i], j, td);
}
}
}
function bigSymbolsReport() {
var list = treemap.biggestSymbols(100);
var headers = ['Rank', 'Size (Bytes)', 'Type', 'Location'];
var styleFunction = function(selection, index) {
if (index === 3) {
selection.style('font-family', 'monospace');
}
};
var recordIndex = 1;
var dataFunction = function(record, index, cell) {
if (index === 0) {
cell.text(recordIndex++);
} else if (index === 1) {
cell.text(D3SymbolTreeMap._pretty(record.value));
} else if (index === 2) {
cell.text(record.t);
} else {
if (treemap.pathFor(record).indexOf('/out') == 0) {
cell.append('span').text(treemap.pathFor(record));
cell.append('br');
cell.append('span').text('Symbol: ');
cell.append('span').text(record.n);
} else {
var href = 'https://code.google.com/p/chromium/codesearch#chromium/src'
+ treemap.pathFor(record)
+ '&q='
+ record.n;
cell.append('a')
.attr('href', href)
.attr('target', '_blank')
.text(treemap.pathFor(record));
cell.append('br');
cell.append('span').text('Symbol: ');
cell.append('span').text(record.n);
}
}
};
showReport('100 Largest Symbols', list, headers, dataFunction, styleFunction);
}
function bigPathsReport() {
var list = treemap.biggestPaths(100);
var headers = ['Rank', 'Size (Bytes)', 'Location'];
var styleFunction = function(selection, index) {
if (index === 2) {
selection.style('font-family', 'monospace');
}
};
var recordIndex = 1;
var dataFunction = function(record, index, cell) {
if (index === 0) {
cell.text(recordIndex++);
} else if (index === 1) {
cell.text(D3SymbolTreeMap._pretty(record.value));
} else if (index === 2) {
if (treemap.pathFor(record).indexOf('/out') == 0) {
cell.text(treemap.pathFor(record));
} else {
var href = 'https://code.google.com/p/chromium/codesearch#chromium/src' + treemap.pathFor(record);
cell.append('a')
.attr('href', href)
.attr('target', '_blank')
.text(treemap.pathFor(record));
}
}
};
showReport('100 Largest Paths', list, headers, dataFunction, styleFunction);
}
function symbolFilterTextChanged() {
if (filterChanging) return true;
filterChanging = true;
var enabled = document.getElementById('symbol_types_filter').value;
for (var x=0; x<=25; x++) {
var checkBox = document.getElementById('check_' + x);
checkBox.checked = (enabled.indexOf(checkBox.value) != -1);
}
filterChanging = false;
}
function updateFilterText() {
if (filterChanging) return true;
filterChanging = true;
var text = '';
for (var x=0; x<=25; x++) {
var checkBox = document.getElementById('check_' + x);
if (checkBox.checked) {
text += checkBox.value;
}
}
document.getElementById('symbol_types_filter').value=text;
filterChanging = false;
}
function initFilterOptions() {
updateFilterText();
for (var x=0; x<=25; x++) {
var checkBox = document.getElementById('check_' + x);
checkBox.onchange=updateFilterText;
var swatch = document.getElementById('swatch_' + x);
swatch.style.backgroundColor = D3SymbolTreeMap.getColorForType(checkBox.value).toString();
}
var gteCheckbox = document.getElementById('check_gte');
gteCheckbox.onchange = function() {
document.getElementById('symbol_filter_gte').disabled = !gteCheckbox.checked;
}
var regexCheckbox = document.getElementById('check_regex');
regexCheckbox.onchange = function() {
document.getElementById('symbol_filter_regex').disabled = !regexCheckbox.checked;
}
var excludeRegexCheckbox = document.getElementById('check_exclude_regex');
excludeRegexCheckbox.onchange = function() {
document.getElementById('symbol_filter_exclude_regex').disabled = !excludeRegexCheckbox.checked;
}
var idealSizes = getIdealSizes();
document.getElementById('width').value = idealSizes.width;
document.getElementById('height').value = idealSizes.height;
saveFilterSettings();
}
function filterSetAll(enabled) {
for (var x=0; x<=25; x++) {
var checkBox = document.getElementById('check_' + x);
checkBox.checked = enabled;
}
updateFilterText();
}
function showOptions() {
loadFilterSettings();
var container = document.getElementById('options_container');
var w = container.offsetWidth;
var h = container.offsetHeight;
container.style.margin = '-' + (h/2) + 'px 0 0 -' + (w/2) + 'px';
container.style.visibility = 'visible';
}
function hideOptions() {
var container = document.getElementById('options_container');
container.style.visibility = 'hidden';
}
function applySettings() {
hideOptions();
var oldWidth = savedSettings.width;
var oldHeight = savedSettings.height;
var oldSymbols = savedSettings.symbolTypes;
var oldRegex = savedSettings.regex;
var oldExcludeRegex = savedSettings.excludeRegex;
var oldGte = savedSettings.gte;
var oldMaxLevels = savedSettings.maxLevels;
saveFilterSettings();
var resizeNeeded = oldWidth !== savedSettings.width || oldHeight !== savedSettings.height;
var regexChanged = oldRegex !== savedSettings.regex;
var excludeRegexChanged = oldExcludeRegex !== savedSettings.excludeRegex;
var symbolsChanged = oldSymbols !== savedSettings.symbolTypes;
var gteChanged = oldGte !== savedSettings.gte;
var filterChanged = regexChanged || excludeRegexChanged || symbolsChanged || gteChanged;
var maxLevelsChanged = oldMaxLevels !== savedSettings.maxLevels;
if (filterChanged) {
// Type filters
typeFilter = function(datum) {
if (datum.depth === 0) return true; // root node
if (datum.t === undefined) return true;
return savedSettings.symbolTypes !== undefined &&
savedSettings.symbolTypes.indexOf(datum.t) !== -1;
}
// Regex filter
var regexFilter = undefined;
if (savedSettings.regex !== undefined && savedSettings.regex.length > 0) {
console.log('filter: regex is "' + savedSettings.regex + '"');
var regex = new RegExp(savedSettings.regex);
regexFilter = function(datum) {
if (datum.depth === 0) return true; // root node
var fullName = this.pathFor(datum);
if (datum.children === undefined) { // it is a leaf node (symbol)
fullName += ':' + datum.n;
}
return regex.test(fullName);
}
}
// Exclude regex filter
var excludeRegexFilter = undefined;
if (savedSettings.excludeRegex !== undefined && savedSettings.excludeRegex.length > 0) {
console.log('filter: exclude-regex is "' + savedSettings.excludeRegex + '"');
var excludeRegex = new RegExp(savedSettings.excludeRegex);
excludeRegexFilter = function(datum) {
if (datum.depth === 0) return true; // root node
var fullName = this.pathFor(datum);
if (datum.children === undefined) { // it is a leaf node (symbol)
fullName += ':' + datum.n;
}
return !excludeRegex.test(fullName);
}
}
// Size filter
var sizeFilter = undefined;
if (savedSettings.gte !== undefined) {
console.log('filter: minimum size is ' + savedSettings.gte + ' bytes');
sizeFilter = function(datum) {
if (datum.children !== undefined) return true; // non-leaf
if (datum.value === undefined) console.log('whoops');
return datum.value >= savedSettings.gte;
}
}
// Make a filter to apply to the tree
var filter = function(datum) {
if (typeFilter && !typeFilter.call(this, datum)) return false;
if (regexFilter && !regexFilter.call(this, datum)) return false;
if (excludeRegexFilter && !excludeRegexFilter.call(this, datum)) return false;
if (sizeFilter && !sizeFilter.call(this, datum)) return false;
return true;
};
treemap.filter(filter);
}
// Adjust levels if needed.
if (maxLevelsChanged) {
treemap.setMaxLevels(savedSettings.maxLevels);
}
// Resize map if necessary.
if (resizeNeeded) {
console.log('desired treemap dimensions have changed, requesting resize');
treemap.resize(savedSettings.width, savedSettings.height);
}
}
function cancelSettings() {
hideOptions();
loadFilterSettings();
}
function saveFilterSettings() {
savedSettings.symbolTypes = document.getElementById('symbol_types_filter').value;
if (document.getElementById('check_regex').checked) {
savedSettings.regex = document.getElementById('symbol_filter_regex').value;
} else {
savedSettings.regex = undefined;
}
if (document.getElementById('check_exclude_regex').checked) {
savedSettings.excludeRegex = document.getElementById('symbol_filter_exclude_regex').value;
} else {
savedSettings.excludeRegex = undefined;
}
if (document.getElementById('check_gte').checked) {
savedSettings.gte = parseInt(document.getElementById('symbol_filter_gte').value);
} else {
savedSettings.gte = undefined;
}
savedSettings.width = parseInt(document.getElementById('width').value);
savedSettings.height = parseInt(document.getElementById('height').value);
savedSettings.maxLevels = parseInt(document.getElementById('max_levels').value);
}
function loadFilterSettings() {
document.getElementById('symbol_types_filter').value = savedSettings.symbolTypes;
symbolFilterTextChanged();
if (savedSettings.regex !== undefined) {
document.getElementById('check_regex').checked = true;
document.getElementById('symbol_filter_regex').value = savedSettings.regex;
} else {
document.getElementById('check_regex').checked = false;
}
if (savedSettings.excludeRegex !== undefined) {
document.getElementById('check_exclude_regex').checked = true;
document.getElementById('symbol_filter_exclude_regex').value = savedSettings.excludeRegex;
} else {
document.getElementById('check_exclude_regex').checked = false;
}
if (savedSettings.gte !== undefined) {
document.getElementById('check_gte').checked = true;
document.getElementById('symbol_filter_gte').value = savedSettings.gte;
} else {
document.getElementById('check_gte').checked = false;
}
document.getElementById('width').value = savedSettings.width;
document.getElementById('height').value = savedSettings.height;
document.getElementById('max_levels').value = savedSettings.maxLevels;
}
function escape(str) {
return str.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
}
</script>
</head>
<body onload='init()'>
<div style='position: absolute; top: 5px; left: 5px;'>
<input type='button' onclick='showOptions()' value='Options &amp; Legend...'>
<span style='-webkit-user-select: none; cursor: help;' title='Click to view the symbol legend or to configure filters and options for the treemap'>[?]</span>
</div>
<div style='position: absolute; right: 5px; top: 5px; white-space: nowrap;'>
Reports:
<input type='button' onclick='bigSymbolsReport()' value='Large Symbols' title='Click to view a report of the largest 100 symbols that are with the bounds of the treemap that is currently displayed.'>
<input type='button' onclick='bigPathsReport()' value='Large Files' title='Click to view a report of the largest 100 source files that are with the bounds of the treemap that is currently displayed.'>
</div>
<div style='text-align: center; margin-bottom: 5px;'>
<span style='font-size: x-large; font-weight: bold; font-variant: small-caps'>Binary Size Analysis<span id='subtitle'></span></span>
<br><span style='font-size: small; font-style: italic;'>Double-click a box to zoom in, double-click outermost title to zoom out.</span>
</div>
<table id='options_container' style='visibility: hidden; border: 3px ridge grey; padding: 0px; top: 50%; left: 50%; position: fixed; z-index: 2147483646; overflow: auto; background-color: rgba(255,255,255,0.9); border-radius: 10px; box-shadow: 10px 10px 5px rgba(80,80,80,0.7);'><tr><td style='vertical-align: top'>
<table cellspacing=0 cellborder=0 style='width:100%'>
<tr><th colspan=3 style='padding-bottom: .25em; text-decoration: underline;'>Symbol Types To Show</th></tr>
<tr>
<td style='width: 33%; white-space: nowrap; vertical-align: top;'>
<span class='swatch' id='swatch_0'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_0' value='A'>Global absolute (A)
<br><span class='swatch' id='swatch_1'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_1' value='B'>Global uninitialized data (B)
<br><span class='swatch' id='swatch_2'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_2' value='b'>Local uninitialized data (b)
<br><span class='swatch' id='swatch_3'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_3' value='C'>Global uninitialized common (C)
<br><span class='swatch' id='swatch_4'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_4' value='D'>Global initialized data (D)
<br><span class='swatch' id='swatch_5'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_5' value='d'>Local initialized data (d)
<br><span class='swatch' id='swatch_6'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_6' value='G'>Global small initialized data (G)
<br><span class='swatch' id='swatch_7'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_7' value='g'>Local small initialized data (g)
<br><span class='swatch' id='swatch_8'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_8' value='i'>Indirect function (i)
</td>
<td style='width: 33%; white-space: nowrap; vertical-align: top;'>
<span class='swatch' id='swatch_9'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_9' value='N'>Debugging (N)
<br><span class='swatch' id='swatch_10'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_10' value='p'>Stack unwind (p)
<br><span class='swatch' id='swatch_11'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_11' value='R'>Global read-only data (R)
<br><span class='swatch' id='swatch_12'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_12' value='r'>Local read-only data (r)
<br><span class='swatch' id='swatch_13'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_13' value='S'>Global small uninitialized data (S)
<br><span class='swatch' id='swatch_14'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_14' value='s'>Local small uninitialized data (s)
<br><span class='swatch' id='swatch_15'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_15' value='T'>Global code (T)
<br><span class='swatch' id='swatch_16'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_16' value='t'>Local code (t)
<br><span class='swatch' id='swatch_17'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_17' value='U'>Undefined (U)
</td>
<td style='width: 33%; white-space: nowrap; vertical-align: top;'>
<span class='swatch' id='swatch_18'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_18' value='u'>Unique (u)
<br><span class='swatch' id='swatch_19'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_19' value='V'>Global weak object (V)
<br><span class='swatch' id='swatch_20'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_20' value='v'>Local weak object (v)
<br><span class='swatch' id='swatch_21'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_21' value='W'>Global weak symbol (W)
<br><span class='swatch' id='swatch_22'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_22' value='w'>Local weak symbol (w)
<br><span class='swatch' id='swatch_23'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_23' value='@'>Vtable entry (@)
<br><span class='swatch' id='swatch_24'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_24' value='-'>STABS debugging (-)
<br><span class='swatch' id='swatch_25'>&nbsp;&nbsp;&nbsp;</span><input checked type='checkbox' id='check_25' value='?'>Unrecognized (?)
</td>
</tr>
<tr><td colspan=3 style='text-align: center; white-space: nowrap; padding-top: 1em;'>
Select <input type='button' onclick='filterSetAll(true)' value='All'>,
<input type='button' onclick='filterSetAll(false)' value='None'>,
or type a string: <input id='symbol_types_filter' size=30 value='' onkeyup='symbolFilterTextChanged()' onblur='updateFilterText()'>
<span style='-webkit-user-select: none; cursor: help;' title='Enter codes from the list above for the symbols you want to see. The checkboxes will update automatically to match the string that you enter.'>[?]</span>
</td></tr>
</table>
</td></tr><tr><td style='vertical-align: top; padding-top: 10px; border-top: 1px solid grey;'>
<table cellspacing=0 cellborder=0 style='width: 100%'>
<tr><th colspan=2 style='padding-bottom: .25em; text-decoration: underline;'>Advanced Options</th></tr>
<tr>
<td style='white-space: nowrap; vertical-align: top;'>
<input type='checkbox' id='check_regex'>
Only include symbols matching this regex:
</td>
<td style='text-align: right; vertical-align: top;'>
<input disabled id='symbol_filter_regex' size=30 value='' style='text-align: right;'>
<span style='-webkit-user-select: none; cursor: help;' title='Enter a javascript regex. Only symbols that match this regex will be shown. This filter applies before any exclusion regex specified below. The format of each symbol is [path]:[symbol_name]'>[?]</span>
</td>
</tr>
<tr>
<td style='white-space: nowrap; vertical-align: top;'>
<input type='checkbox' id='check_exclude_regex'>
Exclude all symbols matching this regex:
</td>
<td style='text-align: right; vertical-align: top;'>
<input disabled id='symbol_filter_exclude_regex' size=30 value='' style='text-align: right;'>
<span style='-webkit-user-select: none; cursor: help;' title='Enter a javascript regex. Symbols that match this tegex will not be shown. This filter applies after any inclusion filter specified above. The format of each symbol is [path]:[symbol_name]'>[?]</span>
</td>
</tr>
<tr>
<td style='white-space: nowrap; vertical-align: top;'>
<input type='checkbox' id='check_gte'>
Only include symbols that are at least <span style='font-style: italic;'>n</span> bytes:
</td>
<td style='text-align: right; vertical-align: top;'>
<input disabled id='symbol_filter_gte' size=8 value='' style='text-align: right;'>
<span style='-webkit-user-select: none; cursor: help;' title='Symbols whose size is less than this value will be hidden.'>[?]</span>
</td>
</tr>
<tr>
<td style='white-space: nowrap vertical-align: top;;'>
Show at most <span style='font-style: italic;'>n</span> levels of detail at a time:
</td>
<td style='text-align: right; vertical-align: top;'>
<input id='max_levels' size=4 value='2' style='text-align: right;'><span style='-webkit-user-select: none; cursor: help;' title='Increasing this value shows more detail without the need to zoom, but uses more computing power.'>[?]</span>
</td>
</tr>
<tr>
<td style='white-space: nowrap vertical-align: top;;'>
Set the size of the treemap to <span style='font-style: italic;'>W x H</span> pixels:
</td>
<td style='text-align: right; vertical-align: top;'>
<input id='width' size=4 value='' style='text-align: right;'>
&nbsp;x&nbsp;<input id='height' size=4 value='' style='text-align: right;'>
</td>
</tr>
</table>
</td></tr>
<tr><td style='padding-top: 10px; text-align: right; border-top: 1px solid grey'>
<input type='button' value='Apply' onclick='applySettings()'>
<input type='button' value='Cancel' onclick='cancelSettings()'>
</td></tr></table>
</body>
</html>

View File

@ -5,16 +5,16 @@
/// This tool compares two JSON size reports produced by
/// --print-instructions-sizes-to and reports which symbols increased in size
/// and which symbols decreased in size.
library vm.snapshot.compare;
library vm_snapshot_analysis.compare;
import 'dart:io';
import 'package:args/command_runner.dart';
import 'package:vm/snapshot/ascii_table.dart';
import 'package:vm/snapshot/program_info.dart';
import 'package:vm/snapshot/utils.dart';
import 'package:vm/snapshot/v8_profile.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 CompareCommand extends Command<void> {
@override
@ -92,6 +92,7 @@ precisely based on their source position (which is included in their name).
case 'package':
return HistogramType.byPackage;
}
return null;
}
File _checkExists(String path) {
@ -103,7 +104,7 @@ precisely based on their source position (which is included in their name).
}
void printComparison(File oldJson, File newJson,
{int maxWidth: 0,
{int maxWidth = 0,
bool collapseAnonymousClosures = false,
HistogramType granularity = HistogramType.bySymbol}) async {
final oldSizes = await loadProgramInfo(oldJson,

View File

@ -5,17 +5,17 @@
/// 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.summary;
library vm_snapshot_analysis.summary;
import 'dart:async';
import 'dart:io';
import 'package:args/command_runner.dart';
import 'package:vm/snapshot/ascii_table.dart';
import 'package:vm/snapshot/program_info.dart';
import 'package:vm/snapshot/utils.dart';
import 'package:vm/snapshot/v8_profile.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

View File

@ -8,20 +8,22 @@
///
/// It used the same visualization framework as Chromium's binary_size tool
/// located in runtime/third_party/binary_size.
library vm.snapshot.commands.treemap;
library vm_snapshot_analysis.commands.treemap;
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'dart:math' show max;
import 'package:path/path.dart' as p;
import 'package:args/command_runner.dart';
import 'package:vm/snapshot/instruction_sizes.dart' as instruction_sizes;
import 'package:vm/snapshot/program_info.dart';
import 'package:vm/snapshot/v8_profile.dart' as v8_profile;
import 'package:vm/snapshot/utils.dart';
import 'package:vm_snapshot_analysis/instruction_sizes.dart'
as instruction_sizes;
import 'package:vm_snapshot_analysis/program_info.dart';
import 'package:vm_snapshot_analysis/v8_profile.dart' as v8_profile;
import 'package:vm_snapshot_analysis/utils.dart';
class TreemapCommand extends Command<void> {
@override
@ -57,8 +59,8 @@ viewed in a browser:
usageException('Need to specify input JSON and output directory.');
}
final input = new File(argResults.rest[0]);
final outputDir = new Directory(argResults.rest[1]);
final input = File(argResults.rest[0]);
final outputDir = Directory(argResults.rest[1]);
if (!input.existsSync()) {
usageException('Input file ${input.path} does not exist!');
@ -93,27 +95,26 @@ Future<void> generateTreeMap(File input, Directory outputDir) async {
// Create output directory and copy all auxiliary files from binary_size tool.
await outputDir.create(recursive: true);
final scriptLocation = p.dirname(Platform.script.toFilePath());
final sdkRoot = p.join(scriptLocation, '..', '..', '..');
final d3SrcDir = p.join(sdkRoot, 'runtime', 'third_party', 'd3', 'src');
final templateDir = p.join(
sdkRoot, 'runtime', 'third_party', 'binary_size', 'src', 'template');
final assetsUri = await Isolate.resolvePackageUri(
Uri.parse('package:vm_snapshot_analysis/src/assets'));
final assetsDir = assetsUri.toFilePath();
final d3SrcDir = p.join(assetsDir, 'd3', 'src');
final d3OutDir = p.join(outputDir.path, 'd3');
await new Directory(d3OutDir).create(recursive: true);
await Directory(d3OutDir).create(recursive: true);
for (var file in ['LICENSE', 'd3.js']) {
await copyFile(d3SrcDir, file, d3OutDir);
}
for (var file in ['index.html', 'D3SymbolTreeMap.js']) {
await copyFile(templateDir, file, outputDir.path);
await copyFile(assetsDir, file, outputDir.path);
}
// Serialize symbol size tree as JSON.
final dataJsPath = p.join(outputDir.path, 'data.js');
final sink = new File(dataJsPath).openWrite();
final sink = File(dataJsPath).openWrite();
sink.write('var tree_data=');
await sink.addStream(new Stream<Object>.fromIterable([tree])
await sink.addStream(Stream<Object>.fromIterable([tree])
.transform(json.encoder.fuse(utf8.encoder)));
await sink.close();
@ -191,7 +192,7 @@ Map<String, dynamic> addChild(
/// Add the given symbol to the tree.
void addSymbol(Map<String, dynamic> root, String path, String name, int size,
{String symbolType: symbolTypeGlobalText}) {
{String symbolType = symbolTypeGlobalText}) {
var node = root;
var depth = 0;
for (var part in path.split('/')) {
@ -225,5 +226,5 @@ Map<String, dynamic> flatten(Map<String, dynamic> node) {
/// Copy file with the given name from [fromDir] to [toDir].
Future<void> copyFile(String fromDir, String name, String toDir) async {
await new File(p.join(fromDir, name)).copy(p.join(toDir, name));
await File(p.join(fromDir, name)).copy(p.join(toDir, name));
}

View File

@ -1,15 +1,16 @@
// 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.
library vm.snapshot.utils;
library vm_snapshot_analysis.utils;
import 'dart:io';
import 'dart:convert';
import 'package:vm/snapshot/ascii_table.dart';
import 'package:vm/snapshot/program_info.dart';
import 'package:vm/snapshot/instruction_sizes.dart' as instruction_sizes;
import 'package:vm/snapshot/v8_profile.dart' as v8_profile;
import 'package:vm_snapshot_analysis/ascii_table.dart';
import 'package:vm_snapshot_analysis/program_info.dart';
import 'package:vm_snapshot_analysis/instruction_sizes.dart'
as instruction_sizes;
import 'package:vm_snapshot_analysis/v8_profile.dart' as v8_profile;
Future<Object> loadJson(File input) async {
return await input

View File

@ -4,12 +4,12 @@
/// This library contains utilities for reading and analyzing snapshot profiles
/// produced by `--write-v8-snapshot-profile-to` VM flag.
library vm.snapshot.v8_profile;
library vm_snapshot_analysis.v8_profile;
import 'package:meta/meta.dart';
import 'package:vm/snapshot/name.dart';
import 'package:vm_snapshot_analysis/name.dart';
import 'package:vm/snapshot/program_info.dart';
import 'package:vm_snapshot_analysis/program_info.dart';
/// This class represents snapshot graph.
///

View File

@ -0,0 +1,20 @@
name: vm_snapshot_analysis
description: Utilities for working with non-symbolic stack traces.
version: 0.1.0
homepage: https://github.com/dart-lang/sdk/tree/master/pkg/vm_snapshot_analysis
environment:
sdk: '>=2.8.0 <3.0.0'
executables:
snapshot_analysis: analyse
dependencies:
args: ^1.6.0
path: ^1.7.0
meta: ^1.1.8
dev_dependencies:
pedantic: ^1.9.0
test: ^1.15.1

View File

@ -2,15 +2,15 @@
// 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 'dart:convert';
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:test/test.dart';
import 'package:vm/snapshot/instruction_sizes.dart' as instruction_sizes;
import 'package:vm/snapshot/program_info.dart';
import 'package:vm/snapshot/utils.dart';
import 'package:vm_snapshot_analysis/instruction_sizes.dart'
as instruction_sizes;
import 'package:vm_snapshot_analysis/program_info.dart';
import 'package:vm_snapshot_analysis/utils.dart';
final dart2native = () {
final sdkBin = path.dirname(Platform.executable);
@ -636,14 +636,6 @@ void main() async {
'makeSomeClosures': {'#size': lessThan(0)},
},
},
'B': {
// There are some cascading changes to CodeSourceMap
'#type': 'class',
'tornOff': {
'#type': 'function',
'#size': lessThan(0),
},
}
}
}
}));

View File

@ -4,7 +4,7 @@
import 'package:test/test.dart';
import 'package:vm/snapshot/name.dart';
import 'package:vm_snapshot_analysis/name.dart';
void main() async {
group('name', () {