# vm_snapshot_analysis 0.7.6

Utilities for analysing AOT snapshot size.




## Installing
```
export PUB_HOSTED_URL=https://pub.mirror.p-hatan.com
flutter pub add 'vm_snapshot_analysis:0.7.6'
```
Published: 2023-07-07T11:40:08.793055Z
Expected archive SHA-256: 5a79b9fbb6be2555090f55b03b23907e75d44c3fd7bdd88da09848aa5a1914c8

Available in cache; verification: upstream_checksum
## Direct dependencies
- args: ^2.0.0
- collection: ^1.15.0
- path: ^1.8.0

## Package README (untrusted upstream documentation)
[![pub package](https://img.shields.io/pub/v/vm_snapshot_analysis.svg)](https://pub.dev/packages/vm_snapshot_analysis)
[![package publisher](https://img.shields.io/pub/publisher/vm_snapshot_analysis.svg)](https://pub.dev/packages/vm_snapshot_analysis/publisher)

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`, `--write-v8-snapshot-profile-to` and
`--trace-precompiler-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.
* `--trace-precompiler-to` gives information about dependencies between
  compiled functions, allowing to determine why certain function was pulled into
  the snapshot.

### Passing flags to the AOT compiler

In both Flutter and `dart compile exe` / `dart compile aot-snapshot` you can use
`--extra-gen-snapshot-options` to pass flags to the AOT compiler:

```console
$ flutter build aot --release --extra-gen-snapshot-options=--write-v8-snapshot-profile-to=profile.json

$ dart compile exe --extra-gen-snapshot-options=--write-v8-snapshot-profile-to=profile.json -o binary input.dart
```

Similarly with `--print-instructions-sizes-to`.

If you are working on the Dart SDK you can use the `pkg/vm/tool/precompiler2`
script, in which case you can just pass these flags directly:

```console
$ pkg/vm/tool/precompiler2 --write-v8-snapshot-profile-to=profile.json input.dart binary
```

## 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 installed 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-profile.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.

This command also supports _estimating_ cumulative impact of a library or a
package together with its dependencies - which can be computed from
a precompiler trace (generated by adding
`--trace-precompiler-to=path/to/trace.json` to `--extra-gen-snapshot-options=`).

```console
$ snapshot_analysis summary [-b granularity] [-w filter] [-s dep-tree-start-depth] [-d dep-tree-display-depth] --precompiler-trace=<input-trace.json> <input-profile.json>
```
For example:

```console
$ snapshot_analysis summary -b package /tmp/profile.json
+-----------------------------+--------------+---------+
| Package                     | Size (Bytes) | Percent |
+-----------------------------+--------------+---------+
| package:compiler            |      5369933 |  38.93% |
| package:front_end           |      2644942 |  19.18% |
| package:kernel              |      1443568 |  10.47% |
| package:_fe_analyzer_shared |       944555 |   6.85% |
...
$ snapshot_analysis summary -b package -s 1 -d 3 --precompiler-trace=/tmp/trace.json /tmp/profile.json
+------------------------------+--------------+---------+
| Package                      | Size (Bytes) | Percent |
+------------------------------+--------------+---------+
| package:compiler (+ 8 deps)  |      5762761 |  41.78% |
| package:front_end (+ 1 deps) |      2708981 |  19.64% |
| package:kernel               |      1443568 |  10.47% |
| package:_fe_analyzer_shared  |       944555 |   6.85% |
...
Dependency trees:

package:compiler (total 5762761 bytes)
├── package:js_ast (total 242490 bytes)
├── package:dart2js_info (total 101280 bytes)
├── package:crypto (total 27434 bytes)
│   ├── package:typed_data (total 11850 bytes)
│   └── package:convert (total 5185 bytes)
├── package:collection (total 15182 bytes)
├── package:_js_interop_checks (total 4627 bytes)
└── package:js_runtime (total 1815 bytes)

package:front_end (total 2708981 bytes)
└── package:package_config (total 64039 bytes)
```

### `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 [--format <format>] <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.

`--format` flag allows to control granularity of the output when `input.json`
is a V8 snapshot profile, available options are:
* `collapsed` essentially renders `ProgramInfo` as a treemap, individual
snapshot nodes are ignored.
* `simplified` same as `collapsed`, but also folds size information from
nested functions into outermost function (e.g. top level function or a
method) producing easy to consume output.
* `data-and-code` collapses snapshot nodes based on whether they represent
data or executable code.
* `object-type` (default) collapses snapshot nodes based on their type only.

### `explain`

#### `explain dynamic-calls`

```console
$ snapshot_analysis explain dynamic-calls <profile.json> <trace.json>
```

This command generates a report listing dynamically dispatched selectors
and their approximate impact on the code size.

```console
snapshot_analysis explain dynamic-calls /tmp/profile.json /tmp/trace.json
+------------------------------+--------------+---------+----------+
| Selector                     | Size (Bytes) | Percent | Of total |
+------------------------------+--------------+---------+----------+
| set:requestHeader            |        10054 |  28.00% |    0.03% |
| get:scale                    |         3630 |  10.11% |    0.01% |
...
Dynamic call to set:requestHeader (retaining ~10054 bytes) occurs in:
    package:my-super-app/src/injector.dart::Injector.handle{body}

Dynamic call to get:scale (retaining ~3630 bytes) occurs in:
    package:some-dependency/src/image.dart::Image.==
```

## 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.

## Precompiler Trace Format (`--write-precompiler-trace-to=...`)

AOT compiler can produce a JSON file containing information about compiled
functions and dependencies between them. This file has the following structure:

```json
{
    "trace": traceArray,
    "entities": entitiesArray,
    "strings": stringsArray,
}
```

- `stringsArray` is an array of strings referenced by other parts of the trace
by their index in this array.
- `entitiesArray` is an flattened array of entities:

    - `"C", <library-uri-idx>, <class-name-idx>, 0` - class record;
    - `"V", <class-idx>, <name-idx>, 0` - static field record;
    - `"F"|"S", <class-idx>, <name-idx>, <selector-id>` - function record (`F` for dynamic functions and `S` for static functions);

  Note that all records in this array occupy the same amount of elements (`4`)
  to make random access by index possible.

- `traceArray` is an flattened array of precompilation events:

    - `"R"` - root event (always the first element)
    - `"E"` - end event (always the last element)
    - `"C", <function-idx>` - function compilation event

  Root and function compilation events can additionally be followed by a
  sequence of references which enumerate outgoing dependencies discovered
  by the AOT compiler:

    - `<entity-idx>` - a reference to a function or a static field;
    - `"S", <selector-idx>` - a dynamic call with the given selector;
    - `"T", <selector-id>` - dispatch table call with the given selector id;

*Flattened array* is an array of records formed by consecutive elements:
`[R0_0, R0_1, R0_2, R1_0, R1_1, R1_2, ...]` here `R0_*` is the first record
and `R1_*` is the second record and so on.

## Features and bugs

Please file feature requests and bugs at the [issue tracker][tracker].

[tracker]: https://github.com/dart-lang/sdk/issues

## Changelog
# Changelog

## 0.7.6

- Allow the old `'patched_class_'` field for `PatchClass` until we change the
  SDK lower bound for this package to a version that does not have it.

## 0.7.5

- Fix for flutter/flutter#130009 where an attempt to lookup the owner info
node for type testing stubs would cause a null check error.

## 0.7.4

- Fix for flutter/flutter#128402 where an attempt to lookup the owner info
node for type testing stubs would cause a null check error.

## 0.7.3-dev

- Update the readme to document the current way to generate Dart AOT snapshots.
- Spelling corrections in dartdoc comments.

## 0.7.2

- Upgrade to `package:lints` 2.0.
- Populate the pubspec `repository` field.

## 0.7.1

- Make `CallGraphNode.dominator` nullable.

## 0.7.0

- Migrate to null-safety.

## 0.6.0

- Update to latest args, path, meta dependency.

## 0.5.6
- Fix for flutter/flutter#76313 causing issues with profiles containing
WSRs serialized as smi-s instead of actual WSR objects.=

## 0.5.5
- Add `deps-display-depth` (`-d`) flag for `summary` command to make the display
depth of outputted dependency trees configurable.
- Rename `deps-collapse-depth` (formerly `-d`) flag for `summary` command to
`deps-start-depth` (now `-s`).
- Add `generateCallGraphWithDominators` method that generates a `CallGraph`
object from precompiler trace.

## 0.5.4
- Fix bug causing name clash for Type class.

## 0.5.3
- Add `compareProgramInfo` that takes in two program info objects and outputs
a `Map` object containing the diff data.

## 0.5.2
- Add support for package paths that look like `package:foo.bar.baz/src/foobar.dart`
- Move `commands` back to lib.

## 0.5.0+1
- Fix broken package by moving non-executable file out of bin/ directory.

## 0.5.0
- Remove `dart:io` dependency from package `lib`, and move `commands` to `bin`.
- Replace `loadProgramInfo` util method with `loadProgramInfoFromJson`, which
expects an `Object` parameter instead of a `File` parameter.
- `buildComparisonTreemap` now expects two `Object` parameters for `oldJson` and
`newJson` instead of two `File` parameters.
- `compare` command now prints difference breakdown by node type when this
information is available.

## 0.4.0

- Add `buildComparisonTreemap` for constructing treemap representing the diff
between two size profiles.
- Implemented support for extracting call graph information from the AOT
compiler trace (`--trace-precompiler-to` flag), see `precompiler_trace.dart`.
- New command `explain dynamic-calls` which estimates the impact of different
dynamic calls on the resulting AOT snapshot size using information from the
size dump (e.g. V8 snapshot profile) and AOT compiler trace.
- `summary` command can now use information from the AOT compiler trace to
group packages/libraries together with their dependencies to given more precise
estimate of how much a specific package/library brings into the snapshot.

## 0.3.0

- Extract treemap construction code into a separate library, to make it
reusable.
- Add ability to collapse leaf nodes in a treemap created from V8 snapshot
profile. This behavior is programmatically controlled by `TreemapFormat format`
parameter and from CLI via `--format` flag. The following options are available
    - `collapsed` essentially renders `ProgramInfo` as a treemap, individual
    snapshot nodes are ignored.
    - `simplified` same as `collapsed`, but also folds size information from
    nested functions into outermost function (e.g. top level function or a
    method) producing easy to consume output.
    - `data-and-code` collapses snapshot nodes based on whether they represent
    data or executable code.
    - `object-type` (default) collapses snapshot nodes based on their type only.
- When computing `ProgramInfo` from a V8 snapshot profile no longer create
`ProgramInfoNode` for `Code` nodes which are owned by a function - instead
directly attribute the `Code` node itself and all retained nodes into
`ProgramInfoNode` for the function itself. For stubs (including allocation
stubs) create an artificial `functionNode` instead of using `NodeType.other`.
The only remaining use of `NodeType.other` is for fields.

## 0.2.0

- Update CLI help message to avoid referring to a snapshot created by pub as the
name of the script.
- Fix owner computation code for V8 profiles: the size of a snapshot node
which corresponds to a `ProgramInfoNode` should be attributed to that
`ProgramInfoNode` and not to its parent. For example `Function` node corresponds
to `ProgramInfoNode` of type `functionNode`, previously the size of `Function`
node would be attributed to the parent of this `ProgramInfoNode`, but it
should be attributed to the node itself.
- Update `README.md` to include more information on how to pass flags to
Dart AOT compiler.
- Add `ProgramInfoNode.size` documentation to clarify the meaning of the member.

## 0.1.0

- Initial release







 
