mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 12:30:03 +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:
parent
2d73cb53a1
commit
117df0e98b
|
@ -11,7 +11,7 @@
|
||||||
"constraint, update this by running tools/generate_package_config.dart."
|
"constraint, update this by running tools/generate_package_config.dart."
|
||||||
],
|
],
|
||||||
"configVersion": 2,
|
"configVersion": 2,
|
||||||
"generated": "2020-06-17T09:39:51.245406",
|
"generated": "2020-06-23T13:49:13.378284",
|
||||||
"generator": "tools/generate_package_config.dart",
|
"generator": "tools/generate_package_config.dart",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
|
@ -560,7 +560,7 @@
|
||||||
"name": "stack_trace",
|
"name": "stack_trace",
|
||||||
"rootUri": "../third_party/pkg/stack_trace",
|
"rootUri": "../third_party/pkg/stack_trace",
|
||||||
"packageUri": "lib/",
|
"packageUri": "lib/",
|
||||||
"languageVersion": "1.23"
|
"languageVersion": "2.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "stagehand",
|
"name": "stagehand",
|
||||||
|
@ -676,6 +676,12 @@
|
||||||
"packageUri": "lib/",
|
"packageUri": "lib/",
|
||||||
"languageVersion": "2.6"
|
"languageVersion": "2.6"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "vm_snapshot_analysis",
|
||||||
|
"rootUri": "../pkg/vm_snapshot_analysis",
|
||||||
|
"packageUri": "lib/",
|
||||||
|
"languageVersion": "2.8"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "watcher",
|
"name": "watcher",
|
||||||
"rootUri": "../third_party/pkg/watcher",
|
"rootUri": "../third_party/pkg/watcher",
|
||||||
|
|
|
@ -112,6 +112,7 @@ typed_data:third_party/pkg/typed_data/lib
|
||||||
usage:third_party/pkg/usage/lib
|
usage:third_party/pkg/usage/lib
|
||||||
vm:pkg/vm/lib
|
vm:pkg/vm/lib
|
||||||
vm_service:pkg/vm_service/lib
|
vm_service:pkg/vm_service/lib
|
||||||
|
vm_snapshot_analysis:pkg/vm_snapshot_analysis/lib
|
||||||
watcher:third_party/pkg/watcher/lib
|
watcher:third_party/pkg/watcher/lib
|
||||||
web_components:third_party/pkg/web_components/lib
|
web_components:third_party/pkg/web_components/lib
|
||||||
web_socket_channel:third_party/pkg/web_socket_channel/lib
|
web_socket_channel:third_party/pkg/web_socket_channel/lib
|
||||||
|
|
3
pkg/vm_snapshot_analysis/.gitignore
vendored
Normal file
3
pkg/vm_snapshot_analysis/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.dart_tool/
|
||||||
|
.packages
|
||||||
|
pubspec.lock
|
6
pkg/vm_snapshot_analysis/AUTHORS
Normal file
6
pkg/vm_snapshot_analysis/AUTHORS
Normal 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
|
5
pkg/vm_snapshot_analysis/CHANGELOG.md
Normal file
5
pkg/vm_snapshot_analysis/CHANGELOG.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
## 0.1.0
|
||||||
|
|
||||||
|
- Initial release
|
26
pkg/vm_snapshot_analysis/LICENSE
Normal file
26
pkg/vm_snapshot_analysis/LICENSE
Normal 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.
|
137
pkg/vm_snapshot_analysis/README.md
Normal file
137
pkg/vm_snapshot_analysis/README.md
Normal 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
|
4
pkg/vm_snapshot_analysis/analysis_options.yaml
Normal file
4
pkg/vm_snapshot_analysis/analysis_options.yaml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
include: package:pedantic/analysis_options.1.8.0.yaml
|
||||||
|
analyzer:
|
||||||
|
exclude:
|
||||||
|
- lib/src/assets/**
|
|
@ -1,15 +1,17 @@
|
||||||
#!/usr/bin/env dart
|
#!/usr/bin/env dart
|
||||||
|
|
||||||
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
|
// 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
|
// 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.
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:args/command_runner.dart';
|
import 'package:args/command_runner.dart';
|
||||||
|
|
||||||
import 'package:vm/snapshot/commands/compare.dart';
|
import 'package:vm_snapshot_analysis/src/commands/compare.dart';
|
||||||
import 'package:vm/snapshot/commands/summary.dart';
|
import 'package:vm_snapshot_analysis/src/commands/summary.dart';
|
||||||
import 'package:vm/snapshot/commands/treemap.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.')
|
'Tools for binary size analysis of Dart VM AOT snapshots.')
|
||||||
..addCommand(TreemapCommand())
|
..addCommand(TreemapCommand())
|
||||||
..addCommand(CompareCommand())
|
..addCommand(CompareCommand())
|
|
@ -2,7 +2,7 @@
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
// 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.
|
// 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;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ class AsciiTable {
|
||||||
|
|
||||||
final List<Row> rows = <Row>[];
|
final List<Row> rows = <Row>[];
|
||||||
|
|
||||||
AsciiTable({List<dynamic> header, this.maxWidth: unlimitedWidth}) {
|
AsciiTable({List<dynamic> header, this.maxWidth = unlimitedWidth}) {
|
||||||
if (header != null) {
|
if (header != null) {
|
||||||
addSeparator();
|
addSeparator();
|
||||||
addRow(header);
|
addRow(header);
|
|
@ -3,10 +3,10 @@
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
/// Helper functions for parsing output of `--print-instructions-sizes-to` flag.
|
/// 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_analysis/name.dart';
|
||||||
import 'package:vm/snapshot/program_info.dart';
|
import 'package:vm_snapshot_analysis/program_info.dart';
|
||||||
|
|
||||||
/// Parse the output of `--print-instructions-sizes-to` saved in the given
|
/// Parse the output of `--print-instructions-sizes-to` saved in the given
|
||||||
/// file [input].
|
/// file [input].
|
|
@ -3,7 +3,7 @@
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
/// Helpers for parsing Code object name produced by Code::QualifiedName
|
/// 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.
|
// Wrapper around the name of a Code object produced by Code::QualifiedName.
|
||||||
//
|
//
|
|
@ -3,11 +3,11 @@
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
/// Classes for representing information about the program structure.
|
/// 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:meta/meta.dart';
|
||||||
|
|
||||||
import 'package:vm/snapshot/v8_profile.dart';
|
import 'package:vm_snapshot_analysis/v8_profile.dart';
|
||||||
|
|
||||||
/// Represents information about compiled program.
|
/// Represents information about compiled program.
|
||||||
class ProgramInfo {
|
class ProgramInfo {
|
938
pkg/vm_snapshot_analysis/lib/src/assets/D3SymbolTreeMap.js
Normal file
938
pkg/vm_snapshot_analysis/lib/src/assets/D3SymbolTreeMap.js
Normal 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;
|
||||||
|
}
|
31
pkg/vm_snapshot_analysis/lib/src/assets/d3/README.chromium
Normal file
31
pkg/vm_snapshot_analysis/lib/src/assets/d3/README.chromium
Normal 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.
|
1
pkg/vm_snapshot_analysis/lib/src/assets/d3/README.dart
Normal file
1
pkg/vm_snapshot_analysis/lib/src/assets/d3/README.dart
Normal file
|
@ -0,0 +1 @@
|
||||||
|
A local copy of third_party/d3 from Chromium project.
|
|
@ -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);
|
26
pkg/vm_snapshot_analysis/lib/src/assets/d3/src/LICENSE
Normal file
26
pkg/vm_snapshot_analysis/lib/src/assets/d3/src/LICENSE
Normal 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.
|
9
pkg/vm_snapshot_analysis/lib/src/assets/d3/src/README.md
Normal file
9
pkg/vm_snapshot_analysis/lib/src/assets/d3/src/README.md
Normal 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. D3’s 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 [mbostock’s bl.ocks](http://bl.ocks.org/mbostock).
|
9297
pkg/vm_snapshot_analysis/lib/src/assets/d3/src/d3.js
vendored
Normal file
9297
pkg/vm_snapshot_analysis/lib/src/assets/d3/src/d3.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
525
pkg/vm_snapshot_analysis/lib/src/assets/index.html
Normal file
525
pkg/vm_snapshot_analysis/lib/src/assets/index.html
Normal 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, '&')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body onload='init()'>
|
||||||
|
<div style='position: absolute; top: 5px; left: 5px;'>
|
||||||
|
<input type='button' onclick='showOptions()' value='Options & 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'> </span><input checked type='checkbox' id='check_0' value='A'>Global absolute (A)
|
||||||
|
<br><span class='swatch' id='swatch_1'> </span><input checked type='checkbox' id='check_1' value='B'>Global uninitialized data (B)
|
||||||
|
<br><span class='swatch' id='swatch_2'> </span><input checked type='checkbox' id='check_2' value='b'>Local uninitialized data (b)
|
||||||
|
<br><span class='swatch' id='swatch_3'> </span><input checked type='checkbox' id='check_3' value='C'>Global uninitialized common (C)
|
||||||
|
<br><span class='swatch' id='swatch_4'> </span><input checked type='checkbox' id='check_4' value='D'>Global initialized data (D)
|
||||||
|
<br><span class='swatch' id='swatch_5'> </span><input checked type='checkbox' id='check_5' value='d'>Local initialized data (d)
|
||||||
|
<br><span class='swatch' id='swatch_6'> </span><input checked type='checkbox' id='check_6' value='G'>Global small initialized data (G)
|
||||||
|
<br><span class='swatch' id='swatch_7'> </span><input checked type='checkbox' id='check_7' value='g'>Local small initialized data (g)
|
||||||
|
<br><span class='swatch' id='swatch_8'> </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'> </span><input checked type='checkbox' id='check_9' value='N'>Debugging (N)
|
||||||
|
<br><span class='swatch' id='swatch_10'> </span><input checked type='checkbox' id='check_10' value='p'>Stack unwind (p)
|
||||||
|
<br><span class='swatch' id='swatch_11'> </span><input checked type='checkbox' id='check_11' value='R'>Global read-only data (R)
|
||||||
|
<br><span class='swatch' id='swatch_12'> </span><input checked type='checkbox' id='check_12' value='r'>Local read-only data (r)
|
||||||
|
<br><span class='swatch' id='swatch_13'> </span><input checked type='checkbox' id='check_13' value='S'>Global small uninitialized data (S)
|
||||||
|
<br><span class='swatch' id='swatch_14'> </span><input checked type='checkbox' id='check_14' value='s'>Local small uninitialized data (s)
|
||||||
|
<br><span class='swatch' id='swatch_15'> </span><input checked type='checkbox' id='check_15' value='T'>Global code (T)
|
||||||
|
<br><span class='swatch' id='swatch_16'> </span><input checked type='checkbox' id='check_16' value='t'>Local code (t)
|
||||||
|
<br><span class='swatch' id='swatch_17'> </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'> </span><input checked type='checkbox' id='check_18' value='u'>Unique (u)
|
||||||
|
<br><span class='swatch' id='swatch_19'> </span><input checked type='checkbox' id='check_19' value='V'>Global weak object (V)
|
||||||
|
<br><span class='swatch' id='swatch_20'> </span><input checked type='checkbox' id='check_20' value='v'>Local weak object (v)
|
||||||
|
<br><span class='swatch' id='swatch_21'> </span><input checked type='checkbox' id='check_21' value='W'>Global weak symbol (W)
|
||||||
|
<br><span class='swatch' id='swatch_22'> </span><input checked type='checkbox' id='check_22' value='w'>Local weak symbol (w)
|
||||||
|
<br><span class='swatch' id='swatch_23'> </span><input checked type='checkbox' id='check_23' value='@'>Vtable entry (@)
|
||||||
|
<br><span class='swatch' id='swatch_24'> </span><input checked type='checkbox' id='check_24' value='-'>STABS debugging (-)
|
||||||
|
<br><span class='swatch' id='swatch_25'> </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;'>
|
||||||
|
x <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>
|
|
@ -5,16 +5,16 @@
|
||||||
/// This tool compares two JSON size reports produced by
|
/// This tool compares two JSON size reports produced by
|
||||||
/// --print-instructions-sizes-to and reports which symbols increased in size
|
/// --print-instructions-sizes-to and reports which symbols increased in size
|
||||||
/// and which symbols decreased in size.
|
/// and which symbols decreased in size.
|
||||||
library vm.snapshot.compare;
|
library vm_snapshot_analysis.compare;
|
||||||
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:args/command_runner.dart';
|
import 'package:args/command_runner.dart';
|
||||||
import 'package:vm/snapshot/ascii_table.dart';
|
|
||||||
|
|
||||||
import 'package:vm/snapshot/program_info.dart';
|
import 'package:vm_snapshot_analysis/ascii_table.dart';
|
||||||
import 'package:vm/snapshot/utils.dart';
|
import 'package:vm_snapshot_analysis/program_info.dart';
|
||||||
import 'package:vm/snapshot/v8_profile.dart';
|
import 'package:vm_snapshot_analysis/utils.dart';
|
||||||
|
import 'package:vm_snapshot_analysis/v8_profile.dart';
|
||||||
|
|
||||||
class CompareCommand extends Command<void> {
|
class CompareCommand extends Command<void> {
|
||||||
@override
|
@override
|
||||||
|
@ -92,6 +92,7 @@ precisely based on their source position (which is included in their name).
|
||||||
case 'package':
|
case 'package':
|
||||||
return HistogramType.byPackage;
|
return HistogramType.byPackage;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
File _checkExists(String path) {
|
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,
|
void printComparison(File oldJson, File newJson,
|
||||||
{int maxWidth: 0,
|
{int maxWidth = 0,
|
||||||
bool collapseAnonymousClosures = false,
|
bool collapseAnonymousClosures = false,
|
||||||
HistogramType granularity = HistogramType.bySymbol}) async {
|
HistogramType granularity = HistogramType.bySymbol}) async {
|
||||||
final oldSizes = await loadProgramInfo(oldJson,
|
final oldSizes = await loadProgramInfo(oldJson,
|
|
@ -5,17 +5,17 @@
|
||||||
/// This tool generates a summary report from a binary size reports produced by
|
/// This tool generates a summary report from a binary size reports produced by
|
||||||
/// the AOT compiler's --print-instructions-sizes-to and
|
/// the AOT compiler's --print-instructions-sizes-to and
|
||||||
/// --write-v8-snapshot-profile-to flags.
|
/// --write-v8-snapshot-profile-to flags.
|
||||||
library vm.snapshot.summary;
|
library vm_snapshot_analysis.summary;
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:args/command_runner.dart';
|
import 'package:args/command_runner.dart';
|
||||||
|
|
||||||
import 'package:vm/snapshot/ascii_table.dart';
|
import 'package:vm_snapshot_analysis/ascii_table.dart';
|
||||||
import 'package:vm/snapshot/program_info.dart';
|
import 'package:vm_snapshot_analysis/program_info.dart';
|
||||||
import 'package:vm/snapshot/utils.dart';
|
import 'package:vm_snapshot_analysis/utils.dart';
|
||||||
import 'package:vm/snapshot/v8_profile.dart';
|
import 'package:vm_snapshot_analysis/v8_profile.dart';
|
||||||
|
|
||||||
class SummaryCommand extends Command<void> {
|
class SummaryCommand extends Command<void> {
|
||||||
@override
|
@override
|
|
@ -8,20 +8,22 @@
|
||||||
///
|
///
|
||||||
/// It used the same visualization framework as Chromium's binary_size tool
|
/// It used the same visualization framework as Chromium's binary_size tool
|
||||||
/// located in runtime/third_party/binary_size.
|
/// located in runtime/third_party/binary_size.
|
||||||
library vm.snapshot.commands.treemap;
|
library vm_snapshot_analysis.commands.treemap;
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:isolate';
|
||||||
import 'dart:math' show max;
|
import 'dart:math' show max;
|
||||||
|
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
import 'package:args/command_runner.dart';
|
import 'package:args/command_runner.dart';
|
||||||
|
|
||||||
import 'package:vm/snapshot/instruction_sizes.dart' as instruction_sizes;
|
import 'package:vm_snapshot_analysis/instruction_sizes.dart'
|
||||||
import 'package:vm/snapshot/program_info.dart';
|
as instruction_sizes;
|
||||||
import 'package:vm/snapshot/v8_profile.dart' as v8_profile;
|
import 'package:vm_snapshot_analysis/program_info.dart';
|
||||||
import 'package:vm/snapshot/utils.dart';
|
import 'package:vm_snapshot_analysis/v8_profile.dart' as v8_profile;
|
||||||
|
import 'package:vm_snapshot_analysis/utils.dart';
|
||||||
|
|
||||||
class TreemapCommand extends Command<void> {
|
class TreemapCommand extends Command<void> {
|
||||||
@override
|
@override
|
||||||
|
@ -57,8 +59,8 @@ viewed in a browser:
|
||||||
usageException('Need to specify input JSON and output directory.');
|
usageException('Need to specify input JSON and output directory.');
|
||||||
}
|
}
|
||||||
|
|
||||||
final input = new File(argResults.rest[0]);
|
final input = File(argResults.rest[0]);
|
||||||
final outputDir = new Directory(argResults.rest[1]);
|
final outputDir = Directory(argResults.rest[1]);
|
||||||
|
|
||||||
if (!input.existsSync()) {
|
if (!input.existsSync()) {
|
||||||
usageException('Input file ${input.path} does not exist!');
|
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.
|
// Create output directory and copy all auxiliary files from binary_size tool.
|
||||||
await outputDir.create(recursive: true);
|
await outputDir.create(recursive: true);
|
||||||
|
|
||||||
final scriptLocation = p.dirname(Platform.script.toFilePath());
|
final assetsUri = await Isolate.resolvePackageUri(
|
||||||
final sdkRoot = p.join(scriptLocation, '..', '..', '..');
|
Uri.parse('package:vm_snapshot_analysis/src/assets'));
|
||||||
final d3SrcDir = p.join(sdkRoot, 'runtime', 'third_party', 'd3', 'src');
|
final assetsDir = assetsUri.toFilePath();
|
||||||
final templateDir = p.join(
|
final d3SrcDir = p.join(assetsDir, 'd3', 'src');
|
||||||
sdkRoot, 'runtime', 'third_party', 'binary_size', 'src', 'template');
|
|
||||||
|
|
||||||
final d3OutDir = p.join(outputDir.path, 'd3');
|
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']) {
|
for (var file in ['LICENSE', 'd3.js']) {
|
||||||
await copyFile(d3SrcDir, file, d3OutDir);
|
await copyFile(d3SrcDir, file, d3OutDir);
|
||||||
}
|
}
|
||||||
for (var file in ['index.html', 'D3SymbolTreeMap.js']) {
|
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.
|
// Serialize symbol size tree as JSON.
|
||||||
final dataJsPath = p.join(outputDir.path, 'data.js');
|
final dataJsPath = p.join(outputDir.path, 'data.js');
|
||||||
final sink = new File(dataJsPath).openWrite();
|
final sink = File(dataJsPath).openWrite();
|
||||||
sink.write('var tree_data=');
|
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)));
|
.transform(json.encoder.fuse(utf8.encoder)));
|
||||||
await sink.close();
|
await sink.close();
|
||||||
|
|
||||||
|
@ -191,7 +192,7 @@ Map<String, dynamic> addChild(
|
||||||
|
|
||||||
/// Add the given symbol to the tree.
|
/// Add the given symbol to the tree.
|
||||||
void addSymbol(Map<String, dynamic> root, String path, String name, int size,
|
void addSymbol(Map<String, dynamic> root, String path, String name, int size,
|
||||||
{String symbolType: symbolTypeGlobalText}) {
|
{String symbolType = symbolTypeGlobalText}) {
|
||||||
var node = root;
|
var node = root;
|
||||||
var depth = 0;
|
var depth = 0;
|
||||||
for (var part in path.split('/')) {
|
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].
|
/// Copy file with the given name from [fromDir] to [toDir].
|
||||||
Future<void> copyFile(String fromDir, String name, String toDir) async {
|
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));
|
||||||
}
|
}
|
|
@ -1,15 +1,16 @@
|
||||||
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
|
// 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
|
// 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.
|
// 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:io';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:vm/snapshot/ascii_table.dart';
|
import 'package:vm_snapshot_analysis/ascii_table.dart';
|
||||||
import 'package:vm/snapshot/program_info.dart';
|
import 'package:vm_snapshot_analysis/program_info.dart';
|
||||||
import 'package:vm/snapshot/instruction_sizes.dart' as instruction_sizes;
|
import 'package:vm_snapshot_analysis/instruction_sizes.dart'
|
||||||
import 'package:vm/snapshot/v8_profile.dart' as v8_profile;
|
as instruction_sizes;
|
||||||
|
import 'package:vm_snapshot_analysis/v8_profile.dart' as v8_profile;
|
||||||
|
|
||||||
Future<Object> loadJson(File input) async {
|
Future<Object> loadJson(File input) async {
|
||||||
return await input
|
return await input
|
|
@ -4,12 +4,12 @@
|
||||||
|
|
||||||
/// This library contains utilities for reading and analyzing snapshot profiles
|
/// This library contains utilities for reading and analyzing snapshot profiles
|
||||||
/// produced by `--write-v8-snapshot-profile-to` VM flag.
|
/// 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: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.
|
/// This class represents snapshot graph.
|
||||||
///
|
///
|
20
pkg/vm_snapshot_analysis/pubspec.yaml
Normal file
20
pkg/vm_snapshot_analysis/pubspec.yaml
Normal 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
|
|
@ -2,15 +2,15 @@
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
// 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.
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
import 'package:vm/snapshot/instruction_sizes.dart' as instruction_sizes;
|
import 'package:vm_snapshot_analysis/instruction_sizes.dart'
|
||||||
import 'package:vm/snapshot/program_info.dart';
|
as instruction_sizes;
|
||||||
import 'package:vm/snapshot/utils.dart';
|
import 'package:vm_snapshot_analysis/program_info.dart';
|
||||||
|
import 'package:vm_snapshot_analysis/utils.dart';
|
||||||
|
|
||||||
final dart2native = () {
|
final dart2native = () {
|
||||||
final sdkBin = path.dirname(Platform.executable);
|
final sdkBin = path.dirname(Platform.executable);
|
||||||
|
@ -636,14 +636,6 @@ void main() async {
|
||||||
'makeSomeClosures': {'#size': lessThan(0)},
|
'makeSomeClosures': {'#size': lessThan(0)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'B': {
|
|
||||||
// There are some cascading changes to CodeSourceMap
|
|
||||||
'#type': 'class',
|
|
||||||
'tornOff': {
|
|
||||||
'#type': 'function',
|
|
||||||
'#size': lessThan(0),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
import 'package:vm/snapshot/name.dart';
|
import 'package:vm_snapshot_analysis/name.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
group('name', () {
|
group('name', () {
|
Loading…
Reference in a new issue