Flutter Engine
The Flutter Engine
|
This package contains libraries and tools you can use to process info files produced when running dart2js with --dump-info
.
The info files contain data about each element included in the output of your program. The data includes information such as:
.dart.js
output,All of this information can help you understand why some piece of code is included in your compiled application, and how far was dart2js able to understand your code. This data can help you make changes to improve the quality and size of your framework or app.
This package focuses on gathering libraries and tools that summarize all of that information. Bear in mind that even with all these tools, it is not trivial to isolate code-size issues. We just hope that these tools make things a bit easier.
Currently, most tools available here can be used to analyze code-size and attribution of code-size to different parts of your app. With time, we hope to add more data to the info files, and include better tools to help understand the results of type inference.
This package is still in flux and we might make breaking changes at any time. Our current goal is not to provide a stable API, we mainly want to expose the functionality and iterate on it. We recommend that you pin a specific version of this package and update when needed.
All tools are provided as commands of a single command-line interface. To run:
There is a short help available on the tool, and more details are provided below.
Dart2js info files are produced in either a binary or JSON format.
This package also exposes libraries to parse and represent the information from the info files. If there is data that is stored in the info files but not exposed by one of our tools, you may be able to use the info APIs to quickly put together your own tool.
AllInfo exposes a Dart representation of all of the collected information. There are deserialization libraries in this package to decode any info file produced by the dart compile js
(before Dart 2.18, dart2js
) --dump-info
option. See lib/binary_serialization.dart
and lib/json_info_codec.dart
to find the binary and JSON decoders respectively. For convenience, package:dart2js_info/src/io.dart
also exposes a helper method that can choose, depending on the extension of the info file, whether to deserialize it using the binary or JSON decoder. For example:
The following tools are a available today:
code_deps
: simple tool that can answer queries about the dependency between functions and fields in your program. Currently it only supports the some_path
query, which shows a dependency path from one function to another.common
: a tool that reports the common elements of two info files. Commonality is determined by the element's name, file path, and URI but not code size.diff
: a tool that diffs two info files and reports which program elements have been added, removed, or changed size. This also tells which elements are no longer deferred or have become deferred.library_size
: a tool that shows how much code was attributed to each library. This tool is configurable so it can group data in many ways (e.g. to tally together all libraries that belong to a package, or all libraries that match certain name pattern).deferred_check
: a tool that verifies that code was split into deferred parts as expected. This tool takes a specification of the expected layout of code into deferred parts, and checks that the output from dart compile js
meets the specification.deferred_size
: a tool that gives a breakdown of the sizes of the deferred parts of the program. This can show how much of your total code size can be loaded deferred.deferred_layout
: a tool that reports which code is included on each output unit.function_size
: a tool that shows how much code was attributed to each function. This tool also uses dependency information to compute dominance and reachability data. This information can sometimes help determine how much savings could come if the function was not included in the program.coverage_server
and coverage_analysis
: dart2js has an experimental feature to gather coverage data of your application. The coverage_log_server
can record this data, and live_code_size_analysis
can correlate that with the info file, so you determine why code that is not used is being included in your app.convert
: a tool that converts info files from one format to another. Accepted inputs are JSON or the internal binary form, outputs can be JSON, backward-compatible JSON, binary, or protobuf schema (as defined in info.proto
).runtime_coverage
: dart2js has an experimental feature to gather runtime coverage data of your application. This tool correlates that with the info file and can output a package-level breakdown of which files were not used during the runtime of your app.show
: a tool that dumps info files in a readable text format.Next we describe in detail how to use each of these tools.
This command-line tool can be used to query for code dependencies. Currently this tool only supports the some_path
query, which gives you the shortest path for how one function depends on another.
Run this tool as follows:
The arguments to the query are regular expressions that can be used to select a single element in your program. If your regular expression is too general and has more than one match, this tool will pick the first match and ignore the rest. Regular expressions are matched against a fully qualified element name, which includes the library and class name (if any) that contains it. A typical qualified name is of this form:
libraryName::ClassName.elementName
If the name of a function your are looking for is unique enough, it might be sufficient to just write that name as your regular expression.
This command-line tool shows common elements between two info files. It can be run as follows:
The tool gives a breakdown of the common elements between the two info files, reporting code size discrepancies if they exist. Here's an example output snippet:
Common elements are sorted by name by default but can be sorted by size with the --order-by-size
flag. Additionally, the tool can be restricted to just packages with the --packages-only
flag.
This command-line tool shows a diff between two info files. It can be run as follows:
The tool gives a breakdown of the difference between the two info files. Here's an example output:
You can also pass --summary
to only show the summary section.
This command-line tool shows the size distribution of generated code among libraries. It can be run as follows:
Libraries can be grouped using regular expressions. You can specify what regular expressions to use by providing a grouping.yaml
file with the --grouping
flag:
The format of the grouping.yaml
file is as follows:
The file should include a single key groups
containing a list of group specifications. Each group is specified by a map of 3 entries:
regexp
(required): a regexp used to match entries that belong to the group.name
(optional): the name given to this group in the output table. If omitted, the name is derived from the regexp as the match's group(1) or group(0) if no group was defined. When names are omitted the group specification implicitly defines several groups, one per observed name.cluster
(optional): a clustering index for how data is shown in a table. Groups with higher cluster indices are shown later in the table after a dividing line. If missing, the cluster index defaults to 0.Here is an example configuration, with comments about what each entry does:
Regardless of the grouping configuration, the tool will display the total code size attributed of all libraries, constants, and the program size.
Note: eventually you should expect all numbers to add up to the program size. Currently dart2js's --dump-info
is not complete, so numbers for bootstrapping code and lazy static initializers are missing.
This tool checks that the output from dart2js meets a given specification, given in a YAML file. It can be run as follows:
The format of the YAML file is:
The YAML file consists of a list of declarations, one for each deferred part expected in the output. At least one of these parts must be named "main"; this is the main part that contains the program entrypoint. Each top-level part contains a list of package names that are expected to be contained in that part, a list of package names that are expected to be in another part, or both. For instance, in the example YAML above the part named "baz" is expected to contain the packages "baz" and "quux" and exclude the package "zardoz".
The names for parts given in the specification YAML file (besides "main") are the same as the name given to the deferred import in the dart file. For instance, if you have ‘import 'package:foo/bar.dart’ deferred as baz;‘ in your dart file, then the corresponding name in the specification file is 'baz’.
This tool gives a breakdown of all of the deferred code in the program by size. It can show how much of the total code size is deferred. It can be run as follows:
The tool will output a table listing all of the deferred imports in the program as well as the "main" chunk, which is not deferred. The output looks like:
This tool reports which code is included in each output unit. It can be run as follows:
The tool will output a table listing all of the deferred output units or chunks, for each unit it will list the set of libraries that contribute code to this unit. If a library contributes to more than one output unit, the tool lists which elements are in one or another output unit. For example, the output might look like this:
In this example, all the code of b.dart
after tree-shaking was included in the output unit 2, but c.dart
was split between output unit 1 and output unit 2.
This command-line tool presents how much each function contributes to the total code of your application. We use dependency information to compute dominance and reachability data as well.
When you run:
the tool produces a table output with lots of entries. Here is an example entry with the corresponding table header:
Such entry means that the function myMethodName
uses 275 bytes, which is 0.01% of the application. That function however calls other functions, which transitively can include up to 74.28% of the application size. Of all those reachable functions, some of them are reachable from other parts of the program, but a subset are dominated by myMethodName
, that is, other parts of the program starting from main
would first go through myMethodName
before reaching those functions. In this example, that subset is 13.97% of the application size. This means that if you somehow can remove your dependency on myMethodName
, you will save at least that 13.97%, and possibly some more from the reachable size, but how much of that we are not certain.
Coverage information requires a bit more setup and work to get them running. The steps are as follows:
--dump-info
and --experiment-call-instrumentation
The flag only works dart2js version 2.2.0 or newer.
mail.dart.js.coverage.json
Runtime code analysis requires both an info file and a runtime data file.
The info file is emitted by compiling a dart2js app with --dump-info
:
Enable the collection of runtime data by compiling a dart2js app with an experimental flag:
After using your app (manually or via integration tests), dump the top-level window object below to a text file:
Finally run this tool:
And with the following to view package-level information:
Here's an example output snippet:
A +
/-
indicates whether or not the element was used at runtime. A M
/D
indicates whether or not the element was in the main or deferred output unit.
This package is developed in github. Please file feature requests and bugs at the issue tracker.