mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 16:13:19 +00:00
[VM] Remove observatory_2 directory and all references to it.
TEST=ci Change-Id: I4e95e59fc909c01517ae59f39d04c38bb353b85a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/330525 Reviewed-by: Ben Konyi <bkonyi@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Siva Annamalai <asiva@google.com>
This commit is contained in:
parent
312b46b444
commit
e0fa7f9d0f
|
@ -229,7 +229,6 @@ class Scrape {
|
||||||
if (entry.path.contains('/analyzer_cli/test/')) continue;
|
if (entry.path.contains('/analyzer_cli/test/')) continue;
|
||||||
if (entry.path.contains('/compiler/test/')) continue;
|
if (entry.path.contains('/compiler/test/')) continue;
|
||||||
if (entry.path.contains('/dart/runtime/observatory/tests/')) continue;
|
if (entry.path.contains('/dart/runtime/observatory/tests/')) continue;
|
||||||
if (entry.path.contains('/dart/runtime/observatory_2/tests/')) continue;
|
|
||||||
if (entry.path.contains('/dart/runtime/tests/')) continue;
|
if (entry.path.contains('/dart/runtime/tests/')) continue;
|
||||||
if (entry.path.contains('/dart/tests/')) continue;
|
if (entry.path.contains('/dart/tests/')) continue;
|
||||||
if (entry.path.contains('/dev_compiler/test/')) continue;
|
if (entry.path.contains('/dev_compiler/test/')) continue;
|
||||||
|
|
|
@ -21,8 +21,6 @@ const _legacyTestSelectors = [
|
||||||
'language_2',
|
'language_2',
|
||||||
'lib_2',
|
'lib_2',
|
||||||
'kernel',
|
'kernel',
|
||||||
'observatory_ui_2',
|
|
||||||
'service_2',
|
|
||||||
'standalone_2',
|
'standalone_2',
|
||||||
'utils',
|
'utils',
|
||||||
'vm',
|
'vm',
|
||||||
|
|
|
@ -31,8 +31,6 @@ final testSuiteDirectories = [
|
||||||
Path('pkg'),
|
Path('pkg'),
|
||||||
Path('runtime/observatory/tests/observatory_ui'),
|
Path('runtime/observatory/tests/observatory_ui'),
|
||||||
Path('runtime/observatory/tests/service'),
|
Path('runtime/observatory/tests/service'),
|
||||||
Path('runtime/observatory_2/tests/observatory_ui_2'),
|
|
||||||
Path('runtime/observatory_2/tests/service_2'),
|
|
||||||
Path('runtime/tests/vm'),
|
Path('runtime/tests/vm'),
|
||||||
Path('samples'),
|
Path('samples'),
|
||||||
Path('tests/corelib'),
|
Path('tests/corelib'),
|
||||||
|
|
|
@ -739,7 +739,7 @@ class StandardTestSuite extends TestSuite {
|
||||||
_enqueueStandardTest(testFile, expectationSet, onTest);
|
_enqueueStandardTest(testFile, expectationSet, onTest);
|
||||||
} else if (configuration.runtime.isBrowser) {
|
} else if (configuration.runtime.isBrowser) {
|
||||||
_enqueueBrowserTest(testFile, expectationSet, onTest);
|
_enqueueBrowserTest(testFile, expectationSet, onTest);
|
||||||
} else if (suiteName == 'service' || suiteName == 'service_2') {
|
} else if (suiteName == 'service') {
|
||||||
_enqueueServiceTest(testFile, expectationSet, onTest);
|
_enqueueServiceTest(testFile, expectationSet, onTest);
|
||||||
} else {
|
} else {
|
||||||
_enqueueStandardTest(testFile, expectationSet, onTest);
|
_enqueueStandardTest(testFile, expectationSet, onTest);
|
||||||
|
|
|
@ -116,9 +116,7 @@ void testSelectors() {
|
||||||
'vm',
|
'vm',
|
||||||
'utils',
|
'utils',
|
||||||
'lib_2',
|
'lib_2',
|
||||||
'service_2',
|
|
||||||
'kernel',
|
'kernel',
|
||||||
'observatory_ui_2',
|
|
||||||
'ffi_2',
|
'ffi_2',
|
||||||
}, configuration.selectors.keys, "suites for $arguments");
|
}, configuration.selectors.keys, "suites for $arguments");
|
||||||
}
|
}
|
||||||
|
|
5
runtime/observatory_2/.gitignore
vendored
5
runtime/observatory_2/.gitignore
vendored
|
@ -1,5 +0,0 @@
|
||||||
bootstrap_css
|
|
||||||
out
|
|
||||||
build
|
|
||||||
.pub
|
|
||||||
.idea
|
|
|
@ -1,261 +0,0 @@
|
||||||
# Copyright 2015 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.
|
|
||||||
|
|
||||||
import("../../build/dart/copy_tree.gni")
|
|
||||||
import("../../build/dart/dart_action.gni")
|
|
||||||
import("observatory_sources.gni")
|
|
||||||
|
|
||||||
prebuilt_dart_action("build_observatory") {
|
|
||||||
visibility = [ ":copy_main_dart_js" ]
|
|
||||||
|
|
||||||
# dart2js produces a .deps file, but it is not in a format that is understood
|
|
||||||
# by ninja, so we explicitly list all the sources here.
|
|
||||||
inputs = [ "../../.dart_tool/package_config.json" ] + observatory_sources
|
|
||||||
|
|
||||||
output = "$target_gen_dir/observatory/web/main.dart.js"
|
|
||||||
outputs = [ output ]
|
|
||||||
if (is_debug) {
|
|
||||||
outputs += [ "$target_gen_dir/observatory/web/main.dart.js.map" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
args = [
|
|
||||||
"compile",
|
|
||||||
"js",
|
|
||||||
"-o",
|
|
||||||
rebase_path(output),
|
|
||||||
"--packages=" + rebase_path("../../.dart_tool/package_config.json"),
|
|
||||||
rebase_path("web/main.dart"),
|
|
||||||
]
|
|
||||||
if (is_debug) {
|
|
||||||
args += [ "--enable-asserts" ]
|
|
||||||
} else {
|
|
||||||
args += [ "--minify" ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# The rules here down to "deploy_observatory" copy files into place such that
|
|
||||||
# they can be packaged into a tar file. These rules do the following copies:
|
|
||||||
#
|
|
||||||
# web/* ->
|
|
||||||
# $target_out_dir/observatory/deployed/web
|
|
||||||
# $target_gen_dir/observatory/web/main.dart.js ->
|
|
||||||
# $target_out_dir/observatory/deployed/web/main.dart.js
|
|
||||||
# ../../third_party/observatory_pub_packages/packages/$PACKAGE/lib/* ->
|
|
||||||
# $target_out_dir/observatory/deployed/web/packages/$PACKAGE
|
|
||||||
# lib/* ->
|
|
||||||
# $target_out_dir/observatory/deployed/web/packages/observatory
|
|
||||||
#
|
|
||||||
# Files matching "observatory_ignore_patterns" are excluded.
|
|
||||||
|
|
||||||
# Files matching these patterns are filtered out of the Observatory assets.
|
|
||||||
observatory_ignore_patterns = [
|
|
||||||
# "\$sdk", this is the first element concatenated into the string below.
|
|
||||||
"*.concat.js",
|
|
||||||
"*.dart",
|
|
||||||
"*.log",
|
|
||||||
"*.precompiled.js",
|
|
||||||
"*.scriptUrls",
|
|
||||||
"*_buildLogs*",
|
|
||||||
"*~",
|
|
||||||
"CustomElements.*",
|
|
||||||
"HTMLImports.*",
|
|
||||||
"MutationObserver.*",
|
|
||||||
"ShadowDOM.*",
|
|
||||||
"bower.json",
|
|
||||||
"dart_support.*",
|
|
||||||
"interop_support.*",
|
|
||||||
"package.json",
|
|
||||||
"unittest*",
|
|
||||||
]
|
|
||||||
|
|
||||||
if (!is_debug) {
|
|
||||||
observatory_ignore_patterns += [ "*.map" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
# The ignore_patterns entry in the scopes accepted by copy_trees() is a
|
|
||||||
# string of comma delimited patterns.
|
|
||||||
observatory_ignore_string = "\$sdk"
|
|
||||||
foreach(pattern, observatory_ignore_patterns) {
|
|
||||||
observatory_ignore_string = "$observatory_ignore_string,$pattern"
|
|
||||||
}
|
|
||||||
|
|
||||||
copy_tree_specs = []
|
|
||||||
|
|
||||||
copy_tree_specs += [
|
|
||||||
{
|
|
||||||
target = "copy_web_package"
|
|
||||||
visibility = [ ":deploy_observatory" ]
|
|
||||||
source = "web"
|
|
||||||
dest = "$target_out_dir/observatory/deployed/web"
|
|
||||||
ignore_patterns = observatory_ignore_string
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
copy_tree_specs += [
|
|
||||||
{
|
|
||||||
target = "copy_observatory_package"
|
|
||||||
visibility = [ ":deploy_observatory" ]
|
|
||||||
source = "lib"
|
|
||||||
dest = "$target_out_dir/observatory/deployed/web/packages/observatory"
|
|
||||||
ignore_patterns = observatory_ignore_string
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
# This is not a rule, rather, it generates rules with names of the form:
|
|
||||||
# "copy_$package_package" for the packages in observatory_pub_packages.
|
|
||||||
copy_trees("copy_observatory_packages") {
|
|
||||||
sources = copy_tree_specs
|
|
||||||
}
|
|
||||||
|
|
||||||
copy("copy_main_dart_js") {
|
|
||||||
visibility = [ ":deploy_observatory" ]
|
|
||||||
deps = [ ":build_observatory" ]
|
|
||||||
sources = [ "$target_gen_dir/observatory/web/main.dart.js" ]
|
|
||||||
if (is_debug) {
|
|
||||||
sources += [ "$target_gen_dir/observatory/web/main.dart.js.map" ]
|
|
||||||
}
|
|
||||||
outputs = [ "$target_out_dir/observatory/deployed/web/{{source_file_part}}" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
group("deploy_observatory") {
|
|
||||||
deps = [
|
|
||||||
":copy_main_dart_js",
|
|
||||||
":copy_observatory_package",
|
|
||||||
":copy_web_package",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
template("observatory_archive") {
|
|
||||||
enable_compression = false
|
|
||||||
if (defined(invoker.compress) && invoker.compress) {
|
|
||||||
enable_compression = true
|
|
||||||
}
|
|
||||||
action(target_name) {
|
|
||||||
deps = [ ":deploy_observatory" ]
|
|
||||||
|
|
||||||
output_name = target_name
|
|
||||||
|
|
||||||
output = "$target_gen_dir/${output_name}.tar"
|
|
||||||
outputs = [ output ]
|
|
||||||
|
|
||||||
script = "../tools/create_archive.py"
|
|
||||||
args = [
|
|
||||||
"--tar_output",
|
|
||||||
rebase_path(output),
|
|
||||||
"--client_root",
|
|
||||||
rebase_path("$target_out_dir/observatory/deployed/web/"),
|
|
||||||
]
|
|
||||||
if (enable_compression) {
|
|
||||||
args += [ "--compress" ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
observatory_archive("compressed_observatory_archive") {
|
|
||||||
compress = true
|
|
||||||
}
|
|
||||||
|
|
||||||
copy("copy_compressed_observatory_archive") {
|
|
||||||
archive_target = ":compressed_observatory_archive"
|
|
||||||
deps = [ archive_target ]
|
|
||||||
archive_dir = get_label_info(archive_target, "target_gen_dir")
|
|
||||||
archive_name = get_label_info(archive_target, "name")
|
|
||||||
archive_file = "${archive_dir}/${archive_name}.tar"
|
|
||||||
sources = [ archive_file ]
|
|
||||||
outputs = [ "$root_out_dir/${archive_name}.tar" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
observatory_archive("observatory_archive") {
|
|
||||||
compress = false
|
|
||||||
}
|
|
||||||
|
|
||||||
copy("copy_observatory_archive") {
|
|
||||||
archive_target = ":observatory_archive"
|
|
||||||
deps = [ archive_target ]
|
|
||||||
archive_dir = get_label_info(archive_target, "target_gen_dir")
|
|
||||||
archive_name = get_label_info(archive_target, "name")
|
|
||||||
archive_file = "${archive_dir}/${archive_name}.tar"
|
|
||||||
sources = [ archive_file ]
|
|
||||||
outputs = [ "$root_out_dir/${archive_name}.tar" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
# Generates a .cc file containing the bytes of the observatory archive in a C
|
|
||||||
# array.
|
|
||||||
#
|
|
||||||
# Parameters:
|
|
||||||
# inner_namespace (required):
|
|
||||||
# The inner C++ namespace that the C array lives in.
|
|
||||||
#
|
|
||||||
# outer_namespace (required):
|
|
||||||
# The outer C++ namespace that the C array lives in.
|
|
||||||
#
|
|
||||||
# archive_file (required):
|
|
||||||
# The path to the observatory archive.
|
|
||||||
#
|
|
||||||
template("observatory_archive_source") {
|
|
||||||
assert(defined(invoker.inner_namespace),
|
|
||||||
"Need inner_namespace in $target_name")
|
|
||||||
assert(defined(invoker.outer_namespace),
|
|
||||||
"Need outer_namespace in $target_name")
|
|
||||||
assert(defined(invoker.archive_file), "Need archive_file in $target_name")
|
|
||||||
|
|
||||||
action(target_name) {
|
|
||||||
forward_variables_from(invoker, [ "deps" ])
|
|
||||||
|
|
||||||
inputs = [ invoker.archive_file ]
|
|
||||||
|
|
||||||
output = "$target_gen_dir/${target_name}.cc"
|
|
||||||
outputs = [ output ]
|
|
||||||
|
|
||||||
script = "../tools/create_archive.py"
|
|
||||||
args = [
|
|
||||||
"--tar_input",
|
|
||||||
rebase_path(invoker.archive_file),
|
|
||||||
"--output",
|
|
||||||
rebase_path(output),
|
|
||||||
"--outer_namespace",
|
|
||||||
invoker.outer_namespace,
|
|
||||||
"--inner_namespace",
|
|
||||||
invoker.inner_namespace,
|
|
||||||
"--name",
|
|
||||||
"observatory_assets_archive",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
observatory_archive_source("embedded_archive_observatory") {
|
|
||||||
outer_namespace = "dart"
|
|
||||||
inner_namespace = "observatory"
|
|
||||||
|
|
||||||
# TODO(zra): In a Fuchsia build, use a prebuilt Observatory archive.
|
|
||||||
archive_target = ":observatory_archive"
|
|
||||||
deps = [ archive_target ]
|
|
||||||
archive_dir = get_label_info(archive_target, "target_gen_dir")
|
|
||||||
archive_name = get_label_info(archive_target, "name")
|
|
||||||
archive_file = "${archive_dir}/${archive_name}.tar"
|
|
||||||
}
|
|
||||||
|
|
||||||
source_set("embedded_observatory_archive") {
|
|
||||||
deps = [ ":embedded_archive_observatory" ]
|
|
||||||
|
|
||||||
sources = [ rebase_path("$target_gen_dir/embedded_archive_observatory.cc") ]
|
|
||||||
}
|
|
||||||
|
|
||||||
observatory_archive_source("standalone_archive_observatory") {
|
|
||||||
outer_namespace = "dart"
|
|
||||||
inner_namespace = "bin"
|
|
||||||
|
|
||||||
# TODO(zra): In a Fuchsia build, use a prebuilt Observatory archive.
|
|
||||||
archive_target = ":compressed_observatory_archive"
|
|
||||||
deps = [ archive_target ]
|
|
||||||
archive_dir = get_label_info(archive_target, "target_gen_dir")
|
|
||||||
archive_name = get_label_info(archive_target, "name")
|
|
||||||
archive_file = "${archive_dir}/${archive_name}.tar"
|
|
||||||
}
|
|
||||||
|
|
||||||
source_set("standalone_observatory_archive") {
|
|
||||||
deps = [ ":standalone_archive_observatory" ]
|
|
||||||
|
|
||||||
sources = [ rebase_path("$target_gen_dir/standalone_archive_observatory.cc") ]
|
|
||||||
}
|
|
|
@ -1,174 +0,0 @@
|
||||||
# Hacking Observatory
|
|
||||||
|
|
||||||
These instructions will guide you through the Observatory development and
|
|
||||||
testing workflow.
|
|
||||||
|
|
||||||
## SDK Setup & Build
|
|
||||||
Getting ready to start.
|
|
||||||
|
|
||||||
Before you start to hack on Observatory, follow the [instructions][build_sdk] to
|
|
||||||
have a working environment in which you are able to build and test the Dart SDK.
|
|
||||||
|
|
||||||
## Run existing tests
|
|
||||||
Before hacking Observatory let's run the existing Observatory tests.
|
|
||||||
We suggest to run all the test in __debug__ mode.
|
|
||||||
|
|
||||||
First build the sdk in debug mode
|
|
||||||
```
|
|
||||||
$ ./tools/build.py --mode debug create_sdk
|
|
||||||
```
|
|
||||||
|
|
||||||
From the root of the sdk repository run:
|
|
||||||
```
|
|
||||||
$ ./tools/test.py -mdebug service
|
|
||||||
```
|
|
||||||
|
|
||||||
## Serve Observatory
|
|
||||||
Observatory is built as part of building the sdk. Previously, it was recommended
|
|
||||||
to run the Observatory using `pub serve` to avoid having to rebuild the sdk for
|
|
||||||
each change to Observatory. However, `pub serve` was deprecated as part of the
|
|
||||||
transition to Dart 2.0.
|
|
||||||
|
|
||||||
[Issue #35678](https://github.com/dart-lang/sdk/issues/35678)
|
|
||||||
tracks changes required to allow for `package:build_runner` to run Observatory
|
|
||||||
without having to rebuild the sdk after each change.
|
|
||||||
|
|
||||||
## Connect to a VM
|
|
||||||
Start a Dart VM with the ``--observe`` flag (as explained in the
|
|
||||||
[get started guide][observatory_get_started]) and connect your Observatory
|
|
||||||
instance to that VM.
|
|
||||||
|
|
||||||
Example script (file name ```clock.dart```):
|
|
||||||
```dart
|
|
||||||
import 'dart:async' show Timer, Duration;
|
|
||||||
|
|
||||||
main() {
|
|
||||||
bool tick = true;
|
|
||||||
new Timer.periodic(const Duration(seconds: 1), (Timer t) {
|
|
||||||
print(tick ? 'tick' : 'tock');
|
|
||||||
tick = !tick;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Start the script:
|
|
||||||
```
|
|
||||||
$ dart --disable-service-origin-check --observe clock.dart
|
|
||||||
```
|
|
||||||
|
|
||||||
## Code Reviews
|
|
||||||
The development workflow of Dart (and Observatory) is based on code reviews.
|
|
||||||
|
|
||||||
Follow the code review [instructions][code_review] to be able to successfully
|
|
||||||
submit your code.
|
|
||||||
|
|
||||||
The main reviewers for Observatory related CLs are:
|
|
||||||
- asiva
|
|
||||||
- bkonyi
|
|
||||||
- rmacnak
|
|
||||||
|
|
||||||
## Write a new service test
|
|
||||||
All the service tests are located in the ```tests/service``` folder.
|
|
||||||
Test file names follow the convention ```<description>_test.dart```
|
|
||||||
(e.g. ```a_brief_description_test.dart```).
|
|
||||||
|
|
||||||
The test is generally structured in the following way.
|
|
||||||
```dart
|
|
||||||
import 'package:test/test.dart';
|
|
||||||
|
|
||||||
main() {
|
|
||||||
// Some code that you need to test.
|
|
||||||
var a = 1 + 2;
|
|
||||||
|
|
||||||
// Some assertions to check the results.
|
|
||||||
expect(a, equal(3));
|
|
||||||
}
|
|
||||||
```
|
|
||||||
See the official [test library][test_library] instructions;
|
|
||||||
|
|
||||||
The ```test_helper.dart``` file expose some functions that allow to run a part
|
|
||||||
of the code into another __VM__.
|
|
||||||
|
|
||||||
To test synchronous operations:
|
|
||||||
```dart
|
|
||||||
import 'test_helper.dart';
|
|
||||||
|
|
||||||
code() {
|
|
||||||
// Write the code you want to be execute into another VM.
|
|
||||||
}
|
|
||||||
|
|
||||||
var tests = [
|
|
||||||
// A series of tests that you want to run against the above code.
|
|
||||||
(Isolate isolate) async {
|
|
||||||
await isolate.reload();
|
|
||||||
// Use the isolate to communicate to the VM.
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
main(args) => runIsolateTestsSynchronous(args,
|
|
||||||
tests,
|
|
||||||
testeeConcurrent: code);
|
|
||||||
```
|
|
||||||
|
|
||||||
In order to test asynchronous operations:
|
|
||||||
```dart
|
|
||||||
import 'test_helper.dart';
|
|
||||||
|
|
||||||
code() async {
|
|
||||||
// Write the asynchronous code you want to be execute into another VM.
|
|
||||||
}
|
|
||||||
|
|
||||||
var tests = [
|
|
||||||
// A series of tests that you want to run against the above code.
|
|
||||||
(Isolate isolate) async {
|
|
||||||
await isolate.reload();
|
|
||||||
// Use the isolate to communicate to the VM.
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
main(args) async => runIsolateTests(args,
|
|
||||||
tests,
|
|
||||||
testeeConcurrent: code);
|
|
||||||
```
|
|
||||||
|
|
||||||
Both ```runIsolateTests``` and ```runIsolateTestsSynchronous``` have the
|
|
||||||
following named parameters:
|
|
||||||
- __testeeBefore__ (void()) a function that is going to be executed before
|
|
||||||
the test
|
|
||||||
- __testeeConcurrent__ (void()) test that is going to be executed
|
|
||||||
- __pause_on_start__ (bool, default: false) pause the Isolate before the first
|
|
||||||
instruction
|
|
||||||
- __pause_on_exit__ (bool, default: false) pause the Isolate after the last
|
|
||||||
instruction
|
|
||||||
- __pause_on_unhandled_exceptions__ (bool, default: false) pause the Isolate at
|
|
||||||
an unhandled exception
|
|
||||||
- __trace_service__ (bool, default: false) trace VM service requests
|
|
||||||
- __trace_compiler__ (bool, default: false) trace compiler operations
|
|
||||||
- __verbose_vm__ (bool, default: false) verbose logging
|
|
||||||
|
|
||||||
|
|
||||||
Some common and reusable test are available from ```service_test_common.dart```:
|
|
||||||
- hasPausedFor
|
|
||||||
- hasStoppedAtBreakpoint
|
|
||||||
- hasStoppedWithUnhandledException
|
|
||||||
- hasStoppedAtExit
|
|
||||||
- hasPausedAtStartcode_review
|
|
||||||
and utility functions:
|
|
||||||
- subscribeToStream
|
|
||||||
- cancelStreamSubscription
|
|
||||||
- asyncStepOver
|
|
||||||
- setBreakpointAtLine
|
|
||||||
- resumeIsolate
|
|
||||||
- resumeAndAwaitEvent
|
|
||||||
- resumeIsolateAndAwaitEvent
|
|
||||||
- stepOver
|
|
||||||
- getClassFromRootLib
|
|
||||||
- rootLibraryFieldValue
|
|
||||||
|
|
||||||
## Run your tests
|
|
||||||
See: __Run existing tests__
|
|
||||||
|
|
||||||
[build_sdk]: https://github.com/dart-lang/sdk/wiki/Building "Building the Dart SDK"
|
|
||||||
[open_observatory]: http://localhost:8080/ "Open Observatory"
|
|
||||||
[observatory_get_started]: https://dart-lang.github.io/observatory/get-started.html "Observatory get started"
|
|
||||||
[code_review]: https://github.com/dart-lang/sdk/wiki/Code-review-workflow-with-GitHub-and-reitveld "Code Review"
|
|
||||||
[test_library]: https://pub.dartlang.org/packages/test "Test Library"
|
|
|
@ -1,16 +0,0 @@
|
||||||
analyzer:
|
|
||||||
errors:
|
|
||||||
dead_code: ignore
|
|
||||||
unused_local_variable: ignore
|
|
||||||
exclude:
|
|
||||||
- tests/service/bad_reload/v2/main.dart
|
|
||||||
- tests/service/complex_reload/v2/main.dart
|
|
||||||
- tests/service/developer_extension_test.dart
|
|
||||||
- tests/service/evaluate_activation_in_method_class_test.dart
|
|
||||||
- tests/service/get_user_level_retaining_path_rpc_test.dart
|
|
||||||
- tests/service/pause_on_unhandled_async_exceptions_test.dart
|
|
||||||
|
|
||||||
linter:
|
|
||||||
rules:
|
|
||||||
- prefer_final_fields
|
|
||||||
- prefer_final_locals
|
|
|
@ -1,421 +0,0 @@
|
||||||
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// A way to test heap snapshot loading and analysis outside of Observatory, and
|
|
||||||
// to handle snapshots that require more memory to analyze than is available in
|
|
||||||
// a web browser.
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:observatory_2/object_graph.dart';
|
|
||||||
|
|
||||||
Future<SnapshotGraph> load(String uri) async {
|
|
||||||
final ws = await WebSocket.connect(uri,
|
|
||||||
compression: CompressionOptions.compressionOff);
|
|
||||||
|
|
||||||
final getVM = new Completer<String>();
|
|
||||||
final reader = new SnapshotReader();
|
|
||||||
|
|
||||||
reader.onProgress.listen(print);
|
|
||||||
|
|
||||||
ws.listen((dynamic response) {
|
|
||||||
if (response is String) {
|
|
||||||
response = json.decode(response);
|
|
||||||
if (response['id'] == 1) {
|
|
||||||
getVM.complete(response['result']['isolates'][0]['id']);
|
|
||||||
}
|
|
||||||
} else if (response is List<int>) {
|
|
||||||
response = new Uint8List.fromList(response);
|
|
||||||
final dataOffset =
|
|
||||||
new ByteData.view(response.buffer).getUint32(0, Endian.little);
|
|
||||||
dynamic metadata = new Uint8List.view(response.buffer, 4, dataOffset - 4);
|
|
||||||
final data = new Uint8List.view(
|
|
||||||
response.buffer, dataOffset, response.length - dataOffset);
|
|
||||||
metadata = utf8.decode(metadata);
|
|
||||||
metadata = json.decode(metadata);
|
|
||||||
var event = metadata['params']['event'];
|
|
||||||
if (event['kind'] == 'HeapSnapshot') {
|
|
||||||
bool last = event['last'] == true;
|
|
||||||
reader.add(data);
|
|
||||||
if (last) {
|
|
||||||
reader.close();
|
|
||||||
ws.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ws.add(json.encode({
|
|
||||||
'jsonrpc': '2.0',
|
|
||||||
'method': 'getVM',
|
|
||||||
'params': {},
|
|
||||||
'id': 1,
|
|
||||||
}));
|
|
||||||
|
|
||||||
final String isolateId = await getVM.future;
|
|
||||||
|
|
||||||
ws.add(json.encode({
|
|
||||||
'jsonrpc': '2.0',
|
|
||||||
'method': 'streamListen',
|
|
||||||
'params': {'streamId': 'HeapSnapshot'},
|
|
||||||
'id': 2,
|
|
||||||
}));
|
|
||||||
ws.add(json.encode({
|
|
||||||
'jsonrpc': '2.0',
|
|
||||||
'method': 'requestHeapSnapshot',
|
|
||||||
'params': {'isolateId': isolateId},
|
|
||||||
'id': 3,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return reader.done;
|
|
||||||
}
|
|
||||||
|
|
||||||
String makeData(dynamic root) {
|
|
||||||
// 'root' can be arbitrarily deep, so we can't directly represent it as a
|
|
||||||
// JSON tree, which cause a stack overflow here encoding it and in the JS
|
|
||||||
// engine decoding it. Instead we flatten the tree into a list of tuples with
|
|
||||||
// a parent pointer and re-inflate it in JS.
|
|
||||||
final indices = <dynamic, int>{};
|
|
||||||
final preorder = <dynamic>[];
|
|
||||||
preorder.add(root);
|
|
||||||
|
|
||||||
for (var index = 0; index < preorder.length; index++) {
|
|
||||||
final object = preorder[index];
|
|
||||||
preorder.addAll(object.children);
|
|
||||||
}
|
|
||||||
|
|
||||||
final flattened = <dynamic>[];
|
|
||||||
for (var index = 0; index < preorder.length; index++) {
|
|
||||||
final object = preorder[index];
|
|
||||||
indices[object] = index;
|
|
||||||
|
|
||||||
flattened.add(object.description);
|
|
||||||
flattened.add(object.klass.name);
|
|
||||||
flattened.add(object.retainedSize);
|
|
||||||
if (index == 0) {
|
|
||||||
flattened.add(null);
|
|
||||||
} else {
|
|
||||||
flattened.add(indices[object.parent]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.encode(flattened);
|
|
||||||
}
|
|
||||||
|
|
||||||
var css = '''
|
|
||||||
.treemapTile {
|
|
||||||
position: absolute;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border: solid 1px;
|
|
||||||
font-size: 10;
|
|
||||||
text-align: center;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
''';
|
|
||||||
|
|
||||||
var js = '''
|
|
||||||
function hash(string) {
|
|
||||||
// Jenkin's one_at_a_time.
|
|
||||||
let h = string.length;
|
|
||||||
for (let i = 0; i < string.length; i++) {
|
|
||||||
h += string.charCodeAt(i);
|
|
||||||
h += h << 10;
|
|
||||||
h ^= h >> 6;
|
|
||||||
}
|
|
||||||
h += h << 3;
|
|
||||||
h ^= h >> 11;
|
|
||||||
h += h << 15;
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
function color(string) {
|
|
||||||
let hue = hash(string) % 360;
|
|
||||||
return "hsl(" + hue + ",60%,60%)";
|
|
||||||
}
|
|
||||||
|
|
||||||
function prettySize(size) {
|
|
||||||
if (size < 1024) return size + "B";
|
|
||||||
size /= 1024;
|
|
||||||
if (size < 1024) return size.toFixed(1) + "KiB";
|
|
||||||
size /= 1024;
|
|
||||||
if (size < 1024) return size.toFixed(1) + "MiB";
|
|
||||||
size /= 1024;
|
|
||||||
return size.toFixed(1) + "GiB";
|
|
||||||
}
|
|
||||||
|
|
||||||
function prettyPercent(fraction) {
|
|
||||||
return (fraction * 100).toFixed(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createTreemapTile(v, width, height, depth) {
|
|
||||||
let div = document.createElement("div");
|
|
||||||
div.className = "treemapTile";
|
|
||||||
div.style["background-color"] = color(v.type);
|
|
||||||
div.ondblclick = function(event) {
|
|
||||||
event.stopPropagation();
|
|
||||||
if (depth == 0) {
|
|
||||||
let dom = v.parent;
|
|
||||||
if (dom == undefined) {
|
|
||||||
// Already at root.
|
|
||||||
} else {
|
|
||||||
showDominatorTree(dom); // Zoom out.
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showDominatorTree(v); // Zoom in.
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let left = 0;
|
|
||||||
let top = 0;
|
|
||||||
|
|
||||||
const kPadding = 5;
|
|
||||||
const kBorder = 1;
|
|
||||||
left += kPadding - kBorder;
|
|
||||||
top += kPadding - kBorder;
|
|
||||||
width -= 2 * kPadding;
|
|
||||||
height -= 2 * kPadding;
|
|
||||||
|
|
||||||
let label = v.name + " [" + prettySize(v.size) + "]";
|
|
||||||
div.title = label;
|
|
||||||
|
|
||||||
if (width < 10 || height < 10) {
|
|
||||||
// Too small: don't render label or children.
|
|
||||||
return div;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.appendChild(document.createTextNode(label));
|
|
||||||
const kLabelHeight = 9;
|
|
||||||
top += kLabelHeight;
|
|
||||||
height -= kLabelHeight;
|
|
||||||
|
|
||||||
if (depth > 2) {
|
|
||||||
// Too deep: don't render children.
|
|
||||||
return div;
|
|
||||||
}
|
|
||||||
if (width < 4 || height < 4) {
|
|
||||||
// Too small: don't render children.
|
|
||||||
return div;
|
|
||||||
}
|
|
||||||
|
|
||||||
let children = new Array();
|
|
||||||
v.children.forEach(function(c) {
|
|
||||||
// Size 0 children seem to confuse the layout algorithm (accumulating
|
|
||||||
// rounding errors?).
|
|
||||||
if (c.size > 0) {
|
|
||||||
children.push(c);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
children.sort(function (a, b) {
|
|
||||||
return b.size - a.size;
|
|
||||||
});
|
|
||||||
|
|
||||||
const scale = width * height / v.size;
|
|
||||||
|
|
||||||
// Bruls M., Huizing K., van Wijk J.J. (2000) Squarified Treemaps. In: de
|
|
||||||
// Leeuw W.C., van Liere R. (eds) Data Visualization 2000. Eurographics.
|
|
||||||
// Springer, Vienna.
|
|
||||||
for (let rowStart = 0; // Index of first child in the next row.
|
|
||||||
rowStart < children.length;) {
|
|
||||||
// Prefer wider rectangles, the better to fit text labels.
|
|
||||||
const GOLDEN_RATIO = 1.61803398875;
|
|
||||||
let verticalSplit = (width / height) > GOLDEN_RATIO;
|
|
||||||
|
|
||||||
let space;
|
|
||||||
if (verticalSplit) {
|
|
||||||
space = height;
|
|
||||||
} else {
|
|
||||||
space = width;
|
|
||||||
}
|
|
||||||
|
|
||||||
let rowMin = children[rowStart].size * scale;
|
|
||||||
let rowMax = rowMin;
|
|
||||||
let rowSum = 0;
|
|
||||||
let lastRatio = 0;
|
|
||||||
|
|
||||||
let rowEnd; // One after index of last child in the next row.
|
|
||||||
for (rowEnd = rowStart; rowEnd < children.length; rowEnd++) {
|
|
||||||
let size = children[rowEnd].size * scale;
|
|
||||||
if (size < rowMin) rowMin = size;
|
|
||||||
if (size > rowMax) rowMax = size;
|
|
||||||
rowSum += size;
|
|
||||||
|
|
||||||
let ratio = Math.max((space * space * rowMax) / (rowSum * rowSum),
|
|
||||||
(rowSum * rowSum) / (space * space * rowMin));
|
|
||||||
if ((lastRatio != 0) && (ratio > lastRatio)) {
|
|
||||||
// Adding the next child makes the aspect ratios worse: remove it and
|
|
||||||
// add the row.
|
|
||||||
rowSum -= size;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
lastRatio = ratio;
|
|
||||||
}
|
|
||||||
|
|
||||||
let rowLeft = left;
|
|
||||||
let rowTop = top;
|
|
||||||
let rowSpace = rowSum / space;
|
|
||||||
|
|
||||||
for (let i = rowStart; i < rowEnd; i++) {
|
|
||||||
let child = children[i];
|
|
||||||
let size = child.size * scale;
|
|
||||||
|
|
||||||
let childWidth;
|
|
||||||
let childHeight;
|
|
||||||
if (verticalSplit) {
|
|
||||||
childWidth = rowSpace;
|
|
||||||
childHeight = size / childWidth;
|
|
||||||
} else {
|
|
||||||
childHeight = rowSpace;
|
|
||||||
childWidth = size / childHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
let childDiv = createTreemapTile(child, childWidth, childHeight, depth + 1);
|
|
||||||
childDiv.style.left = rowLeft + "px";
|
|
||||||
childDiv.style.top = rowTop + "px";
|
|
||||||
// Oversize the final div by kBorder to make the borders overlap.
|
|
||||||
childDiv.style.width = (childWidth + kBorder) + "px";
|
|
||||||
childDiv.style.height = (childHeight + kBorder) + "px";
|
|
||||||
div.appendChild(childDiv);
|
|
||||||
|
|
||||||
if (verticalSplit)
|
|
||||||
rowTop += childHeight;
|
|
||||||
else
|
|
||||||
rowLeft += childWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verticalSplit) {
|
|
||||||
left += rowSpace;
|
|
||||||
width -= rowSpace;
|
|
||||||
} else {
|
|
||||||
top += rowSpace;
|
|
||||||
height -= rowSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
rowStart = rowEnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
return div;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setBody(div) {
|
|
||||||
let body = document.body;
|
|
||||||
while (body.firstChild) {
|
|
||||||
body.removeChild(body.firstChild);
|
|
||||||
}
|
|
||||||
body.appendChild(div);
|
|
||||||
}
|
|
||||||
|
|
||||||
function showDominatorTree(v) {
|
|
||||||
let header = document.createElement("div");
|
|
||||||
header.textContent = "Dominator Tree";
|
|
||||||
header.title =
|
|
||||||
"Double click a box to zoom in.\\n" +
|
|
||||||
"Double click the outermost box to zoom out.";
|
|
||||||
header.className = "headerRow";
|
|
||||||
header.style["flex-grow"] = 0;
|
|
||||||
header.style["padding"] = "5px";
|
|
||||||
header.style["border-bottom"] = "solid 1px";
|
|
||||||
|
|
||||||
let content = document.createElement("div");
|
|
||||||
content.style["flex-basis"] = 0;
|
|
||||||
content.style["flex-grow"] = 1;
|
|
||||||
|
|
||||||
let column = document.createElement("div");
|
|
||||||
column.style["width"] = "100%";
|
|
||||||
column.style["height"] = "100%";
|
|
||||||
column.style["border"] = "solid 2px";
|
|
||||||
column.style["display"] = "flex";
|
|
||||||
column.style["flex-direction"] = "column";
|
|
||||||
column.appendChild(header);
|
|
||||||
column.appendChild(content);
|
|
||||||
|
|
||||||
setBody(column);
|
|
||||||
|
|
||||||
// Add the content div to the document first so the browser will calculate
|
|
||||||
// the available width and height.
|
|
||||||
let w = content.offsetWidth;
|
|
||||||
let h = content.offsetHeight;
|
|
||||||
|
|
||||||
let topTile = createTreemapTile(v, w, h, 0);
|
|
||||||
topTile.style.width = w;
|
|
||||||
topTile.style.height = h;
|
|
||||||
topTile.style.border = "none";
|
|
||||||
content.appendChild(topTile);
|
|
||||||
}
|
|
||||||
|
|
||||||
function inflateData(flattened) {
|
|
||||||
// 'root' can be arbitrarily deep, so we need to use an explicit stack
|
|
||||||
// instead of the call stack.
|
|
||||||
let nodes = new Array();
|
|
||||||
let i = 0;
|
|
||||||
while (i < flattened.length) {
|
|
||||||
let node = {
|
|
||||||
"name": flattened[i++],
|
|
||||||
"type": flattened[i++],
|
|
||||||
"size": flattened[i++],
|
|
||||||
"children": [],
|
|
||||||
"parent": null
|
|
||||||
};
|
|
||||||
nodes.push(node);
|
|
||||||
|
|
||||||
let parentIndex = flattened[i++];
|
|
||||||
if (parentIndex != null) {
|
|
||||||
let parent = nodes[parentIndex];
|
|
||||||
parent.children.push(node);
|
|
||||||
node.parent = parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nodes[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
var root = __DATA__;
|
|
||||||
root = inflateData(root);
|
|
||||||
|
|
||||||
showDominatorTree(root);
|
|
||||||
''';
|
|
||||||
|
|
||||||
var html = '''
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Dart Heap Snapshot</title>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
||||||
<style>$css</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<script>$js</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
''';
|
|
||||||
|
|
||||||
main(List<String> args) async {
|
|
||||||
if (args.length < 1) {
|
|
||||||
print('Usage: heap_snapshot.dart <vm-service-uri>');
|
|
||||||
exitCode = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var uri = Uri.parse(args[0]);
|
|
||||||
if (uri.isScheme('http')) {
|
|
||||||
uri = uri.replace(scheme: 'ws');
|
|
||||||
} else if (uri.isScheme('https')) {
|
|
||||||
uri = uri.replace(scheme: 'wss');
|
|
||||||
}
|
|
||||||
if (!uri.path.endsWith('/ws')) {
|
|
||||||
uri = uri.resolve('ws');
|
|
||||||
}
|
|
||||||
|
|
||||||
final snapshot = await load(uri.toString());
|
|
||||||
|
|
||||||
final dir = await Directory.systemTemp.createTemp('heap-snapshot');
|
|
||||||
final path = dir.path + '/merged-dominator.html';
|
|
||||||
final file = await File(path).create();
|
|
||||||
final tree = makeData(snapshot.mergedRoot);
|
|
||||||
await file.writeAsString(html.replaceAll('__DATA__', tree));
|
|
||||||
print('Wrote file://' + path);
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library shell;
|
|
||||||
|
|
||||||
import 'package:observatory_2/service_io.dart';
|
|
||||||
|
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
// Simple demo for service_io library. Connects to localhost on the default
|
|
||||||
// port, picks the first isolate, reads requests from stdin, and prints
|
|
||||||
// results to stdout. Example session:
|
|
||||||
// <<< isolate isolates/1071334835
|
|
||||||
// >>> /classes/40
|
|
||||||
// <<< {"type":"Class","id":"classes\/40","name":"num","user_name":"num",...
|
|
||||||
// >>> /objects/0
|
|
||||||
// >>> {"type":"Array","class":{"type":"@Class","id":"classes\/62",...
|
|
||||||
|
|
||||||
void repl(VM vm, Isolate isolate, String lastResult) {
|
|
||||||
print(lastResult);
|
|
||||||
Map params = {
|
|
||||||
'objectId': stdin.readLineSync(),
|
|
||||||
};
|
|
||||||
isolate.invokeRpcNoUpgrade('getObject', params).then((Map result) {
|
|
||||||
repl(vm, isolate, result.toString());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
String addr = 'ws://localhost:8181/ws';
|
|
||||||
new WebSocketVM(new WebSocketVMTarget(addr)).load().then((serviceObject) {
|
|
||||||
VM vm = serviceObject;
|
|
||||||
Isolate isolate = vm.isolates.first;
|
|
||||||
repl(vm, isolate, 'isolate ${isolate.id}');
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library allocation_profiler;
|
|
||||||
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/service.dart' as S;
|
|
||||||
|
|
||||||
part 'src/allocation_profile/allocation_profile.dart';
|
|
|
@ -1,28 +0,0 @@
|
||||||
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library app;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:html';
|
|
||||||
|
|
||||||
import 'package:logging/logging.dart';
|
|
||||||
import 'package:observatory_2/service_html.dart';
|
|
||||||
import 'package:observatory_2/elements.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/uris.dart';
|
|
||||||
import 'package:observatory_2/event.dart';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/repositories.dart';
|
|
||||||
import 'package:observatory_2/tracer.dart';
|
|
||||||
import 'package:stack_trace/stack_trace.dart';
|
|
||||||
|
|
||||||
export 'package:observatory_2/utils.dart';
|
|
||||||
|
|
||||||
part 'src/app/application.dart';
|
|
||||||
part 'src/app/location_manager.dart';
|
|
||||||
part 'src/app/notification.dart';
|
|
||||||
part 'src/app/page.dart';
|
|
||||||
part 'src/app/settings.dart';
|
|
||||||
part 'src/app/view_model.dart';
|
|
|
@ -1,9 +0,0 @@
|
||||||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library cli;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
|
|
||||||
part 'src/cli/command.dart';
|
|
|
@ -1,12 +0,0 @@
|
||||||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library debugger;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/service.dart';
|
|
||||||
|
|
||||||
part 'src/debugger/debugger.dart';
|
|
||||||
part 'src/debugger/debugger_location.dart';
|
|
|
@ -1,98 +0,0 @@
|
||||||
library observatory_elements;
|
|
||||||
|
|
||||||
export 'package:observatory_2/src/elements/allocation_profile.dart';
|
|
||||||
export 'package:observatory_2/src/elements/class_allocation_profile.dart';
|
|
||||||
export 'package:observatory_2/src/elements/class_instances.dart';
|
|
||||||
export 'package:observatory_2/src/elements/class_ref.dart';
|
|
||||||
export 'package:observatory_2/src/elements/class_tree.dart';
|
|
||||||
export 'package:observatory_2/src/elements/class_view.dart';
|
|
||||||
export 'package:observatory_2/src/elements/code_ref.dart';
|
|
||||||
export 'package:observatory_2/src/elements/code_view.dart';
|
|
||||||
export 'package:observatory_2/src/elements/containers/search_bar.dart';
|
|
||||||
export 'package:observatory_2/src/elements/containers/virtual_collection.dart';
|
|
||||||
export 'package:observatory_2/src/elements/containers/virtual_tree.dart';
|
|
||||||
export 'package:observatory_2/src/elements/context_ref.dart';
|
|
||||||
export 'package:observatory_2/src/elements/context_view.dart';
|
|
||||||
export 'package:observatory_2/src/elements/cpu_profile.dart';
|
|
||||||
export 'package:observatory_2/src/elements/cpu_profile/virtual_tree.dart';
|
|
||||||
export 'package:observatory_2/src/elements/cpu_profile_table.dart';
|
|
||||||
export 'package:observatory_2/src/elements/curly_block.dart';
|
|
||||||
export 'package:observatory_2/src/elements/debugger.dart';
|
|
||||||
export 'package:observatory_2/src/elements/error_ref.dart';
|
|
||||||
export 'package:observatory_2/src/elements/error_view.dart';
|
|
||||||
export 'package:observatory_2/src/elements/eval_box.dart';
|
|
||||||
export 'package:observatory_2/src/elements/field_ref.dart';
|
|
||||||
export 'package:observatory_2/src/elements/field_view.dart';
|
|
||||||
export 'package:observatory_2/src/elements/flag_list.dart';
|
|
||||||
export 'package:observatory_2/src/elements/function_ref.dart';
|
|
||||||
export 'package:observatory_2/src/elements/function_view.dart';
|
|
||||||
export 'package:observatory_2/src/elements/general_error.dart';
|
|
||||||
export 'package:observatory_2/src/elements/heap_map.dart';
|
|
||||||
export 'package:observatory_2/src/elements/heap_snapshot.dart';
|
|
||||||
export 'package:observatory_2/src/elements/process_snapshot.dart';
|
|
||||||
export 'package:observatory_2/src/elements/helpers/rendering_queue.dart';
|
|
||||||
export 'package:observatory_2/src/elements/icdata_ref.dart';
|
|
||||||
export 'package:observatory_2/src/elements/icdata_view.dart';
|
|
||||||
export 'package:observatory_2/src/elements/instance_ref.dart';
|
|
||||||
export 'package:observatory_2/src/elements/instance_view.dart';
|
|
||||||
export 'package:observatory_2/src/elements/isolate/counter_chart.dart';
|
|
||||||
export 'package:observatory_2/src/elements/isolate/location.dart';
|
|
||||||
export 'package:observatory_2/src/elements/isolate/run_state.dart';
|
|
||||||
export 'package:observatory_2/src/elements/isolate/shared_summary.dart';
|
|
||||||
export 'package:observatory_2/src/elements/isolate/summary.dart';
|
|
||||||
export 'package:observatory_2/src/elements/isolate_reconnect.dart';
|
|
||||||
export 'package:observatory_2/src/elements/isolate_ref.dart';
|
|
||||||
export 'package:observatory_2/src/elements/isolate_view.dart';
|
|
||||||
export 'package:observatory_2/src/elements/json_view.dart';
|
|
||||||
export 'package:observatory_2/src/elements/library_ref.dart';
|
|
||||||
export 'package:observatory_2/src/elements/library_view.dart';
|
|
||||||
export 'package:observatory_2/src/elements/local_var_descriptors_ref.dart';
|
|
||||||
export 'package:observatory_2/src/elements/logging.dart';
|
|
||||||
export 'package:observatory_2/src/elements/megamorphiccache_ref.dart';
|
|
||||||
export 'package:observatory_2/src/elements/megamorphiccache_view.dart';
|
|
||||||
export 'package:observatory_2/src/elements/metric/details.dart';
|
|
||||||
export 'package:observatory_2/src/elements/metric/graph.dart';
|
|
||||||
export 'package:observatory_2/src/elements/metrics.dart';
|
|
||||||
export 'package:observatory_2/src/elements/native_memory_profiler.dart';
|
|
||||||
export 'package:observatory_2/src/elements/nav/class_menu.dart';
|
|
||||||
export 'package:observatory_2/src/elements/nav/isolate_menu.dart';
|
|
||||||
export 'package:observatory_2/src/elements/nav/library_menu.dart';
|
|
||||||
export 'package:observatory_2/src/elements/nav/menu_item.dart';
|
|
||||||
export 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
export 'package:observatory_2/src/elements/nav/notify_event.dart';
|
|
||||||
export 'package:observatory_2/src/elements/nav/notify_exception.dart';
|
|
||||||
export 'package:observatory_2/src/elements/nav/refresh.dart';
|
|
||||||
export 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
export 'package:observatory_2/src/elements/nav/vm_menu.dart';
|
|
||||||
export 'package:observatory_2/src/elements/object_common.dart';
|
|
||||||
export 'package:observatory_2/src/elements/object_view.dart';
|
|
||||||
export 'package:observatory_2/src/elements/objectpool_ref.dart';
|
|
||||||
export 'package:observatory_2/src/elements/objectpool_view.dart';
|
|
||||||
export 'package:observatory_2/src/elements/objectstore_view.dart';
|
|
||||||
export 'package:observatory_2/src/elements/observatory_application.dart';
|
|
||||||
export 'package:observatory_2/src/elements/pc_descriptors_ref.dart';
|
|
||||||
export 'package:observatory_2/src/elements/persistent_handles.dart';
|
|
||||||
export 'package:observatory_2/src/elements/ports.dart';
|
|
||||||
export 'package:observatory_2/src/elements/sample_buffer_control.dart';
|
|
||||||
export 'package:observatory_2/src/elements/script_inset.dart';
|
|
||||||
export 'package:observatory_2/src/elements/script_ref.dart';
|
|
||||||
export 'package:observatory_2/src/elements/script_view.dart';
|
|
||||||
export 'package:observatory_2/src/elements/sentinel_value.dart';
|
|
||||||
export 'package:observatory_2/src/elements/sentinel_view.dart';
|
|
||||||
export 'package:observatory_2/src/elements/singletargetcache_ref.dart';
|
|
||||||
export 'package:observatory_2/src/elements/singletargetcache_view.dart';
|
|
||||||
export 'package:observatory_2/src/elements/source_inset.dart';
|
|
||||||
export 'package:observatory_2/src/elements/source_link.dart';
|
|
||||||
export 'package:observatory_2/src/elements/stack_trace_tree_config.dart';
|
|
||||||
export 'package:observatory_2/src/elements/strongly_reachable_instances.dart';
|
|
||||||
export 'package:observatory_2/src/elements/subtypetestcache_ref.dart';
|
|
||||||
export 'package:observatory_2/src/elements/subtypetestcache_view.dart';
|
|
||||||
export 'package:observatory_2/src/elements/timeline/dashboard.dart';
|
|
||||||
export 'package:observatory_2/src/elements/timeline_page.dart';
|
|
||||||
export 'package:observatory_2/src/elements/type_arguments_ref.dart';
|
|
||||||
export 'package:observatory_2/src/elements/unknown_ref.dart';
|
|
||||||
export 'package:observatory_2/src/elements/unlinkedcall_ref.dart';
|
|
||||||
export 'package:observatory_2/src/elements/unlinkedcall_view.dart';
|
|
||||||
export 'package:observatory_2/src/elements/vm_connect.dart';
|
|
||||||
export 'package:observatory_2/src/elements/vm_connect_target.dart';
|
|
||||||
export 'package:observatory_2/src/elements/vm_view.dart';
|
|
|
@ -1,380 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'package:logging/logging.dart';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/service.dart' as S;
|
|
||||||
|
|
||||||
class VMUpdateEvent implements M.VMUpdateEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.VMRef vm;
|
|
||||||
VMUpdateEvent(this.timestamp, this.vm) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(vm != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class IsolateStartEvent implements M.IsolateStartEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
IsolateStartEvent(this.timestamp, this.isolate) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class IsolateRunnableEvent implements M.IsolateRunnableEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
IsolateRunnableEvent(this.timestamp, this.isolate) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class IsolateExitEvent implements M.IsolateExitEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
IsolateExitEvent(this.timestamp, this.isolate) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class IsolateUpdateEvent implements M.IsolateUpdateEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
IsolateUpdateEvent(this.timestamp, this.isolate) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class IsolateReloadEvent implements M.IsolateReloadEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
final M.ErrorRef error;
|
|
||||||
IsolateReloadEvent(this.timestamp, this.isolate, this.error) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(error != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ServiceExtensionAddedEvent implements M.ServiceExtensionAddedEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
final String extensionRPC;
|
|
||||||
ServiceExtensionAddedEvent(this.timestamp, this.isolate, this.extensionRPC) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(extensionRPC != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DebuggerSettingsUpdateEvent implements M.DebuggerSettingsUpdateEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
DebuggerSettingsUpdateEvent(this.timestamp, this.isolate) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PauseStartEvent implements M.PauseStartEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
PauseStartEvent(this.timestamp, this.isolate) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PauseExitEvent implements M.PauseExitEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
PauseExitEvent(this.timestamp, this.isolate) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PauseBreakpointEvent implements M.PauseBreakpointEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
final Iterable<M.Breakpoint> pauseBreakpoints;
|
|
||||||
final M.Frame topFrame;
|
|
||||||
final bool atAsyncSuspension;
|
|
||||||
|
|
||||||
/// [optional]
|
|
||||||
final M.Breakpoint breakpoint;
|
|
||||||
PauseBreakpointEvent(
|
|
||||||
this.timestamp,
|
|
||||||
this.isolate,
|
|
||||||
Iterable<M.Breakpoint> pauseBreakpoints,
|
|
||||||
this.topFrame,
|
|
||||||
this.atAsyncSuspension,
|
|
||||||
[this.breakpoint])
|
|
||||||
: pauseBreakpoints = new List.unmodifiable(pauseBreakpoints) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(pauseBreakpoints != null);
|
|
||||||
assert(topFrame != null);
|
|
||||||
assert(atAsyncSuspension != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PauseInterruptedEvent implements M.PauseInterruptedEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
final M.Frame topFrame;
|
|
||||||
final bool atAsyncSuspension;
|
|
||||||
PauseInterruptedEvent(
|
|
||||||
this.timestamp, this.isolate, this.topFrame, this.atAsyncSuspension) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(atAsyncSuspension != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PausePostRequestEvent implements M.PausePostRequestEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
final M.Frame topFrame;
|
|
||||||
final bool atAsyncSuspension;
|
|
||||||
PausePostRequestEvent(
|
|
||||||
this.timestamp, this.isolate, this.topFrame, this.atAsyncSuspension) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(atAsyncSuspension != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PauseExceptionEvent implements M.PauseExceptionEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
final M.Frame topFrame;
|
|
||||||
final M.InstanceRef exception;
|
|
||||||
PauseExceptionEvent(
|
|
||||||
this.timestamp, this.isolate, this.topFrame, this.exception) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(topFrame != null);
|
|
||||||
assert(exception != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ResumeEvent implements M.ResumeEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
final M.Frame topFrame;
|
|
||||||
ResumeEvent(this.timestamp, this.isolate, this.topFrame) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class BreakpointAddedEvent implements M.BreakpointAddedEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
final M.Breakpoint breakpoint;
|
|
||||||
BreakpointAddedEvent(this.timestamp, this.isolate, this.breakpoint) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(breakpoint != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class BreakpointResolvedEvent implements M.BreakpointResolvedEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
final M.Breakpoint breakpoint;
|
|
||||||
BreakpointResolvedEvent(this.timestamp, this.isolate, this.breakpoint) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(breakpoint != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class BreakpointRemovedEvent implements M.BreakpointRemovedEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
final M.Breakpoint breakpoint;
|
|
||||||
BreakpointRemovedEvent(this.timestamp, this.isolate, this.breakpoint) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(breakpoint != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class InspectEvent implements M.InspectEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
final M.InstanceRef inspectee;
|
|
||||||
InspectEvent(this.timestamp, this.isolate, this.inspectee) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(inspectee != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NoneEvent implements M.NoneEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
NoneEvent(this.timestamp, this.isolate) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GCEvent implements M.GCEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
GCEvent(this.timestamp, this.isolate) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LoggingEvent implements M.LoggingEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
final Map logRecord;
|
|
||||||
LoggingEvent(this.timestamp, this.isolate, this.logRecord) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(logRecord != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ExtensionEvent implements M.ExtensionEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
final String extensionKind;
|
|
||||||
final M.ExtensionData extensionData;
|
|
||||||
ExtensionEvent(
|
|
||||||
this.timestamp, this.isolate, this.extensionKind, this.extensionData) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(extensionKind != null);
|
|
||||||
assert(extensionData != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TimelineEventsEvent implements M.TimelineEventsEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final M.IsolateRef isolate;
|
|
||||||
final Iterable<M.TimelineEvent> timelineEvents;
|
|
||||||
TimelineEventsEvent(
|
|
||||||
this.timestamp, this.isolate, Iterable<M.TimelineEvent> timelineEvents)
|
|
||||||
: timelineEvents = new List.unmodifiable(timelineEvents) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(timelineEvents != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ConnectionClosedEvent implements M.ConnectionClosedEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final String reason;
|
|
||||||
ConnectionClosedEvent(this.timestamp, this.reason) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(reason != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ServiceRegisteredEvent implements M.ServiceRegisteredEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final String service;
|
|
||||||
final String method;
|
|
||||||
final String alias;
|
|
||||||
ServiceRegisteredEvent(
|
|
||||||
this.timestamp, this.service, this.method, this.alias) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(service != null);
|
|
||||||
assert(method != null);
|
|
||||||
assert(alias != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ServiceUnregisteredEvent implements M.ServiceUnregisteredEvent {
|
|
||||||
final DateTime timestamp;
|
|
||||||
final String service;
|
|
||||||
final String method;
|
|
||||||
ServiceUnregisteredEvent(this.timestamp, this.service, this.method) {
|
|
||||||
assert(timestamp != null);
|
|
||||||
assert(service != null);
|
|
||||||
assert(method != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
M.Event createEventFromServiceEvent(S.ServiceEvent event) {
|
|
||||||
switch (event.kind) {
|
|
||||||
case S.ServiceEvent.kVMUpdate:
|
|
||||||
return new VMUpdateEvent(event.timestamp, event.vm);
|
|
||||||
case S.ServiceEvent.kIsolateStart:
|
|
||||||
return new IsolateStartEvent(event.timestamp, event.isolate);
|
|
||||||
case S.ServiceEvent.kIsolateRunnable:
|
|
||||||
return new IsolateRunnableEvent(event.timestamp, event.isolate);
|
|
||||||
case S.ServiceEvent.kIsolateUpdate:
|
|
||||||
return new IsolateUpdateEvent(event.timestamp, event.isolate);
|
|
||||||
case S.ServiceEvent.kIsolateReload:
|
|
||||||
return new IsolateReloadEvent(
|
|
||||||
event.timestamp, event.isolate, event.error);
|
|
||||||
case S.ServiceEvent.kIsolateExit:
|
|
||||||
return new IsolateExitEvent(event.timestamp, event.isolate);
|
|
||||||
case S.ServiceEvent.kBreakpointAdded:
|
|
||||||
return new BreakpointAddedEvent(
|
|
||||||
event.timestamp, event.isolate, event.breakpoint);
|
|
||||||
case S.ServiceEvent.kBreakpointResolved:
|
|
||||||
return new BreakpointResolvedEvent(
|
|
||||||
event.timestamp, event.isolate, event.breakpoint);
|
|
||||||
case S.ServiceEvent.kBreakpointRemoved:
|
|
||||||
return new BreakpointRemovedEvent(
|
|
||||||
event.timestamp, event.isolate, event.breakpoint);
|
|
||||||
case S.ServiceEvent.kDebuggerSettingsUpdate:
|
|
||||||
return new DebuggerSettingsUpdateEvent(event.timestamp, event.isolate);
|
|
||||||
case S.ServiceEvent.kResume:
|
|
||||||
return new ResumeEvent(event.timestamp, event.isolate, event.topFrame);
|
|
||||||
case S.ServiceEvent.kPauseStart:
|
|
||||||
return new PauseStartEvent(event.timestamp, event.isolate);
|
|
||||||
case S.ServiceEvent.kPauseExit:
|
|
||||||
return new PauseExitEvent(event.timestamp, event.isolate);
|
|
||||||
case S.ServiceEvent.kPausePostRequest:
|
|
||||||
return new PausePostRequestEvent(event.timestamp, event.isolate,
|
|
||||||
event.topFrame, event.atAsyncSuspension);
|
|
||||||
case S.ServiceEvent.kPauseBreakpoint:
|
|
||||||
return new PauseBreakpointEvent(
|
|
||||||
event.timestamp,
|
|
||||||
event.isolate,
|
|
||||||
event.pauseBreakpoints,
|
|
||||||
event.topFrame,
|
|
||||||
event.atAsyncSuspension,
|
|
||||||
event.breakpoint);
|
|
||||||
case S.Isolate.kLoggingStream:
|
|
||||||
return new LoggingEvent(event.timestamp, event.isolate, event.logRecord);
|
|
||||||
case S.ServiceEvent.kPauseInterrupted:
|
|
||||||
return new PauseInterruptedEvent(event.timestamp, event.isolate,
|
|
||||||
event.topFrame, event.atAsyncSuspension);
|
|
||||||
case S.ServiceEvent.kPauseException:
|
|
||||||
return new PauseExceptionEvent(
|
|
||||||
event.timestamp, event.isolate, event.topFrame, event.exception);
|
|
||||||
case S.ServiceEvent.kInspect:
|
|
||||||
return new InspectEvent(event.timestamp, event.isolate, event.inspectee);
|
|
||||||
case S.ServiceEvent.kGC:
|
|
||||||
return new GCEvent(event.timestamp, event.isolate);
|
|
||||||
case S.ServiceEvent.kServiceRegistered:
|
|
||||||
return new ServiceRegisteredEvent(
|
|
||||||
event.timestamp, event.service, event.method, event.alias);
|
|
||||||
case S.ServiceEvent.kServiceUnregistered:
|
|
||||||
return new ServiceUnregisteredEvent(
|
|
||||||
event.timestamp, event.service, event.method);
|
|
||||||
case S.ServiceEvent.kNone:
|
|
||||||
return new NoneEvent(event.timestamp, event.isolate);
|
|
||||||
default:
|
|
||||||
// Ignore unrecognized events.
|
|
||||||
Logger.root.severe('Unrecognized event: $event');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library models;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/object_graph.dart';
|
|
||||||
|
|
||||||
part 'src/models/exceptions.dart';
|
|
||||||
|
|
||||||
part 'src/models/objects/allocation_profile.dart';
|
|
||||||
part 'src/models/objects/breakpoint.dart';
|
|
||||||
part 'src/models/objects/class.dart';
|
|
||||||
part 'src/models/objects/code.dart';
|
|
||||||
part 'src/models/objects/context.dart';
|
|
||||||
part 'src/models/objects/error.dart';
|
|
||||||
part 'src/models/objects/event.dart';
|
|
||||||
part 'src/models/objects/extension_data.dart';
|
|
||||||
part 'src/models/objects/field.dart';
|
|
||||||
part 'src/models/objects/flag.dart';
|
|
||||||
part 'src/models/objects/frame.dart';
|
|
||||||
part 'src/models/objects/function.dart';
|
|
||||||
part 'src/models/objects/guarded.dart';
|
|
||||||
part 'src/models/objects/heap_space.dart';
|
|
||||||
part 'src/models/objects/icdata.dart';
|
|
||||||
part 'src/models/objects/inbound_references.dart';
|
|
||||||
part 'src/models/objects/instance.dart';
|
|
||||||
part 'src/models/objects/isolate.dart';
|
|
||||||
part 'src/models/objects/isolate_group.dart';
|
|
||||||
part 'src/models/objects/library.dart';
|
|
||||||
part 'src/models/objects/local_var_descriptors.dart';
|
|
||||||
part 'src/models/objects/map_association.dart';
|
|
||||||
part 'src/models/objects/megamorphiccache.dart';
|
|
||||||
part 'src/models/objects/metric.dart';
|
|
||||||
part 'src/models/objects/notification.dart';
|
|
||||||
part 'src/models/objects/object.dart';
|
|
||||||
part 'src/models/objects/objectpool.dart';
|
|
||||||
part 'src/models/objects/objectstore.dart';
|
|
||||||
part 'src/models/objects/pc_descriptors.dart';
|
|
||||||
part 'src/models/objects/persistent_handles.dart';
|
|
||||||
part 'src/models/objects/ports.dart';
|
|
||||||
part 'src/models/objects/retaining_path.dart';
|
|
||||||
part 'src/models/objects/sample_profile.dart';
|
|
||||||
part 'src/models/objects/script.dart';
|
|
||||||
part 'src/models/objects/sentinel.dart';
|
|
||||||
part 'src/models/objects/service.dart';
|
|
||||||
part 'src/models/objects/single_target_cache.dart';
|
|
||||||
part 'src/models/objects/source_location.dart';
|
|
||||||
part 'src/models/objects/subtype_test_cache.dart';
|
|
||||||
part 'src/models/objects/target.dart';
|
|
||||||
part 'src/models/objects/timeline.dart';
|
|
||||||
part 'src/models/objects/timeline_event.dart';
|
|
||||||
part 'src/models/objects/type_arguments.dart';
|
|
||||||
part 'src/models/objects/unknown.dart';
|
|
||||||
part 'src/models/objects/unlinked_call.dart';
|
|
||||||
part 'src/models/objects/vm.dart';
|
|
||||||
|
|
||||||
part 'src/models/repositories/allocation_profile.dart';
|
|
||||||
part 'src/models/repositories/breakpoint.dart';
|
|
||||||
part 'src/models/repositories/class.dart';
|
|
||||||
part 'src/models/repositories/context.dart';
|
|
||||||
part 'src/models/repositories/editor.dart';
|
|
||||||
part 'src/models/repositories/eval.dart';
|
|
||||||
part 'src/models/repositories/event.dart';
|
|
||||||
part 'src/models/repositories/field.dart';
|
|
||||||
part 'src/models/repositories/flag.dart';
|
|
||||||
part 'src/models/repositories/function.dart';
|
|
||||||
part 'src/models/repositories/heap_snapshot.dart';
|
|
||||||
part 'src/models/repositories/icdata.dart';
|
|
||||||
part 'src/models/repositories/inbound_references.dart';
|
|
||||||
part 'src/models/repositories/instance.dart';
|
|
||||||
part 'src/models/repositories/isolate.dart';
|
|
||||||
part 'src/models/repositories/isolate_group.dart';
|
|
||||||
part 'src/models/repositories/library.dart';
|
|
||||||
part 'src/models/repositories/megamorphiccache.dart';
|
|
||||||
part 'src/models/repositories/metric.dart';
|
|
||||||
part 'src/models/repositories/notification.dart';
|
|
||||||
part 'src/models/repositories/object.dart';
|
|
||||||
part 'src/models/repositories/objectpool.dart';
|
|
||||||
part 'src/models/repositories/objectstore.dart';
|
|
||||||
part 'src/models/repositories/persistent_handles.dart';
|
|
||||||
part 'src/models/repositories/ports.dart';
|
|
||||||
part 'src/models/repositories/reachable_size.dart';
|
|
||||||
part 'src/models/repositories/retained_size.dart';
|
|
||||||
part 'src/models/repositories/retaining_path.dart';
|
|
||||||
part 'src/models/repositories/sample_profile.dart';
|
|
||||||
part 'src/models/repositories/script.dart';
|
|
||||||
part 'src/models/repositories/single_target_cache.dart';
|
|
||||||
part 'src/models/repositories/strongly_reachable_instances.dart';
|
|
||||||
part 'src/models/repositories/subtype_test_cache.dart';
|
|
||||||
part 'src/models/repositories/target.dart';
|
|
||||||
part 'src/models/repositories/timeline.dart';
|
|
||||||
part 'src/models/repositories/type_arguments.dart';
|
|
||||||
part 'src/models/repositories/unlinked_call.dart';
|
|
||||||
part 'src/models/repositories/vm.dart';
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,56 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library repositories;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'package:observatory_2/allocation_profile.dart';
|
|
||||||
import 'package:observatory_2/sample_profile.dart';
|
|
||||||
import 'package:observatory_2/object_graph.dart';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/service.dart' as S;
|
|
||||||
import 'package:observatory_2/service_common.dart' as SC;
|
|
||||||
import 'package:observatory_2/src/repositories/timeline_base.dart';
|
|
||||||
|
|
||||||
part 'src/repositories/allocation_profile.dart';
|
|
||||||
part 'src/repositories/breakpoint.dart';
|
|
||||||
part 'src/repositories/class.dart';
|
|
||||||
part 'src/repositories/context.dart';
|
|
||||||
part 'src/repositories/editor.dart';
|
|
||||||
part 'src/repositories/eval.dart';
|
|
||||||
part 'src/repositories/event.dart';
|
|
||||||
part 'src/repositories/field.dart';
|
|
||||||
part 'src/repositories/flag.dart';
|
|
||||||
part 'src/repositories/function.dart';
|
|
||||||
part 'src/repositories/heap_snapshot.dart';
|
|
||||||
part 'src/repositories/icdata.dart';
|
|
||||||
part 'src/repositories/inbound_references.dart';
|
|
||||||
part 'src/repositories/instance.dart';
|
|
||||||
part 'src/repositories/isolate.dart';
|
|
||||||
part 'src/repositories/isolate_group.dart';
|
|
||||||
part 'src/repositories/library.dart';
|
|
||||||
part 'src/repositories/megamorphiccache.dart';
|
|
||||||
part 'src/repositories/metric.dart';
|
|
||||||
part 'src/repositories/notification.dart';
|
|
||||||
part 'src/repositories/object.dart';
|
|
||||||
part 'src/repositories/objectpool.dart';
|
|
||||||
part 'src/repositories/objectstore.dart';
|
|
||||||
part 'src/repositories/persistent_handles.dart';
|
|
||||||
part 'src/repositories/ports.dart';
|
|
||||||
part 'src/repositories/reachable_size.dart';
|
|
||||||
part 'src/repositories/retained_size.dart';
|
|
||||||
part 'src/repositories/retaining_path.dart';
|
|
||||||
part 'src/repositories/sample_profile.dart';
|
|
||||||
part 'src/repositories/script.dart';
|
|
||||||
part 'src/repositories/settings.dart';
|
|
||||||
part 'src/repositories/single_target_cache.dart';
|
|
||||||
part 'src/repositories/strongly_reachable_instances.dart';
|
|
||||||
part 'src/repositories/subtype_test_cache.dart';
|
|
||||||
part 'src/repositories/target.dart';
|
|
||||||
part 'src/repositories/timeline.dart';
|
|
||||||
part 'src/repositories/type_arguments.dart';
|
|
||||||
part 'src/repositories/unlinked_call.dart';
|
|
||||||
part 'src/repositories/vm.dart';
|
|
|
@ -1,13 +0,0 @@
|
||||||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library sample_profiler;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:logging/logging.dart';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/service.dart';
|
|
||||||
import 'package:observatory_2/utils.dart';
|
|
||||||
|
|
||||||
part 'src/sample_profile/sample_profile.dart';
|
|
|
@ -1,20 +0,0 @@
|
||||||
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library service;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'dart:math' as math;
|
|
||||||
|
|
||||||
import 'package:logging/logging.dart';
|
|
||||||
import 'package:observatory_2/event.dart' show createEventFromServiceEvent;
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/object_graph.dart';
|
|
||||||
import 'package:observatory_2/sample_profile.dart';
|
|
||||||
import 'package:observatory_2/service_common.dart';
|
|
||||||
import 'package:observatory_2/tracer.dart';
|
|
||||||
|
|
||||||
part 'src/service/object.dart';
|
|
|
@ -1,343 +0,0 @@
|
||||||
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library service_common;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:logging/logging.dart';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/service.dart';
|
|
||||||
|
|
||||||
// Export the service library.
|
|
||||||
export 'package:observatory_2/service.dart';
|
|
||||||
|
|
||||||
/// Description of a VM target.
|
|
||||||
class WebSocketVMTarget implements M.Target {
|
|
||||||
// Last time this VM has been connected to.
|
|
||||||
int lastConnectionTime = 0;
|
|
||||||
bool get hasEverConnected => lastConnectionTime > 0;
|
|
||||||
|
|
||||||
// Chrome VM or standalone;
|
|
||||||
bool chrome = false;
|
|
||||||
bool get standalone => !chrome;
|
|
||||||
|
|
||||||
// User defined name.
|
|
||||||
String name;
|
|
||||||
// Network address of VM.
|
|
||||||
String networkAddress;
|
|
||||||
|
|
||||||
WebSocketVMTarget(this.networkAddress) {
|
|
||||||
name = networkAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
WebSocketVMTarget.fromMap(Map json) {
|
|
||||||
lastConnectionTime = json['lastConnectionTime'];
|
|
||||||
chrome = json['chrome'];
|
|
||||||
name = json['name'];
|
|
||||||
networkAddress = json['networkAddress'];
|
|
||||||
if (name == null) {
|
|
||||||
name = networkAddress;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Map toJson() {
|
|
||||||
return {
|
|
||||||
'lastConnectionTime': lastConnectionTime,
|
|
||||||
'chrome': chrome,
|
|
||||||
'name': name,
|
|
||||||
'networkAddress': networkAddress,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _WebSocketRequest {
|
|
||||||
final String method;
|
|
||||||
final Map params;
|
|
||||||
final Completer<Map> completer;
|
|
||||||
|
|
||||||
_WebSocketRequest(this.method, this.params)
|
|
||||||
: completer = new Completer<Map>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Minimal common interface for 'WebSocket' in [dart:io] and [dart:html].
|
|
||||||
abstract class CommonWebSocket {
|
|
||||||
Future<void> connect(WebSocketVMTarget target, void onOpen(),
|
|
||||||
void onMessage(dynamic data), void onError(), void onClose());
|
|
||||||
bool get isOpen;
|
|
||||||
void send(dynamic data);
|
|
||||||
void close();
|
|
||||||
Future<ByteData> nonStringToByteData(dynamic data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [CommonWebSocketVM] communicates with a Dart VM over a CommonWebSocket.
|
|
||||||
/// The Dart VM can be embedded in Chromium or standalone.
|
|
||||||
abstract class CommonWebSocketVM extends VM {
|
|
||||||
final Completer _connected = new Completer();
|
|
||||||
final Completer<String> _disconnected = new Completer<String>();
|
|
||||||
final WebSocketVMTarget target;
|
|
||||||
final Map<String, _WebSocketRequest> _delayedRequests =
|
|
||||||
new Map<String, _WebSocketRequest>();
|
|
||||||
final Map<String, _WebSocketRequest> _pendingRequests =
|
|
||||||
new Map<String, _WebSocketRequest>();
|
|
||||||
int _requestSerial = 0;
|
|
||||||
bool _hasInitiatedConnect = false;
|
|
||||||
Utf8Decoder _utf8Decoder = const Utf8Decoder();
|
|
||||||
|
|
||||||
String get displayName => '${name}@${target.name}';
|
|
||||||
|
|
||||||
CommonWebSocket _webSocket;
|
|
||||||
|
|
||||||
CommonWebSocketVM(this.target, this._webSocket) {
|
|
||||||
assert(target != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _notifyConnect() {
|
|
||||||
if (!_connected.isCompleted) {
|
|
||||||
Logger.root
|
|
||||||
.info('WebSocketVM connection opened: ${target.networkAddress}');
|
|
||||||
_connected.complete(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future get onConnect => _connected.future;
|
|
||||||
bool get wasOrIsConnected => _connected.isCompleted;
|
|
||||||
bool get isConnected => wasOrIsConnected && !isDisconnected;
|
|
||||||
void _notifyDisconnect(String reason) {
|
|
||||||
if (!_disconnected.isCompleted) {
|
|
||||||
Logger.root
|
|
||||||
.info('WebSocketVM connection error: ${target.networkAddress}');
|
|
||||||
_disconnected.complete(reason);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> get onDisconnect => _disconnected.future;
|
|
||||||
bool get isDisconnected => _disconnected.isCompleted;
|
|
||||||
|
|
||||||
void disconnect({String reason = 'WebSocket closed'}) {
|
|
||||||
if (_hasInitiatedConnect) {
|
|
||||||
if (_webSocket != null) {
|
|
||||||
_webSocket.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// We don't need to cancel requests and notify here. These
|
|
||||||
// functions will be called again when the onClose callback
|
|
||||||
// fires. However, we may have a better 'reason' string now, so
|
|
||||||
// let's take care of business.
|
|
||||||
_cancelAllRequests(reason);
|
|
||||||
_notifyDisconnect(reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Map> invokeRpcRaw(String method, Map params) async {
|
|
||||||
if (!_hasInitiatedConnect) {
|
|
||||||
_hasInitiatedConnect = true;
|
|
||||||
try {
|
|
||||||
await _webSocket.connect(
|
|
||||||
target, _onOpen, _onMessage, _onError, _onClose);
|
|
||||||
} catch (_, stack) {
|
|
||||||
_webSocket = null;
|
|
||||||
var exception = new NetworkRpcException('WebSocket closed');
|
|
||||||
return new Future.error(exception, stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_disconnected.isCompleted) {
|
|
||||||
// This connection was closed already.
|
|
||||||
var exception = new NetworkRpcException(await onDisconnect);
|
|
||||||
return new Future.error(exception);
|
|
||||||
}
|
|
||||||
String serial = (_requestSerial++).toString();
|
|
||||||
var request = new _WebSocketRequest(method, <String, dynamic>{
|
|
||||||
...params,
|
|
||||||
// Include internal response data.
|
|
||||||
'_includePrivateMembers': true,
|
|
||||||
});
|
|
||||||
if ((_webSocket != null) && _webSocket.isOpen) {
|
|
||||||
// Already connected, send request immediately.
|
|
||||||
_sendRequest(serial, request);
|
|
||||||
} else {
|
|
||||||
// Not connected yet, add to delayed requests.
|
|
||||||
_delayedRequests[serial] = request;
|
|
||||||
}
|
|
||||||
return request.completer.future;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onClose() {
|
|
||||||
_cancelAllRequests('WebSocket closed');
|
|
||||||
_notifyDisconnect('WebSocket closed');
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebSocket error event handler.
|
|
||||||
void _onError() {
|
|
||||||
// TODO(turnidge): The implementors of CommonWebSocket have more
|
|
||||||
// error information available. Consider providing that here.
|
|
||||||
_cancelAllRequests('WebSocket closed due to error');
|
|
||||||
_notifyDisconnect('WebSocket closed due to error');
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebSocket open event handler.
|
|
||||||
void _onOpen() {
|
|
||||||
target.lastConnectionTime = new DateTime.now().millisecondsSinceEpoch;
|
|
||||||
_sendAllDelayedRequests();
|
|
||||||
_notifyConnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
Map _parseJSON(String message) {
|
|
||||||
var map;
|
|
||||||
try {
|
|
||||||
map = json.decode(message);
|
|
||||||
} catch (e, st) {
|
|
||||||
Logger.root.severe('Disconnecting: Error decoding message: $e\n$st');
|
|
||||||
disconnect(reason: 'Connection saw corrupt JSON message: $e');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (map == null) {
|
|
||||||
Logger.root.severe("Disconnecting: Unable to decode 'null' message");
|
|
||||||
disconnect(reason: "Connection saw 'null' message");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onBinaryMessage(dynamic data) {
|
|
||||||
_webSocket.nonStringToByteData(data).then((ByteData bytes) {
|
|
||||||
var metadataOffset = 4;
|
|
||||||
var dataOffset = bytes.getUint32(0, Endian.little);
|
|
||||||
var metadataLength = dataOffset - metadataOffset;
|
|
||||||
var dataLength = bytes.lengthInBytes - dataOffset;
|
|
||||||
var metadata = _utf8Decoder.convert(new Uint8List.view(
|
|
||||||
bytes.buffer, bytes.offsetInBytes + metadataOffset, metadataLength));
|
|
||||||
var data = new Uint8List.view(
|
|
||||||
bytes.buffer, bytes.offsetInBytes + dataOffset, dataLength);
|
|
||||||
var map = _parseJSON(metadata);
|
|
||||||
if (map == null || map['method'] != 'streamNotify') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var event = map['params']['event'];
|
|
||||||
var streamId = map['params']['streamId'];
|
|
||||||
scheduleMicrotask(() {
|
|
||||||
postServiceEvent(streamId, event, data);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onStringMessage(String data) {
|
|
||||||
var map = _parseJSON(data);
|
|
||||||
if (map == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (map['method'] == 'streamNotify') {
|
|
||||||
var event = map['params']['event'];
|
|
||||||
var streamId = map['params']['streamId'];
|
|
||||||
scheduleMicrotask(() {
|
|
||||||
postServiceEvent(streamId, event, null);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract serial and result.
|
|
||||||
var serial = map['id'];
|
|
||||||
|
|
||||||
// Complete request.
|
|
||||||
var request = _pendingRequests.remove(serial);
|
|
||||||
if (request == null) {
|
|
||||||
Logger.root.severe('Received unexpected message: ${map}');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (request.method != 'getTagProfile' &&
|
|
||||||
request.method != 'getIsolateMetric' &&
|
|
||||||
request.method != 'getVMMetric') {
|
|
||||||
Logger.root.info('RESPONSE [${serial}] ${request.method}');
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = map['result'];
|
|
||||||
if (result != null) {
|
|
||||||
request.completer.complete(result);
|
|
||||||
} else {
|
|
||||||
var exception = new ServerRpcException.fromMap(map['error']);
|
|
||||||
request.completer.completeError(exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebSocket message event handler.
|
|
||||||
void _onMessage(dynamic data) {
|
|
||||||
if (data is! String) {
|
|
||||||
_onBinaryMessage(data);
|
|
||||||
} else {
|
|
||||||
_onStringMessage(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _cancelRequests(
|
|
||||||
Map<String, _WebSocketRequest> requests, String message) {
|
|
||||||
requests.forEach((String serial, _WebSocketRequest request) {
|
|
||||||
var exception = new NetworkRpcException(message +
|
|
||||||
'(id: $serial method: ${request.method} params: ${request.params})');
|
|
||||||
request.completer.completeError(exception);
|
|
||||||
});
|
|
||||||
requests.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Cancel all pending and delayed requests by completing them with an error.
|
|
||||||
void _cancelAllRequests(String reason) {
|
|
||||||
String message = 'Canceling request: $reason';
|
|
||||||
if (_pendingRequests.length > 0) {
|
|
||||||
Logger.root.info('Canceling all pending requests.');
|
|
||||||
_cancelRequests(_pendingRequests, message);
|
|
||||||
}
|
|
||||||
if (_delayedRequests.length > 0) {
|
|
||||||
Logger.root.info('Canceling all delayed requests.');
|
|
||||||
_cancelRequests(_delayedRequests, message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Send all delayed requests.
|
|
||||||
void _sendAllDelayedRequests() {
|
|
||||||
assert(_webSocket.isOpen);
|
|
||||||
if (_delayedRequests.length == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Logger.root.info('Sending all delayed requests.');
|
|
||||||
// Send all delayed requests.
|
|
||||||
_delayedRequests.forEach(_sendRequest);
|
|
||||||
// Clear all delayed requests.
|
|
||||||
_delayedRequests.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Send the request over WebSocket.
|
|
||||||
void _sendRequest(String serial, _WebSocketRequest request) {
|
|
||||||
assert(_webSocket.isOpen);
|
|
||||||
// Mark request as pending.
|
|
||||||
assert(_pendingRequests.containsKey(serial) == false);
|
|
||||||
_pendingRequests[serial] = request;
|
|
||||||
var message;
|
|
||||||
// Encode message.
|
|
||||||
if (target.chrome) {
|
|
||||||
message = json.encode({
|
|
||||||
'id': int.parse(serial),
|
|
||||||
'method': 'Dart.observatoryQuery',
|
|
||||||
'params': {'id': serial, 'query': request.method}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
message = json.encode({
|
|
||||||
'jsonrpc': '2.0',
|
|
||||||
'id': serial,
|
|
||||||
'method': request.method,
|
|
||||||
'params': request.params
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (request.method != 'getTagProfile' &&
|
|
||||||
request.method != 'getIsolateMetric' &&
|
|
||||||
request.method != 'getVMMetric') {
|
|
||||||
Logger.root.info(
|
|
||||||
'GET [${serial}] ${request.method}(${request.params}) from ${target.networkAddress}');
|
|
||||||
}
|
|
||||||
// Send message.
|
|
||||||
_webSocket.send(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
String toString() => displayName;
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library service_html;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:observatory_2/service_common.dart';
|
|
||||||
|
|
||||||
// Export the service library.
|
|
||||||
export 'package:observatory_2/service_common.dart';
|
|
||||||
|
|
||||||
class _HtmlWebSocket implements CommonWebSocket {
|
|
||||||
WebSocket _webSocket;
|
|
||||||
|
|
||||||
Future<void> connect(WebSocketVMTarget target, void onOpen(),
|
|
||||||
void onMessage(dynamic data), void onError(), void onClose()) async {
|
|
||||||
// The VM service will attempt to redirect our websocket connection request
|
|
||||||
// to DDS, but the dart:html WebSocket doesn't follow redirects. Instead of
|
|
||||||
// relying on a redirect, we'll request the websocket URI from the service.
|
|
||||||
|
|
||||||
// TODO(bkonyi): re-enable when DDS is enabled. Currently causing Observatory
|
|
||||||
// failures when running with Flutter (see
|
|
||||||
// https://github.com/flutter/flutter/issues/64333)
|
|
||||||
/*Uri getWebSocketUriRequest = Uri.parse(target.networkAddress);
|
|
||||||
getWebSocketUriRequest =
|
|
||||||
getWebSocketUriRequest.replace(scheme: 'http', pathSegments: [
|
|
||||||
...getWebSocketUriRequest.pathSegments.where((e) => e != 'ws'),
|
|
||||||
'getWebSocketTarget',
|
|
||||||
]);
|
|
||||||
final response = json.decode(await HttpRequest.getString(
|
|
||||||
getWebSocketUriRequest.toString(),
|
|
||||||
));
|
|
||||||
if (!response.containsKey('result') ||
|
|
||||||
!response['result'].containsKey('uri')) {
|
|
||||||
onError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
target.networkAddress = response['result']['uri'];
|
|
||||||
*/
|
|
||||||
_webSocket = new WebSocket(target.networkAddress);
|
|
||||||
_webSocket.onClose.listen((CloseEvent) => onClose());
|
|
||||||
_webSocket.onError.listen((Event) => onError());
|
|
||||||
_webSocket.onOpen.listen((Event) => onOpen());
|
|
||||||
_webSocket.onMessage.listen((MessageEvent event) => onMessage(event.data));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get isOpen => _webSocket.readyState == WebSocket.OPEN;
|
|
||||||
|
|
||||||
void send(dynamic data) {
|
|
||||||
_webSocket.send(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void close() {
|
|
||||||
_webSocket.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<ByteData> nonStringToByteData(dynamic data) {
|
|
||||||
assert(data is Blob);
|
|
||||||
FileReader fileReader = new FileReader();
|
|
||||||
fileReader.readAsArrayBuffer(data);
|
|
||||||
return fileReader.onLoadEnd.first.then((e) {
|
|
||||||
Uint8List result = fileReader.result as Uint8List;
|
|
||||||
return new ByteData.view(
|
|
||||||
result.buffer, result.offsetInBytes, result.length);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The [WebSocketVM] communicates with a Dart VM over WebSocket. The Dart VM
|
|
||||||
/// can be embedded in Chromium or standalone. In the case of Chromium, we
|
|
||||||
/// make the service requests via the Chrome Remote Debugging Protocol.
|
|
||||||
class WebSocketVM extends CommonWebSocketVM {
|
|
||||||
WebSocketVM(WebSocketVMTarget target) : super(target, new _HtmlWebSocket());
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library service_io;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:io';
|
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:logging/logging.dart';
|
|
||||||
import 'package:observatory_2/service_common.dart';
|
|
||||||
|
|
||||||
// Export the service library.
|
|
||||||
export 'package:observatory_2/service_common.dart';
|
|
||||||
|
|
||||||
class _IOWebSocket implements CommonWebSocket {
|
|
||||||
WebSocket _webSocket;
|
|
||||||
|
|
||||||
Future<void> connect(WebSocketVMTarget target, void onOpen(),
|
|
||||||
void onMessage(dynamic data), void onError(), void onClose()) async {
|
|
||||||
try {
|
|
||||||
_webSocket = await WebSocket.connect(target.networkAddress);
|
|
||||||
_webSocket.listen(onMessage,
|
|
||||||
onError: (dynamic) => onError(),
|
|
||||||
onDone: onClose,
|
|
||||||
cancelOnError: true);
|
|
||||||
onOpen();
|
|
||||||
} catch (_) {
|
|
||||||
onError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get isOpen =>
|
|
||||||
(_webSocket != null) && (_webSocket.readyState == WebSocket.open);
|
|
||||||
|
|
||||||
void send(dynamic data) {
|
|
||||||
_webSocket.add(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void close() {
|
|
||||||
if (_webSocket != null) {
|
|
||||||
_webSocket.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<ByteData> nonStringToByteData(dynamic data) {
|
|
||||||
assert(data is Uint8List);
|
|
||||||
Logger.root.info('Binary data size in bytes: ${data.lengthInBytes}');
|
|
||||||
return new Future.sync(() =>
|
|
||||||
new ByteData.view(data.buffer, data.offsetInBytes, data.lengthInBytes));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The [WebSocketVM] communicates with a Dart VM over WebSocket.
|
|
||||||
class WebSocketVM extends CommonWebSocketVM {
|
|
||||||
WebSocketVM(WebSocketVMTarget target) : super(target, new _IOWebSocket());
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
part of allocation_profiler;
|
|
||||||
|
|
||||||
class AllocationProfile implements M.AllocationProfile {
|
|
||||||
static const _lastServiceGC = 'dateLastServiceGC';
|
|
||||||
final DateTime lastServiceGC;
|
|
||||||
static const _lastAccumulatorReset = 'dateLastAccumulatorReset';
|
|
||||||
final DateTime lastAccumulatorReset;
|
|
||||||
final S.HeapSpace newSpace;
|
|
||||||
final S.HeapSpace oldSpace;
|
|
||||||
final S.HeapSpace totalSpace;
|
|
||||||
final Iterable<M.ClassHeapStats> members;
|
|
||||||
|
|
||||||
AllocationProfile(S.ServiceMap map, {Map/*<String, List<String>>*/ defaults})
|
|
||||||
: lastAccumulatorReset = _intString2DateTime(map[_lastAccumulatorReset]),
|
|
||||||
lastServiceGC = _intString2DateTime(map[_lastServiceGC]),
|
|
||||||
oldSpace = new S.HeapSpace()..update(map['_heaps']['old']),
|
|
||||||
newSpace = new S.HeapSpace()..update(map['_heaps']['new']),
|
|
||||||
totalSpace = new S.HeapSpace(),
|
|
||||||
members = _convertMembers(map['members'], defaults: defaults) {
|
|
||||||
totalSpace.add(oldSpace);
|
|
||||||
totalSpace.add(newSpace);
|
|
||||||
}
|
|
||||||
|
|
||||||
static DateTime _intString2DateTime(String milliseconds) {
|
|
||||||
if ((milliseconds == null) || milliseconds == '') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new DateTime.fromMillisecondsSinceEpoch(int.parse(milliseconds));
|
|
||||||
}
|
|
||||||
|
|
||||||
static ClassHeapStats _convertMember(/*S.ServiceMap*/ map) {
|
|
||||||
assert(map['type'] == 'ClassHeapStats');
|
|
||||||
return new ClassHeapStats(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<M.ClassHeapStats> _convertMembers(Iterable/*<S.ServiceMap>*/ raw,
|
|
||||||
{Map/*<String, List<String>>*/ defaults}) {
|
|
||||||
final List<M.ClassHeapStats> members =
|
|
||||||
raw.map<ClassHeapStats>(_convertMember).toList();
|
|
||||||
if (defaults == null) {
|
|
||||||
return members;
|
|
||||||
}
|
|
||||||
final Map<String, List<ClassHeapStats>> aliases =
|
|
||||||
new Map.fromIterable(defaults.keys, value: (_) => <ClassHeapStats>[]);
|
|
||||||
final Map<String, List<ClassHeapStats>> accumulators =
|
|
||||||
<String, List<ClassHeapStats>>{};
|
|
||||||
defaults.forEach((/*String*/ key, /*List<String>*/ values) {
|
|
||||||
final classes = aliases[key];
|
|
||||||
accumulators.addAll(new Map.fromIterable(values, value: (_) => classes));
|
|
||||||
});
|
|
||||||
final List<M.ClassHeapStats> result = <M.ClassHeapStats>[];
|
|
||||||
members.forEach((M.ClassHeapStats member) {
|
|
||||||
if (accumulators.containsKey(member.clazz.id)) {
|
|
||||||
accumulators[member.clazz.id].add(member);
|
|
||||||
} else {
|
|
||||||
result.add(member);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return result
|
|
||||||
..addAll(
|
|
||||||
aliases.keys.map((key) => new ClassesHeapStats(key, aliases[key])));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ClassHeapStats implements M.ClassHeapStats {
|
|
||||||
final S.Class clazz;
|
|
||||||
final String displayName = null;
|
|
||||||
final S.Allocations newSpace;
|
|
||||||
final S.Allocations oldSpace;
|
|
||||||
|
|
||||||
ClassHeapStats(Map map)
|
|
||||||
: clazz = map['class'],
|
|
||||||
oldSpace = new S.Allocations()..update(map['_old']),
|
|
||||||
newSpace = new S.Allocations()..update(map['_new']);
|
|
||||||
}
|
|
||||||
|
|
||||||
class ClassesHeapStats implements M.ClassHeapStats {
|
|
||||||
final S.Class clazz = null;
|
|
||||||
final String displayName;
|
|
||||||
final S.Allocations newSpace;
|
|
||||||
final S.Allocations oldSpace;
|
|
||||||
|
|
||||||
ClassesHeapStats(this.displayName, Iterable<ClassHeapStats> classes)
|
|
||||||
: oldSpace = new S.Allocations()..combine(classes.map((m) => m.oldSpace)),
|
|
||||||
newSpace = new S.Allocations()..combine(classes.map((m) => m.newSpace));
|
|
||||||
}
|
|
|
@ -1,294 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
part of app;
|
|
||||||
|
|
||||||
/// The observatory application. Instances of this are created and owned
|
|
||||||
/// by the observatory_application custom element.
|
|
||||||
class ObservatoryApplication {
|
|
||||||
static ObservatoryApplication app;
|
|
||||||
final RenderingQueue queue = new RenderingQueue();
|
|
||||||
final TargetRepository targets = new TargetRepository(isConnectedVMTarget);
|
|
||||||
final EventRepository events = new EventRepository();
|
|
||||||
final NotificationRepository notifications = new NotificationRepository();
|
|
||||||
final _pageRegistry = <Page>[];
|
|
||||||
LocationManager _locationManager;
|
|
||||||
LocationManager get locationManager => _locationManager;
|
|
||||||
Page currentPage;
|
|
||||||
bool _vmConnected = false;
|
|
||||||
VM _vm;
|
|
||||||
VM get vm => _vm;
|
|
||||||
|
|
||||||
static bool isConnectedVMTarget(M.Target target) {
|
|
||||||
if (app._vm is CommonWebSocketVM) {
|
|
||||||
if ((app._vm as CommonWebSocketVM).target == target) {
|
|
||||||
return app._vm.isConnected;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_switchVM(VM newVM) {
|
|
||||||
final VM oldVM = _vm;
|
|
||||||
|
|
||||||
Logger.root.info('_switchVM from:${oldVM} to:${newVM}');
|
|
||||||
|
|
||||||
if (oldVM == newVM) {
|
|
||||||
// Do nothing.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldVM != null) {
|
|
||||||
print('disconnecting from the old VM ${oldVM}--');
|
|
||||||
// Disconnect from current VM.
|
|
||||||
stopGCEventListener();
|
|
||||||
notifications.deleteAll();
|
|
||||||
oldVM.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newVM != null) {
|
|
||||||
// Mark that we haven't connected yet.
|
|
||||||
_vmConnected = false;
|
|
||||||
// On connect:
|
|
||||||
newVM.onConnect.then((_) async {
|
|
||||||
// We connected.
|
|
||||||
_vmConnected = true;
|
|
||||||
notifications.deleteDisconnectEvents();
|
|
||||||
await newVM.load();
|
|
||||||
// TODO(cbernaschina) smart connection of streams in the events object.
|
|
||||||
newVM.listenEventStream(VM.kVMStream, _onEvent);
|
|
||||||
newVM.listenEventStream(VM.kIsolateStream, _onEvent);
|
|
||||||
newVM.listenEventStream(VM.kDebugStream, _onEvent);
|
|
||||||
newVM.listenEventStream(VM.kServiceStream, _onEvent);
|
|
||||||
});
|
|
||||||
// On disconnect:
|
|
||||||
newVM.onDisconnect.then((String reason) {
|
|
||||||
if (this.vm != newVM) {
|
|
||||||
// This disconnect event occurred *after* a new VM was installed.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Let anyone looking at the targets know that we have disconnected
|
|
||||||
// from one.
|
|
||||||
targets.emitDisconnectEvent();
|
|
||||||
if (!_vmConnected) {
|
|
||||||
// Connection error. Navigate back to the connect page.
|
|
||||||
Logger.root.info('Connection failed, navigating to VM connect page.');
|
|
||||||
// Clear the vm.
|
|
||||||
_vm = null;
|
|
||||||
app.locationManager.go(Uris.vmConnect());
|
|
||||||
} else {
|
|
||||||
// Disconnect. Stay at the current page and push an a connection
|
|
||||||
// closed event.
|
|
||||||
Logger.root.info('Lost an existing connection to a VM');
|
|
||||||
events.add(new ConnectionClosedEvent(new DateTime.now(), reason));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_vm = newVM;
|
|
||||||
}
|
|
||||||
|
|
||||||
StreamSubscription _gcSubscription;
|
|
||||||
StreamSubscription _loggingSubscription;
|
|
||||||
|
|
||||||
Future startGCEventListener() async {
|
|
||||||
if (_gcSubscription != null || _vm == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_gcSubscription = await _vm.listenEventStream(VM.kGCStream, _onEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future startLoggingEventListener() async {
|
|
||||||
if (_loggingSubscription != null || _vm == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_loggingSubscription =
|
|
||||||
await _vm.listenEventStream(Isolate.kLoggingStream, _onEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future stopGCEventListener() async {
|
|
||||||
if (_gcSubscription == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_gcSubscription.cancel();
|
|
||||||
_gcSubscription = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future stopLoggingEventListener() async {
|
|
||||||
if (_loggingSubscription == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_loggingSubscription.cancel();
|
|
||||||
_loggingSubscription = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final ObservatoryApplicationElement rootElement;
|
|
||||||
|
|
||||||
ServiceObject lastErrorOrException;
|
|
||||||
|
|
||||||
void _initOnce() {
|
|
||||||
assert(app == null);
|
|
||||||
app = this;
|
|
||||||
_registerPages();
|
|
||||||
// Visit the current page.
|
|
||||||
locationManager._visit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _deletePauseEvents(e) {
|
|
||||||
notifications.deletePauseEvents(isolate: e.isolate);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _addNotification(M.Event e) {
|
|
||||||
notifications.add(new EventNotification(e));
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onEvent(ServiceEvent event) {
|
|
||||||
assert(event.kind != ServiceEvent.kNone);
|
|
||||||
M.Event e = createEventFromServiceEvent(event);
|
|
||||||
if (e != null) {
|
|
||||||
events.add(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _registerPages() {
|
|
||||||
_pageRegistry.add(new VMPage(this));
|
|
||||||
_pageRegistry.add(new FlagsPage(this));
|
|
||||||
_pageRegistry.add(new NativeMemoryProfilerPage(this));
|
|
||||||
_pageRegistry.add(new InspectPage(this));
|
|
||||||
_pageRegistry.add(new ClassTreePage(this));
|
|
||||||
_pageRegistry.add(new DebuggerPage(this));
|
|
||||||
_pageRegistry.add(new ObjectStorePage(this));
|
|
||||||
_pageRegistry.add(new CpuProfilerPage(this));
|
|
||||||
_pageRegistry.add(new TableCpuProfilerPage(this));
|
|
||||||
_pageRegistry.add(new AllocationProfilerPage(this));
|
|
||||||
_pageRegistry.add(new HeapMapPage(this));
|
|
||||||
_pageRegistry.add(new HeapSnapshotPage(this));
|
|
||||||
_pageRegistry.add(new VMConnectPage(this));
|
|
||||||
_pageRegistry.add(new IsolateReconnectPage(this));
|
|
||||||
_pageRegistry.add(new ErrorViewPage(this));
|
|
||||||
_pageRegistry.add(new MetricsPage(this));
|
|
||||||
_pageRegistry.add(new PersistentHandlesPage(this));
|
|
||||||
_pageRegistry.add(new PortsPage(this));
|
|
||||||
_pageRegistry.add(new LoggingPage(this));
|
|
||||||
_pageRegistry.add(new TimelinePage(this));
|
|
||||||
_pageRegistry.add(new TimelineDashboardPage(this));
|
|
||||||
_pageRegistry.add(new ProcessSnapshotPage(this));
|
|
||||||
// Note that ErrorPage must be the last entry in the list as it is
|
|
||||||
// the catch all.
|
|
||||||
_pageRegistry.add(new ErrorPage(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
void _visit(Uri uri, Map internalArguments) {
|
|
||||||
if (internalArguments['trace'] != null) {
|
|
||||||
var traceArg = internalArguments['trace'];
|
|
||||||
if (traceArg == 'on') {
|
|
||||||
Tracer.start();
|
|
||||||
} else if (traceArg == 'off') {
|
|
||||||
Tracer.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Tracer.current != null) {
|
|
||||||
Tracer.current.reset();
|
|
||||||
}
|
|
||||||
for (var i = 0; i < _pageRegistry.length; i++) {
|
|
||||||
var page = _pageRegistry[i];
|
|
||||||
if (page.canVisit(uri)) {
|
|
||||||
_installPage(page);
|
|
||||||
page.visit(uri, internalArguments);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new ArgumentError.value(uri, 'uri');
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the Observatory application page.
|
|
||||||
void _installPage(Page page) {
|
|
||||||
assert(page != null);
|
|
||||||
if (currentPage == page) {
|
|
||||||
// Already installed.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (currentPage != null) {
|
|
||||||
Logger.root.info('Uninstalling page: $currentPage');
|
|
||||||
currentPage.onUninstall();
|
|
||||||
// Clear children.
|
|
||||||
rootElement.children.clear();
|
|
||||||
}
|
|
||||||
Logger.root.info('Installing page: $page');
|
|
||||||
try {
|
|
||||||
page.onInstall();
|
|
||||||
} catch (e) {
|
|
||||||
Logger.root.severe('Failed to install page: $e');
|
|
||||||
}
|
|
||||||
// Add new page.
|
|
||||||
rootElement.children.add(page.element);
|
|
||||||
|
|
||||||
// Remember page.
|
|
||||||
currentPage = page;
|
|
||||||
}
|
|
||||||
|
|
||||||
ObservatoryApplication(this.rootElement) {
|
|
||||||
_locationManager = new LocationManager(this);
|
|
||||||
targets.onChange.listen((TargetChangeEvent e) {
|
|
||||||
if (e.disconnected) {
|
|
||||||
// We don't care about disconnected events.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (targets.current == null) {
|
|
||||||
_switchVM(null);
|
|
||||||
} else {
|
|
||||||
final bool currentTarget =
|
|
||||||
(_vm as WebSocketVM)?.target == targets.current;
|
|
||||||
final bool currentTargetConnected = (_vm != null) && _vm.isConnected;
|
|
||||||
if (!currentTarget || !currentTargetConnected) {
|
|
||||||
_switchVM(new WebSocketVM(targets.current));
|
|
||||||
_vm.onConnect.then((_) {
|
|
||||||
app.locationManager.go(Uris.vm());
|
|
||||||
});
|
|
||||||
_vm.load();
|
|
||||||
} else if (currentTargetConnected) {
|
|
||||||
app.locationManager.go(Uris.vm());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Logger.root.info('Setting initial target to ${targets.current.name}');
|
|
||||||
_switchVM(new WebSocketVM(targets.current));
|
|
||||||
_initOnce();
|
|
||||||
|
|
||||||
// delete pause events.
|
|
||||||
events.onIsolateExit.listen(_deletePauseEvents);
|
|
||||||
events.onResume.listen(_deletePauseEvents);
|
|
||||||
events.onPauseStart.listen(_deletePauseEvents);
|
|
||||||
events.onPauseExit.listen(_deletePauseEvents);
|
|
||||||
events.onPauseBreakpoint.listen(_deletePauseEvents);
|
|
||||||
events.onPauseInterrupted.listen(_deletePauseEvents);
|
|
||||||
events.onPauseException.listen(_deletePauseEvents);
|
|
||||||
|
|
||||||
// show notification for an event.
|
|
||||||
events.onIsolateReload.listen(_addNotification);
|
|
||||||
events.onPauseExit.listen(_addNotification);
|
|
||||||
events.onPauseBreakpoint.listen(_addNotification);
|
|
||||||
events.onPauseInterrupted.listen(_addNotification);
|
|
||||||
events.onPauseException.listen(_addNotification);
|
|
||||||
events.onInspect.listen(_addNotification);
|
|
||||||
events.onConnectionClosed.listen(_addNotification);
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleException(e, st) {
|
|
||||||
if (e is ServerRpcException) {
|
|
||||||
if (e.code == ServerRpcException.kFeatureDisabled) return;
|
|
||||||
if (e.code == ServerRpcException.kIsolateMustBePaused) return;
|
|
||||||
if (e.code == ServerRpcException.kCannotAddBreakpoint) return;
|
|
||||||
Logger.root.fine('Dropping exception: ${e}\n${st}');
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(turnidge): Report this failure via analytics.
|
|
||||||
Logger.root.warning('Caught exception: ${e}\n${st}');
|
|
||||||
notifications.add(new ExceptionNotification(e, stacktrace: st));
|
|
||||||
}
|
|
||||||
|
|
||||||
// This map keeps track of which curly-blocks have been expanded by the user.
|
|
||||||
Map<String, bool> expansions = {};
|
|
||||||
}
|
|
|
@ -1,167 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
part of app;
|
|
||||||
|
|
||||||
class LocationManager {
|
|
||||||
final ObservatoryApplication _app;
|
|
||||||
|
|
||||||
/// [internalArguments] are parameters specified after a '---' in the
|
|
||||||
/// application URL.
|
|
||||||
final Map<String, String> internalArguments = new Map<String, String>();
|
|
||||||
|
|
||||||
Uri _uri;
|
|
||||||
|
|
||||||
/// [uri] is the application uri. Application uris consist of a path and
|
|
||||||
/// the queryParameters map.
|
|
||||||
Uri get uri => _uri;
|
|
||||||
|
|
||||||
LocationManager(this._app) {
|
|
||||||
window.onPopState.listen(_onBrowserNavigation);
|
|
||||||
// Determine initial application path.
|
|
||||||
var applicationPath = '${window.location.hash}';
|
|
||||||
if ((window.location.hash == '') || (window.location.hash == '#')) {
|
|
||||||
// Observatory has loaded but no application path has been specified,
|
|
||||||
// use the default.
|
|
||||||
// By default we navigate to the VM page.
|
|
||||||
applicationPath = Uris.vm();
|
|
||||||
}
|
|
||||||
// Update current application path.
|
|
||||||
window.history
|
|
||||||
.replaceState(applicationPath, document.title, applicationPath);
|
|
||||||
_updateApplicationLocation(applicationPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getBoolParameter(String name, bool defaultValue) {
|
|
||||||
var value = uri.queryParameters[name];
|
|
||||||
if ("true" == value) return true;
|
|
||||||
if ("false" == value) return false;
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called whenever the browser changes the location bar (e.g. forward or
|
|
||||||
/// back button press).
|
|
||||||
void _onBrowserNavigation(PopStateEvent event) {
|
|
||||||
_updateApplicationLocation(window.location.hash);
|
|
||||||
_visit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Given an application url, generate an href link.
|
|
||||||
String makeLink(String url) => '#$url';
|
|
||||||
|
|
||||||
/// Update the application location. After this function returns,
|
|
||||||
/// [uri] and [debugArguments] will be updated.
|
|
||||||
_updateApplicationLocation(String url) {
|
|
||||||
if (url == Uris.vmConnect()) {
|
|
||||||
// When we go to the vm-connect page, drop all notifications.
|
|
||||||
_app.notifications.deleteAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chop off leading '#'.
|
|
||||||
if (url.startsWith('#')) {
|
|
||||||
url = url.substring(1);
|
|
||||||
}
|
|
||||||
// Fall through handles '#/'
|
|
||||||
// Chop off leading '/'.
|
|
||||||
if (url.startsWith('/')) {
|
|
||||||
url = url.substring(1);
|
|
||||||
}
|
|
||||||
// Parse out debug arguments.
|
|
||||||
if (url.contains('---')) {
|
|
||||||
var chunks = url.split('---');
|
|
||||||
url = chunks[0];
|
|
||||||
if ((chunks.length > 1) && (chunks[1] != '')) {
|
|
||||||
internalArguments.clear();
|
|
||||||
try {
|
|
||||||
internalArguments.addAll(Uri.splitQueryString(chunks[1]));
|
|
||||||
} catch (e) {
|
|
||||||
Logger.root.warning('Could not parse debug arguments ${e}');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_uri = Uri.parse(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add [url] to the browser history.
|
|
||||||
_addToBrowserHistory(String url) {
|
|
||||||
window.history.pushState(url, document.title, url);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Notify the current page that something has changed.
|
|
||||||
_visit() {
|
|
||||||
Chain.capture(() => _app._visit(_uri, internalArguments), onError: (e, st) {
|
|
||||||
if (e is IsolateNotFound) {
|
|
||||||
var newPath = ((_app.vm == null || _app.vm.isDisconnected)
|
|
||||||
? '/vm-connect'
|
|
||||||
: '/isolate-reconnect');
|
|
||||||
var parameters = <String, dynamic>{};
|
|
||||||
parameters.addAll(_uri.queryParameters);
|
|
||||||
parameters['originalUri'] = _uri.toString();
|
|
||||||
parameters['isolateId'] = parameters['isolateId'];
|
|
||||||
var generatedUri = new Uri(path: newPath, queryParameters: parameters);
|
|
||||||
go(makeLink(generatedUri.toString()), true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Surface any uncaught exceptions.
|
|
||||||
_app.handleException(e, st);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Navigate to [url].
|
|
||||||
void go(String url, [bool addToBrowserHistory = true]) {
|
|
||||||
if (addToBrowserHistory) {
|
|
||||||
_addToBrowserHistory(url);
|
|
||||||
}
|
|
||||||
_updateApplicationLocation(url);
|
|
||||||
_visit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Starting with the current uri path and queryParameters, update
|
|
||||||
/// queryParameters present in [updateParameters], then generate a new uri
|
|
||||||
/// and navigate to that.
|
|
||||||
goReplacingParameters(Map updatedParameters,
|
|
||||||
[bool addToBrowserHistory = true]) {
|
|
||||||
go(makeLinkReplacingParameters(updatedParameters), addToBrowserHistory);
|
|
||||||
}
|
|
||||||
|
|
||||||
makeLinkReplacingParameters(Map updatedParameters) {
|
|
||||||
var parameters = new Map.from(_uri.queryParameters);
|
|
||||||
updatedParameters.forEach((k, v) {
|
|
||||||
parameters[k] = v;
|
|
||||||
});
|
|
||||||
// Ensure path starts with a slash.
|
|
||||||
var path = uri.path.startsWith('/') ? uri.path : '/${uri.path}';
|
|
||||||
var generatedUri = new Uri(path: path, queryParameters: parameters);
|
|
||||||
return makeLink(generatedUri.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
goForwardingParameters(String newPath, [bool addToBrowserHistory = true]) {
|
|
||||||
go(makeLinkForwardingParameters(newPath), addToBrowserHistory);
|
|
||||||
}
|
|
||||||
|
|
||||||
makeLinkForwardingParameters(String newPath) {
|
|
||||||
var parameters = _uri.queryParameters;
|
|
||||||
var generatedUri = new Uri(path: newPath, queryParameters: parameters);
|
|
||||||
return makeLink(generatedUri.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Utility event handler when clicking on application url link.
|
|
||||||
void onGoto(MouseEvent event) {
|
|
||||||
if ((event.button > 0) ||
|
|
||||||
event.metaKey ||
|
|
||||||
event.ctrlKey ||
|
|
||||||
event.shiftKey ||
|
|
||||||
event.altKey) {
|
|
||||||
// Mouse event is not a left-click OR
|
|
||||||
// mouse event is a left-click with a modifier key:
|
|
||||||
// let browser handle.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.preventDefault();
|
|
||||||
// 'currentTarget' is the dom element that would process the event.
|
|
||||||
// If we use 'target' we might get an <em> element or somesuch.
|
|
||||||
Element target = event.currentTarget;
|
|
||||||
go(target.attributes['href']);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
part of app;
|
|
||||||
|
|
||||||
class ExceptionNotification implements M.ExceptionNotification {
|
|
||||||
final exception;
|
|
||||||
|
|
||||||
/// [optional]
|
|
||||||
final StackTrace stacktrace;
|
|
||||||
ExceptionNotification(this.exception, {this.stacktrace});
|
|
||||||
}
|
|
||||||
|
|
||||||
class EventNotification implements M.EventNotification {
|
|
||||||
final M.Event event;
|
|
||||||
EventNotification(this.event);
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,45 +0,0 @@
|
||||||
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
part of app;
|
|
||||||
|
|
||||||
/// Static settings database.
|
|
||||||
class _Settings {
|
|
||||||
static Storage _storage = window.localStorage;
|
|
||||||
|
|
||||||
/// Associated [value] with [key]. [value] must be JSON encodable.
|
|
||||||
static void set(String key, dynamic value) {
|
|
||||||
_storage[key] = json.encode(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get value associated with [key]. Return value will be a JSON encodable
|
|
||||||
/// object.
|
|
||||||
static dynamic get(String key) {
|
|
||||||
var value = _storage[key];
|
|
||||||
if (value == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return json.decode(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A group of settings each prefixed with group name and a dot.
|
|
||||||
class SettingsGroup {
|
|
||||||
/// Group name
|
|
||||||
final String group;
|
|
||||||
|
|
||||||
SettingsGroup(this.group);
|
|
||||||
|
|
||||||
String _fullKey(String key) => '$group.$key';
|
|
||||||
|
|
||||||
void set(String key, dynamic value) {
|
|
||||||
var fullKey = _fullKey(key);
|
|
||||||
_Settings.set(fullKey, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
dynamic get(String key) {
|
|
||||||
var fullKey = _fullKey(key);
|
|
||||||
return _Settings.get(fullKey);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,104 +0,0 @@
|
||||||
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
part of app;
|
|
||||||
|
|
||||||
typedef String ValueFormatter(dynamic value);
|
|
||||||
|
|
||||||
class SortedTableColumn {
|
|
||||||
static String toStringFormatter(dynamic v) {
|
|
||||||
return v != null ? v.toString() : '<null>';
|
|
||||||
}
|
|
||||||
|
|
||||||
final String label;
|
|
||||||
final ValueFormatter formatter;
|
|
||||||
SortedTableColumn.withFormatter(this.label, this.formatter);
|
|
||||||
SortedTableColumn(this.label) : formatter = toStringFormatter;
|
|
||||||
}
|
|
||||||
|
|
||||||
class SortedTableRow {
|
|
||||||
final List values;
|
|
||||||
SortedTableRow(this.values);
|
|
||||||
}
|
|
||||||
|
|
||||||
class SortedTable {
|
|
||||||
final List<SortedTableColumn> columns;
|
|
||||||
final List<SortedTableRow> rows = <SortedTableRow>[];
|
|
||||||
final List<int> sortedRows = [];
|
|
||||||
|
|
||||||
SortedTable(this.columns);
|
|
||||||
|
|
||||||
int _sortColumnIndex = 0;
|
|
||||||
set sortColumnIndex(var index) {
|
|
||||||
assert(index >= 0);
|
|
||||||
assert(index < columns.length);
|
|
||||||
_sortColumnIndex = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
int get sortColumnIndex => _sortColumnIndex;
|
|
||||||
bool _sortDescending = true;
|
|
||||||
bool get sortDescending => _sortDescending;
|
|
||||||
set sortDescending(var descending) {
|
|
||||||
_sortDescending = descending;
|
|
||||||
}
|
|
||||||
|
|
||||||
dynamic getSortKeyFor(int row, int col) {
|
|
||||||
return rows[row].values[col];
|
|
||||||
}
|
|
||||||
|
|
||||||
int _sortFuncDescending(int i, int j) {
|
|
||||||
var a = getSortKeyFor(i, _sortColumnIndex);
|
|
||||||
var b = getSortKeyFor(j, _sortColumnIndex);
|
|
||||||
return b.compareTo(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
int _sortFuncAscending(int i, int j) {
|
|
||||||
var a = getSortKeyFor(i, _sortColumnIndex);
|
|
||||||
var b = getSortKeyFor(j, _sortColumnIndex);
|
|
||||||
return a.compareTo(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sort() {
|
|
||||||
assert(_sortColumnIndex >= 0);
|
|
||||||
assert(_sortColumnIndex < columns.length);
|
|
||||||
if (_sortDescending) {
|
|
||||||
sortedRows.sort(_sortFuncDescending);
|
|
||||||
} else {
|
|
||||||
sortedRows.sort(_sortFuncAscending);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void clearRows() {
|
|
||||||
rows.clear();
|
|
||||||
sortedRows.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void addRow(SortedTableRow row) {
|
|
||||||
sortedRows.add(rows.length);
|
|
||||||
rows.add(row);
|
|
||||||
}
|
|
||||||
|
|
||||||
String getFormattedValue(int row, int column) {
|
|
||||||
var value = getValue(row, column);
|
|
||||||
var formatter = columns[column].formatter;
|
|
||||||
return formatter(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
String getColumnLabel(int column) {
|
|
||||||
assert(column >= 0);
|
|
||||||
assert(column < columns.length);
|
|
||||||
// TODO(johnmccutchan): Move expander display decisions into html once
|
|
||||||
// tables and templates are better supported.
|
|
||||||
const arrowUp = '\u25BC';
|
|
||||||
const arrowDown = '\u25B2';
|
|
||||||
if (column != _sortColumnIndex) {
|
|
||||||
return columns[column].label + '\u2003';
|
|
||||||
}
|
|
||||||
return columns[column].label + (_sortDescending ? arrowUp : arrowDown);
|
|
||||||
}
|
|
||||||
|
|
||||||
dynamic getValue(int row, int column) {
|
|
||||||
return rows[row].values[column];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,280 +0,0 @@
|
||||||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
part of cli;
|
|
||||||
|
|
||||||
// Splits a line into a list of string args. Each arg retains any
|
|
||||||
// trailing whitespace so that we can reconstruct the original command
|
|
||||||
// line from the pieces.
|
|
||||||
List<String> _splitLine(String line) {
|
|
||||||
line = line.trimLeft();
|
|
||||||
var args = <String>[];
|
|
||||||
int pos = 0;
|
|
||||||
while (pos < line.length) {
|
|
||||||
int startPos = pos;
|
|
||||||
|
|
||||||
// Advance to end of word.
|
|
||||||
for (; pos < line.length && line[pos] != ' '; pos++);
|
|
||||||
|
|
||||||
// Advance to end of spaces.
|
|
||||||
for (; pos < line.length && line[pos] == ' '; pos++);
|
|
||||||
|
|
||||||
args.add(line.substring(startPos, pos));
|
|
||||||
}
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Concatenates the first 'count' args.
|
|
||||||
String _concatArgs(List<String> args, int count) {
|
|
||||||
if (count == 0) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return '${args.sublist(0, count).join('')}';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shared functionality for RootCommand and Command.
|
|
||||||
abstract class _CommandBase {
|
|
||||||
_CommandBase(List<Command> children) {
|
|
||||||
assert(children != null);
|
|
||||||
_children.addAll(children);
|
|
||||||
for (var child in _children) {
|
|
||||||
child._parent = this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A command may optionally have sub-commands.
|
|
||||||
List<Command> _children = <Command>[];
|
|
||||||
|
|
||||||
_CommandBase _parent;
|
|
||||||
int get _depth => (_parent == null ? 0 : _parent._depth + 1);
|
|
||||||
|
|
||||||
// Override in subclasses to provide command-specific argument completion.
|
|
||||||
//
|
|
||||||
// Given a list of arguments to this command, provide a list of
|
|
||||||
// possible completions for those arguments.
|
|
||||||
Future<List<String>> complete(List<String> args) =>
|
|
||||||
new Future.value(<String>[]);
|
|
||||||
|
|
||||||
// Override in subclasses to provide command-specific execution.
|
|
||||||
Future run(List<String> args);
|
|
||||||
|
|
||||||
// Returns a list of local subcommands which match the args.
|
|
||||||
List<Command> _matchLocal(String argWithSpace, bool preferExact) {
|
|
||||||
var matches = <Command>[];
|
|
||||||
var arg = argWithSpace.trimRight();
|
|
||||||
for (var child in _children) {
|
|
||||||
if (child.name.startsWith(arg)) {
|
|
||||||
if (preferExact && ((child.name == arg) || (child.alias == arg))) {
|
|
||||||
return [child];
|
|
||||||
}
|
|
||||||
matches.add(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return matches;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the set of commands could be triggered by a list of
|
|
||||||
// arguments.
|
|
||||||
List<Command> _match(List<String> args, bool preferExact) {
|
|
||||||
if (args.isEmpty) {
|
|
||||||
return <Command>[];
|
|
||||||
}
|
|
||||||
bool lastArg = (args.length == 1);
|
|
||||||
var matches = _matchLocal(args[0], !lastArg || preferExact);
|
|
||||||
if (matches.isEmpty) {
|
|
||||||
return <Command>[];
|
|
||||||
} else if (matches.length == 1) {
|
|
||||||
var childMatches = matches[0]._match(args.sublist(1), preferExact);
|
|
||||||
if (childMatches.isEmpty) {
|
|
||||||
return matches;
|
|
||||||
} else {
|
|
||||||
return childMatches;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return matches;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Builds a list of completions for this command.
|
|
||||||
Future<List<String>> _buildCompletions(
|
|
||||||
List<String> args, bool addEmptyString) {
|
|
||||||
return complete(args.sublist(_depth, args.length)).then((completions) {
|
|
||||||
if (addEmptyString &&
|
|
||||||
completions.isEmpty &&
|
|
||||||
args[args.length - 1] == '') {
|
|
||||||
// Special case allowance for an empty particle at the end of
|
|
||||||
// the command.
|
|
||||||
completions = <String>[''];
|
|
||||||
}
|
|
||||||
var prefix = _concatArgs(args, _depth);
|
|
||||||
return completions.map((str) => '${prefix}${str}').toList();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The root of a tree of commands.
|
|
||||||
class RootCommand extends _CommandBase {
|
|
||||||
RootCommand(List<Command> children, [List<String> history])
|
|
||||||
: this._(children, history ?? ['']);
|
|
||||||
|
|
||||||
RootCommand._(List<Command> children, List<String> history)
|
|
||||||
: history = history,
|
|
||||||
historyPos = history.length - 1,
|
|
||||||
super(children);
|
|
||||||
|
|
||||||
// Provides a list of possible completions for a line of text.
|
|
||||||
Future<List<String>> completeCommand(String line) {
|
|
||||||
var args = _splitLine(line);
|
|
||||||
bool showAll = line.endsWith(' ') || args.isEmpty;
|
|
||||||
if (showAll) {
|
|
||||||
// Adding an empty string to the end causes us to match all
|
|
||||||
// subcommands of the last command.
|
|
||||||
args.add('');
|
|
||||||
}
|
|
||||||
var commands = _match(args, false);
|
|
||||||
if (commands.isEmpty) {
|
|
||||||
// No matching commands.
|
|
||||||
return new Future.value(<String>[]);
|
|
||||||
}
|
|
||||||
int matchLen = commands[0]._depth;
|
|
||||||
if (matchLen < args.length) {
|
|
||||||
// We were able to find a command which matches a prefix of the
|
|
||||||
// args, but not the full list.
|
|
||||||
if (commands.length == 1) {
|
|
||||||
// The matching command is unique. Attempt to provide local
|
|
||||||
// argument completion from the command.
|
|
||||||
return commands[0]._buildCompletions(args, true);
|
|
||||||
} else {
|
|
||||||
// An ambiguous prefix match leaves us nowhere. The user is
|
|
||||||
// typing a bunch of stuff that we don't know how to complete.
|
|
||||||
return new Future.value(<String>[]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have found a set of commands which match all of the args.
|
|
||||||
// Return the completion strings.
|
|
||||||
var prefix = _concatArgs(args, args.length - 1);
|
|
||||||
var completions =
|
|
||||||
commands.map((command) => '${prefix}${command.name} ').toList();
|
|
||||||
if (matchLen == args.length) {
|
|
||||||
// If we are showing all possibilities, also include local
|
|
||||||
// completions for the parent command.
|
|
||||||
return commands[0]
|
|
||||||
._parent
|
|
||||||
._buildCompletions(args, false)
|
|
||||||
.then((localCompletions) {
|
|
||||||
completions.addAll(localCompletions);
|
|
||||||
return completions;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return new Future.value(completions);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Runs a command.
|
|
||||||
Future runCommand(String line) {
|
|
||||||
_historyAdvance(line);
|
|
||||||
var args = _splitLine(line);
|
|
||||||
var commands = _match(args, true);
|
|
||||||
if (commands.isEmpty) {
|
|
||||||
return new Future.error(new NoSuchCommandException(line));
|
|
||||||
} else if (commands.length == 1) {
|
|
||||||
return commands[0].run(args.sublist(commands[0]._depth));
|
|
||||||
} else {
|
|
||||||
return new Future.error(new AmbiguousCommandException(line, commands));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find all matching commands. Useful for implementing help systems.
|
|
||||||
List<Command> matchCommand(List<String> args, bool preferExact) {
|
|
||||||
if (args.isEmpty) {
|
|
||||||
// Adding an empty string to the end causes us to match all
|
|
||||||
// subcommands of the last command.
|
|
||||||
args.add('');
|
|
||||||
}
|
|
||||||
return _match(args, preferExact);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command line history always contains one slot to hold the current
|
|
||||||
// line, so we start off with one entry.
|
|
||||||
List<String> history;
|
|
||||||
int historyPos;
|
|
||||||
|
|
||||||
String historyPrev(String line) {
|
|
||||||
if (historyPos == 0) {
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
history[historyPos] = line;
|
|
||||||
historyPos--;
|
|
||||||
return history[historyPos];
|
|
||||||
}
|
|
||||||
|
|
||||||
String historyNext(String line) {
|
|
||||||
if (historyPos == history.length - 1) {
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
history[historyPos] = line;
|
|
||||||
historyPos++;
|
|
||||||
return history[historyPos];
|
|
||||||
}
|
|
||||||
|
|
||||||
void _historyAdvance(String line) {
|
|
||||||
// Replace the last history line.
|
|
||||||
historyPos = history.length - 1;
|
|
||||||
history[historyPos] = line;
|
|
||||||
|
|
||||||
// Create an empty spot for the next line.
|
|
||||||
history.add('');
|
|
||||||
historyPos++;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future run(List<String> args) {
|
|
||||||
throw 'should-not-execute-the-root-command';
|
|
||||||
}
|
|
||||||
|
|
||||||
toString() => 'RootCommand';
|
|
||||||
}
|
|
||||||
|
|
||||||
// A node in the command tree.
|
|
||||||
abstract class Command extends _CommandBase {
|
|
||||||
Command(this.name, List<Command> children) : super(children);
|
|
||||||
|
|
||||||
final String name;
|
|
||||||
String alias;
|
|
||||||
|
|
||||||
String get fullName {
|
|
||||||
if (_parent is RootCommand) {
|
|
||||||
return name;
|
|
||||||
} else {
|
|
||||||
Command parent = _parent;
|
|
||||||
return '${parent.fullName} $name';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toString() => 'Command(${name})';
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class CommandException implements Exception {}
|
|
||||||
|
|
||||||
class AmbiguousCommandException extends CommandException {
|
|
||||||
AmbiguousCommandException(this.command, this.matches);
|
|
||||||
|
|
||||||
final String command;
|
|
||||||
final List<Command> matches;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
List<String> matchNames =
|
|
||||||
matches.map((Command command) => '${command.fullName}').toList();
|
|
||||||
return "Command '$command' is ambiguous: $matchNames";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NoSuchCommandException extends CommandException {
|
|
||||||
NoSuchCommandException(this.command);
|
|
||||||
|
|
||||||
final String command;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() => "No such command: '$command'";
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
part of debugger;
|
|
||||||
|
|
||||||
// TODO(turnidge): Move more of ObservatoryDebugger to this class.
|
|
||||||
abstract class Debugger {
|
|
||||||
VM get vm;
|
|
||||||
Isolate get isolate;
|
|
||||||
M.ObjectRepository objects;
|
|
||||||
ServiceMap get stack;
|
|
||||||
int get currentFrame;
|
|
||||||
}
|
|
|
@ -1,462 +0,0 @@
|
||||||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
part of debugger;
|
|
||||||
|
|
||||||
class DebuggerLocation {
|
|
||||||
DebuggerLocation.file(this.script, this.line, this.col);
|
|
||||||
DebuggerLocation.func(this.function);
|
|
||||||
DebuggerLocation.error(this.errorMessage);
|
|
||||||
|
|
||||||
static RegExp sourceLocMatcher = new RegExp(r'^([^\d:][^:]+:)?(\d+)(:\d+)?');
|
|
||||||
static RegExp packageLocMatcher =
|
|
||||||
new RegExp(r'^package:([^\d:][^:]+:)?(\d+)(:\d+)?');
|
|
||||||
static RegExp functionMatcher = new RegExp(r'^([^.]+)([.][^.]+)?');
|
|
||||||
|
|
||||||
/// Parses a source location description.
|
|
||||||
///
|
|
||||||
/// Formats:
|
|
||||||
/// '' - current position
|
|
||||||
/// 13 - line 13, current script
|
|
||||||
/// 13:20 - line 13, col 20, current script
|
|
||||||
/// script.dart:13 - line 13, script.dart
|
|
||||||
/// script.dart:13:20 - line 13, col 20, script.dart
|
|
||||||
/// package:a/b.dart:13 - line 13, "b.dart" in package "a".
|
|
||||||
/// package:a/b.dart:13:20 - line 13, col 20, "b.dart" in package "a".
|
|
||||||
/// main - function
|
|
||||||
/// FormatException - constructor
|
|
||||||
/// _SHA1._updateHash - method
|
|
||||||
static Future<DebuggerLocation> parse(Debugger debugger, String locDesc) {
|
|
||||||
if (locDesc == '') {
|
|
||||||
// Special case: '' means return current location.
|
|
||||||
return _currentLocation(debugger);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the location description.
|
|
||||||
var match = sourceLocMatcher.firstMatch(locDesc);
|
|
||||||
if (match != null) {
|
|
||||||
return _parseScriptLine(debugger, match);
|
|
||||||
}
|
|
||||||
match = packageLocMatcher.firstMatch(locDesc);
|
|
||||||
if (match != null) {
|
|
||||||
return _parseScriptLine(debugger, match, package: true);
|
|
||||||
}
|
|
||||||
match = functionMatcher.firstMatch(locDesc);
|
|
||||||
if (match != null) {
|
|
||||||
return _parseFunction(debugger, match);
|
|
||||||
}
|
|
||||||
return new Future.value(
|
|
||||||
new DebuggerLocation.error("Invalid source location '${locDesc}'"));
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<Frame> _currentFrame(Debugger debugger) async {
|
|
||||||
ServiceMap stack = debugger.stack;
|
|
||||||
if (stack == null || stack['frames'].length == 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return stack['frames'][debugger.currentFrame];
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<DebuggerLocation> _currentLocation(Debugger debugger) async {
|
|
||||||
var frame = await _currentFrame(debugger);
|
|
||||||
if (frame == null) {
|
|
||||||
return new DebuggerLocation.error(
|
|
||||||
'A script must be provided when the stack is empty');
|
|
||||||
}
|
|
||||||
Script script = frame.location.script;
|
|
||||||
await script.load();
|
|
||||||
var line = script.tokenToLine(frame.location.tokenPos);
|
|
||||||
var col = script.tokenToCol(frame.location.tokenPos);
|
|
||||||
return new DebuggerLocation.file(script, line, col);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<DebuggerLocation> _parseScriptLine(
|
|
||||||
Debugger debugger, Match match,
|
|
||||||
{bool package = false}) async {
|
|
||||||
var scriptName = match.group(1);
|
|
||||||
if (package) {
|
|
||||||
scriptName = "package:$scriptName";
|
|
||||||
}
|
|
||||||
if (scriptName != null) {
|
|
||||||
scriptName = scriptName.substring(0, scriptName.length - 1);
|
|
||||||
}
|
|
||||||
var lineStr = match.group(2);
|
|
||||||
assert(lineStr != null);
|
|
||||||
var colStr = match.group(3);
|
|
||||||
if (colStr != null) {
|
|
||||||
colStr = colStr.substring(1);
|
|
||||||
}
|
|
||||||
var line = int.tryParse(lineStr) ?? -1;
|
|
||||||
var col = (colStr != null ? int.tryParse(colStr) ?? -1 : null);
|
|
||||||
if (line == -1) {
|
|
||||||
return new Future.value(
|
|
||||||
new DebuggerLocation.error("Line '${lineStr}' must be an integer"));
|
|
||||||
}
|
|
||||||
if (col == -1) {
|
|
||||||
return new Future.value(
|
|
||||||
new DebuggerLocation.error("Column '${colStr}' must be an integer"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scriptName != null) {
|
|
||||||
// Resolve the script.
|
|
||||||
Set<Script> scripts = await _lookupScript(debugger.isolate, scriptName);
|
|
||||||
if (scripts.length == 0) {
|
|
||||||
scripts =
|
|
||||||
await _lookupScript(debugger.isolate, scriptName, useUri: true);
|
|
||||||
}
|
|
||||||
if (scripts.length == 0) {
|
|
||||||
return new DebuggerLocation.error("Script '${scriptName}' not found");
|
|
||||||
} else if (scripts.length == 1) {
|
|
||||||
return new DebuggerLocation.file(scripts.single, line, col);
|
|
||||||
} else {
|
|
||||||
// TODO(turnidge): Allow the user to disambiguate.
|
|
||||||
return new DebuggerLocation.error(
|
|
||||||
"Script '${scriptName}' is ambiguous");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No script provided. Default to top of stack for now.
|
|
||||||
var frame = await _currentFrame(debugger);
|
|
||||||
if (frame == null) {
|
|
||||||
return new Future.value(new DebuggerLocation.error(
|
|
||||||
'A script must be provided when the stack is empty'));
|
|
||||||
}
|
|
||||||
Script script = frame.location.script;
|
|
||||||
await script.load();
|
|
||||||
return new DebuggerLocation.file(script, line, col);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<Set<Script>> _lookupScript(Isolate isolate, String name,
|
|
||||||
{bool allowPrefix = false, bool useUri = false}) {
|
|
||||||
var pending = <Future>[];
|
|
||||||
for (var lib in isolate.libraries) {
|
|
||||||
if (!lib.loaded) {
|
|
||||||
pending.add(lib.load());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Future.wait(pending).then((_) {
|
|
||||||
var matches = <Script>{};
|
|
||||||
for (var lib in isolate.libraries) {
|
|
||||||
for (var script in lib.scripts) {
|
|
||||||
final String haystack = useUri ? script.uri : script.name;
|
|
||||||
if (allowPrefix) {
|
|
||||||
if (haystack.startsWith(name)) {
|
|
||||||
matches.add(script);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (name == haystack) {
|
|
||||||
matches.add(script);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return matches;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<ServiceFunction> _lookupFunction(Isolate isolate, String name,
|
|
||||||
{bool allowPrefix = false}) {
|
|
||||||
var matches = <ServiceFunction>[];
|
|
||||||
for (var lib in isolate.libraries) {
|
|
||||||
assert(lib.loaded);
|
|
||||||
for (var function in lib.functions) {
|
|
||||||
if (allowPrefix) {
|
|
||||||
if (function.name.startsWith(name)) {
|
|
||||||
matches.add(function);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (name == function.name) {
|
|
||||||
matches.add(function);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return matches;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<List<Class>> _lookupClass(Isolate isolate, String name,
|
|
||||||
{bool allowPrefix = false}) async {
|
|
||||||
if (isolate == null) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
var pending = <Future>[];
|
|
||||||
for (var lib in isolate.libraries) {
|
|
||||||
assert(lib.loaded);
|
|
||||||
for (var cls in lib.classes) {
|
|
||||||
if (!cls.loaded) {
|
|
||||||
pending.add(cls.load());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await Future.wait(pending);
|
|
||||||
var matches = <Class>[];
|
|
||||||
for (var lib in isolate.libraries) {
|
|
||||||
for (var cls in lib.classes) {
|
|
||||||
if (allowPrefix) {
|
|
||||||
if (cls.name.startsWith(name)) {
|
|
||||||
matches.add(cls);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (name == cls.name) {
|
|
||||||
matches.add(cls);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return matches;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ServiceFunction _getConstructor(Class cls, String name) {
|
|
||||||
for (var function in cls.functions) {
|
|
||||||
assert(cls.loaded);
|
|
||||||
if (name == function.name) {
|
|
||||||
return function;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(turnidge): This does not handle named functions which are
|
|
||||||
// inside of named functions, e.g. foo.bar.baz.
|
|
||||||
static Future<DebuggerLocation> _parseFunction(
|
|
||||||
Debugger debugger, Match match) {
|
|
||||||
Isolate isolate = debugger.isolate;
|
|
||||||
var base = match.group(1);
|
|
||||||
var qualifier = match.group(2);
|
|
||||||
assert(base != null);
|
|
||||||
|
|
||||||
return _lookupClass(isolate, base).then((classes) {
|
|
||||||
var functions = [];
|
|
||||||
if (qualifier == null) {
|
|
||||||
// Unqualified name is either a function or a constructor.
|
|
||||||
functions.addAll(_lookupFunction(isolate, base));
|
|
||||||
|
|
||||||
for (var cls in classes) {
|
|
||||||
// Look for a self-named constructor.
|
|
||||||
var constructor = _getConstructor(cls, cls.name);
|
|
||||||
if (constructor != null) {
|
|
||||||
functions.add(constructor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Qualified name.
|
|
||||||
var functionName = qualifier.substring(1);
|
|
||||||
for (var cls in classes) {
|
|
||||||
assert(cls.loaded);
|
|
||||||
for (var function in cls.functions) {
|
|
||||||
if (function.kind == M.FunctionKind.constructor) {
|
|
||||||
// Constructor names are class-qualified.
|
|
||||||
if (match.group(0) == function.name) {
|
|
||||||
functions.add(function);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (functionName == function.name) {
|
|
||||||
functions.add(function);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (functions.length == 0) {
|
|
||||||
return new DebuggerLocation.error(
|
|
||||||
"Function '${match.group(0)}' not found");
|
|
||||||
} else if (functions.length == 1) {
|
|
||||||
return new DebuggerLocation.func(functions[0]);
|
|
||||||
} else {
|
|
||||||
// TODO(turnidge): Allow the user to disambiguate.
|
|
||||||
return new DebuggerLocation.error(
|
|
||||||
"Function '${match.group(0)}' is ambiguous");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static RegExp partialSourceLocMatcher =
|
|
||||||
new RegExp(r'^([^\d:]?[^:]+[:]?)?(\d+)?([:]\d*)?');
|
|
||||||
static RegExp partialFunctionMatcher = new RegExp(r'^([^.]*)([.][^.]*)?');
|
|
||||||
|
|
||||||
/// Completes a partial source location description.
|
|
||||||
static Future<List<String>> complete(Debugger debugger, String locDesc) {
|
|
||||||
var pending = <Future<List<String>>>[];
|
|
||||||
var match = partialFunctionMatcher.firstMatch(locDesc);
|
|
||||||
if (match != null) {
|
|
||||||
pending.add(_completeFunction(debugger, match));
|
|
||||||
}
|
|
||||||
|
|
||||||
match = partialSourceLocMatcher.firstMatch(locDesc);
|
|
||||||
if (match != null) {
|
|
||||||
pending.add(_completeFile(debugger, match));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Future.wait(pending).then((List<List<String>> responses) {
|
|
||||||
var completions = <String>[];
|
|
||||||
for (var response in responses) {
|
|
||||||
completions.addAll(response);
|
|
||||||
}
|
|
||||||
return completions;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<List<String>> _completeFunction(
|
|
||||||
Debugger debugger, Match match) {
|
|
||||||
Isolate isolate = debugger.isolate;
|
|
||||||
var base = match.group(1);
|
|
||||||
var qualifier = match.group(2);
|
|
||||||
base = (base == null ? '' : base);
|
|
||||||
|
|
||||||
if (qualifier == null) {
|
|
||||||
return _lookupClass(isolate, base, allowPrefix: true).then((classes) {
|
|
||||||
var completions = <String>[];
|
|
||||||
|
|
||||||
// Complete top-level function names.
|
|
||||||
var functions = _lookupFunction(isolate, base, allowPrefix: true);
|
|
||||||
var funcNames = functions.map((f) => f.name).toList();
|
|
||||||
funcNames.sort();
|
|
||||||
completions.addAll(funcNames);
|
|
||||||
|
|
||||||
// Complete class names.
|
|
||||||
var classNames = classes.map((f) => f.name).toList();
|
|
||||||
classNames.sort();
|
|
||||||
completions.addAll(classNames);
|
|
||||||
|
|
||||||
return completions;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return _lookupClass(isolate, base, allowPrefix: false).then((classes) {
|
|
||||||
var completions = <String>[];
|
|
||||||
for (var cls in classes) {
|
|
||||||
for (var function in cls.functions) {
|
|
||||||
if (function.kind == M.FunctionKind.constructor) {
|
|
||||||
if (function.name.startsWith(match.group(0))) {
|
|
||||||
completions.add(function.name);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (function.qualifiedName.startsWith(match.group(0))) {
|
|
||||||
completions.add(function.qualifiedName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
completions.sort();
|
|
||||||
return completions;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _startsWithDigit(String s) {
|
|
||||||
return '0'.compareTo(s[0]) <= 0 && '9'.compareTo(s[0]) >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<List<String>> _completeFile(
|
|
||||||
Debugger debugger, Match match) async {
|
|
||||||
var scriptName;
|
|
||||||
var scriptNameComplete = false;
|
|
||||||
var lineStr;
|
|
||||||
var lineStrComplete = false;
|
|
||||||
var colStr;
|
|
||||||
if (_startsWithDigit(match.group(1))) {
|
|
||||||
// CASE 1: We have matched a prefix of (lineStr:)(colStr)
|
|
||||||
var frame = await _currentFrame(debugger);
|
|
||||||
if (frame == null) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
scriptName = frame.location.script.name;
|
|
||||||
scriptNameComplete = true;
|
|
||||||
lineStr = match.group(1);
|
|
||||||
lineStr = (lineStr == null ? '' : lineStr);
|
|
||||||
if (lineStr.endsWith(':')) {
|
|
||||||
lineStr = lineStr.substring(0, lineStr.length - 1);
|
|
||||||
lineStrComplete = true;
|
|
||||||
}
|
|
||||||
colStr = match.group(2);
|
|
||||||
colStr = (colStr == null ? '' : colStr);
|
|
||||||
} else {
|
|
||||||
// CASE 2: We have matched a prefix of (scriptName:)(lineStr)(:colStr)
|
|
||||||
scriptName = match.group(1);
|
|
||||||
scriptName = (scriptName == null ? '' : scriptName);
|
|
||||||
if (scriptName.endsWith(':')) {
|
|
||||||
scriptName = scriptName.substring(0, scriptName.length - 1);
|
|
||||||
scriptNameComplete = true;
|
|
||||||
}
|
|
||||||
lineStr = match.group(2);
|
|
||||||
lineStr = (lineStr == null ? '' : lineStr);
|
|
||||||
colStr = match.group(3);
|
|
||||||
colStr = (colStr == null ? '' : colStr);
|
|
||||||
if (colStr.startsWith(':')) {
|
|
||||||
lineStrComplete = true;
|
|
||||||
colStr = colStr.substring(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!scriptNameComplete) {
|
|
||||||
// The script name is incomplete. Complete it.
|
|
||||||
var scripts =
|
|
||||||
await _lookupScript(debugger.isolate, scriptName, allowPrefix: true);
|
|
||||||
var completions = <String>[];
|
|
||||||
for (var script in scripts) {
|
|
||||||
completions.add(script.name + ':');
|
|
||||||
}
|
|
||||||
completions.sort();
|
|
||||||
return completions;
|
|
||||||
} else {
|
|
||||||
// The script name is complete. Look it up.
|
|
||||||
var scripts =
|
|
||||||
await _lookupScript(debugger.isolate, scriptName, allowPrefix: false);
|
|
||||||
if (scripts.isEmpty) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
var script = scripts.first;
|
|
||||||
await script.load();
|
|
||||||
if (!lineStrComplete) {
|
|
||||||
// Complete the line.
|
|
||||||
var sharedPrefix = '${script.name}:';
|
|
||||||
var completions = <String>[];
|
|
||||||
var report = await script.isolate
|
|
||||||
.getSourceReport([Isolate.kPossibleBreakpointsReport], script);
|
|
||||||
Set<int> possibleBpts = getPossibleBreakpointLines(report, script);
|
|
||||||
for (var line in possibleBpts) {
|
|
||||||
var currentLineStr = line.toString();
|
|
||||||
if (currentLineStr.startsWith(lineStr)) {
|
|
||||||
completions.add('${sharedPrefix}${currentLineStr} ');
|
|
||||||
completions.add('${sharedPrefix}${currentLineStr}:');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return completions;
|
|
||||||
} else {
|
|
||||||
// Complete the column.
|
|
||||||
int lineNum = int.parse(lineStr);
|
|
||||||
var scriptLine = script.getLine(lineNum);
|
|
||||||
var sharedPrefix = '${script.name}:${lineStr}:';
|
|
||||||
var completions = <String>[];
|
|
||||||
int maxCol = scriptLine.text.trimRight().runes.length;
|
|
||||||
for (int i = 1; i <= maxCol; i++) {
|
|
||||||
var currentColStr = i.toString();
|
|
||||||
if (currentColStr.startsWith(colStr)) {
|
|
||||||
completions.add('${sharedPrefix}${currentColStr} ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return completions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String toString() {
|
|
||||||
if (valid) {
|
|
||||||
if (function != null) {
|
|
||||||
return '${function.qualifiedName}';
|
|
||||||
} else if (col != null) {
|
|
||||||
return '${script.name}:${line}:${col}';
|
|
||||||
} else {
|
|
||||||
return '${script.name}:${line}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 'invalid source location (${errorMessage})';
|
|
||||||
}
|
|
||||||
|
|
||||||
Script script;
|
|
||||||
int line;
|
|
||||||
int col;
|
|
||||||
ServiceFunction function;
|
|
||||||
String errorMessage;
|
|
||||||
bool get valid => (errorMessage == null);
|
|
||||||
}
|
|
|
@ -1,573 +0,0 @@
|
||||||
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/class_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/containers/virtual_collection.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/refresh.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/vm_menu.dart';
|
|
||||||
import 'package:observatory_2/utils.dart';
|
|
||||||
|
|
||||||
enum _SortingField {
|
|
||||||
newInstances,
|
|
||||||
newInternalSize,
|
|
||||||
newExternalSize,
|
|
||||||
newSize,
|
|
||||||
oldInstances,
|
|
||||||
oldInternalSize,
|
|
||||||
oldExternalSize,
|
|
||||||
oldSize,
|
|
||||||
instances,
|
|
||||||
internalSize,
|
|
||||||
externalSize,
|
|
||||||
size,
|
|
||||||
className,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum _SortingDirection { ascending, descending }
|
|
||||||
|
|
||||||
class AllocationProfileElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<AllocationProfileElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<AllocationProfileElement>> get onRendered =>
|
|
||||||
_r.onRendered;
|
|
||||||
|
|
||||||
M.VM _vm;
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
M.AllocationProfileRepository _repository;
|
|
||||||
M.AllocationProfile _profile;
|
|
||||||
bool _autoRefresh = false;
|
|
||||||
bool _isCompacted = false;
|
|
||||||
StreamSubscription _gcSubscription;
|
|
||||||
_SortingField _sortingField = _SortingField.size;
|
|
||||||
_SortingDirection _sortingDirection = _SortingDirection.descending;
|
|
||||||
|
|
||||||
M.VMRef get vm => _vm;
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.NotificationRepository get notifications => _notifications;
|
|
||||||
|
|
||||||
factory AllocationProfileElement(
|
|
||||||
M.VM vm,
|
|
||||||
M.IsolateRef isolate,
|
|
||||||
M.EventRepository events,
|
|
||||||
M.NotificationRepository notifications,
|
|
||||||
M.AllocationProfileRepository repository,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(vm != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
assert(notifications != null);
|
|
||||||
assert(repository != null);
|
|
||||||
AllocationProfileElement e = new AllocationProfileElement.created();
|
|
||||||
e._r = new RenderingScheduler<AllocationProfileElement>(e, queue: queue);
|
|
||||||
e._vm = vm;
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
e._notifications = notifications;
|
|
||||||
e._repository = repository;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
AllocationProfileElement.created() : super.created('allocation-profile');
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
_refresh();
|
|
||||||
_gcSubscription = _events.onGCEvent.listen((e) {
|
|
||||||
if (_autoRefresh && (e.isolate.id == _isolate.id)) {
|
|
||||||
_refresh();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
_gcSubscription.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
children = <Element>[
|
|
||||||
navBar(<Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
|
|
||||||
new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
|
|
||||||
navMenu('allocation profile'),
|
|
||||||
(new NavRefreshElement(
|
|
||||||
label: 'Download', disabled: _profile == null, queue: _r.queue)
|
|
||||||
..onRefresh.listen((_) => _downloadCSV()))
|
|
||||||
.element,
|
|
||||||
(new NavRefreshElement(label: 'GC', queue: _r.queue)
|
|
||||||
..onRefresh.listen((_) => _refresh(gc: true)))
|
|
||||||
.element,
|
|
||||||
(new NavRefreshElement(queue: _r.queue)
|
|
||||||
..onRefresh.listen((_) => _refresh()))
|
|
||||||
.element,
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['nav-option']
|
|
||||||
..children = <Element>[
|
|
||||||
new CheckboxInputElement()
|
|
||||||
..id = 'allocation-profile-auto-refresh'
|
|
||||||
..checked = _autoRefresh
|
|
||||||
..onChange.listen((_) => _autoRefresh = !_autoRefresh),
|
|
||||||
new LabelElement()
|
|
||||||
..htmlFor = 'allocation-profile-auto-refresh'
|
|
||||||
..text = 'Auto-refresh on GC'
|
|
||||||
],
|
|
||||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
|
||||||
]),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered-big']
|
|
||||||
..children = <Element>[
|
|
||||||
new HeadingElement.h2()..text = 'Allocation Profile',
|
|
||||||
new HRElement()
|
|
||||||
]
|
|
||||||
];
|
|
||||||
if (_profile == null) {
|
|
||||||
children.addAll([
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered-big']
|
|
||||||
..children = <Element>[new HeadingElement.h2()..text = 'Loading...']
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
children.addAll([
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered-big']
|
|
||||||
..children = _isCompacted
|
|
||||||
? []
|
|
||||||
: [
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'last forced GC at',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = _profile.lastServiceGC == null
|
|
||||||
? '---'
|
|
||||||
: '${_profile.lastServiceGC}',
|
|
||||||
],
|
|
||||||
],
|
|
||||||
new HRElement(),
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered-big', 'compactable']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['heap-space', 'left']
|
|
||||||
..children = _isCompacted
|
|
||||||
? [
|
|
||||||
new HeadingElement.h2()
|
|
||||||
..text = 'New Generation '
|
|
||||||
'(${_usedCaption(_profile.newSpace)})',
|
|
||||||
]
|
|
||||||
: [
|
|
||||||
new HeadingElement.h2()..text = 'New Generation',
|
|
||||||
new BRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = _createSpaceMembers(_profile.newSpace),
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['heap-space', 'left']
|
|
||||||
..children = _isCompacted
|
|
||||||
? [
|
|
||||||
new HeadingElement.h2()
|
|
||||||
..text = 'Old Generation '
|
|
||||||
'(${_usedCaption(_profile.oldSpace)})',
|
|
||||||
]
|
|
||||||
: [
|
|
||||||
new HeadingElement.h2()..text = 'Old Generation',
|
|
||||||
new BRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = _createSpaceMembers(_profile.oldSpace),
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['heap-space', 'left']
|
|
||||||
..children = _isCompacted
|
|
||||||
? [
|
|
||||||
new HeadingElement.h2()
|
|
||||||
..text = 'Total '
|
|
||||||
'(${_usedCaption(_profile.totalSpace)})',
|
|
||||||
]
|
|
||||||
: [
|
|
||||||
new HeadingElement.h2()..text = 'Total',
|
|
||||||
new BRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = _createSpaceMembers(_profile.totalSpace),
|
|
||||||
],
|
|
||||||
new ButtonElement()
|
|
||||||
..classes = ['compact']
|
|
||||||
..text = _isCompacted ? 'expand ▼' : 'compact ▲'
|
|
||||||
..onClick.listen((_) {
|
|
||||||
_isCompacted = !_isCompacted;
|
|
||||||
_r.dirty();
|
|
||||||
}),
|
|
||||||
new HRElement()
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = _isCompacted ? ['collection', 'expanded'] : ['collection']
|
|
||||||
..children = <Element>[
|
|
||||||
new VirtualCollectionElement(
|
|
||||||
_createCollectionLine, _updateCollectionLine,
|
|
||||||
createHeader: _createCollectionHeader,
|
|
||||||
search: _search,
|
|
||||||
items: _profile.members.toList()..sort(_createSorter()),
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_createSorter() {
|
|
||||||
var getter;
|
|
||||||
switch (_sortingField) {
|
|
||||||
case _SortingField.newInternalSize:
|
|
||||||
getter = _getNewInternalSize;
|
|
||||||
break;
|
|
||||||
case _SortingField.newExternalSize:
|
|
||||||
getter = _getNewExternalSize;
|
|
||||||
break;
|
|
||||||
case _SortingField.newSize:
|
|
||||||
getter = _getNewSize;
|
|
||||||
break;
|
|
||||||
case _SortingField.newInstances:
|
|
||||||
getter = _getNewInstances;
|
|
||||||
break;
|
|
||||||
case _SortingField.oldInternalSize:
|
|
||||||
getter = _getOldInternalSize;
|
|
||||||
break;
|
|
||||||
case _SortingField.oldExternalSize:
|
|
||||||
getter = _getOldExternalSize;
|
|
||||||
break;
|
|
||||||
case _SortingField.oldSize:
|
|
||||||
getter = _getOldSize;
|
|
||||||
break;
|
|
||||||
case _SortingField.oldInstances:
|
|
||||||
getter = _getOldInstances;
|
|
||||||
break;
|
|
||||||
case _SortingField.internalSize:
|
|
||||||
getter = _getInternalSize;
|
|
||||||
break;
|
|
||||||
case _SortingField.externalSize:
|
|
||||||
getter = _getExternalSize;
|
|
||||||
break;
|
|
||||||
case _SortingField.size:
|
|
||||||
getter = _getSize;
|
|
||||||
break;
|
|
||||||
case _SortingField.instances:
|
|
||||||
getter = _getInstances;
|
|
||||||
break;
|
|
||||||
case _SortingField.className:
|
|
||||||
getter = (M.ClassHeapStats s) => s.clazz.name;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch (_sortingDirection) {
|
|
||||||
case _SortingDirection.ascending:
|
|
||||||
int sort(M.ClassHeapStats a, M.ClassHeapStats b) {
|
|
||||||
return getter(a).compareTo(getter(b));
|
|
||||||
}
|
|
||||||
return sort;
|
|
||||||
case _SortingDirection.descending:
|
|
||||||
int sort(M.ClassHeapStats a, M.ClassHeapStats b) {
|
|
||||||
return getter(b).compareTo(getter(a));
|
|
||||||
}
|
|
||||||
return sort;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static HtmlElement _createCollectionLine() => new DivElement()
|
|
||||||
..classes = ['collection-item']
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['bytes']
|
|
||||||
..text = '0B',
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['bytes']
|
|
||||||
..text = '0B',
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['bytes']
|
|
||||||
..text = '0B',
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['instances']
|
|
||||||
..text = '0',
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['bytes']
|
|
||||||
..text = '0B',
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['bytes']
|
|
||||||
..text = '0B',
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['bytes']
|
|
||||||
..text = '0B',
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['instances']
|
|
||||||
..text = '0',
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['bytes']
|
|
||||||
..text = '0B',
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['bytes']
|
|
||||||
..text = '0B',
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['bytes']
|
|
||||||
..text = '0B',
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['instances']
|
|
||||||
..text = '0',
|
|
||||||
new SpanElement()..classes = ['name']
|
|
||||||
];
|
|
||||||
|
|
||||||
List<HtmlElement> _createCollectionHeader() => [
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['collection-item']
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['group']
|
|
||||||
..text = 'New Generation',
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['group']
|
|
||||||
..text = 'Old Generation',
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['group']
|
|
||||||
..text = 'Total',
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['group']
|
|
||||||
..text = '',
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['collection-item']
|
|
||||||
..children = <Element>[
|
|
||||||
_createHeaderButton(const ['bytes'], 'Internal',
|
|
||||||
_SortingField.newInternalSize, _SortingDirection.descending),
|
|
||||||
_createHeaderButton(const ['bytes'], 'External',
|
|
||||||
_SortingField.newExternalSize, _SortingDirection.descending),
|
|
||||||
_createHeaderButton(const ['bytes'], 'Size', _SortingField.newSize,
|
|
||||||
_SortingDirection.descending),
|
|
||||||
_createHeaderButton(const ['instances'], 'Instances',
|
|
||||||
_SortingField.newInstances, _SortingDirection.descending),
|
|
||||||
_createHeaderButton(const ['bytes'], 'Internal',
|
|
||||||
_SortingField.oldInternalSize, _SortingDirection.descending),
|
|
||||||
_createHeaderButton(const ['bytes'], 'External',
|
|
||||||
_SortingField.oldExternalSize, _SortingDirection.descending),
|
|
||||||
_createHeaderButton(const ['bytes'], 'Size', _SortingField.oldSize,
|
|
||||||
_SortingDirection.descending),
|
|
||||||
_createHeaderButton(const ['instances'], 'Instances',
|
|
||||||
_SortingField.oldInstances, _SortingDirection.descending),
|
|
||||||
_createHeaderButton(const ['bytes'], 'Internal',
|
|
||||||
_SortingField.internalSize, _SortingDirection.descending),
|
|
||||||
_createHeaderButton(const ['bytes'], 'External',
|
|
||||||
_SortingField.externalSize, _SortingDirection.descending),
|
|
||||||
_createHeaderButton(const ['bytes'], 'Size', _SortingField.size,
|
|
||||||
_SortingDirection.descending),
|
|
||||||
_createHeaderButton(const ['instances'], 'Instances',
|
|
||||||
_SortingField.instances, _SortingDirection.descending),
|
|
||||||
_createHeaderButton(const ['name'], 'Class',
|
|
||||||
_SortingField.className, _SortingDirection.ascending)
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
ButtonElement _createHeaderButton(List<String> classes, String text,
|
|
||||||
_SortingField field, _SortingDirection direction) =>
|
|
||||||
new ButtonElement()
|
|
||||||
..classes = classes
|
|
||||||
..text = _sortingField != field
|
|
||||||
? text
|
|
||||||
: _sortingDirection == _SortingDirection.ascending
|
|
||||||
? '$text▼'
|
|
||||||
: '$text▲'
|
|
||||||
..onClick.listen((_) => _setSorting(field, direction));
|
|
||||||
|
|
||||||
void _setSorting(_SortingField field, _SortingDirection defaultDirection) {
|
|
||||||
if (_sortingField == field) {
|
|
||||||
switch (_sortingDirection) {
|
|
||||||
case _SortingDirection.descending:
|
|
||||||
_sortingDirection = _SortingDirection.ascending;
|
|
||||||
break;
|
|
||||||
case _SortingDirection.ascending:
|
|
||||||
_sortingDirection = _SortingDirection.descending;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_sortingDirection = defaultDirection;
|
|
||||||
_sortingField = field;
|
|
||||||
}
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateCollectionLine(Element e, itemDynamic, index) {
|
|
||||||
M.ClassHeapStats item = itemDynamic;
|
|
||||||
e.children[0].text = Utils.formatSize(_getNewInternalSize(item));
|
|
||||||
e.children[1].text = Utils.formatSize(_getNewExternalSize(item));
|
|
||||||
e.children[2].text = Utils.formatSize(_getNewSize(item));
|
|
||||||
e.children[3].text = '${_getNewInstances(item)}';
|
|
||||||
e.children[4].text = Utils.formatSize(_getOldInternalSize(item));
|
|
||||||
e.children[5].text = Utils.formatSize(_getOldExternalSize(item));
|
|
||||||
e.children[6].text = Utils.formatSize(_getOldSize(item));
|
|
||||||
e.children[7].text = '${_getOldInstances(item)}';
|
|
||||||
e.children[8].text = Utils.formatSize(_getInternalSize(item));
|
|
||||||
e.children[9].text = Utils.formatSize(_getExternalSize(item));
|
|
||||||
e.children[10].text = Utils.formatSize(_getSize(item));
|
|
||||||
e.children[11].text = '${_getInstances(item)}';
|
|
||||||
e.children[12] = new ClassRefElement(_isolate, item.clazz, queue: _r.queue)
|
|
||||||
.element
|
|
||||||
..classes = ['name'];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _search(Pattern pattern, itemDynamic) {
|
|
||||||
M.ClassHeapStats item = itemDynamic;
|
|
||||||
return item.clazz.name.contains(pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
static String _usedCaption(M.HeapSpace space) =>
|
|
||||||
'${Utils.formatSize(space.used)}'
|
|
||||||
' of '
|
|
||||||
'${Utils.formatSize(space.capacity)}';
|
|
||||||
|
|
||||||
static List<Element> _createSpaceMembers(M.HeapSpace space) {
|
|
||||||
final used = _usedCaption(space);
|
|
||||||
final ext = '${Utils.formatSize(space.external)}';
|
|
||||||
final collections = '${space.collections}';
|
|
||||||
final avgCollectionTime =
|
|
||||||
'${Utils.formatDurationInMilliseconds(space.avgCollectionTime)} ms';
|
|
||||||
final totalCollectionTime =
|
|
||||||
'${Utils.formatDurationInSeconds(space.totalCollectionTime)} secs';
|
|
||||||
final avgCollectionPeriod =
|
|
||||||
'${Utils.formatDurationInMilliseconds(space.avgCollectionPeriod)} ms';
|
|
||||||
return [
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'used',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = used
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'external',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = ext
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'collections',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = collections
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'average collection time',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = avgCollectionTime
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _refresh({bool gc = false, bool reset = false}) async {
|
|
||||||
_profile = null;
|
|
||||||
_r.dirty();
|
|
||||||
_profile = await _repository.get(_isolate, gc: gc, reset: reset);
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _downloadCSV() {
|
|
||||||
assert(_profile != null);
|
|
||||||
final header = [
|
|
||||||
'"New Internal"',
|
|
||||||
'"New External"',
|
|
||||||
'"New Size"',
|
|
||||||
'"New Instances"',
|
|
||||||
'"Old Internal"',
|
|
||||||
'"Old External"',
|
|
||||||
'"Old Size"',
|
|
||||||
'"Old Instances"',
|
|
||||||
'"Internal"',
|
|
||||||
'"External"',
|
|
||||||
'"Size"',
|
|
||||||
'"Instances"',
|
|
||||||
'Class'
|
|
||||||
].join(',') +
|
|
||||||
'\n';
|
|
||||||
AnchorElement tl = document.createElement('a');
|
|
||||||
tl
|
|
||||||
..attributes['href'] = 'data:text/plain;charset=utf-8,' +
|
|
||||||
Uri.encodeComponent(header +
|
|
||||||
(_profile.members.toList()..sort(_createSorter()))
|
|
||||||
.map(_csvOut)
|
|
||||||
.join('\n'))
|
|
||||||
..attributes['download'] = 'heap-profile.csv'
|
|
||||||
..click();
|
|
||||||
}
|
|
||||||
|
|
||||||
static _csvOut(M.ClassHeapStats s) {
|
|
||||||
return [
|
|
||||||
_getNewInternalSize(s),
|
|
||||||
_getNewExternalSize(s),
|
|
||||||
_getNewSize(s),
|
|
||||||
_getNewInstances(s),
|
|
||||||
_getOldInternalSize(s),
|
|
||||||
_getOldExternalSize(s),
|
|
||||||
_getOldSize(s),
|
|
||||||
_getOldInstances(s),
|
|
||||||
_getInternalSize(s),
|
|
||||||
_getExternalSize(s),
|
|
||||||
_getSize(s),
|
|
||||||
_getInstances(s),
|
|
||||||
s.clazz.name
|
|
||||||
].join(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _getNewInstances(M.ClassHeapStats s) => s.newSpace.instances;
|
|
||||||
static int _getNewInternalSize(M.ClassHeapStats s) => s.newSpace.internalSize;
|
|
||||||
static int _getNewExternalSize(M.ClassHeapStats s) => s.newSpace.externalSize;
|
|
||||||
static int _getNewSize(M.ClassHeapStats s) => s.newSpace.size;
|
|
||||||
static int _getOldInstances(M.ClassHeapStats s) => s.oldSpace.instances;
|
|
||||||
static int _getOldInternalSize(M.ClassHeapStats s) => s.oldSpace.internalSize;
|
|
||||||
static int _getOldExternalSize(M.ClassHeapStats s) => s.oldSpace.externalSize;
|
|
||||||
static int _getOldSize(M.ClassHeapStats s) => s.oldSpace.size;
|
|
||||||
static int _getInstances(M.ClassHeapStats s) =>
|
|
||||||
s.newSpace.instances + s.oldSpace.instances;
|
|
||||||
static int _getInternalSize(M.ClassHeapStats s) =>
|
|
||||||
s.newSpace.internalSize + s.oldSpace.internalSize;
|
|
||||||
static int _getExternalSize(M.ClassHeapStats s) =>
|
|
||||||
s.newSpace.externalSize + s.oldSpace.externalSize;
|
|
||||||
static int _getSize(M.ClassHeapStats s) => s.newSpace.size + s.oldSpace.size;
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/cpu_profile/virtual_tree.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/sample_buffer_control.dart';
|
|
||||||
import 'package:observatory_2/src/elements/stack_trace_tree_config.dart';
|
|
||||||
|
|
||||||
class ClassAllocationProfileElement extends CustomElement
|
|
||||||
implements Renderable {
|
|
||||||
RenderingScheduler<ClassAllocationProfileElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<ClassAllocationProfileElement>> get onRendered =>
|
|
||||||
_r.onRendered;
|
|
||||||
|
|
||||||
M.VM _vm;
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.Class _cls;
|
|
||||||
M.ClassSampleProfileRepository _profiles;
|
|
||||||
Stream<M.SampleProfileLoadingProgressEvent> _progressStream;
|
|
||||||
M.SampleProfileLoadingProgress _progress;
|
|
||||||
M.SampleProfileTag _tag = M.SampleProfileTag.none;
|
|
||||||
ProfileTreeMode _mode = ProfileTreeMode.function;
|
|
||||||
M.ProfileTreeDirection _direction = M.ProfileTreeDirection.exclusive;
|
|
||||||
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.Class get cls => _cls;
|
|
||||||
|
|
||||||
factory ClassAllocationProfileElement(M.VM vm, M.IsolateRef isolate,
|
|
||||||
M.Class cls, M.ClassSampleProfileRepository profiles,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(vm != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(cls != null);
|
|
||||||
assert(profiles != null);
|
|
||||||
ClassAllocationProfileElement e =
|
|
||||||
new ClassAllocationProfileElement.created();
|
|
||||||
e._r =
|
|
||||||
new RenderingScheduler<ClassAllocationProfileElement>(e, queue: queue);
|
|
||||||
e._vm = vm;
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._cls = cls;
|
|
||||||
e._profiles = profiles;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassAllocationProfileElement.created()
|
|
||||||
: super.created('class-allocation-profile');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
_request();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
children = <Element>[];
|
|
||||||
_r.disable(notify: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
if (_progress == null) {
|
|
||||||
children = const [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final content = <HtmlElement>[
|
|
||||||
(new SampleBufferControlElement(_vm, _progress, _progressStream,
|
|
||||||
selectedTag: _tag, queue: _r.queue)
|
|
||||||
..onTagChange.listen((e) {
|
|
||||||
_tag = e.element.selectedTag;
|
|
||||||
_request(forceFetch: true);
|
|
||||||
}))
|
|
||||||
.element
|
|
||||||
];
|
|
||||||
if (_progress.status == M.SampleProfileLoadingStatus.loaded) {
|
|
||||||
CpuProfileVirtualTreeElement tree;
|
|
||||||
content.addAll([
|
|
||||||
new BRElement(),
|
|
||||||
(new StackTraceTreeConfigElement(
|
|
||||||
mode: _mode,
|
|
||||||
direction: _direction,
|
|
||||||
showFilter: false,
|
|
||||||
queue: _r.queue)
|
|
||||||
..onModeChange.listen((e) {
|
|
||||||
_mode = tree.mode = e.element.mode;
|
|
||||||
})
|
|
||||||
..onDirectionChange.listen((e) {
|
|
||||||
_direction = tree.direction = e.element.direction;
|
|
||||||
}))
|
|
||||||
.element,
|
|
||||||
new BRElement(),
|
|
||||||
(tree = new CpuProfileVirtualTreeElement(_isolate, _progress.profile,
|
|
||||||
queue: _r.queue))
|
|
||||||
.element
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
children = content;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _request({bool forceFetch = false}) async {
|
|
||||||
_progress = null;
|
|
||||||
_progressStream =
|
|
||||||
_profiles.get(_isolate, _cls, _tag, forceFetch: forceFetch);
|
|
||||||
_r.dirty();
|
|
||||||
_progress = (await _progressStream.first).progress;
|
|
||||||
_r.dirty();
|
|
||||||
if (M.isSampleProcessRunning(_progress.status)) {
|
|
||||||
_progress = (await _progressStream.last).progress;
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,322 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/any_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/sentinel_value.dart';
|
|
||||||
import 'package:observatory_2/src/elements/strongly_reachable_instances.dart';
|
|
||||||
import 'package:observatory_2/utils.dart';
|
|
||||||
|
|
||||||
class ClassInstancesElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<ClassInstancesElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<ClassInstancesElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.Class _cls;
|
|
||||||
M.RetainedSizeRepository _retainedSizes;
|
|
||||||
M.ReachableSizeRepository _reachableSizes;
|
|
||||||
M.StronglyReachableInstancesRepository _stronglyReachableInstances;
|
|
||||||
M.ObjectRepository _objects;
|
|
||||||
M.Guarded<M.InstanceRef> _allInstances = null;
|
|
||||||
bool _loadingAllInstances = false;
|
|
||||||
M.Guarded<M.InstanceRef> _allSubclassInstances = null;
|
|
||||||
bool _loadingAllSubclassInstances = false;
|
|
||||||
M.Guarded<M.InstanceRef> _allImplementorInstances = null;
|
|
||||||
bool _loadingAllImplementorInstances = false;
|
|
||||||
M.Guarded<M.Instance> _retainedSize = null;
|
|
||||||
bool _loadingRetainedBytes = false;
|
|
||||||
M.Guarded<M.Instance> _reachableSize = null;
|
|
||||||
bool _loadingReachableBytes = false;
|
|
||||||
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.Class get cls => _cls;
|
|
||||||
|
|
||||||
factory ClassInstancesElement(
|
|
||||||
M.IsolateRef isolate,
|
|
||||||
M.Class cls,
|
|
||||||
M.RetainedSizeRepository retainedSizes,
|
|
||||||
M.ReachableSizeRepository reachableSizes,
|
|
||||||
M.StronglyReachableInstancesRepository stronglyReachableInstances,
|
|
||||||
M.ObjectRepository objects,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(cls != null);
|
|
||||||
assert(retainedSizes != null);
|
|
||||||
assert(reachableSizes != null);
|
|
||||||
assert(stronglyReachableInstances != null);
|
|
||||||
assert(objects != null);
|
|
||||||
ClassInstancesElement e = new ClassInstancesElement.created();
|
|
||||||
e._r = new RenderingScheduler<ClassInstancesElement>(e, queue: queue);
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._cls = cls;
|
|
||||||
e._retainedSizes = retainedSizes;
|
|
||||||
e._reachableSizes = reachableSizes;
|
|
||||||
e._stronglyReachableInstances = stronglyReachableInstances;
|
|
||||||
e._objects = objects;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassInstancesElement.created() : super.created('class-instances');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
StronglyReachableInstancesElement _strong;
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
_strong = _strong ??
|
|
||||||
new StronglyReachableInstancesElement(
|
|
||||||
_isolate, _cls, _stronglyReachableInstances, _objects,
|
|
||||||
queue: _r.queue);
|
|
||||||
final instanceCount = _cls.newSpace.instances + _cls.oldSpace.instances;
|
|
||||||
final size = Utils.formatSize(_cls.newSpace.size + _cls.oldSpace.size);
|
|
||||||
children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = const ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = const ['memberName']
|
|
||||||
..text = 'currently allocated',
|
|
||||||
new DivElement()
|
|
||||||
..classes = const ['memberValue']
|
|
||||||
..text = 'count ${instanceCount} (shallow size ${size})'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'strongly reachable ',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = <Element>[_strong.element]
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'all direct instances'
|
|
||||||
..title = 'All instances whose class is exactly this class',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = _createAllInstances()
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'all instances of subclasses'
|
|
||||||
..title =
|
|
||||||
'All instances whose class is a subclass of this class',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = _createAllSubclassInstances()
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'all instances of implementors'
|
|
||||||
..title =
|
|
||||||
'All instances whose class implements the implicit interface of this class',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = _createAllImplementorInstances()
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..title = 'Space reachable from this object, '
|
|
||||||
'excluding class references'
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'Reachable size ',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = _createReachableSizeValue()
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..title = 'Space that would be reclaimed if references to this '
|
|
||||||
'object were replaced with null'
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'Retained size ',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = _createRetainedSizeValue()
|
|
||||||
],
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createAllInstances() {
|
|
||||||
final content = <Element>[];
|
|
||||||
if (_allInstances != null) {
|
|
||||||
if (_allInstances.isSentinel) {
|
|
||||||
content.add(
|
|
||||||
new SentinelValueElement(_allInstances.asSentinel, queue: _r.queue)
|
|
||||||
.element);
|
|
||||||
} else {
|
|
||||||
content.add(anyRef(_isolate, _allInstances.asValue, _objects));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
content.add(new SpanElement()..text = '...');
|
|
||||||
}
|
|
||||||
final button = new ButtonElement()
|
|
||||||
..classes = ['reachable_size']
|
|
||||||
..disabled = _loadingAllInstances
|
|
||||||
..text = '↺';
|
|
||||||
button.onClick.listen((_) async {
|
|
||||||
button.disabled = true;
|
|
||||||
_loadingAllInstances = true;
|
|
||||||
_allInstances =
|
|
||||||
await _stronglyReachableInstances.getAsArray(_isolate, _cls);
|
|
||||||
_loadingAllInstances = false;
|
|
||||||
_r.dirty();
|
|
||||||
});
|
|
||||||
content.add(button);
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createAllSubclassInstances() {
|
|
||||||
final content = <Element>[];
|
|
||||||
if (_allSubclassInstances != null) {
|
|
||||||
if (_allSubclassInstances.isSentinel) {
|
|
||||||
content.add(new SentinelValueElement(_allSubclassInstances.asSentinel,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element);
|
|
||||||
} else {
|
|
||||||
content.add(anyRef(_isolate, _allSubclassInstances.asValue, _objects));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
content.add(new SpanElement()..text = '...');
|
|
||||||
}
|
|
||||||
final button = new ButtonElement()
|
|
||||||
..classes = ['reachable_size']
|
|
||||||
..disabled = _loadingAllSubclassInstances
|
|
||||||
..text = '↺';
|
|
||||||
button.onClick.listen((_) async {
|
|
||||||
button.disabled = true;
|
|
||||||
_loadingAllSubclassInstances = true;
|
|
||||||
_allSubclassInstances = await _stronglyReachableInstances
|
|
||||||
.getAsArray(_isolate, _cls, includeSubclasses: true);
|
|
||||||
_loadingAllSubclassInstances = false;
|
|
||||||
_r.dirty();
|
|
||||||
});
|
|
||||||
content.add(button);
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createAllImplementorInstances() {
|
|
||||||
final content = <Element>[];
|
|
||||||
if (_allImplementorInstances != null) {
|
|
||||||
if (_allImplementorInstances.isSentinel) {
|
|
||||||
content.add(new SentinelValueElement(
|
|
||||||
_allImplementorInstances.asSentinel,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element);
|
|
||||||
} else {
|
|
||||||
content
|
|
||||||
.add(anyRef(_isolate, _allImplementorInstances.asValue, _objects));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
content.add(new SpanElement()..text = '...');
|
|
||||||
}
|
|
||||||
final button = new ButtonElement()
|
|
||||||
..classes = ['reachable_size']
|
|
||||||
..disabled = _loadingAllImplementorInstances
|
|
||||||
..text = '↺';
|
|
||||||
button.onClick.listen((_) async {
|
|
||||||
button.disabled = true;
|
|
||||||
_loadingAllImplementorInstances = true;
|
|
||||||
_allImplementorInstances = await _stronglyReachableInstances
|
|
||||||
.getAsArray(_isolate, _cls, includeImplementors: true);
|
|
||||||
_loadingAllImplementorInstances = false;
|
|
||||||
_r.dirty();
|
|
||||||
});
|
|
||||||
content.add(button);
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createReachableSizeValue() {
|
|
||||||
final content = <Element>[];
|
|
||||||
if (_reachableSize != null) {
|
|
||||||
if (_reachableSize.isSentinel) {
|
|
||||||
content.add(
|
|
||||||
new SentinelValueElement(_reachableSize.asSentinel, queue: _r.queue)
|
|
||||||
.element);
|
|
||||||
} else {
|
|
||||||
content.add(new SpanElement()
|
|
||||||
..text = Utils.formatSize(
|
|
||||||
int.parse(_reachableSize.asValue.valueAsString)));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
content.add(new SpanElement()..text = '...');
|
|
||||||
}
|
|
||||||
final button = new ButtonElement()
|
|
||||||
..classes = ['reachable_size']
|
|
||||||
..disabled = _loadingReachableBytes
|
|
||||||
..text = '↺';
|
|
||||||
button.onClick.listen((_) async {
|
|
||||||
button.disabled = true;
|
|
||||||
_loadingReachableBytes = true;
|
|
||||||
_reachableSize = await _reachableSizes.get(_isolate, _cls.id);
|
|
||||||
_r.dirty();
|
|
||||||
});
|
|
||||||
content.add(button);
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createRetainedSizeValue() {
|
|
||||||
final content = <Element>[];
|
|
||||||
if (_retainedSize != null) {
|
|
||||||
if (_retainedSize.isSentinel) {
|
|
||||||
content.add(
|
|
||||||
new SentinelValueElement(_retainedSize.asSentinel, queue: _r.queue)
|
|
||||||
.element);
|
|
||||||
} else {
|
|
||||||
content.add(new SpanElement()
|
|
||||||
..text =
|
|
||||||
Utils.formatSize(int.parse(_retainedSize.asValue.valueAsString)));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
content.add(new SpanElement()..text = '...');
|
|
||||||
}
|
|
||||||
final button = new ButtonElement()
|
|
||||||
..classes = ['retained_size']
|
|
||||||
..disabled = _loadingRetainedBytes
|
|
||||||
..text = '↺';
|
|
||||||
button.onClick.listen((_) async {
|
|
||||||
button.disabled = true;
|
|
||||||
_loadingRetainedBytes = true;
|
|
||||||
_retainedSize = await _retainedSizes.get(_isolate, _cls.id);
|
|
||||||
_r.dirty();
|
|
||||||
});
|
|
||||||
content.add(button);
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/uris.dart';
|
|
||||||
|
|
||||||
class ClassRefElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<ClassRefElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<ClassRefElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.ClassRef _class;
|
|
||||||
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.ClassRef get cls => _class;
|
|
||||||
|
|
||||||
factory ClassRefElement(M.IsolateRef isolate, M.ClassRef cls,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(cls != null);
|
|
||||||
ClassRefElement e = new ClassRefElement.created();
|
|
||||||
e._r = new RenderingScheduler<ClassRefElement>(e, queue: queue);
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._class = cls;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassRefElement.created() : super.created('class-ref');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
children = <Element>[
|
|
||||||
new AnchorElement(
|
|
||||||
href: (_isolate == null)
|
|
||||||
? null
|
|
||||||
: Uris.inspect(_isolate, object: _class))
|
|
||||||
..text = _class.name
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,188 +0,0 @@
|
||||||
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library class_tree_element;
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/class_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/containers/virtual_tree.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/vm_menu.dart';
|
|
||||||
|
|
||||||
class ClassTreeElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<ClassTreeElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<ClassTreeElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.VMRef _vm;
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
M.ClassRepository _classes;
|
|
||||||
M.Class _object;
|
|
||||||
final _subclasses = <String, Iterable<M.Class>>{};
|
|
||||||
final _mixins = <String, List<M.Instance>>{};
|
|
||||||
|
|
||||||
factory ClassTreeElement(
|
|
||||||
M.VMRef vm,
|
|
||||||
M.IsolateRef isolate,
|
|
||||||
M.EventRepository events,
|
|
||||||
M.NotificationRepository notifications,
|
|
||||||
M.ClassRepository classes,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(vm != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
assert(notifications != null);
|
|
||||||
assert(classes != null);
|
|
||||||
ClassTreeElement e = new ClassTreeElement.created();
|
|
||||||
e._r = new RenderingScheduler<ClassTreeElement>(e, queue: queue);
|
|
||||||
e._vm = vm;
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
e._notifications = notifications;
|
|
||||||
e._classes = classes;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassTreeElement.created() : super.created('class-tree');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_refresh();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
children = <Element>[];
|
|
||||||
_r.disable(notify: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
VirtualTreeElement _tree;
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
children = <Element>[
|
|
||||||
navBar(<Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
|
|
||||||
new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
|
|
||||||
navMenu('class hierarchy'),
|
|
||||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
|
||||||
]),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered']
|
|
||||||
..children = <Element>[
|
|
||||||
new HeadingElement.h1()
|
|
||||||
..text = 'Class Hierarchy (${_subclasses.length})',
|
|
||||||
new BRElement(),
|
|
||||||
new HRElement(),
|
|
||||||
_object == null
|
|
||||||
? (new HeadingElement.h2()..text = 'Loading...')
|
|
||||||
: _createTree()
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
Element _createTree() {
|
|
||||||
_tree = new VirtualTreeElement(_create, _update, _children,
|
|
||||||
items: [_object], search: _search, queue: _r.queue);
|
|
||||||
_tree.expand(_object, autoExpandSingleChildNodes: true);
|
|
||||||
return _tree.element;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _refresh() async {
|
|
||||||
_object = null;
|
|
||||||
_subclasses.clear();
|
|
||||||
_mixins.clear();
|
|
||||||
_object = await _register(await _classes.getObject(_isolate));
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<M.Class> _register(M.Class cls) async {
|
|
||||||
_subclasses[cls.id] = await Future.wait(
|
|
||||||
(await Future.wait(cls.subclasses.map(_getActualChildren)))
|
|
||||||
.expand((f) => f)
|
|
||||||
.map(_register));
|
|
||||||
return cls;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Iterable<M.Class>> _getActualChildren(M.ClassRef ref) async {
|
|
||||||
var cls = await _classes.get(_isolate, ref.id);
|
|
||||||
if (cls.isPatch) {
|
|
||||||
return const [];
|
|
||||||
}
|
|
||||||
if (cls.mixin == null) {
|
|
||||||
return [cls];
|
|
||||||
}
|
|
||||||
return (await Future.wait(cls.subclasses.map(_getActualChildren)))
|
|
||||||
.expand((f) => f)
|
|
||||||
..forEach((subcls) {
|
|
||||||
_mixins[subcls.id] = (_mixins[subcls.id] ?? [])..add(cls.mixin);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static HtmlElement _create(toggle) {
|
|
||||||
return new DivElement()
|
|
||||||
..classes = ['class-tree-item']
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..classes = ['lines'],
|
|
||||||
new ButtonElement()
|
|
||||||
..classes = ['expander']
|
|
||||||
..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
|
|
||||||
new SpanElement()..classes = ['name']
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
void _update(HtmlElement el, classDynamic, int index) {
|
|
||||||
M.Class cls = classDynamic;
|
|
||||||
virtualTreeUpdateLines(el.children[0], index);
|
|
||||||
if (cls.subclasses.isEmpty) {
|
|
||||||
el.children[1].text = '';
|
|
||||||
} else {
|
|
||||||
el.children[1].text = _tree.isExpanded(cls) ? '▼' : '►';
|
|
||||||
}
|
|
||||||
el.children[2].children = <Element>[
|
|
||||||
new ClassRefElement(_isolate, cls, queue: _r.queue).element
|
|
||||||
];
|
|
||||||
if (_mixins[cls.id] != null) {
|
|
||||||
el.children[2].children.addAll(_createMixins(_mixins[cls.id]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _search(Pattern pattern, classDynamic) {
|
|
||||||
M.Class cls = classDynamic;
|
|
||||||
return cls.name.contains(pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createMixins(List<M.Instance> types) {
|
|
||||||
final children = types
|
|
||||||
.expand((type) => <Element>[
|
|
||||||
new SpanElement()..text = ', ',
|
|
||||||
type.typeClass == null
|
|
||||||
? (new SpanElement()..text = type.name.split('<').first)
|
|
||||||
: new ClassRefElement(_isolate, type.typeClass,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
])
|
|
||||||
.toList();
|
|
||||||
children.first.text = ' with ';
|
|
||||||
return children;
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterable<M.Class> _children(classDynamic) {
|
|
||||||
M.Class cls = classDynamic;
|
|
||||||
return _subclasses[cls.id];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,492 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library class_view_element;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/class_allocation_profile.dart';
|
|
||||||
import 'package:observatory_2/src/elements/class_instances.dart';
|
|
||||||
import 'package:observatory_2/src/elements/class_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/curly_block.dart';
|
|
||||||
import 'package:observatory_2/src/elements/error_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/eval_box.dart';
|
|
||||||
import 'package:observatory_2/src/elements/field_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/function_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/any_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/instance_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/library_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/class_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/refresh.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/vm_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/object_common.dart';
|
|
||||||
import 'package:observatory_2/src/elements/source_inset.dart';
|
|
||||||
import 'package:observatory_2/src/elements/source_link.dart';
|
|
||||||
|
|
||||||
class ClassViewElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<ClassViewElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<ClassViewElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.VM _vm;
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
M.Class _cls;
|
|
||||||
M.ClassRepository _classes;
|
|
||||||
M.RetainedSizeRepository _retainedSizes;
|
|
||||||
M.ReachableSizeRepository _reachableSizes;
|
|
||||||
M.InboundReferencesRepository _references;
|
|
||||||
M.RetainingPathRepository _retainingPaths;
|
|
||||||
M.StronglyReachableInstancesRepository _stronglyReachableInstances;
|
|
||||||
M.FieldRepository _fields;
|
|
||||||
M.ScriptRepository _scripts;
|
|
||||||
M.ObjectRepository _objects;
|
|
||||||
M.EvalRepository _eval;
|
|
||||||
M.ClassSampleProfileRepository _profiles;
|
|
||||||
Iterable<M.Field> _classFields;
|
|
||||||
|
|
||||||
M.VMRef get vm => _vm;
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.NotificationRepository get notifications => _notifications;
|
|
||||||
M.Class get cls => _cls;
|
|
||||||
|
|
||||||
factory ClassViewElement(
|
|
||||||
M.VM vm,
|
|
||||||
M.IsolateRef isolate,
|
|
||||||
M.Class cls,
|
|
||||||
M.EventRepository events,
|
|
||||||
M.NotificationRepository notifications,
|
|
||||||
M.ClassRepository classes,
|
|
||||||
M.RetainedSizeRepository retainedSizes,
|
|
||||||
M.ReachableSizeRepository reachableSizes,
|
|
||||||
M.InboundReferencesRepository references,
|
|
||||||
M.RetainingPathRepository retainingPaths,
|
|
||||||
M.FieldRepository fields,
|
|
||||||
M.ScriptRepository scripts,
|
|
||||||
M.ObjectRepository objects,
|
|
||||||
M.EvalRepository eval,
|
|
||||||
M.StronglyReachableInstancesRepository stronglyReachable,
|
|
||||||
M.ClassSampleProfileRepository profiles,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(vm != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
assert(notifications != null);
|
|
||||||
assert(cls != null);
|
|
||||||
assert(classes != null);
|
|
||||||
assert(retainedSizes != null);
|
|
||||||
assert(reachableSizes != null);
|
|
||||||
assert(references != null);
|
|
||||||
assert(retainingPaths != null);
|
|
||||||
assert(fields != null);
|
|
||||||
assert(scripts != null);
|
|
||||||
assert(objects != null);
|
|
||||||
assert(eval != null);
|
|
||||||
assert(stronglyReachable != null);
|
|
||||||
assert(profiles != null);
|
|
||||||
ClassViewElement e = new ClassViewElement.created();
|
|
||||||
e._r = new RenderingScheduler<ClassViewElement>(e, queue: queue);
|
|
||||||
e._vm = vm;
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
e._notifications = notifications;
|
|
||||||
e._cls = cls;
|
|
||||||
e._classes = classes;
|
|
||||||
e._retainedSizes = retainedSizes;
|
|
||||||
e._reachableSizes = reachableSizes;
|
|
||||||
e._references = references;
|
|
||||||
e._retainingPaths = retainingPaths;
|
|
||||||
e._fields = fields;
|
|
||||||
e._scripts = scripts;
|
|
||||||
e._objects = objects;
|
|
||||||
e._eval = eval;
|
|
||||||
e._stronglyReachableInstances = stronglyReachable;
|
|
||||||
e._profiles = profiles;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassViewElement.created() : super.created('class-view');
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
_loadAdditionalData();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectCommonElement _common;
|
|
||||||
ClassInstancesElement _classInstances;
|
|
||||||
bool _loadProfile = false;
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
_common = _common ??
|
|
||||||
new ObjectCommonElement(_isolate, _cls, _retainedSizes, _reachableSizes,
|
|
||||||
_references, _retainingPaths, _objects,
|
|
||||||
queue: _r.queue);
|
|
||||||
_classInstances = _classInstances ??
|
|
||||||
new ClassInstancesElement(_isolate, _cls, _retainedSizes,
|
|
||||||
_reachableSizes, _stronglyReachableInstances, _objects,
|
|
||||||
queue: _r.queue);
|
|
||||||
var header = '';
|
|
||||||
if (_cls.isAbstract) {
|
|
||||||
header += 'abstract ';
|
|
||||||
}
|
|
||||||
if (_cls.isPatch) {
|
|
||||||
header += 'patch ';
|
|
||||||
}
|
|
||||||
children = <Element>[
|
|
||||||
navBar(<Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
|
|
||||||
new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
|
|
||||||
new NavClassMenuElement(_isolate, _cls, queue: _r.queue).element,
|
|
||||||
(new NavRefreshElement(
|
|
||||||
label: 'Refresh Allocation Profile', queue: _r.queue)
|
|
||||||
..onRefresh.listen((e) {
|
|
||||||
e.element.disabled = true;
|
|
||||||
_loadProfile = true;
|
|
||||||
_r.dirty();
|
|
||||||
}))
|
|
||||||
.element,
|
|
||||||
(new NavRefreshElement(queue: _r.queue)
|
|
||||||
..onRefresh.listen((e) {
|
|
||||||
e.element.disabled = true;
|
|
||||||
_common = null;
|
|
||||||
_classInstances = null;
|
|
||||||
_fieldsExpanded = null;
|
|
||||||
_functionsExpanded = null;
|
|
||||||
_refresh();
|
|
||||||
}))
|
|
||||||
.element,
|
|
||||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
|
||||||
]),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered-big']
|
|
||||||
..children = <Element>[
|
|
||||||
new HeadingElement.h2()..text = '$header class ${_cls.name}',
|
|
||||||
new HRElement(),
|
|
||||||
_common.element,
|
|
||||||
new BRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = _createMembers(),
|
|
||||||
new DivElement()
|
|
||||||
..children = _cls.error == null
|
|
||||||
? const []
|
|
||||||
: [
|
|
||||||
new HRElement(),
|
|
||||||
new ErrorRefElement(_cls.error, queue: _r.queue).element
|
|
||||||
],
|
|
||||||
new HRElement(),
|
|
||||||
new EvalBoxElement(_isolate, _cls, _objects, _eval, queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new HRElement(),
|
|
||||||
new HeadingElement.h2()..text = 'Fields & Functions',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = _createElements(),
|
|
||||||
new HRElement(),
|
|
||||||
new HeadingElement.h2()..text = 'Instances',
|
|
||||||
new DivElement()..children = [_classInstances.element],
|
|
||||||
new HRElement(),
|
|
||||||
new HeadingElement.h2()..text = 'Allocations',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'Tracing allocations? ',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = _cls.traceAllocations
|
|
||||||
? [
|
|
||||||
new SpanElement()..text = 'Yes ',
|
|
||||||
new ButtonElement()
|
|
||||||
..text = 'disable'
|
|
||||||
..onClick.listen((e) async {
|
|
||||||
(e.target as ButtonElement).disabled = true;
|
|
||||||
await _profiles.disable(_isolate, _cls);
|
|
||||||
_loadProfile = true;
|
|
||||||
_refresh();
|
|
||||||
})
|
|
||||||
]
|
|
||||||
: [
|
|
||||||
new SpanElement()..text = 'No ',
|
|
||||||
new ButtonElement()
|
|
||||||
..text = 'enable'
|
|
||||||
..onClick.listen((e) async {
|
|
||||||
(e.target as ButtonElement).disabled = true;
|
|
||||||
await _profiles.enable(_isolate, _cls);
|
|
||||||
_refresh();
|
|
||||||
})
|
|
||||||
]
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..children = _loadProfile
|
|
||||||
? [
|
|
||||||
new ClassAllocationProfileElement(
|
|
||||||
_vm, _isolate, _cls, _profiles,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
: const [],
|
|
||||||
new DivElement()
|
|
||||||
..children = _cls.location != null
|
|
||||||
? [
|
|
||||||
new HRElement(),
|
|
||||||
new SourceInsetElement(_isolate, _cls.location, _scripts,
|
|
||||||
_objects, _events,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
: const [],
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _fieldsExpanded;
|
|
||||||
bool _functionsExpanded;
|
|
||||||
|
|
||||||
List<Element> _createMembers() {
|
|
||||||
final members = <Element>[];
|
|
||||||
if (_cls.library != null) {
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'library',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = <Element>[
|
|
||||||
new LibraryRefElement(_isolate, _cls.library, queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (_cls.location != null) {
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'script',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = <Element>[
|
|
||||||
new SourceLinkElement(_isolate, _cls.location, _scripts,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (_cls.superclass != null) {
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'superclass',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = <Element>[
|
|
||||||
new ClassRefElement(_isolate, _cls.superclass, queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (_cls.superType != null) {
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'supertype',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = <Element>[
|
|
||||||
new InstanceRefElement(_isolate, _cls.superType, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (cls.mixin != null) {
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'mixin',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = <Element>[
|
|
||||||
new InstanceRefElement(_isolate, _cls.mixin, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (_cls.subclasses.length > 0) {
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'extended by',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = (_cls.subclasses
|
|
||||||
.expand((subcls) => <Element>[
|
|
||||||
new ClassRefElement(_isolate, subcls, queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new SpanElement()..text = ', '
|
|
||||||
])
|
|
||||||
.toList()
|
|
||||||
..removeLast())
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
members.add(new BRElement());
|
|
||||||
|
|
||||||
if (_cls.interfaces.length > 0) {
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'implements',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = (_cls.interfaces
|
|
||||||
.expand((interf) => <Element>[
|
|
||||||
new InstanceRefElement(_isolate, interf, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new SpanElement()..text = ', '
|
|
||||||
])
|
|
||||||
.toList()
|
|
||||||
..removeLast())
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (_cls.name != _cls.vmName) {
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'vm name',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = '${_cls.vmName}'
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
return members;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createElements() {
|
|
||||||
final members = <Element>[];
|
|
||||||
if (_classFields != null && _classFields.isNotEmpty) {
|
|
||||||
final fields = _classFields.toList();
|
|
||||||
_fieldsExpanded = _fieldsExpanded ?? (fields.length <= 8);
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'fields ${fields.length}',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = <Element>[
|
|
||||||
(new CurlyBlockElement(expanded: _fieldsExpanded)
|
|
||||||
..onToggle
|
|
||||||
.listen((e) => _fieldsExpanded = e.control.expanded)
|
|
||||||
..content = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = (fields
|
|
||||||
.map<Element>((f) => new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
new FieldRefElement(_isolate, f, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = f.staticValue == null
|
|
||||||
? const []
|
|
||||||
: [
|
|
||||||
anyRef(
|
|
||||||
_isolate, f.staticValue, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
]
|
|
||||||
])
|
|
||||||
.toList())
|
|
||||||
])
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_cls.functions.isNotEmpty) {
|
|
||||||
final functions = _cls.functions.toList();
|
|
||||||
_functionsExpanded = _functionsExpanded ?? (functions.length <= 8);
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'functions (${functions.length})',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = <Element>[
|
|
||||||
(new CurlyBlockElement(expanded: _functionsExpanded)
|
|
||||||
..onToggle
|
|
||||||
.listen((e) => _functionsExpanded = e.control.expanded)
|
|
||||||
..content = (functions
|
|
||||||
.map<Element>((f) => new DivElement()
|
|
||||||
..classes = ['indent']
|
|
||||||
..children = <Element>[
|
|
||||||
new FunctionRefElement(_isolate, f, queue: _r.queue)
|
|
||||||
.element
|
|
||||||
])
|
|
||||||
.toList()))
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
return members;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _refresh() async {
|
|
||||||
_cls = await _classes.get(_isolate, _cls.id);
|
|
||||||
await _loadAdditionalData();
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _loadAdditionalData() async {
|
|
||||||
_classFields =
|
|
||||||
await Future.wait(_cls.fields.map((f) => _fields.get(_isolate, f.id)));
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library code_ref_element;
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M
|
|
||||||
show IsolateRef, CodeRef, isSyntheticCode;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/uris.dart';
|
|
||||||
|
|
||||||
class CodeRefElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<CodeRefElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<CodeRefElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.CodeRef _code;
|
|
||||||
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.CodeRef get code => _code;
|
|
||||||
|
|
||||||
factory CodeRefElement(M.IsolateRef isolate, M.CodeRef code,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(code != null);
|
|
||||||
CodeRefElement e = new CodeRefElement.created();
|
|
||||||
e._r = new RenderingScheduler<CodeRefElement>(e, queue: queue);
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._code = code;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
CodeRefElement.created() : super.created('code-ref');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
children = <Element>[];
|
|
||||||
_r.disable(notify: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
children = <Element>[
|
|
||||||
new AnchorElement(
|
|
||||||
href: ((M.isSyntheticCode(_code.kind)) || (_isolate == null))
|
|
||||||
? null
|
|
||||||
: Uris.inspect(_isolate, object: _code))
|
|
||||||
..text = _code.name
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,641 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library code_view_element;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'package:observatory_2/sample_profile.dart';
|
|
||||||
import 'package:observatory_2/service.dart' as S;
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/app.dart'
|
|
||||||
show SortedTable, SortedTableColumn, SortedTableRow;
|
|
||||||
import 'package:observatory_2/src/elements/curly_block.dart';
|
|
||||||
import 'package:observatory_2/src/elements/function_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/any_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/refresh.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/vm_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/object_common.dart';
|
|
||||||
import 'package:observatory_2/src/elements/objectpool_ref.dart';
|
|
||||||
import 'package:observatory_2/utils.dart';
|
|
||||||
|
|
||||||
class DisassemblyTable extends SortedTable {
|
|
||||||
DisassemblyTable(columns) : super(columns);
|
|
||||||
}
|
|
||||||
|
|
||||||
class InlineTable extends SortedTable {
|
|
||||||
InlineTable(columns) : super(columns);
|
|
||||||
}
|
|
||||||
|
|
||||||
class CodeViewElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<CodeViewElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<CodeViewElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.VM _vm;
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
M.Code _code;
|
|
||||||
M.RetainedSizeRepository _retainedSizes;
|
|
||||||
M.ReachableSizeRepository _reachableSizes;
|
|
||||||
M.InboundReferencesRepository _references;
|
|
||||||
M.RetainingPathRepository _retainingPaths;
|
|
||||||
M.ObjectRepository _objects;
|
|
||||||
DisassemblyTable disassemblyTable;
|
|
||||||
InlineTable inlineTable;
|
|
||||||
|
|
||||||
static const kDisassemblyColumnIndex = 3;
|
|
||||||
|
|
||||||
M.VMRef get vm => _vm;
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.NotificationRepository get notifications => _notifications;
|
|
||||||
M.Code get context => _code;
|
|
||||||
|
|
||||||
factory CodeViewElement(
|
|
||||||
M.VM vm,
|
|
||||||
M.IsolateRef isolate,
|
|
||||||
M.Code code,
|
|
||||||
M.EventRepository events,
|
|
||||||
M.NotificationRepository notifications,
|
|
||||||
M.RetainedSizeRepository retainedSizes,
|
|
||||||
M.ReachableSizeRepository reachableSizes,
|
|
||||||
M.InboundReferencesRepository references,
|
|
||||||
M.RetainingPathRepository retainingPaths,
|
|
||||||
M.ObjectRepository objects,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(vm != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
assert(notifications != null);
|
|
||||||
assert(code != null);
|
|
||||||
assert(objects != null);
|
|
||||||
assert(retainedSizes != null);
|
|
||||||
assert(reachableSizes != null);
|
|
||||||
assert(references != null);
|
|
||||||
assert(retainingPaths != null);
|
|
||||||
CodeViewElement e = new CodeViewElement.created();
|
|
||||||
e._r = new RenderingScheduler<CodeViewElement>(e, queue: queue);
|
|
||||||
e._vm = vm;
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
e._notifications = notifications;
|
|
||||||
e._code = code;
|
|
||||||
e._objects = objects;
|
|
||||||
e._retainedSizes = retainedSizes;
|
|
||||||
e._reachableSizes = reachableSizes;
|
|
||||||
e._references = references;
|
|
||||||
e._retainingPaths = retainingPaths;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
CodeViewElement.created() : super.created('code-view') {
|
|
||||||
var columns = [
|
|
||||||
new SortedTableColumn('Address'),
|
|
||||||
new SortedTableColumn('Inclusive'),
|
|
||||||
new SortedTableColumn('Exclusive'),
|
|
||||||
new SortedTableColumn('Disassembly'),
|
|
||||||
new SortedTableColumn('Objects'),
|
|
||||||
];
|
|
||||||
disassemblyTable = new DisassemblyTable(columns);
|
|
||||||
columns = [
|
|
||||||
new SortedTableColumn('Address'),
|
|
||||||
new SortedTableColumn('Inclusive'),
|
|
||||||
new SortedTableColumn('Exclusive'),
|
|
||||||
new SortedTableColumn('Functions'),
|
|
||||||
];
|
|
||||||
inlineTable = new InlineTable(columns);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
TableElement _disassemblyTable;
|
|
||||||
TableElement _inlineRangeTable;
|
|
||||||
Element _disassemblyTableBody;
|
|
||||||
Element _inlineRangeTableBody;
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
if (_inlineRangeTable == null) {
|
|
||||||
_inlineRangeTable = new TableElement()..classes = ['table'];
|
|
||||||
_inlineRangeTable.createTHead().children = <Element>[
|
|
||||||
new TableRowElement()
|
|
||||||
..children = <Element>[
|
|
||||||
document.createElement('th')
|
|
||||||
..classes = ['address']
|
|
||||||
..text = 'Address Range',
|
|
||||||
document.createElement('th')
|
|
||||||
..classes = ['tick']
|
|
||||||
..text = 'Inclusive',
|
|
||||||
document.createElement('th')
|
|
||||||
..classes = ['tick']
|
|
||||||
..text = 'Exclusive',
|
|
||||||
document.createElement('th')..text = 'Functions',
|
|
||||||
]
|
|
||||||
];
|
|
||||||
_inlineRangeTableBody = _inlineRangeTable.createTBody();
|
|
||||||
_inlineRangeTableBody.classes = ['monospace'];
|
|
||||||
}
|
|
||||||
if (_disassemblyTable == null) {
|
|
||||||
_disassemblyTable = new TableElement()..classes = ['table'];
|
|
||||||
_disassemblyTable.createTHead().children = <Element>[
|
|
||||||
new TableRowElement()
|
|
||||||
..children = <Element>[
|
|
||||||
document.createElement('th')
|
|
||||||
..classes = ['address']
|
|
||||||
..text = 'Address Range',
|
|
||||||
document.createElement('th')
|
|
||||||
..classes = ['tick']
|
|
||||||
..title = 'Ticks with PC on the stack'
|
|
||||||
..text = 'Inclusive',
|
|
||||||
document.createElement('th')
|
|
||||||
..classes = ['tick']
|
|
||||||
..title = 'Ticks with PC at top of stack'
|
|
||||||
..text = 'Exclusive',
|
|
||||||
document.createElement('th')
|
|
||||||
..classes = ['disassembly']
|
|
||||||
..text = 'Disassembly',
|
|
||||||
document.createElement('th')
|
|
||||||
..classes = ['object']
|
|
||||||
..text = 'Object',
|
|
||||||
]
|
|
||||||
];
|
|
||||||
_disassemblyTableBody = _disassemblyTable.createTBody();
|
|
||||||
_disassemblyTableBody.classes = ['monospace'];
|
|
||||||
}
|
|
||||||
final inlinedFunctions = _code.inlinedFunctions.toList();
|
|
||||||
final S.Code code = _code as S.Code;
|
|
||||||
children = <Element>[
|
|
||||||
navBar(<Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
|
|
||||||
new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
|
|
||||||
navMenu(_code.name),
|
|
||||||
(new NavRefreshElement(queue: _r.queue)
|
|
||||||
..onRefresh.listen((e) async {
|
|
||||||
e.element.disabled = true;
|
|
||||||
_refresh();
|
|
||||||
}))
|
|
||||||
.element,
|
|
||||||
(new NavRefreshElement(label: 'refresh ticks', queue: _r.queue)
|
|
||||||
..onRefresh.listen((e) async {
|
|
||||||
e.element.disabled = true;
|
|
||||||
_refreshTicks();
|
|
||||||
}))
|
|
||||||
.element,
|
|
||||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
|
||||||
]),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered-big']
|
|
||||||
..children = <Element>[
|
|
||||||
new HeadingElement.h1()
|
|
||||||
..text = (M.isDartCode(_code.kind) && _code.isOptimized)
|
|
||||||
? 'Optimized code for ${_code.name}'
|
|
||||||
: 'Code for ${_code.name}',
|
|
||||||
new HRElement(),
|
|
||||||
new ObjectCommonElement(_isolate, _code, _retainedSizes,
|
|
||||||
_reachableSizes, _references, _retainingPaths, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new BRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'Kind',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = _codeKindToString(_code.kind)
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = M.isDartCode(_code.kind)
|
|
||||||
? const []
|
|
||||||
: [
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'Optimized',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = _code.isOptimized ? 'Yes' : 'No'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'Function',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = <Element>[
|
|
||||||
new FunctionRefElement(_isolate, _code.function,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = code.profile == null
|
|
||||||
? const []
|
|
||||||
: [
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'Inclusive',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = '${code.profile.formattedInclusiveTicks}'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = code.profile == null
|
|
||||||
? const []
|
|
||||||
: [
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'Exclusive',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = '${code.profile.formattedExclusiveTicks}'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'Object pool',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = <Element>[
|
|
||||||
new ObjectPoolRefElement(_isolate, _code.objectPool,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = inlinedFunctions.isNotEmpty
|
|
||||||
? const []
|
|
||||||
: [
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text =
|
|
||||||
'inlined functions (${inlinedFunctions.length})',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = <Element>[
|
|
||||||
(new CurlyBlockElement(
|
|
||||||
expanded: inlinedFunctions.length < 8,
|
|
||||||
queue: _r.queue)
|
|
||||||
..content = inlinedFunctions
|
|
||||||
.map<Element>((f) =>
|
|
||||||
new FunctionRefElement(_isolate, f,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element)
|
|
||||||
.toList())
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]
|
|
||||||
],
|
|
||||||
new HRElement(),
|
|
||||||
_inlineRangeTable,
|
|
||||||
new HRElement(),
|
|
||||||
_disassemblyTable
|
|
||||||
],
|
|
||||||
];
|
|
||||||
_updateDisassembly();
|
|
||||||
_updateInline();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _refresh() async {
|
|
||||||
S.Code code = _code as S.Code;
|
|
||||||
await code.reload();
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _refreshTicks() async {
|
|
||||||
S.Code code = _code as S.Code;
|
|
||||||
final isolate = code.isolate;
|
|
||||||
S.ServiceMap response =
|
|
||||||
await isolate.invokeRpc('_getCpuProfile', {'tags': 'None'});
|
|
||||||
final cpuProfile = new SampleProfile();
|
|
||||||
await cpuProfile.load(isolate, response);
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
String _formattedAddress(S.CodeInstruction instruction) {
|
|
||||||
if (instruction.address == 0) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return '0x${instruction.address.toRadixString(16)}';
|
|
||||||
}
|
|
||||||
|
|
||||||
String _formattedAddressRange(S.CodeInlineInterval interval) {
|
|
||||||
String start = interval.start.toRadixString(16);
|
|
||||||
String end = interval.end.toRadixString(16);
|
|
||||||
return '[0x$start, 0x$end)';
|
|
||||||
}
|
|
||||||
|
|
||||||
String _formattedInclusiveInterval(S.CodeInlineInterval interval) {
|
|
||||||
S.Code code = _code as S.Code;
|
|
||||||
if (code.profile == null) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
var intervalTick = code.profile.intervalTicks[interval.start];
|
|
||||||
if (intervalTick == null) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
// Don't show inclusive ticks if they are the same as exclusive ticks.
|
|
||||||
if (intervalTick.inclusiveTicks == intervalTick.exclusiveTicks) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
var pcent = Utils.formatPercent(
|
|
||||||
intervalTick.inclusiveTicks, code.profile.profile.sampleCount);
|
|
||||||
return '$pcent (${intervalTick.inclusiveTicks})';
|
|
||||||
}
|
|
||||||
|
|
||||||
String _formattedExclusiveInterval(S.CodeInlineInterval interval) {
|
|
||||||
S.Code code = _code as S.Code;
|
|
||||||
if (code.profile == null) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
var intervalTick = code.profile.intervalTicks[interval.start];
|
|
||||||
if (intervalTick == null) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
var pcent = Utils.formatPercent(
|
|
||||||
intervalTick.exclusiveTicks, code.profile.profile.sampleCount);
|
|
||||||
return '$pcent (${intervalTick.exclusiveTicks})';
|
|
||||||
}
|
|
||||||
|
|
||||||
String _formattedInclusive(S.CodeInstruction instruction) {
|
|
||||||
S.Code code = _code as S.Code;
|
|
||||||
if (code.profile == null) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
var tick = code.profile.addressTicks[instruction.address];
|
|
||||||
if (tick == null) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
// Don't show inclusive ticks if they are the same as exclusive ticks.
|
|
||||||
if (tick.inclusiveTicks == tick.exclusiveTicks) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
var pcent = Utils.formatPercent(
|
|
||||||
tick.inclusiveTicks, code.profile.profile.sampleCount);
|
|
||||||
return '$pcent (${tick.inclusiveTicks})';
|
|
||||||
}
|
|
||||||
|
|
||||||
String _formattedExclusive(S.CodeInstruction instruction) {
|
|
||||||
S.Code code = _code as S.Code;
|
|
||||||
if (code.profile == null) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
var tick = code.profile.addressTicks[instruction.address];
|
|
||||||
if (tick == null) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
var pcent = Utils.formatPercent(
|
|
||||||
tick.exclusiveTicks, code.profile.profile.sampleCount);
|
|
||||||
return '$pcent (${tick.exclusiveTicks})';
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateDisassemblyTable() {
|
|
||||||
S.Code code = _code as S.Code;
|
|
||||||
disassemblyTable.clearRows();
|
|
||||||
if (code == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (S.CodeInstruction instruction in code.instructions) {
|
|
||||||
var row = [
|
|
||||||
_formattedAddress(instruction),
|
|
||||||
_formattedInclusive(instruction),
|
|
||||||
_formattedExclusive(instruction),
|
|
||||||
instruction.human,
|
|
||||||
instruction.object
|
|
||||||
];
|
|
||||||
disassemblyTable.addRow(new SortedTableRow(row));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _addDisassemblyDOMRow() {
|
|
||||||
var tableBody = _disassemblyTableBody;
|
|
||||||
assert(tableBody != null);
|
|
||||||
var tr = new TableRowElement();
|
|
||||||
|
|
||||||
var cell;
|
|
||||||
|
|
||||||
// Add new space.
|
|
||||||
cell = tr.insertCell(-1);
|
|
||||||
cell.classes.add('monospace');
|
|
||||||
cell = tr.insertCell(-1);
|
|
||||||
cell.classes.add('monospace');
|
|
||||||
cell = tr.insertCell(-1);
|
|
||||||
cell.classes.add('monospace');
|
|
||||||
cell = tr.insertCell(-1);
|
|
||||||
cell.classes.add('monospace');
|
|
||||||
cell = tr.insertCell(-1);
|
|
||||||
cell.classes.add('monospace');
|
|
||||||
|
|
||||||
tableBody.children.add(tr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _fillDisassemblyDOMRow(TableRowElement tr, int rowIndex) {
|
|
||||||
final row = disassemblyTable.rows[rowIndex];
|
|
||||||
final n = row.values.length;
|
|
||||||
for (var i = 0; i < n; i++) {
|
|
||||||
final cell = tr.children[i];
|
|
||||||
final content = row.values[i];
|
|
||||||
if (content is S.HeapObject) {
|
|
||||||
cell.children = <Element>[
|
|
||||||
anyRef(_isolate, content, _objects, queue: _r.queue)
|
|
||||||
];
|
|
||||||
} else if (content != null) {
|
|
||||||
String text = '$content';
|
|
||||||
if (i == kDisassemblyColumnIndex) {
|
|
||||||
// Disassembly might be a comment. Reduce indentation, change styling,
|
|
||||||
// widen to span next column (which should be empty).
|
|
||||||
if (text.startsWith(' ;;')) {
|
|
||||||
cell.attributes['colspan'] = '2';
|
|
||||||
cell.classes.add('code-comment');
|
|
||||||
text = text.substring(6);
|
|
||||||
} else {
|
|
||||||
cell.attributes['colspan'] = '1';
|
|
||||||
cell.classes.remove('code-comment');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cell.text = text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateDisassemblyDOMTable() {
|
|
||||||
var tableBody = _disassemblyTableBody;
|
|
||||||
assert(tableBody != null);
|
|
||||||
// Resize DOM table.
|
|
||||||
if (tableBody.children.length > disassemblyTable.sortedRows.length) {
|
|
||||||
// Shrink the table.
|
|
||||||
var deadRows =
|
|
||||||
tableBody.children.length - disassemblyTable.sortedRows.length;
|
|
||||||
for (var i = 0; i < deadRows; i++) {
|
|
||||||
tableBody.children.removeLast();
|
|
||||||
}
|
|
||||||
} else if (tableBody.children.length < disassemblyTable.sortedRows.length) {
|
|
||||||
// Grow table.
|
|
||||||
var newRows =
|
|
||||||
disassemblyTable.sortedRows.length - tableBody.children.length;
|
|
||||||
for (var i = 0; i < newRows; i++) {
|
|
||||||
_addDisassemblyDOMRow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(tableBody.children.length == disassemblyTable.sortedRows.length);
|
|
||||||
|
|
||||||
// Fill table.
|
|
||||||
var i = 0;
|
|
||||||
for (var tr in tableBody.children) {
|
|
||||||
var rowIndex = disassemblyTable.sortedRows[i];
|
|
||||||
_fillDisassemblyDOMRow(tr, rowIndex);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateDisassembly() {
|
|
||||||
_updateDisassemblyTable();
|
|
||||||
_updateDisassemblyDOMTable();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateInlineTable() {
|
|
||||||
inlineTable.clearRows();
|
|
||||||
S.Code code = _code as S.Code;
|
|
||||||
for (S.CodeInlineInterval interval in code.inlineIntervals) {
|
|
||||||
var row = [
|
|
||||||
interval,
|
|
||||||
_formattedInclusiveInterval(interval),
|
|
||||||
_formattedExclusiveInterval(interval),
|
|
||||||
interval.functions
|
|
||||||
];
|
|
||||||
inlineTable.addRow(new SortedTableRow(row));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _addInlineDOMRow() {
|
|
||||||
var tableBody = _inlineRangeTableBody;
|
|
||||||
assert(tableBody != null);
|
|
||||||
var tr = new TableRowElement();
|
|
||||||
|
|
||||||
var cell;
|
|
||||||
|
|
||||||
// Add new space.
|
|
||||||
cell = tr.insertCell(-1);
|
|
||||||
cell.classes.add('monospace');
|
|
||||||
cell = tr.insertCell(-1);
|
|
||||||
cell.classes.add('monospace');
|
|
||||||
cell = tr.insertCell(-1);
|
|
||||||
cell.classes.add('monospace');
|
|
||||||
cell = tr.insertCell(-1);
|
|
||||||
|
|
||||||
tableBody.children.add(tr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _fillInlineDOMRow(TableRowElement tr, int rowIndex) {
|
|
||||||
var row = inlineTable.rows[rowIndex];
|
|
||||||
var columns = row.values.length;
|
|
||||||
var addressRangeColumn = 0;
|
|
||||||
var functionsColumn = columns - 1;
|
|
||||||
|
|
||||||
{
|
|
||||||
var addressRangeCell = tr.children[addressRangeColumn];
|
|
||||||
var interval = row.values[addressRangeColumn];
|
|
||||||
var addressRangeString = _formattedAddressRange(interval);
|
|
||||||
var addressRangeElement = new SpanElement();
|
|
||||||
addressRangeElement.classes.add('monospace');
|
|
||||||
addressRangeElement.text = addressRangeString;
|
|
||||||
addressRangeCell.children.clear();
|
|
||||||
addressRangeCell.children.add(addressRangeElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = addressRangeColumn + 1; i < columns - 1; i++) {
|
|
||||||
var cell = tr.children[i];
|
|
||||||
cell.text = row.values[i].toString();
|
|
||||||
}
|
|
||||||
var functions = row.values[functionsColumn];
|
|
||||||
var functionsCell = tr.children[functionsColumn];
|
|
||||||
functionsCell.children.clear();
|
|
||||||
for (var func in functions) {
|
|
||||||
functionsCell.children
|
|
||||||
.add(new FunctionRefElement(_isolate, func, queue: _r.queue).element);
|
|
||||||
var gap = new SpanElement();
|
|
||||||
gap.style.minWidth = '1em';
|
|
||||||
gap.text = ' ';
|
|
||||||
functionsCell.children.add(gap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateInlineDOMTable() {
|
|
||||||
var tableBody = _inlineRangeTableBody;
|
|
||||||
// Resize DOM table.
|
|
||||||
if (tableBody.children.length > inlineTable.sortedRows.length) {
|
|
||||||
// Shrink the table.
|
|
||||||
var deadRows = tableBody.children.length - inlineTable.sortedRows.length;
|
|
||||||
for (var i = 0; i < deadRows; i++) {
|
|
||||||
tableBody.children.removeLast();
|
|
||||||
}
|
|
||||||
} else if (tableBody.children.length < inlineTable.sortedRows.length) {
|
|
||||||
// Grow table.
|
|
||||||
var newRows = inlineTable.sortedRows.length - tableBody.children.length;
|
|
||||||
for (var i = 0; i < newRows; i++) {
|
|
||||||
_addInlineDOMRow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(tableBody.children.length == inlineTable.sortedRows.length);
|
|
||||||
// Fill table.
|
|
||||||
for (var i = 0; i < inlineTable.sortedRows.length; i++) {
|
|
||||||
var rowIndex = inlineTable.sortedRows[i];
|
|
||||||
var tr = tableBody.children[i];
|
|
||||||
_fillInlineDOMRow(tr, rowIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateInline() {
|
|
||||||
_updateInlineTable();
|
|
||||||
_updateInlineDOMTable();
|
|
||||||
}
|
|
||||||
|
|
||||||
static String _codeKindToString(M.CodeKind kind) {
|
|
||||||
switch (kind) {
|
|
||||||
case M.CodeKind.dart:
|
|
||||||
return 'dart';
|
|
||||||
case M.CodeKind.native:
|
|
||||||
return 'native';
|
|
||||||
case M.CodeKind.stub:
|
|
||||||
return 'stub';
|
|
||||||
case M.CodeKind.tag:
|
|
||||||
return 'tag';
|
|
||||||
case M.CodeKind.collected:
|
|
||||||
return 'collected';
|
|
||||||
}
|
|
||||||
throw new Exception('Unknown CodeKind ($kind)');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,205 +0,0 @@
|
||||||
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:math' as math;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
|
|
||||||
class SearchResultSelected {
|
|
||||||
final SearchBarElement element;
|
|
||||||
final dynamic item;
|
|
||||||
SearchResultSelected(this.element, this.item);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef Iterable<dynamic> SearchBarSearchCallback(Pattern pattern);
|
|
||||||
|
|
||||||
class SearchBarElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<SearchBarElement> _r;
|
|
||||||
|
|
||||||
StreamController<SearchResultSelected> _onSearchResultSelected =
|
|
||||||
new StreamController<SearchResultSelected>.broadcast();
|
|
||||||
|
|
||||||
Stream<RenderedEvent<SearchBarElement>> get onRendered => _r.onRendered;
|
|
||||||
Stream<SearchResultSelected> get onSearchResultSelected =>
|
|
||||||
_onSearchResultSelected.stream;
|
|
||||||
|
|
||||||
StreamSubscription _onKeyDownSubscription;
|
|
||||||
|
|
||||||
Element _workspace;
|
|
||||||
SearchBarSearchCallback _search;
|
|
||||||
bool _isOpen;
|
|
||||||
bool _focusRequested = false;
|
|
||||||
String _lastValue = '';
|
|
||||||
List _results = const [];
|
|
||||||
int _current = 0;
|
|
||||||
|
|
||||||
bool get isOpen => _isOpen;
|
|
||||||
dynamic get current => _results.isNotEmpty ? _results[_current] : null;
|
|
||||||
|
|
||||||
set isOpen(bool value) {
|
|
||||||
if (!value) {
|
|
||||||
_input.value = '';
|
|
||||||
_lastValue = '';
|
|
||||||
if (_results.isNotEmpty) {
|
|
||||||
_results = const [];
|
|
||||||
_current = 0;
|
|
||||||
_triggerSearchResultSelected();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_isOpen = _r.checkAndReact(_isOpen, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
factory SearchBarElement(SearchBarSearchCallback search,
|
|
||||||
{bool isOpen = false, Element workspace, RenderingQueue queue}) {
|
|
||||||
assert(search != null);
|
|
||||||
assert(isOpen != null);
|
|
||||||
SearchBarElement e = new SearchBarElement.created();
|
|
||||||
e._r = new RenderingScheduler<SearchBarElement>(e, queue: queue);
|
|
||||||
e._search = search;
|
|
||||||
e._isOpen = isOpen;
|
|
||||||
e._workspace = workspace;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchBarElement.created() : super.created('search-bar');
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
_workspace?.tabIndex = 1;
|
|
||||||
_onKeyDownSubscription = (_workspace ?? window).onKeyDown.listen((e) {
|
|
||||||
if (e.key.toLowerCase() == 'f' &&
|
|
||||||
!e.shiftKey &&
|
|
||||||
!e.altKey &&
|
|
||||||
e.ctrlKey != e.metaKey) {
|
|
||||||
if (e.metaKey == window.navigator.platform.startsWith('Mac')) {
|
|
||||||
e.stopPropagation();
|
|
||||||
e.preventDefault();
|
|
||||||
isOpen = true;
|
|
||||||
_focusRequested = true;
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
_onKeyDownSubscription.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
TextInputElement _input;
|
|
||||||
SpanElement _resultsArea;
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
if (_input == null) {
|
|
||||||
_input = new TextInputElement()
|
|
||||||
..onKeyPress.listen((e) {
|
|
||||||
if (e.keyCode == KeyCode.ENTER) {
|
|
||||||
if (_input.value == '') {
|
|
||||||
_lastValue = '';
|
|
||||||
if (_results.isNotEmpty) {
|
|
||||||
_results = const [];
|
|
||||||
_current = 0;
|
|
||||||
_triggerSearchResultSelected();
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
} else if (_input.value != _lastValue) {
|
|
||||||
_lastValue = _input.value;
|
|
||||||
_results = _doSearch(_input.value);
|
|
||||||
_current = 0;
|
|
||||||
_triggerSearchResultSelected();
|
|
||||||
_r.dirty();
|
|
||||||
} else {
|
|
||||||
if (e.shiftKey) {
|
|
||||||
_prev();
|
|
||||||
} else {
|
|
||||||
_next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
_resultsArea = new SpanElement();
|
|
||||||
children = <Element>[
|
|
||||||
_input,
|
|
||||||
_resultsArea,
|
|
||||||
new ButtonElement()
|
|
||||||
..text = '❌'
|
|
||||||
..onClick.listen((_) {
|
|
||||||
isOpen = false;
|
|
||||||
})
|
|
||||||
];
|
|
||||||
}
|
|
||||||
_resultsArea.nodes = [
|
|
||||||
new ButtonElement()
|
|
||||||
..text = '▲'
|
|
||||||
..disabled = _results.isEmpty
|
|
||||||
..onClick.listen((_) => _prev()),
|
|
||||||
new Text(
|
|
||||||
'${math.min(_current + 1, _results.length)} / ${_results.length}'),
|
|
||||||
new ButtonElement()
|
|
||||||
..text = '▼'
|
|
||||||
..disabled = _results.isEmpty
|
|
||||||
..onClick.listen((_) => _next()),
|
|
||||||
];
|
|
||||||
style.visibility = isOpen ? null : 'collapse';
|
|
||||||
if (_focusRequested) {
|
|
||||||
_input.focus();
|
|
||||||
_focusRequested = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void update() {
|
|
||||||
if (!isOpen || _lastValue == '') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final item = current;
|
|
||||||
_results = _doSearch(_lastValue);
|
|
||||||
_current = math.max(0, _results.indexOf(item));
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<dynamic> _doSearch(String value) =>
|
|
||||||
_search(new _CaseInsensitivePatternString(value)).toList(growable: false);
|
|
||||||
|
|
||||||
void _prev() {
|
|
||||||
if (_results.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_current = (_current + _results.length - 1) % _results.length;
|
|
||||||
_triggerSearchResultSelected();
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _next() {
|
|
||||||
if (_results.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_current = (_current + 1) % _results.length;
|
|
||||||
_triggerSearchResultSelected();
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _triggerSearchResultSelected() {
|
|
||||||
_onSearchResultSelected.add(new SearchResultSelected(this, current));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CaseInsensitivePatternString implements Pattern {
|
|
||||||
final String _pattern;
|
|
||||||
|
|
||||||
_CaseInsensitivePatternString(String pattern)
|
|
||||||
: this._pattern = pattern.toLowerCase();
|
|
||||||
|
|
||||||
Iterable<Match> allMatches(String string, [int start = 0]) =>
|
|
||||||
_pattern.allMatches(string.toLowerCase(), start);
|
|
||||||
|
|
||||||
Match matchAsPrefix(String string, [int start = 0]) =>
|
|
||||||
_pattern.matchAsPrefix(string.toLowerCase(), start);
|
|
||||||
}
|
|
|
@ -1,259 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:math' as math;
|
|
||||||
import 'package:observatory_2/src/elements/containers/search_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
|
|
||||||
typedef HtmlElement VirtualCollectionCreateCallback();
|
|
||||||
typedef List<HtmlElement> VirtualCollectionHeaderCallback();
|
|
||||||
typedef void VirtualCollectionUpdateCallback(
|
|
||||||
HtmlElement el, dynamic item, int index);
|
|
||||||
typedef bool VirtualCollectionSearchCallback(Pattern pattern, dynamic item);
|
|
||||||
|
|
||||||
class VirtualCollectionElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<VirtualCollectionElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<VirtualCollectionElement>> get onRendered =>
|
|
||||||
_r.onRendered;
|
|
||||||
|
|
||||||
VirtualCollectionCreateCallback _create;
|
|
||||||
VirtualCollectionHeaderCallback _createHeader;
|
|
||||||
VirtualCollectionUpdateCallback _update;
|
|
||||||
VirtualCollectionSearchCallback _search;
|
|
||||||
double _itemHeight;
|
|
||||||
int _top;
|
|
||||||
double _height;
|
|
||||||
List _items;
|
|
||||||
StreamSubscription _onScrollSubscription;
|
|
||||||
StreamSubscription _onResizeSubscription;
|
|
||||||
|
|
||||||
List get items => _items;
|
|
||||||
|
|
||||||
set items(Iterable value) {
|
|
||||||
_items = new List.unmodifiable(value);
|
|
||||||
_top = null;
|
|
||||||
_searcher?.update();
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
factory VirtualCollectionElement(VirtualCollectionCreateCallback create,
|
|
||||||
VirtualCollectionUpdateCallback update,
|
|
||||||
{Iterable items = const [],
|
|
||||||
VirtualCollectionHeaderCallback createHeader,
|
|
||||||
VirtualCollectionSearchCallback search,
|
|
||||||
RenderingQueue queue}) {
|
|
||||||
assert(create != null);
|
|
||||||
assert(update != null);
|
|
||||||
assert(items != null);
|
|
||||||
VirtualCollectionElement e = new VirtualCollectionElement.created();
|
|
||||||
e._r = new RenderingScheduler<VirtualCollectionElement>(e, queue: queue);
|
|
||||||
e._create = create;
|
|
||||||
e._createHeader = createHeader;
|
|
||||||
e._update = update;
|
|
||||||
e._search = search;
|
|
||||||
e._items = new List.unmodifiable(items);
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
VirtualCollectionElement.created() : super.created('virtual-collection');
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
_top = null;
|
|
||||||
_itemHeight = null;
|
|
||||||
_onScrollSubscription = _viewport.onScroll.listen(_onScroll);
|
|
||||||
_onResizeSubscription = window.onResize.listen(_onResize);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = const [];
|
|
||||||
_onScrollSubscription.cancel();
|
|
||||||
_onResizeSubscription.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
DivElement _header;
|
|
||||||
SearchBarElement _searcher;
|
|
||||||
final DivElement _viewport = new DivElement()
|
|
||||||
..classes = ['viewport', 'container'];
|
|
||||||
final DivElement _spacer = new DivElement()..classes = ['spacer'];
|
|
||||||
final DivElement _buffer = new DivElement()..classes = ['buffer'];
|
|
||||||
|
|
||||||
static int safeFloor(double x) {
|
|
||||||
if (x.isNaN) return 0;
|
|
||||||
return x.floor();
|
|
||||||
}
|
|
||||||
|
|
||||||
static int safeCeil(double x) {
|
|
||||||
if (x.isNaN) return 0;
|
|
||||||
return x.ceil();
|
|
||||||
}
|
|
||||||
|
|
||||||
dynamic getItemFromElement(HtmlElement element) {
|
|
||||||
final el_index = _buffer.children.indexOf(element);
|
|
||||||
if (el_index < 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final item_index =
|
|
||||||
_top + el_index - safeFloor(_buffer.children.length * _inverse_preload);
|
|
||||||
if (0 <= item_index && item_index < items.length) {
|
|
||||||
return _items[item_index];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The preloaded element before and after the visible area are:
|
|
||||||
/// 1/preload_size of the number of items in the visible area.
|
|
||||||
static const int _preload = 2;
|
|
||||||
|
|
||||||
/// L = length of all the elements loaded
|
|
||||||
/// l = length of the visible area
|
|
||||||
///
|
|
||||||
/// L = l + 2 * l / _preload
|
|
||||||
/// l = L * _preload / (_preload + 2)
|
|
||||||
///
|
|
||||||
/// tail = l / _preload = L * 1 / (_preload + 2) = L * _inverse_preload
|
|
||||||
static const double _inverse_preload = 1 / (_preload + 2);
|
|
||||||
|
|
||||||
var _takeIntoView;
|
|
||||||
|
|
||||||
void takeIntoView(item) {
|
|
||||||
_takeIntoView = item;
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
if (children.isEmpty) {
|
|
||||||
children = <Element>[
|
|
||||||
_viewport
|
|
||||||
..children = <Element>[
|
|
||||||
_spacer
|
|
||||||
..children = <Element>[
|
|
||||||
_buffer..children = <Element>[_create()]
|
|
||||||
],
|
|
||||||
]
|
|
||||||
];
|
|
||||||
if (_search != null) {
|
|
||||||
_searcher =
|
|
||||||
_searcher ?? new SearchBarElement(_doSearch, queue: _r.queue)
|
|
||||||
..onSearchResultSelected.listen((e) {
|
|
||||||
takeIntoView(e.item);
|
|
||||||
});
|
|
||||||
children.insert(0, _searcher.element);
|
|
||||||
}
|
|
||||||
if (_createHeader != null) {
|
|
||||||
_header = new DivElement()
|
|
||||||
..classes = ['header', 'container']
|
|
||||||
..children = _createHeader();
|
|
||||||
children.insert(0, _header);
|
|
||||||
final rect = _header.getBoundingClientRect();
|
|
||||||
_header.classes.add('attached');
|
|
||||||
_viewport.style.top = '${rect.height}px';
|
|
||||||
final width = _header.children.fold(0.0, _foldWidth);
|
|
||||||
_buffer.style.minWidth = '${width}px';
|
|
||||||
}
|
|
||||||
_itemHeight = _buffer.children[0].getBoundingClientRect().height;
|
|
||||||
_height = getBoundingClientRect().height;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_takeIntoView != null) {
|
|
||||||
final index = items.indexOf(_takeIntoView);
|
|
||||||
if (index >= 0) {
|
|
||||||
final minScrollTop = _itemHeight * (index + 1) - _height;
|
|
||||||
final maxScrollTop = _itemHeight * index;
|
|
||||||
_viewport.scrollTop =
|
|
||||||
safeFloor((maxScrollTop - minScrollTop) / 2 + minScrollTop);
|
|
||||||
}
|
|
||||||
_takeIntoView = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final top = safeFloor(_viewport.scrollTop / _itemHeight);
|
|
||||||
|
|
||||||
_spacer.style.height = '${_itemHeight * (_items.length)}px';
|
|
||||||
final tail_length = safeCeil(_height / _itemHeight / _preload);
|
|
||||||
final length = tail_length * 2 + tail_length * _preload;
|
|
||||||
|
|
||||||
if (_buffer.children.length < length) {
|
|
||||||
while (_buffer.children.length != length) {
|
|
||||||
var e = _create();
|
|
||||||
e..style.display = 'hidden';
|
|
||||||
_buffer.children.add(e);
|
|
||||||
}
|
|
||||||
_top = null; // force update;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((_top == null) || ((top - _top).abs() >= tail_length)) {
|
|
||||||
_buffer.style.top = '${_itemHeight * (top - tail_length)}px';
|
|
||||||
int i = top - tail_length;
|
|
||||||
for (final HtmlElement e in _buffer.children) {
|
|
||||||
if (0 <= i && i < _items.length) {
|
|
||||||
e.style.display = null;
|
|
||||||
_update(e, _items[i], i);
|
|
||||||
} else {
|
|
||||||
e.style.display = 'hidden';
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
_top = top;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_searcher != null) {
|
|
||||||
final current = _searcher.current;
|
|
||||||
int i = _top - tail_length;
|
|
||||||
for (final HtmlElement e in _buffer.children) {
|
|
||||||
if (0 <= i && i < _items.length) {
|
|
||||||
if (_items[i] == current) {
|
|
||||||
e.classes.add('marked');
|
|
||||||
} else {
|
|
||||||
e.classes.remove('marked');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_updateHeader();
|
|
||||||
}
|
|
||||||
|
|
||||||
double _foldWidth(double value, Element child) {
|
|
||||||
return math.max(value, child.getBoundingClientRect().width);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateHeader() {
|
|
||||||
if (_header != null) {
|
|
||||||
_header.style.left = '${-_viewport.scrollLeft}px';
|
|
||||||
final width = _buffer.getBoundingClientRect().width;
|
|
||||||
_header.children.last.style.width = '${width}px';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onScroll(_) {
|
|
||||||
_r.dirty();
|
|
||||||
// We anticipate the header in advance to avoid flickering
|
|
||||||
_updateHeader();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onResize(_) {
|
|
||||||
final newHeight = getBoundingClientRect().height;
|
|
||||||
if (newHeight > _height) {
|
|
||||||
_height = newHeight;
|
|
||||||
_r.dirty();
|
|
||||||
} else {
|
|
||||||
// Even if we are not updating the structure the computed size is going to
|
|
||||||
// change
|
|
||||||
_updateHeader();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterable<dynamic> _doSearch(Pattern search) {
|
|
||||||
return _items.where((item) => _search(search, item));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,183 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:collection';
|
|
||||||
import 'dart:math' as Math;
|
|
||||||
import 'package:observatory_2/src/elements/containers/virtual_collection.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
|
|
||||||
typedef HtmlElement VirtualTreeCreateCallback(
|
|
||||||
toggle({bool autoToggleSingleChildNodes, bool autoToggleWholeTree}));
|
|
||||||
typedef void VirtualTreeUpdateCallback(HtmlElement el, dynamic item, int depth);
|
|
||||||
typedef Iterable<dynamic> VirtualTreeGetChildrenCallback(dynamic value);
|
|
||||||
typedef bool VirtualTreeSearchCallback(Pattern pattern, dynamic item);
|
|
||||||
|
|
||||||
void virtualTreeUpdateLines(SpanElement element, int n) {
|
|
||||||
n = Math.max(0, n);
|
|
||||||
while (element.children.length > n) {
|
|
||||||
element.children.removeLast();
|
|
||||||
}
|
|
||||||
while (element.children.length < n) {
|
|
||||||
element.children.add(new SpanElement());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class VirtualTreeElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<VirtualTreeElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<VirtualTreeElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
VirtualTreeGetChildrenCallback _children;
|
|
||||||
List _items;
|
|
||||||
List _depths;
|
|
||||||
final Set _expanded = new Set();
|
|
||||||
|
|
||||||
List get items => _items;
|
|
||||||
|
|
||||||
set items(Iterable value) {
|
|
||||||
_items = new List.unmodifiable(value);
|
|
||||||
_expanded.clear();
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
factory VirtualTreeElement(VirtualTreeCreateCallback create,
|
|
||||||
VirtualTreeUpdateCallback update, VirtualTreeGetChildrenCallback children,
|
|
||||||
{Iterable items = const [],
|
|
||||||
VirtualTreeSearchCallback search,
|
|
||||||
RenderingQueue queue}) {
|
|
||||||
assert(create != null);
|
|
||||||
assert(update != null);
|
|
||||||
assert(children != null);
|
|
||||||
assert(items != null);
|
|
||||||
VirtualTreeElement e = new VirtualTreeElement.created();
|
|
||||||
e._r = new RenderingScheduler<VirtualTreeElement>(e, queue: queue);
|
|
||||||
e._children = children;
|
|
||||||
e._collection = new VirtualCollectionElement(() {
|
|
||||||
var element;
|
|
||||||
return element = create((
|
|
||||||
{bool autoToggleSingleChildNodes = false,
|
|
||||||
bool autoToggleWholeTree = false}) {
|
|
||||||
var item = e._collection.getItemFromElement(element);
|
|
||||||
if (e.isExpanded(item)) {
|
|
||||||
e.collapse(item,
|
|
||||||
autoCollapseWholeTree: autoToggleWholeTree,
|
|
||||||
autoCollapseSingleChildNodes: autoToggleSingleChildNodes);
|
|
||||||
} else {
|
|
||||||
e.expand(item,
|
|
||||||
autoExpandWholeTree: autoToggleWholeTree,
|
|
||||||
autoExpandSingleChildNodes: autoToggleSingleChildNodes);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, (HtmlElement el, dynamic item, int index) {
|
|
||||||
update(el, item, e._depths[index]);
|
|
||||||
}, search: search, queue: queue);
|
|
||||||
e._items = new List.unmodifiable(items);
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
VirtualTreeElement.created() : super.created('virtual-tree');
|
|
||||||
|
|
||||||
bool isExpanded(item) {
|
|
||||||
return _expanded.contains(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
void expand(item,
|
|
||||||
{bool autoExpandSingleChildNodes = false,
|
|
||||||
bool autoExpandWholeTree = false}) {
|
|
||||||
if (_expanded.add(item)) _r.dirty();
|
|
||||||
if (autoExpandWholeTree) {
|
|
||||||
// The tree is potentially very deep, simple recursion can produce a
|
|
||||||
// Stack Overflow
|
|
||||||
Queue pendingNodes = new Queue();
|
|
||||||
pendingNodes.addAll(_children(item));
|
|
||||||
while (pendingNodes.isNotEmpty) {
|
|
||||||
final item = pendingNodes.removeFirst();
|
|
||||||
if (_expanded.add(item)) _r.dirty();
|
|
||||||
pendingNodes.addAll(_children(item));
|
|
||||||
}
|
|
||||||
} else if (autoExpandSingleChildNodes) {
|
|
||||||
var children = _children(item);
|
|
||||||
while (children.length == 1) {
|
|
||||||
_expanded.add(children.first);
|
|
||||||
children = _children(children.first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void collapse(item,
|
|
||||||
{bool autoCollapseSingleChildNodes = false,
|
|
||||||
bool autoCollapseWholeTree = false}) {
|
|
||||||
if (_expanded.remove(item)) _r.dirty();
|
|
||||||
if (autoCollapseWholeTree) {
|
|
||||||
// The tree is potentially very deep, simple recursion can produce a
|
|
||||||
// Stack Overflow
|
|
||||||
Queue pendingNodes = new Queue();
|
|
||||||
pendingNodes.addAll(_children(item));
|
|
||||||
while (pendingNodes.isNotEmpty) {
|
|
||||||
final item = pendingNodes.removeFirst();
|
|
||||||
if (_expanded.remove(item)) _r.dirty();
|
|
||||||
pendingNodes.addAll(_children(item));
|
|
||||||
}
|
|
||||||
} else if (autoCollapseSingleChildNodes) {
|
|
||||||
var children = _children(item);
|
|
||||||
while (children.length == 1) {
|
|
||||||
_expanded.remove(children.first);
|
|
||||||
children = _children(children.first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = const [];
|
|
||||||
}
|
|
||||||
|
|
||||||
VirtualCollectionElement _collection;
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
if (children.length == 0) {
|
|
||||||
children = <Element>[_collection.element];
|
|
||||||
}
|
|
||||||
|
|
||||||
final items = [];
|
|
||||||
final depths = new List.filled(_items.length, 0, growable: true);
|
|
||||||
|
|
||||||
{
|
|
||||||
final toDo = new Queue();
|
|
||||||
|
|
||||||
toDo.addAll(_items);
|
|
||||||
while (toDo.isNotEmpty) {
|
|
||||||
final item = toDo.removeFirst();
|
|
||||||
|
|
||||||
items.add(item);
|
|
||||||
if (isExpanded(item)) {
|
|
||||||
final children = _children(item);
|
|
||||||
children
|
|
||||||
.toList(growable: false)
|
|
||||||
.reversed
|
|
||||||
.forEach((c) => toDo.addFirst(c));
|
|
||||||
final depth = depths[items.length - 1];
|
|
||||||
depths.insertAll(
|
|
||||||
items.length, new List.filled(children.length, depth + 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_depths = depths;
|
|
||||||
_collection.items = items;
|
|
||||||
|
|
||||||
_r.waitFor([_collection.onRendered.first]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,143 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/curly_block.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/any_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/uris.dart';
|
|
||||||
|
|
||||||
class ContextRefElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<ContextRefElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<ContextRefElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.ContextRef _context;
|
|
||||||
M.ObjectRepository _objects;
|
|
||||||
M.Context _loadedContext;
|
|
||||||
bool _expandable;
|
|
||||||
bool _expanded = false;
|
|
||||||
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.ContextRef get context => _context;
|
|
||||||
|
|
||||||
factory ContextRefElement(
|
|
||||||
M.IsolateRef isolate, M.ContextRef context, M.ObjectRepository objects,
|
|
||||||
{RenderingQueue queue, bool expandable = true}) {
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(context != null);
|
|
||||||
assert(objects != null);
|
|
||||||
ContextRefElement e = new ContextRefElement.created();
|
|
||||||
e._r = new RenderingScheduler<ContextRefElement>(e, queue: queue);
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._context = context;
|
|
||||||
e._objects = objects;
|
|
||||||
e._expandable = expandable;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
ContextRefElement.created() : super.created('context-ref');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _refresh() async {
|
|
||||||
_loadedContext = await _objects.get(_isolate, _context.id);
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
var children = <HtmlElement>[
|
|
||||||
new AnchorElement(href: Uris.inspect(_isolate, object: _context))
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['emphasize']
|
|
||||||
..text = 'Context',
|
|
||||||
new SpanElement()..text = ' (${_context.length})',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
if (_expandable) {
|
|
||||||
children.addAll([
|
|
||||||
new SpanElement()..text = ' ',
|
|
||||||
(new CurlyBlockElement(expanded: _expanded, queue: _r.queue)
|
|
||||||
..content = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['indent']
|
|
||||||
..children = _createValue()
|
|
||||||
]
|
|
||||||
..onToggle.listen((e) async {
|
|
||||||
_expanded = e.control.expanded;
|
|
||||||
if (_expanded) {
|
|
||||||
e.control.disabled = true;
|
|
||||||
await _refresh();
|
|
||||||
e.control.disabled = false;
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.element
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
this.children = children;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createValue() {
|
|
||||||
if (_loadedContext == null) {
|
|
||||||
return [new SpanElement()..text = 'Loading...'];
|
|
||||||
}
|
|
||||||
var members = <Element>[];
|
|
||||||
if (_loadedContext.parentContext != null) {
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'parent context',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
new ContextRefElement(
|
|
||||||
_isolate, _loadedContext.parentContext, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (_loadedContext.variables.isNotEmpty) {
|
|
||||||
var variables = _loadedContext.variables.toList();
|
|
||||||
for (var index = 0; index < variables.length; index++) {
|
|
||||||
var variable = variables[index];
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = '[ $index ]',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
anyRef(_isolate, variable.value, _objects, queue: _r.queue)
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = members
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,191 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/context_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/curly_block.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/any_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/class_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/refresh.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/vm_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/object_common.dart';
|
|
||||||
|
|
||||||
class ContextViewElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<ContextViewElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<ContextViewElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.VM _vm;
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
M.Context _context;
|
|
||||||
M.ContextRepository _contexts;
|
|
||||||
M.RetainedSizeRepository _retainedSizes;
|
|
||||||
M.ReachableSizeRepository _reachableSizes;
|
|
||||||
M.InboundReferencesRepository _references;
|
|
||||||
M.RetainingPathRepository _retainingPaths;
|
|
||||||
M.ObjectRepository _objects;
|
|
||||||
|
|
||||||
M.VMRef get vm => _vm;
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.NotificationRepository get notifications => _notifications;
|
|
||||||
M.Context get context => _context;
|
|
||||||
|
|
||||||
factory ContextViewElement(
|
|
||||||
M.VM vm,
|
|
||||||
M.IsolateRef isolate,
|
|
||||||
M.Context context,
|
|
||||||
M.EventRepository events,
|
|
||||||
M.NotificationRepository notifications,
|
|
||||||
M.ContextRepository contexts,
|
|
||||||
M.RetainedSizeRepository retainedSizes,
|
|
||||||
M.ReachableSizeRepository reachableSizes,
|
|
||||||
M.InboundReferencesRepository references,
|
|
||||||
M.RetainingPathRepository retainingPaths,
|
|
||||||
M.ObjectRepository objects,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(vm != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
assert(notifications != null);
|
|
||||||
assert(context != null);
|
|
||||||
assert(contexts != null);
|
|
||||||
assert(retainedSizes != null);
|
|
||||||
assert(reachableSizes != null);
|
|
||||||
assert(references != null);
|
|
||||||
assert(retainingPaths != null);
|
|
||||||
assert(objects != null);
|
|
||||||
ContextViewElement e = new ContextViewElement.created();
|
|
||||||
e._r = new RenderingScheduler<ContextViewElement>(e, queue: queue);
|
|
||||||
e._vm = vm;
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
e._notifications = notifications;
|
|
||||||
e._context = context;
|
|
||||||
e._contexts = contexts;
|
|
||||||
e._retainedSizes = retainedSizes;
|
|
||||||
e._reachableSizes = reachableSizes;
|
|
||||||
e._references = references;
|
|
||||||
e._retainingPaths = retainingPaths;
|
|
||||||
e._objects = objects;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
ContextViewElement.created() : super.created('context-view');
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
var content = <Element>[
|
|
||||||
navBar(<Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
|
|
||||||
new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
|
|
||||||
new NavClassMenuElement(_isolate, _context.clazz, queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
navMenu('instance'),
|
|
||||||
(new NavRefreshElement(queue: _r.queue)
|
|
||||||
..onRefresh.listen((e) async {
|
|
||||||
e.element.disabled = true;
|
|
||||||
_context = await _contexts.get(_isolate, _context.id);
|
|
||||||
_r.dirty();
|
|
||||||
}))
|
|
||||||
.element,
|
|
||||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
|
||||||
]),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered-big']
|
|
||||||
..children = <Element>[
|
|
||||||
new HeadingElement.h2()..text = 'Context',
|
|
||||||
new HRElement(),
|
|
||||||
new ObjectCommonElement(_isolate, _context, _retainedSizes,
|
|
||||||
_reachableSizes, _references, _retainingPaths, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
];
|
|
||||||
if (_context.parentContext != null) {
|
|
||||||
content.addAll([
|
|
||||||
new BRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered-big']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'parent context',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
new ContextRefElement(
|
|
||||||
_isolate, _context.parentContext, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
content.add(new HRElement());
|
|
||||||
if (_context.variables.isNotEmpty) {
|
|
||||||
int index = 0;
|
|
||||||
content.addAll([
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered-big']
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = 'Variables ',
|
|
||||||
(new CurlyBlockElement(expanded: true, queue: _r.queue)
|
|
||||||
..content = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = _context.variables
|
|
||||||
.map<Element>((variable) => new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = '[ ${++index} ]',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
anyRef(_isolate, variable.value, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
]
|
|
||||||
])
|
|
||||||
.toList()
|
|
||||||
])
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
children = content;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,169 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library cpu_profile_element;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/cpu_profile/virtual_tree.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/uris.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/refresh.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/vm_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/sample_buffer_control.dart';
|
|
||||||
import 'package:observatory_2/src/elements/stack_trace_tree_config.dart';
|
|
||||||
|
|
||||||
class CpuProfileElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<CpuProfileElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<CpuProfileElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.VM _vm;
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
M.IsolateSampleProfileRepository _profiles;
|
|
||||||
Stream<M.SampleProfileLoadingProgressEvent> _progressStream;
|
|
||||||
M.SampleProfileLoadingProgress _progress;
|
|
||||||
M.SampleProfileTag _tag = M.SampleProfileTag.none;
|
|
||||||
ProfileTreeMode _mode = ProfileTreeMode.function;
|
|
||||||
M.ProfileTreeDirection _direction = M.ProfileTreeDirection.exclusive;
|
|
||||||
String _filter = '';
|
|
||||||
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.NotificationRepository get notifications => _notifications;
|
|
||||||
M.IsolateSampleProfileRepository get profiles => _profiles;
|
|
||||||
M.VMRef get vm => _vm;
|
|
||||||
|
|
||||||
factory CpuProfileElement(
|
|
||||||
M.VM vm,
|
|
||||||
M.IsolateRef isolate,
|
|
||||||
M.EventRepository events,
|
|
||||||
M.NotificationRepository notifications,
|
|
||||||
M.IsolateSampleProfileRepository profiles,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(vm != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
assert(notifications != null);
|
|
||||||
assert(profiles != null);
|
|
||||||
CpuProfileElement e = new CpuProfileElement.created();
|
|
||||||
e._r = new RenderingScheduler<CpuProfileElement>(e, queue: queue);
|
|
||||||
e._vm = vm;
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
e._notifications = notifications;
|
|
||||||
e._profiles = profiles;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
CpuProfileElement.created() : super.created('cpu-profile');
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
_request();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
var content = <Element>[
|
|
||||||
navBar(<Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
|
|
||||||
new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
|
|
||||||
navMenu('cpu profile', link: Uris.cpuProfiler(_isolate)),
|
|
||||||
(new NavRefreshElement(queue: _r.queue)..onRefresh.listen(_refresh))
|
|
||||||
.element,
|
|
||||||
(new NavRefreshElement(label: 'Clear', queue: _r.queue)
|
|
||||||
..onRefresh.listen(_clearCpuProfile))
|
|
||||||
.element,
|
|
||||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
|
||||||
]),
|
|
||||||
];
|
|
||||||
if (_progress == null) {
|
|
||||||
children = content;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
content.add((new SampleBufferControlElement(_vm, _progress, _progressStream,
|
|
||||||
selectedTag: _tag, queue: _r.queue)
|
|
||||||
..onTagChange.listen((e) {
|
|
||||||
_tag = e.element.selectedTag;
|
|
||||||
_request();
|
|
||||||
}))
|
|
||||||
.element);
|
|
||||||
if (_progress.status == M.SampleProfileLoadingStatus.loaded) {
|
|
||||||
CpuProfileVirtualTreeElement tree;
|
|
||||||
content.addAll([
|
|
||||||
new BRElement(),
|
|
||||||
(new StackTraceTreeConfigElement(
|
|
||||||
mode: _mode,
|
|
||||||
direction: _direction,
|
|
||||||
filter: _filter,
|
|
||||||
queue: _r.queue)
|
|
||||||
..onModeChange.listen((e) {
|
|
||||||
_mode = tree.mode = e.element.mode;
|
|
||||||
})
|
|
||||||
..onFilterChange.listen((e) {
|
|
||||||
_filter = e.element.filter.trim();
|
|
||||||
tree.filters = _filter.isNotEmpty
|
|
||||||
? [
|
|
||||||
(node) {
|
|
||||||
return node.name.contains(_filter);
|
|
||||||
}
|
|
||||||
]
|
|
||||||
: const [];
|
|
||||||
})
|
|
||||||
..onDirectionChange.listen((e) {
|
|
||||||
_direction = tree.direction = e.element.direction;
|
|
||||||
}))
|
|
||||||
.element,
|
|
||||||
new BRElement(),
|
|
||||||
(tree = new CpuProfileVirtualTreeElement(_isolate, _progress.profile,
|
|
||||||
queue: _r.queue))
|
|
||||||
.element
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
children = content;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _request({bool clear = false, bool forceFetch = false}) async {
|
|
||||||
_progress = null;
|
|
||||||
_progressStream =
|
|
||||||
_profiles.get(isolate, _tag, clear: clear, forceFetch: forceFetch);
|
|
||||||
_r.dirty();
|
|
||||||
_progress = (await _progressStream.first).progress;
|
|
||||||
_r.dirty();
|
|
||||||
if (M.isSampleProcessRunning(_progress.status)) {
|
|
||||||
_progress = (await _progressStream.last).progress;
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _clearCpuProfile(RefreshEvent e) async {
|
|
||||||
e.element.disabled = true;
|
|
||||||
await _request(clear: true);
|
|
||||||
e.element.disabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _refresh(e) async {
|
|
||||||
e.element.disabled = true;
|
|
||||||
await _request(forceFetch: true);
|
|
||||||
e.element.disabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,308 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:math' as Math;
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/stack_trace_tree_config.dart'
|
|
||||||
show ProfileTreeMode;
|
|
||||||
import 'package:observatory_2/src/elements/code_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/containers/virtual_tree.dart';
|
|
||||||
import 'package:observatory_2/src/elements/function_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/utils.dart';
|
|
||||||
|
|
||||||
export 'package:observatory_2/src/elements/stack_trace_tree_config.dart'
|
|
||||||
show ProfileTreeMode;
|
|
||||||
|
|
||||||
class CpuProfileVirtualTreeElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<CpuProfileVirtualTreeElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<CpuProfileVirtualTreeElement>> get onRendered =>
|
|
||||||
_r.onRendered;
|
|
||||||
|
|
||||||
M.ProfileTreeDirection _direction;
|
|
||||||
ProfileTreeMode _mode;
|
|
||||||
M.SampleProfileType _type;
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.SampleProfile _profile;
|
|
||||||
Iterable<M.CallTreeNodeFilter> _filters;
|
|
||||||
|
|
||||||
M.ProfileTreeDirection get direction => _direction;
|
|
||||||
ProfileTreeMode get mode => _mode;
|
|
||||||
M.SampleProfileType get type => _type;
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.SampleProfile get profile => _profile;
|
|
||||||
Iterable<M.CallTreeNodeFilter> get filters => _filters;
|
|
||||||
|
|
||||||
set direction(M.ProfileTreeDirection value) =>
|
|
||||||
_direction = _r.checkAndReact(_direction, value);
|
|
||||||
set mode(ProfileTreeMode value) => _mode = _r.checkAndReact(_mode, value);
|
|
||||||
set filters(Iterable<M.CallTreeNodeFilter> value) {
|
|
||||||
_filters = new List.unmodifiable(value);
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
factory CpuProfileVirtualTreeElement(Object owner, M.SampleProfile profile,
|
|
||||||
{ProfileTreeMode mode = ProfileTreeMode.function,
|
|
||||||
M.SampleProfileType type = M.SampleProfileType.cpu,
|
|
||||||
M.ProfileTreeDirection direction = M.ProfileTreeDirection.exclusive,
|
|
||||||
RenderingQueue queue}) {
|
|
||||||
assert(profile != null);
|
|
||||||
assert(mode != null);
|
|
||||||
assert(direction != null);
|
|
||||||
CpuProfileVirtualTreeElement e = new CpuProfileVirtualTreeElement.created();
|
|
||||||
e._r =
|
|
||||||
new RenderingScheduler<CpuProfileVirtualTreeElement>(e, queue: queue);
|
|
||||||
e._isolate = owner;
|
|
||||||
e._profile = profile;
|
|
||||||
e._mode = mode;
|
|
||||||
e._type = type;
|
|
||||||
e._direction = direction;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
CpuProfileVirtualTreeElement.created()
|
|
||||||
: super.created('cpu-profile-virtual-tree');
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
VirtualTreeElement _tree;
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
var tree;
|
|
||||||
var create;
|
|
||||||
var update;
|
|
||||||
var search;
|
|
||||||
switch (type) {
|
|
||||||
case M.SampleProfileType.cpu:
|
|
||||||
create = _createCpuRow;
|
|
||||||
if (mode == ProfileTreeMode.code) {
|
|
||||||
update = _updateCpuCodeRow;
|
|
||||||
search = _searchCode;
|
|
||||||
tree = _profile.loadCodeTree(_direction);
|
|
||||||
} else if (mode == ProfileTreeMode.function) {
|
|
||||||
update = _updateCpuFunctionRow;
|
|
||||||
search = _searchFunction;
|
|
||||||
tree = _profile.loadFunctionTree(_direction);
|
|
||||||
} else {
|
|
||||||
throw new Exception('Unknown ProfileTreeMode: $mode');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case M.SampleProfileType.memory:
|
|
||||||
create = _createMemoryRow;
|
|
||||||
if (mode == ProfileTreeMode.code) {
|
|
||||||
update = _updateMemoryCodeRow;
|
|
||||||
search = _searchCode;
|
|
||||||
tree = _profile.loadCodeTree(_direction);
|
|
||||||
} else if (mode == ProfileTreeMode.function) {
|
|
||||||
update = _updateMemoryFunctionRow;
|
|
||||||
search = _searchFunction;
|
|
||||||
tree = _profile.loadFunctionTree(_direction);
|
|
||||||
} else {
|
|
||||||
throw new Exception('Unknown ProfileTreeMode: $mode');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception('Unknown SampleProfileType: $type');
|
|
||||||
}
|
|
||||||
if (filters != null) {
|
|
||||||
tree = filters.fold(tree, (tree, filter) {
|
|
||||||
return tree?.filtered(filter);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (tree == null) {
|
|
||||||
children = <Element>[new HeadingElement.h1()..text = 'No Results'];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_tree = new VirtualTreeElement(create, update, _getChildren,
|
|
||||||
items: tree.root.children, search: search, queue: _r.queue);
|
|
||||||
if (tree.root.children.length == 0) {
|
|
||||||
children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['tree-item']
|
|
||||||
..children = <Element>[new HeadingElement.h1()..text = 'No Samples']
|
|
||||||
];
|
|
||||||
return;
|
|
||||||
} else if (tree.root.children.length == 1) {
|
|
||||||
_tree.expand(tree.root.children.first, autoExpandSingleChildNodes: true);
|
|
||||||
}
|
|
||||||
children = <Element>[_tree.element];
|
|
||||||
}
|
|
||||||
|
|
||||||
static HtmlElement _createCpuRow(toggle) {
|
|
||||||
return new DivElement()
|
|
||||||
..classes = ['tree-item']
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['inclusive']
|
|
||||||
..title = 'global % on stack',
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['exclusive']
|
|
||||||
..title = 'global % executing',
|
|
||||||
new SpanElement()..classes = ['lines'],
|
|
||||||
new ButtonElement()
|
|
||||||
..classes = ['expander']
|
|
||||||
..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['percentage']
|
|
||||||
..title = 'tree node %',
|
|
||||||
new SpanElement()..classes = ['name']
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
static HtmlElement _createMemoryRow(toggle) {
|
|
||||||
return new DivElement()
|
|
||||||
..classes = ['tree-item']
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['inclusive']
|
|
||||||
..title = 'memory allocated from resulting calls: ',
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['exclusive']
|
|
||||||
..title = 'memory allocated during execution: ',
|
|
||||||
new SpanElement()..classes = ['lines'],
|
|
||||||
new ButtonElement()
|
|
||||||
..classes = ['expander']
|
|
||||||
..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['percentage']
|
|
||||||
..title = 'tree node %',
|
|
||||||
new SpanElement()..classes = ['name']
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
static Iterable<M.CallTreeNode> _getChildren(nodeDynamic) {
|
|
||||||
M.CallTreeNode node = nodeDynamic;
|
|
||||||
return node.children;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const String _expandedIcon = '▼';
|
|
||||||
static const String _collapsedIcon = '►';
|
|
||||||
|
|
||||||
void _updateCpuFunctionRow(HtmlElement element, itemDynamic, int depth) {
|
|
||||||
M.FunctionCallTreeNode item = itemDynamic;
|
|
||||||
element.children[0].text = Utils.formatPercentNormalized(
|
|
||||||
item.profileFunction.normalizedInclusiveTicks);
|
|
||||||
element.children[1].text = Utils.formatPercentNormalized(
|
|
||||||
item.profileFunction.normalizedExclusiveTicks);
|
|
||||||
_updateLines(element.children[2].children, depth);
|
|
||||||
if (item.children.isNotEmpty) {
|
|
||||||
element.children[3].text =
|
|
||||||
_tree.isExpanded(item) ? _expandedIcon : _collapsedIcon;
|
|
||||||
} else {
|
|
||||||
element.children[3].text = '';
|
|
||||||
}
|
|
||||||
element.children[4].text = Utils.formatPercentNormalized(item.percentage);
|
|
||||||
element.children[5] = (new FunctionRefElement(
|
|
||||||
_isolate, item.profileFunction.function,
|
|
||||||
queue: _r.queue)
|
|
||||||
..classes = ['name'])
|
|
||||||
.element;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateMemoryFunctionRow(HtmlElement element, itemDynamic, int depth) {
|
|
||||||
M.FunctionCallTreeNode item = itemDynamic;
|
|
||||||
element.children[0].text =
|
|
||||||
Utils.formatSize(item.inclusiveNativeAllocations);
|
|
||||||
element.children[0].title = 'memory allocated from resulting calls: '
|
|
||||||
'${item.inclusiveNativeAllocations}B';
|
|
||||||
element.children[1].text =
|
|
||||||
Utils.formatSize(item.exclusiveNativeAllocations);
|
|
||||||
element.children[1].title = 'memory allocated during execution: '
|
|
||||||
'${item.exclusiveNativeAllocations}B';
|
|
||||||
_updateLines(element.children[2].children, depth);
|
|
||||||
if (item.children.isNotEmpty) {
|
|
||||||
element.children[3].text =
|
|
||||||
_tree.isExpanded(item) ? _expandedIcon : _collapsedIcon;
|
|
||||||
} else {
|
|
||||||
element.children[3].text = '';
|
|
||||||
}
|
|
||||||
element.children[4].text = Utils.formatPercentNormalized(item.percentage);
|
|
||||||
element.children[5] = (new FunctionRefElement(
|
|
||||||
null, item.profileFunction.function,
|
|
||||||
queue: _r.queue)
|
|
||||||
..classes = ['name'])
|
|
||||||
.element;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _searchFunction(Pattern pattern, itemDynamic) {
|
|
||||||
M.FunctionCallTreeNode item = itemDynamic;
|
|
||||||
return M
|
|
||||||
.getFunctionFullName(item.profileFunction.function)
|
|
||||||
.contains(pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateCpuCodeRow(HtmlElement element, itemDynamic, int depth) {
|
|
||||||
M.CodeCallTreeNode item = itemDynamic;
|
|
||||||
element.children[0].text = Utils.formatPercentNormalized(
|
|
||||||
item.profileCode.normalizedInclusiveTicks);
|
|
||||||
element.children[1].text = Utils.formatPercentNormalized(
|
|
||||||
item.profileCode.normalizedExclusiveTicks);
|
|
||||||
_updateLines(element.children[2].children, depth);
|
|
||||||
if (item.children.isNotEmpty) {
|
|
||||||
element.children[3].text =
|
|
||||||
_tree.isExpanded(item) ? _expandedIcon : _collapsedIcon;
|
|
||||||
} else {
|
|
||||||
element.children[3].text = '';
|
|
||||||
}
|
|
||||||
element.children[4].text = Utils.formatPercentNormalized(item.percentage);
|
|
||||||
element.children[5] =
|
|
||||||
(new CodeRefElement(_isolate, item.profileCode.code, queue: _r.queue)
|
|
||||||
..classes = ['name'])
|
|
||||||
.element;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateMemoryCodeRow(HtmlElement element, itemDynamic, int depth) {
|
|
||||||
M.CodeCallTreeNode item = itemDynamic;
|
|
||||||
element.children[0].text =
|
|
||||||
Utils.formatSize(item.inclusiveNativeAllocations);
|
|
||||||
element.children[0].title = 'memory allocated from resulting calls: '
|
|
||||||
'${item.inclusiveNativeAllocations}B';
|
|
||||||
element.children[1].text =
|
|
||||||
Utils.formatSize(item.exclusiveNativeAllocations);
|
|
||||||
element.children[1].title = 'memory allocated during execution: '
|
|
||||||
'${item.exclusiveNativeAllocations}B';
|
|
||||||
_updateLines(element.children[2].children, depth);
|
|
||||||
if (item.children.isNotEmpty) {
|
|
||||||
element.children[3].text =
|
|
||||||
_tree.isExpanded(item) ? _expandedIcon : _collapsedIcon;
|
|
||||||
} else {
|
|
||||||
element.children[3].text = '';
|
|
||||||
}
|
|
||||||
element.children[4].text = Utils.formatPercentNormalized(item.percentage);
|
|
||||||
element.children[5] =
|
|
||||||
(new CodeRefElement(null, item.profileCode.code, queue: _r.queue)
|
|
||||||
..classes = ['name'])
|
|
||||||
.element;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _searchCode(Pattern pattern, itemDynamic) {
|
|
||||||
M.CodeCallTreeNode item = itemDynamic;
|
|
||||||
return item.profileCode.code.name.contains(pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
static _updateLines(List<Element> lines, int n) {
|
|
||||||
n = Math.max(0, n);
|
|
||||||
while (lines.length > n) {
|
|
||||||
lines.removeLast();
|
|
||||||
}
|
|
||||||
while (lines.length < n) {
|
|
||||||
lines.add(new SpanElement());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,473 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library cpu_profile_table_element;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/containers/virtual_collection.dart';
|
|
||||||
import 'package:observatory_2/src/elements/cpu_profile/virtual_tree.dart';
|
|
||||||
import 'package:observatory_2/src/elements/function_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/refresh.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/vm_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/sample_buffer_control.dart';
|
|
||||||
import 'package:observatory_2/src/elements/stack_trace_tree_config.dart';
|
|
||||||
import 'package:observatory_2/utils.dart';
|
|
||||||
|
|
||||||
enum _Table { functions, caller, callee }
|
|
||||||
|
|
||||||
enum _SortingField { exclusive, inclusive, caller, callee, method }
|
|
||||||
|
|
||||||
enum _SortingDirection { ascending, descending }
|
|
||||||
|
|
||||||
class CpuProfileTableElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<CpuProfileTableElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<CpuProfileTableElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.VM _vm;
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
M.IsolateSampleProfileRepository _profiles;
|
|
||||||
Stream<M.SampleProfileLoadingProgressEvent> _progressStream;
|
|
||||||
M.SampleProfileLoadingProgress _progress;
|
|
||||||
final _sortingField = <_Table, _SortingField>{
|
|
||||||
_Table.functions: _SortingField.exclusive,
|
|
||||||
_Table.caller: _SortingField.caller,
|
|
||||||
_Table.callee: _SortingField.callee,
|
|
||||||
};
|
|
||||||
final _sortingDirection = <_Table, _SortingDirection>{
|
|
||||||
_Table.functions: _SortingDirection.descending,
|
|
||||||
_Table.caller: _SortingDirection.descending,
|
|
||||||
_Table.callee: _SortingDirection.descending,
|
|
||||||
};
|
|
||||||
String _filter = '';
|
|
||||||
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.NotificationRepository get notifications => _notifications;
|
|
||||||
M.IsolateSampleProfileRepository get profiles => _profiles;
|
|
||||||
M.VMRef get vm => _vm;
|
|
||||||
|
|
||||||
factory CpuProfileTableElement(
|
|
||||||
M.VM vm,
|
|
||||||
M.IsolateRef isolate,
|
|
||||||
M.EventRepository events,
|
|
||||||
M.NotificationRepository notifications,
|
|
||||||
M.IsolateSampleProfileRepository profiles,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(vm != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
assert(notifications != null);
|
|
||||||
assert(profiles != null);
|
|
||||||
CpuProfileTableElement e = new CpuProfileTableElement.created();
|
|
||||||
e._r = new RenderingScheduler<CpuProfileTableElement>(e, queue: queue);
|
|
||||||
e._vm = vm;
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
e._notifications = notifications;
|
|
||||||
e._profiles = profiles;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
CpuProfileTableElement.created() : super.created('cpu-profile-table');
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
_request();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
var content = <Element>[
|
|
||||||
navBar(<Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
|
|
||||||
new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
|
|
||||||
navMenu('cpu profile (table)'),
|
|
||||||
(new NavRefreshElement(queue: _r.queue)..onRefresh.listen(_refresh))
|
|
||||||
.element,
|
|
||||||
(new NavRefreshElement(label: 'Clear', queue: _r.queue)
|
|
||||||
..onRefresh.listen(_clearCpuSamples))
|
|
||||||
.element,
|
|
||||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
|
||||||
]),
|
|
||||||
];
|
|
||||||
if (_progress == null) {
|
|
||||||
children = content;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
content.add(new SampleBufferControlElement(_vm, _progress, _progressStream,
|
|
||||||
showTag: false, queue: _r.queue)
|
|
||||||
.element);
|
|
||||||
if (_progress.status == M.SampleProfileLoadingStatus.loaded) {
|
|
||||||
content.add(new BRElement());
|
|
||||||
content.addAll(_createTables());
|
|
||||||
content.add(new BRElement());
|
|
||||||
content.addAll(_createTree());
|
|
||||||
}
|
|
||||||
children = content;
|
|
||||||
}
|
|
||||||
|
|
||||||
M.ProfileFunction _selected;
|
|
||||||
VirtualCollectionElement _functions;
|
|
||||||
VirtualCollectionElement _callers;
|
|
||||||
VirtualCollectionElement _callees;
|
|
||||||
|
|
||||||
List<Element> _createTables() {
|
|
||||||
_functions = _functions ??
|
|
||||||
new VirtualCollectionElement(_createFunction, _updateFunction,
|
|
||||||
createHeader: _createFunctionHeader,
|
|
||||||
search: _searchFunction,
|
|
||||||
queue: _r.queue);
|
|
||||||
// If there's no samples, don't populate the function list.
|
|
||||||
_functions.items = (_progress.profile.sampleCount != 0)
|
|
||||||
? _progress.profile.functions.toList()
|
|
||||||
: []
|
|
||||||
..sort(_createSorter(_Table.functions));
|
|
||||||
_functions.takeIntoView(_selected);
|
|
||||||
_callers = _callers ??
|
|
||||||
new VirtualCollectionElement(_createCaller, _updateCaller,
|
|
||||||
createHeader: _createCallerHeader,
|
|
||||||
search: _searchFunction,
|
|
||||||
queue: _r.queue);
|
|
||||||
_callees = _callees ??
|
|
||||||
new VirtualCollectionElement(_createCallee, _updateCallee,
|
|
||||||
createHeader: _createCalleeHeader,
|
|
||||||
search: _searchFunction,
|
|
||||||
queue: _r.queue);
|
|
||||||
if (_selected != null) {
|
|
||||||
_callers.items = _selected.callers.keys.toList()
|
|
||||||
..sort(_createSorter(_Table.caller));
|
|
||||||
_callees.items = _selected.callees.keys.toList()
|
|
||||||
..sort(_createSorter(_Table.callee));
|
|
||||||
} else {
|
|
||||||
_callers.items = const [];
|
|
||||||
_callees.items = const [];
|
|
||||||
}
|
|
||||||
return <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['profile-trees']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['profile-trees-all']
|
|
||||||
..children = <Element>[_functions.element],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['profile-trees-current']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['profile-trees-caller']
|
|
||||||
..children = <Element>[_callers.element],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['profile-trees-selected']
|
|
||||||
..children = _selected == null
|
|
||||||
? [new SpanElement()..text = 'No element selected']
|
|
||||||
: [
|
|
||||||
new FunctionRefElement(_isolate, _selected.function,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['profile-trees-callee']
|
|
||||||
..children = <Element>[_callees.element]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
HtmlElement _createFunction() {
|
|
||||||
final element = new DivElement()
|
|
||||||
..classes = ['function-item']
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['exclusive']
|
|
||||||
..text = '0%',
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['inclusive']
|
|
||||||
..text = '0%',
|
|
||||||
new SpanElement()..classes = ['name']
|
|
||||||
];
|
|
||||||
element.onClick.listen((e) {
|
|
||||||
if (e.target is AnchorElement) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_selected = _functions.getItemFromElement(element);
|
|
||||||
_r.dirty();
|
|
||||||
});
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateFunction(Element e, itemDynamic, int index) {
|
|
||||||
M.ProfileFunction item = itemDynamic;
|
|
||||||
if (item == _selected) {
|
|
||||||
e.classes = ['function-item', 'selected'];
|
|
||||||
} else {
|
|
||||||
e.classes = ['function-item'];
|
|
||||||
}
|
|
||||||
e.children[0].text = Utils.formatPercentNormalized(_getExclusiveT(item));
|
|
||||||
e.children[1].text = Utils.formatPercentNormalized(_getInclusiveT(item));
|
|
||||||
e.children[2].text = M.getFunctionFullName(item.function);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<HtmlElement> _createFunctionHeader() => [
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['function-item']
|
|
||||||
..children = <Element>[
|
|
||||||
_createHeaderButton(
|
|
||||||
const ['exclusive'],
|
|
||||||
'Execution(%)',
|
|
||||||
_Table.functions,
|
|
||||||
_SortingField.exclusive,
|
|
||||||
_SortingDirection.descending),
|
|
||||||
_createHeaderButton(
|
|
||||||
const ['inclusive'],
|
|
||||||
'Stack(%)',
|
|
||||||
_Table.functions,
|
|
||||||
_SortingField.inclusive,
|
|
||||||
_SortingDirection.descending),
|
|
||||||
_createHeaderButton(const ['name'], 'Method', _Table.functions,
|
|
||||||
_SortingField.method, _SortingDirection.ascending),
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
bool _searchFunction(Pattern pattern, itemDynamic) {
|
|
||||||
M.ProfileFunction item = itemDynamic;
|
|
||||||
return M.getFunctionFullName(item.function).contains(pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _setSorting(
|
|
||||||
_Table table, _SortingField field, _SortingDirection defaultDirection) {
|
|
||||||
if (_sortingField[table] == field) {
|
|
||||||
switch (_sortingDirection[table]) {
|
|
||||||
case _SortingDirection.descending:
|
|
||||||
_sortingDirection[table] = _SortingDirection.ascending;
|
|
||||||
break;
|
|
||||||
case _SortingDirection.ascending:
|
|
||||||
_sortingDirection[table] = _SortingDirection.descending;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_sortingDirection[table] = defaultDirection;
|
|
||||||
_sortingField[table] = field;
|
|
||||||
}
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
HtmlElement _createCallee() {
|
|
||||||
final element = new DivElement()
|
|
||||||
..classes = ['function-item']
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['inclusive']
|
|
||||||
..text = '0%',
|
|
||||||
new SpanElement()..classes = ['name']
|
|
||||||
];
|
|
||||||
element.onClick.listen((e) {
|
|
||||||
if (e.target is AnchorElement) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_selected = _callees.getItemFromElement(element);
|
|
||||||
_r.dirty();
|
|
||||||
});
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateCallee(Element e, item, int index) {
|
|
||||||
e.children[0].text = Utils.formatPercentNormalized(_getCalleeT(item));
|
|
||||||
e.children[1].text = M.getFunctionFullName(item.function);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<HtmlElement> _createCalleeHeader() => [
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['function-item']
|
|
||||||
..children = <Element>[
|
|
||||||
_createHeaderButton(
|
|
||||||
const ['inclusive'],
|
|
||||||
'Callees(%)',
|
|
||||||
_Table.callee,
|
|
||||||
_SortingField.callee,
|
|
||||||
_SortingDirection.descending),
|
|
||||||
_createHeaderButton(const ['name'], 'Method', _Table.callee,
|
|
||||||
_SortingField.method, _SortingDirection.ascending),
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
HtmlElement _createCaller() {
|
|
||||||
final element = new DivElement()
|
|
||||||
..classes = ['function-item']
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['inclusive']
|
|
||||||
..text = '0%',
|
|
||||||
new SpanElement()..classes = ['name']
|
|
||||||
];
|
|
||||||
element.onClick.listen((e) {
|
|
||||||
if (e.target is AnchorElement) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_selected = _callers.getItemFromElement(element);
|
|
||||||
_r.dirty();
|
|
||||||
});
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateCaller(Element e, item, int index) {
|
|
||||||
e.children[0].text = Utils.formatPercentNormalized(_getCallerT(item));
|
|
||||||
e.children[1].text = M.getFunctionFullName(item.function);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<HtmlElement> _createCallerHeader() => [
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['function-item']
|
|
||||||
..children = <Element>[
|
|
||||||
_createHeaderButton(
|
|
||||||
const ['inclusive'],
|
|
||||||
'Callers(%)',
|
|
||||||
_Table.caller,
|
|
||||||
_SortingField.caller,
|
|
||||||
_SortingDirection.descending),
|
|
||||||
_createHeaderButton(const ['name'], 'Method', _Table.caller,
|
|
||||||
_SortingField.method, _SortingDirection.ascending),
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
ButtonElement _createHeaderButton(List<String> classes, String text,
|
|
||||||
_Table table, _SortingField field, _SortingDirection direction) =>
|
|
||||||
new ButtonElement()
|
|
||||||
..classes = classes
|
|
||||||
..text = _sortingField[table] != field
|
|
||||||
? text
|
|
||||||
: _sortingDirection[table] == _SortingDirection.ascending
|
|
||||||
? '$text▼'
|
|
||||||
: '$text▲'
|
|
||||||
..onClick.listen((_) => _setSorting(table, field, direction));
|
|
||||||
|
|
||||||
List<Element> _createTree() {
|
|
||||||
CpuProfileVirtualTreeElement tree;
|
|
||||||
return [
|
|
||||||
(new StackTraceTreeConfigElement(
|
|
||||||
showMode: false,
|
|
||||||
showDirection: false,
|
|
||||||
mode: ProfileTreeMode.function,
|
|
||||||
direction: M.ProfileTreeDirection.exclusive,
|
|
||||||
filter: _filter,
|
|
||||||
queue: _r.queue)
|
|
||||||
..onFilterChange.listen((e) {
|
|
||||||
_filter = e.element.filter.trim();
|
|
||||||
tree.filters = _filter.isNotEmpty
|
|
||||||
? [
|
|
||||||
_filterTree,
|
|
||||||
(node) {
|
|
||||||
return node.name.contains(_filter);
|
|
||||||
}
|
|
||||||
]
|
|
||||||
: [_filterTree];
|
|
||||||
}))
|
|
||||||
.element,
|
|
||||||
new BRElement(),
|
|
||||||
(tree = new CpuProfileVirtualTreeElement(_isolate, _progress.profile,
|
|
||||||
mode: ProfileTreeMode.function,
|
|
||||||
direction: M.ProfileTreeDirection.exclusive,
|
|
||||||
queue: _r.queue)
|
|
||||||
..filters = _filter.isNotEmpty
|
|
||||||
? [
|
|
||||||
_filterTree,
|
|
||||||
(node) {
|
|
||||||
return node.name.contains(_filter);
|
|
||||||
}
|
|
||||||
]
|
|
||||||
: [_filterTree])
|
|
||||||
.element
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _filterTree(nodeDynamic) {
|
|
||||||
M.FunctionCallTreeNode node = nodeDynamic;
|
|
||||||
return node.profileFunction == _selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _request({bool clear = false, bool forceFetch = false}) async {
|
|
||||||
_progress = null;
|
|
||||||
_progressStream = _profiles.get(isolate, M.SampleProfileTag.vmOnly,
|
|
||||||
clear: clear, forceFetch: forceFetch);
|
|
||||||
_r.dirty();
|
|
||||||
_progress = (await _progressStream.first).progress;
|
|
||||||
_r.dirty();
|
|
||||||
if (M.isSampleProcessRunning(_progress.status)) {
|
|
||||||
_progress = (await _progressStream.last).progress;
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _clearCpuSamples(RefreshEvent e) async {
|
|
||||||
e.element.disabled = true;
|
|
||||||
await _request(clear: true);
|
|
||||||
e.element.disabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _refresh(e) async {
|
|
||||||
e.element.disabled = true;
|
|
||||||
await _request(forceFetch: true);
|
|
||||||
e.element.disabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_createSorter(_Table table) {
|
|
||||||
var getter;
|
|
||||||
switch (_sortingField[table]) {
|
|
||||||
case _SortingField.exclusive:
|
|
||||||
getter = _getExclusiveT;
|
|
||||||
break;
|
|
||||||
case _SortingField.inclusive:
|
|
||||||
getter = _getInclusiveT;
|
|
||||||
break;
|
|
||||||
case _SortingField.callee:
|
|
||||||
getter = _getCalleeT;
|
|
||||||
break;
|
|
||||||
case _SortingField.caller:
|
|
||||||
getter = _getCallerT;
|
|
||||||
break;
|
|
||||||
case _SortingField.method:
|
|
||||||
getter = (M.ProfileFunction s) => M.getFunctionFullName(s.function);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch (_sortingDirection[table]) {
|
|
||||||
case _SortingDirection.ascending:
|
|
||||||
int sort(a, b) {
|
|
||||||
return getter(a).compareTo(getter(b));
|
|
||||||
}
|
|
||||||
return sort;
|
|
||||||
case _SortingDirection.descending:
|
|
||||||
int sort(a, b) {
|
|
||||||
return getter(b).compareTo(getter(a));
|
|
||||||
}
|
|
||||||
return sort;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static double _getExclusiveT(M.ProfileFunction f) =>
|
|
||||||
f.normalizedExclusiveTicks;
|
|
||||||
static double _getInclusiveT(M.ProfileFunction f) =>
|
|
||||||
f.normalizedInclusiveTicks;
|
|
||||||
double _getCalleeT(M.ProfileFunction f) =>
|
|
||||||
_selected.callees[f] / _selected.callees.values.reduce((a, b) => a + b);
|
|
||||||
double _getCallerT(M.ProfileFunction f) =>
|
|
||||||
_selected.callers[f] / _selected.callers.values.reduce((a, b) => a + b);
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,102 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library curly_block_element;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
|
|
||||||
class CurlyBlockToggleEvent {
|
|
||||||
final CurlyBlockElement control;
|
|
||||||
|
|
||||||
CurlyBlockToggleEvent(this.control);
|
|
||||||
}
|
|
||||||
|
|
||||||
class CurlyBlockElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<CurlyBlockElement> _r;
|
|
||||||
|
|
||||||
final StreamController<CurlyBlockToggleEvent> _onToggle =
|
|
||||||
new StreamController<CurlyBlockToggleEvent>.broadcast();
|
|
||||||
Stream<CurlyBlockToggleEvent> get onToggle => _onToggle.stream;
|
|
||||||
Stream<RenderedEvent<CurlyBlockElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
bool _expanded;
|
|
||||||
bool _disabled;
|
|
||||||
Iterable<Element> _content = const [];
|
|
||||||
|
|
||||||
bool get expanded => _expanded;
|
|
||||||
bool get disabled => _disabled;
|
|
||||||
Iterable<Element> get content => _content;
|
|
||||||
|
|
||||||
set expanded(bool value) {
|
|
||||||
if (_expanded != value) _onToggle.add(new CurlyBlockToggleEvent(this));
|
|
||||||
_expanded = _r.checkAndReact(_expanded, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
set disabled(bool value) => _disabled = _r.checkAndReact(_disabled, value);
|
|
||||||
set content(Iterable<Element> value) {
|
|
||||||
_content = value.toList();
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
factory CurlyBlockElement(
|
|
||||||
{bool expanded = false, bool disabled = false, RenderingQueue queue}) {
|
|
||||||
assert(expanded != null);
|
|
||||||
assert(disabled != null);
|
|
||||||
CurlyBlockElement e = new CurlyBlockElement.created();
|
|
||||||
e._r = new RenderingScheduler<CurlyBlockElement>(e, queue: queue);
|
|
||||||
e._expanded = expanded;
|
|
||||||
e._disabled = disabled;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
CurlyBlockElement.created() : super.created('curly-block');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
void toggle() {
|
|
||||||
if (disabled) {
|
|
||||||
_r.scheduleNotification();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
expanded = !expanded;
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
List<Element> content = <Element>[new SpanElement()..text = '{'];
|
|
||||||
SpanElement label = new SpanElement()
|
|
||||||
..classes = disabled ? ['curly-block', 'disabled'] : ['curly-block']
|
|
||||||
..innerHtml = expanded
|
|
||||||
? ' ⊟ '
|
|
||||||
: ' ⊞ ';
|
|
||||||
if (disabled) {
|
|
||||||
content.add(label);
|
|
||||||
} else {
|
|
||||||
content.add(new AnchorElement()
|
|
||||||
..onClick.listen((_) {
|
|
||||||
toggle();
|
|
||||||
})
|
|
||||||
..children = <Element>[label]);
|
|
||||||
}
|
|
||||||
if (expanded) {
|
|
||||||
content.add(new BRElement());
|
|
||||||
content.addAll(_content);
|
|
||||||
}
|
|
||||||
content.add(new SpanElement()..text = '}');
|
|
||||||
children = content;
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,48 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library error_ref_element;
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' show ErrorRef;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
|
|
||||||
class ErrorRefElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<ErrorRefElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<ErrorRefElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
ErrorRef _error;
|
|
||||||
|
|
||||||
ErrorRef get error => _error;
|
|
||||||
|
|
||||||
factory ErrorRefElement(ErrorRef error, {RenderingQueue queue}) {
|
|
||||||
assert(error != null);
|
|
||||||
ErrorRefElement e = new ErrorRefElement.created();
|
|
||||||
e._r = new RenderingScheduler<ErrorRefElement>(e, queue: queue);
|
|
||||||
e._error = error;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorRefElement.created() : super.created('error-ref');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
children = <Element>[];
|
|
||||||
_r.disable(notify: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
children = <Element>[new PreElement()..text = error.message];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library error_view_element;
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
|
|
||||||
class ErrorViewElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<ErrorViewElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<ErrorViewElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.Error _error;
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
|
|
||||||
M.Error get error => _error;
|
|
||||||
|
|
||||||
factory ErrorViewElement(
|
|
||||||
M.NotificationRepository notifications, M.Error error,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(error != null);
|
|
||||||
assert(notifications != null);
|
|
||||||
ErrorViewElement e = new ErrorViewElement.created();
|
|
||||||
e._r = new RenderingScheduler<ErrorViewElement>(e, queue: queue);
|
|
||||||
e._error = error;
|
|
||||||
e._notifications = notifications;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorViewElement.created() : super.created('error-view');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
children = <Element>[];
|
|
||||||
_r.disable(notify: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
children = <Element>[
|
|
||||||
navBar(<Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
|
||||||
]),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered']
|
|
||||||
..children = <Element>[
|
|
||||||
new HeadingElement.h1()
|
|
||||||
..text = 'Error: ${_kindToString(_error.kind)}',
|
|
||||||
new BRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['well']
|
|
||||||
..children = <Element>[new PreElement()..text = error.message]
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
static String _kindToString(M.ErrorKind kind) {
|
|
||||||
switch (kind) {
|
|
||||||
case M.ErrorKind.unhandledException:
|
|
||||||
return 'Unhandled Exception';
|
|
||||||
case M.ErrorKind.languageError:
|
|
||||||
return 'Language Error';
|
|
||||||
case M.ErrorKind.internalError:
|
|
||||||
return 'Internal Error';
|
|
||||||
case M.ErrorKind.terminationError:
|
|
||||||
return 'Termination Error';
|
|
||||||
}
|
|
||||||
throw new Exception('Unknown M.ErrorKind ($kind)');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,206 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library eval_box_element;
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/any_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
|
|
||||||
class EvalBoxElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<EvalBoxElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<EvalBoxElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.ObjectRef _context;
|
|
||||||
M.ObjectRepository _objects;
|
|
||||||
M.EvalRepository _eval;
|
|
||||||
final _results = <_ExpressionDescription>[];
|
|
||||||
String _expression = '';
|
|
||||||
bool _multiline;
|
|
||||||
Iterable<String> _quickExpressions;
|
|
||||||
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.ObjectRef get context => _context;
|
|
||||||
|
|
||||||
factory EvalBoxElement(M.IsolateRef isolate, M.ObjectRef context,
|
|
||||||
M.ObjectRepository objects, M.EvalRepository eval,
|
|
||||||
{bool multiline = false,
|
|
||||||
Iterable<String> quickExpressions = const [],
|
|
||||||
RenderingQueue queue}) {
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(context != null);
|
|
||||||
assert(objects != null);
|
|
||||||
assert(eval != null);
|
|
||||||
assert(multiline != null);
|
|
||||||
assert(quickExpressions != null);
|
|
||||||
EvalBoxElement e = new EvalBoxElement.created();
|
|
||||||
e._r = new RenderingScheduler<EvalBoxElement>(e, queue: queue);
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._context = context;
|
|
||||||
e._objects = objects;
|
|
||||||
e._eval = eval;
|
|
||||||
e._multiline = multiline;
|
|
||||||
e._quickExpressions = new List.unmodifiable(quickExpressions);
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
EvalBoxElement.created() : super.created('eval-box');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
_results.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['quicks']
|
|
||||||
..children = _quickExpressions
|
|
||||||
.map<Element>((q) => new ButtonElement()
|
|
||||||
..text = q
|
|
||||||
..onClick.listen((_) {
|
|
||||||
_expression = q;
|
|
||||||
_run();
|
|
||||||
}))
|
|
||||||
.toList(),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['heading']
|
|
||||||
..children = <Element>[
|
|
||||||
new FormElement()
|
|
||||||
..autocomplete = 'on'
|
|
||||||
..children = <Element>[
|
|
||||||
_multiline ? _createEvalTextArea() : _createEvalTextBox(),
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['buttons']
|
|
||||||
..children = <Element>[
|
|
||||||
_createEvalButton(),
|
|
||||||
_createMultilineCheckbox(),
|
|
||||||
new SpanElement()..text = 'Multi-line'
|
|
||||||
]
|
|
||||||
]
|
|
||||||
],
|
|
||||||
new TableElement()
|
|
||||||
..children = _results.reversed
|
|
||||||
.map<Element>((result) => new TableRowElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new TableCellElement()
|
|
||||||
..classes = ['historyExpr']
|
|
||||||
..children = <Element>[
|
|
||||||
new ButtonElement()
|
|
||||||
..text = result.expression
|
|
||||||
..onClick.listen((_) {
|
|
||||||
_expression = result.expression;
|
|
||||||
_r.dirty();
|
|
||||||
})
|
|
||||||
],
|
|
||||||
new TableCellElement()
|
|
||||||
..classes = ['historyValue']
|
|
||||||
..children = <Element>[
|
|
||||||
result.isPending
|
|
||||||
? (new SpanElement()..text = 'Pending...')
|
|
||||||
: anyRef(_isolate, result.value, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
],
|
|
||||||
new TableCellElement()
|
|
||||||
..classes = ['historyDelete']
|
|
||||||
..children = <Element>[
|
|
||||||
new ButtonElement()
|
|
||||||
..text = '✖ Remove'
|
|
||||||
..onClick.listen((_) {
|
|
||||||
_results.remove(result);
|
|
||||||
_r.dirty();
|
|
||||||
})
|
|
||||||
]
|
|
||||||
])
|
|
||||||
.toList()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
TextAreaElement _createEvalTextArea() {
|
|
||||||
var area = new TextAreaElement()
|
|
||||||
..classes = ['textbox']
|
|
||||||
..placeholder = 'evaluate an expression'
|
|
||||||
..value = _expression
|
|
||||||
..onKeyUp.where((e) => e.key == '\n').listen((e) {
|
|
||||||
e.preventDefault();
|
|
||||||
_run();
|
|
||||||
});
|
|
||||||
area.onInput.listen((e) {
|
|
||||||
_expression = area.value;
|
|
||||||
});
|
|
||||||
return area;
|
|
||||||
}
|
|
||||||
|
|
||||||
TextInputElement _createEvalTextBox() {
|
|
||||||
_expression = (_expression ?? '').split('\n')[0];
|
|
||||||
var textbox = new TextInputElement()
|
|
||||||
..classes = ['textbox']
|
|
||||||
..placeholder = 'evaluate an expression'
|
|
||||||
..value = _expression
|
|
||||||
..onKeyUp.where((e) => e.key == '\n').listen((e) {
|
|
||||||
e.preventDefault();
|
|
||||||
_run();
|
|
||||||
});
|
|
||||||
textbox.onInput.listen((e) {
|
|
||||||
_expression = textbox.value;
|
|
||||||
});
|
|
||||||
return textbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
ButtonElement _createEvalButton() {
|
|
||||||
final button = new ButtonElement()
|
|
||||||
..text = 'Evaluate'
|
|
||||||
..onClick.listen((e) {
|
|
||||||
e.preventDefault();
|
|
||||||
_run();
|
|
||||||
});
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckboxInputElement _createMultilineCheckbox() {
|
|
||||||
final checkbox = new CheckboxInputElement()..checked = _multiline;
|
|
||||||
checkbox.onClick.listen((e) {
|
|
||||||
e.preventDefault();
|
|
||||||
_multiline = checkbox.checked;
|
|
||||||
_r.dirty();
|
|
||||||
});
|
|
||||||
return checkbox;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _run() async {
|
|
||||||
if (_expression == null || _expression.isEmpty) return;
|
|
||||||
final expression = _expression;
|
|
||||||
_expression = null;
|
|
||||||
final result = new _ExpressionDescription.pending(expression);
|
|
||||||
_results.add(result);
|
|
||||||
_r.dirty();
|
|
||||||
final index = _results.indexOf(result);
|
|
||||||
_results[index] = new _ExpressionDescription(
|
|
||||||
expression, await _eval.evaluate(_isolate, _context, expression));
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ExpressionDescription {
|
|
||||||
final String expression;
|
|
||||||
final M.ObjectRef value;
|
|
||||||
bool get isPending => value == null;
|
|
||||||
|
|
||||||
_ExpressionDescription(this.expression, this.value);
|
|
||||||
_ExpressionDescription.pending(this.expression) : value = null;
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/uris.dart';
|
|
||||||
import 'package:observatory_2/src/elements/instance_ref.dart';
|
|
||||||
|
|
||||||
class FieldRefElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<FieldRefElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<FieldRefElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.FieldRef _field;
|
|
||||||
M.ObjectRepository _objects;
|
|
||||||
bool _expandable;
|
|
||||||
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.FieldRef get field => _field;
|
|
||||||
|
|
||||||
factory FieldRefElement(
|
|
||||||
M.IsolateRef isolate, M.FieldRef field, M.ObjectRepository objects,
|
|
||||||
{RenderingQueue queue, bool expandable = true}) {
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(field != null);
|
|
||||||
assert(objects != null);
|
|
||||||
FieldRefElement e = new FieldRefElement.created();
|
|
||||||
e._r = new RenderingScheduler<FieldRefElement>(e, queue: queue);
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._field = field;
|
|
||||||
e._objects = objects;
|
|
||||||
e._expandable = expandable;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
FieldRefElement.created() : super.created('field-ref');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
var header = '';
|
|
||||||
if (_field.isStatic) {
|
|
||||||
if (_field.dartOwner is M.ClassRef) {
|
|
||||||
header += 'static ';
|
|
||||||
} else {
|
|
||||||
header += 'top-level ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_field.isFinal) {
|
|
||||||
header += 'final ';
|
|
||||||
} else if (_field.isConst) {
|
|
||||||
header += 'const ';
|
|
||||||
} else if (_field.declaredType.name == 'dynamic') {
|
|
||||||
header += 'var ';
|
|
||||||
}
|
|
||||||
if (_field.declaredType.name == 'dynamic') {
|
|
||||||
children = <Element>[
|
|
||||||
new SpanElement()..text = header,
|
|
||||||
new AnchorElement(href: Uris.inspect(_isolate, object: _field))
|
|
||||||
..text = _field.name
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
children = <Element>[
|
|
||||||
new SpanElement()..text = header,
|
|
||||||
new InstanceRefElement(_isolate, _field.declaredType, _objects,
|
|
||||||
queue: _r.queue, expandable: _expandable)
|
|
||||||
.element,
|
|
||||||
new SpanElement()..text = ' ',
|
|
||||||
new AnchorElement(href: Uris.inspect(_isolate, object: _field))
|
|
||||||
..text = _field.name
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,288 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library field_view_element;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/class_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/any_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/class_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/library_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/refresh.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/vm_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/object_common.dart';
|
|
||||||
import 'package:observatory_2/src/elements/script_inset.dart';
|
|
||||||
import 'package:observatory_2/src/elements/source_link.dart';
|
|
||||||
|
|
||||||
class FieldViewElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<FieldViewElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<FieldViewElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.VM _vm;
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
M.Field _field;
|
|
||||||
M.LibraryRef _library;
|
|
||||||
M.FieldRepository _fields;
|
|
||||||
M.ClassRepository _classes;
|
|
||||||
M.RetainedSizeRepository _retainedSizes;
|
|
||||||
M.ReachableSizeRepository _reachableSizes;
|
|
||||||
M.InboundReferencesRepository _references;
|
|
||||||
M.RetainingPathRepository _retainingPaths;
|
|
||||||
M.ScriptRepository _scripts;
|
|
||||||
M.ObjectRepository _objects;
|
|
||||||
|
|
||||||
M.VMRef get vm => _vm;
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.NotificationRepository get notifications => _notifications;
|
|
||||||
M.Field get field => _field;
|
|
||||||
|
|
||||||
factory FieldViewElement(
|
|
||||||
M.VM vm,
|
|
||||||
M.IsolateRef isolate,
|
|
||||||
M.Field field,
|
|
||||||
M.EventRepository events,
|
|
||||||
M.NotificationRepository notifications,
|
|
||||||
M.FieldRepository fields,
|
|
||||||
M.ClassRepository classes,
|
|
||||||
M.RetainedSizeRepository retainedSizes,
|
|
||||||
M.ReachableSizeRepository reachableSizes,
|
|
||||||
M.InboundReferencesRepository references,
|
|
||||||
M.RetainingPathRepository retainingPaths,
|
|
||||||
M.ScriptRepository scripts,
|
|
||||||
M.ObjectRepository objects,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(vm != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
assert(notifications != null);
|
|
||||||
assert(field != null);
|
|
||||||
assert(fields != null);
|
|
||||||
assert(classes != null);
|
|
||||||
assert(retainedSizes != null);
|
|
||||||
assert(reachableSizes != null);
|
|
||||||
assert(references != null);
|
|
||||||
assert(retainingPaths != null);
|
|
||||||
assert(scripts != null);
|
|
||||||
assert(objects != null);
|
|
||||||
FieldViewElement e = new FieldViewElement.created();
|
|
||||||
e._r = new RenderingScheduler<FieldViewElement>(e, queue: queue);
|
|
||||||
e._vm = vm;
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
e._notifications = notifications;
|
|
||||||
e._field = field;
|
|
||||||
e._fields = fields;
|
|
||||||
e._classes = classes;
|
|
||||||
e._retainedSizes = retainedSizes;
|
|
||||||
e._reachableSizes = reachableSizes;
|
|
||||||
e._references = references;
|
|
||||||
e._retainingPaths = retainingPaths;
|
|
||||||
e._scripts = scripts;
|
|
||||||
e._objects = objects;
|
|
||||||
if (field.dartOwner is M.LibraryRef) {
|
|
||||||
e._library = field.dartOwner;
|
|
||||||
}
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
FieldViewElement.created() : super.created('field-view');
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
_refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
var header = '';
|
|
||||||
if (_field.isStatic) {
|
|
||||||
if (_field.dartOwner is M.ClassRef) {
|
|
||||||
header += 'static ';
|
|
||||||
} else {
|
|
||||||
header += 'top-level ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_field.isFinal) {
|
|
||||||
header += 'final ';
|
|
||||||
} else if (_field.isConst) {
|
|
||||||
header += 'const ';
|
|
||||||
}
|
|
||||||
if (_field.declaredType.name == 'dynamic') {
|
|
||||||
header += 'var';
|
|
||||||
} else {
|
|
||||||
header += _field.declaredType.name;
|
|
||||||
}
|
|
||||||
children = <Element>[
|
|
||||||
navBar(_createMenu()),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered-big']
|
|
||||||
..children = <Element>[
|
|
||||||
new HeadingElement.h2()..text = '$header ${field.name}',
|
|
||||||
new HRElement(),
|
|
||||||
new ObjectCommonElement(_isolate, _field, _retainedSizes,
|
|
||||||
_reachableSizes, _references, _retainingPaths, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new BRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = _createMembers(),
|
|
||||||
new HRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..children = _field.location == null
|
|
||||||
? const []
|
|
||||||
: [
|
|
||||||
new ScriptInsetElement(_isolate, _field.location.script,
|
|
||||||
_scripts, _objects, _events,
|
|
||||||
startPos: field.location.tokenPos,
|
|
||||||
endPos: field.location.tokenPos,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
],
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createMenu() {
|
|
||||||
final menu = <Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
|
|
||||||
new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element
|
|
||||||
];
|
|
||||||
if (_library != null) {
|
|
||||||
menu.add(new NavLibraryMenuElement(_isolate, _library, queue: _r.queue)
|
|
||||||
.element);
|
|
||||||
} else if (_field.dartOwner is M.ClassRef) {
|
|
||||||
menu.add(
|
|
||||||
new NavClassMenuElement(_isolate, _field.dartOwner, queue: _r.queue)
|
|
||||||
.element);
|
|
||||||
}
|
|
||||||
menu.addAll(<Element>[
|
|
||||||
navMenu(_field.name),
|
|
||||||
(new NavRefreshElement(queue: _r.queue)
|
|
||||||
..onRefresh.listen((e) {
|
|
||||||
e.element.disabled = true;
|
|
||||||
_refresh();
|
|
||||||
}))
|
|
||||||
.element,
|
|
||||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
|
||||||
]);
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createMembers() {
|
|
||||||
final members = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'owner',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
_field.dartOwner == null
|
|
||||||
? (new SpanElement()..text = '...')
|
|
||||||
: anyRef(_isolate, _field.dartOwner, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
]
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'script',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
new SourceLinkElement(_isolate, field.location, _scripts,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]
|
|
||||||
];
|
|
||||||
if (!_field.isStatic) {
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..title = 'The types observed for this field at runtime. '
|
|
||||||
'Fields that are observed to have a single type at runtime '
|
|
||||||
'or to never be null may allow for additional optimization.'
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'observed types',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = _createGuard()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (_field.staticValue != null) {
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'static value',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
anyRef(_isolate, _field.staticValue, _objects, queue: _r.queue)
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
return members;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createGuard() {
|
|
||||||
final guard = <Element>[];
|
|
||||||
switch (_field.guardClassKind) {
|
|
||||||
case M.GuardClassKind.unknown:
|
|
||||||
guard.add(new SpanElement()..text = 'none');
|
|
||||||
break;
|
|
||||||
case M.GuardClassKind.dynamic:
|
|
||||||
guard.add(new SpanElement()..text = 'various');
|
|
||||||
break;
|
|
||||||
case M.GuardClassKind.single:
|
|
||||||
guard.add(
|
|
||||||
new ClassRefElement(_isolate, _field.guardClass, queue: _r.queue)
|
|
||||||
.element);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
guard.add(new SpanElement()
|
|
||||||
..text =
|
|
||||||
_field.guardNullable ? '— null observed' : '— null not observed');
|
|
||||||
return guard;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _refresh() async {
|
|
||||||
_field = await _fields.get(_isolate, _field.id);
|
|
||||||
if (_field.dartOwner is M.LibraryRef) {
|
|
||||||
_library = _field.dartOwner;
|
|
||||||
} else if (_field.dartOwner is M.ClassRef) {
|
|
||||||
_library = (await _classes.get(_isolate, _field.dartOwner.id)).library;
|
|
||||||
}
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,143 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library flag_list_element;
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/uris.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/refresh.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/vm_menu.dart';
|
|
||||||
|
|
||||||
class FlagListElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<FlagListElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<FlagListElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.VMRef _vm;
|
|
||||||
M.EventRepository _events;
|
|
||||||
M.FlagsRepository _repository;
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
Iterable<M.Flag> _flags;
|
|
||||||
|
|
||||||
M.VMRef get vm => _vm;
|
|
||||||
|
|
||||||
factory FlagListElement(M.VMRef vm, M.EventRepository events,
|
|
||||||
M.FlagsRepository repository, M.NotificationRepository notifications,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(vm != null);
|
|
||||||
assert(events != null);
|
|
||||||
assert(repository != null);
|
|
||||||
assert(notifications != null);
|
|
||||||
FlagListElement e = new FlagListElement.created();
|
|
||||||
e._r = new RenderingScheduler<FlagListElement>(e, queue: queue);
|
|
||||||
e._vm = vm;
|
|
||||||
e._events = events;
|
|
||||||
e._repository = repository;
|
|
||||||
e._notifications = notifications;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
FlagListElement.created() : super.created('flag-list');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_refresh();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
children = <Element>[];
|
|
||||||
_r.disable(notify: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
final content = <Element>[];
|
|
||||||
if (_flags == null) {
|
|
||||||
content.add(new HeadingElement.h1()..text = 'Loading Flags...');
|
|
||||||
} else {
|
|
||||||
final modified = _flags.where(_isModified);
|
|
||||||
final unmodified = _flags.where(_isUnmodified);
|
|
||||||
|
|
||||||
if (modified.isNotEmpty) {
|
|
||||||
content.add(new HeadingElement.h1()..text = 'Modified Flags');
|
|
||||||
content.add(new BRElement());
|
|
||||||
content.addAll(modified.expand(_renderFlag));
|
|
||||||
content.add(new HRElement());
|
|
||||||
}
|
|
||||||
|
|
||||||
content.add(new HeadingElement.h1()..text = 'Unmodified Flags');
|
|
||||||
content.add(new BRElement());
|
|
||||||
|
|
||||||
if (unmodified.isEmpty) {
|
|
||||||
content.add(new HeadingElement.h2()..text = 'None');
|
|
||||||
} else {
|
|
||||||
content.addAll(unmodified.expand(_renderFlag));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
children = <Element>[
|
|
||||||
navBar(<Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
|
|
||||||
navMenu('flags', link: Uris.flags()),
|
|
||||||
(new NavRefreshElement(queue: _r.queue)
|
|
||||||
..onRefresh.listen((e) async {
|
|
||||||
e.element.disabled = true;
|
|
||||||
try {
|
|
||||||
await _refresh();
|
|
||||||
} finally {
|
|
||||||
e.element.disabled = false;
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.element,
|
|
||||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
|
||||||
]),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered']
|
|
||||||
..children = content,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _refresh() {
|
|
||||||
return _repository.list().then((flags) {
|
|
||||||
_flags = flags;
|
|
||||||
_r.dirty();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _isModified(M.Flag flag) => flag.modified;
|
|
||||||
static bool _isUnmodified(M.Flag flag) => !flag.modified;
|
|
||||||
|
|
||||||
static List<Element> _renderFlag(M.Flag flag) {
|
|
||||||
return [
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['comment']
|
|
||||||
..text = '// ${flag.comment}',
|
|
||||||
new DivElement()
|
|
||||||
..classes =
|
|
||||||
flag.modified ? ['flag', 'modified'] : ['flag', 'unmodified']
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['name']
|
|
||||||
..text = flag.name,
|
|
||||||
new SpanElement()..text = '=',
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['value']
|
|
||||||
..text = flag.valueAsString ?? 'NULL'
|
|
||||||
],
|
|
||||||
new BRElement(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library function_ref_element;
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M
|
|
||||||
show
|
|
||||||
IsolateRef,
|
|
||||||
FunctionRef,
|
|
||||||
isSyntheticFunction,
|
|
||||||
ClassRef,
|
|
||||||
ObjectRef,
|
|
||||||
getFunctionFullName;
|
|
||||||
import 'package:observatory_2/src/elements/class_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/uris.dart';
|
|
||||||
|
|
||||||
class FunctionRefElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<FunctionRefElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<FunctionRefElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.FunctionRef _function;
|
|
||||||
bool _qualified;
|
|
||||||
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.FunctionRef get function => _function;
|
|
||||||
bool get qualified => _qualified;
|
|
||||||
|
|
||||||
factory FunctionRefElement(M.IsolateRef isolate, M.FunctionRef function,
|
|
||||||
{bool qualified = true, RenderingQueue queue}) {
|
|
||||||
assert(function != null);
|
|
||||||
assert(qualified != null);
|
|
||||||
FunctionRefElement e = new FunctionRefElement.created();
|
|
||||||
e._r = new RenderingScheduler<FunctionRefElement>(e, queue: queue);
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._function = function;
|
|
||||||
e._qualified = qualified;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionRefElement.created() : super.created('function-ref');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
children = <Element>[];
|
|
||||||
title = '';
|
|
||||||
_r.disable(notify: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
var content = <Element>[
|
|
||||||
new AnchorElement(
|
|
||||||
href: (M.isSyntheticFunction(_function.kind) || (_isolate == null))
|
|
||||||
? null
|
|
||||||
: Uris.inspect(_isolate, object: _function))
|
|
||||||
..text = _function.name
|
|
||||||
];
|
|
||||||
if (qualified) {
|
|
||||||
M.ObjectRef owner = _function.dartOwner;
|
|
||||||
while (owner is M.FunctionRef) {
|
|
||||||
M.FunctionRef function = (owner as M.FunctionRef);
|
|
||||||
content.addAll([
|
|
||||||
new SpanElement()..text = '.',
|
|
||||||
new AnchorElement(
|
|
||||||
href: (M.isSyntheticFunction(function.kind) || (_isolate == null))
|
|
||||||
? null
|
|
||||||
: Uris.inspect(_isolate, object: function))
|
|
||||||
..text = function.name
|
|
||||||
]);
|
|
||||||
owner = function.dartOwner;
|
|
||||||
}
|
|
||||||
if (owner is M.ClassRef) {
|
|
||||||
content.addAll([
|
|
||||||
new SpanElement()..text = '.',
|
|
||||||
new ClassRefElement(_isolate, owner, queue: _r.queue).element
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
children = content.reversed.toList(growable: false);
|
|
||||||
title = M.getFunctionFullName(_function);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,426 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library function_view_element;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/code_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/field_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/instance_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/any_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/class_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/library_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/refresh.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/vm_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/object_common.dart';
|
|
||||||
import 'package:observatory_2/src/elements/source_inset.dart';
|
|
||||||
import 'package:observatory_2/src/elements/source_link.dart';
|
|
||||||
|
|
||||||
class FunctionViewElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<FunctionViewElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<FunctionViewElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.VM _vm;
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
M.ServiceFunction _function;
|
|
||||||
M.LibraryRef _library;
|
|
||||||
M.FunctionRepository _functions;
|
|
||||||
M.ClassRepository _classes;
|
|
||||||
M.RetainedSizeRepository _retainedSizes;
|
|
||||||
M.ReachableSizeRepository _reachableSizes;
|
|
||||||
M.InboundReferencesRepository _references;
|
|
||||||
M.RetainingPathRepository _retainingPaths;
|
|
||||||
M.ScriptRepository _scripts;
|
|
||||||
M.ObjectRepository _objects;
|
|
||||||
|
|
||||||
M.VMRef get vm => _vm;
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.NotificationRepository get notifications => _notifications;
|
|
||||||
M.ServiceFunction get function => _function;
|
|
||||||
|
|
||||||
factory FunctionViewElement(
|
|
||||||
M.VM vm,
|
|
||||||
M.IsolateRef isolate,
|
|
||||||
M.ServiceFunction function,
|
|
||||||
M.EventRepository events,
|
|
||||||
M.NotificationRepository notifications,
|
|
||||||
M.FunctionRepository functions,
|
|
||||||
M.ClassRepository classes,
|
|
||||||
M.RetainedSizeRepository retainedSizes,
|
|
||||||
M.ReachableSizeRepository reachableSizes,
|
|
||||||
M.InboundReferencesRepository references,
|
|
||||||
M.RetainingPathRepository retainingPaths,
|
|
||||||
M.ScriptRepository scripts,
|
|
||||||
M.ObjectRepository objects,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(vm != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
assert(notifications != null);
|
|
||||||
assert(function != null);
|
|
||||||
assert(functions != null);
|
|
||||||
assert(classes != null);
|
|
||||||
assert(retainedSizes != null);
|
|
||||||
assert(reachableSizes != null);
|
|
||||||
assert(references != null);
|
|
||||||
assert(retainingPaths != null);
|
|
||||||
assert(scripts != null);
|
|
||||||
assert(objects != null);
|
|
||||||
FunctionViewElement e = new FunctionViewElement.created();
|
|
||||||
e._r = new RenderingScheduler<FunctionViewElement>(e, queue: queue);
|
|
||||||
e._vm = vm;
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
e._notifications = notifications;
|
|
||||||
e._function = function;
|
|
||||||
e._functions = functions;
|
|
||||||
e._classes = classes;
|
|
||||||
e._retainedSizes = retainedSizes;
|
|
||||||
e._reachableSizes = reachableSizes;
|
|
||||||
e._references = references;
|
|
||||||
e._retainingPaths = retainingPaths;
|
|
||||||
e._scripts = scripts;
|
|
||||||
e._objects = objects;
|
|
||||||
if (function.dartOwner is M.LibraryRef) {
|
|
||||||
e._library = function.dartOwner;
|
|
||||||
}
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionViewElement.created() : super.created('function-view');
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
_refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
children = <Element>[
|
|
||||||
navBar(_createMenu()),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered-big']
|
|
||||||
..children = <Element>[
|
|
||||||
new HeadingElement.h2()..text = 'Function ${_function.name}',
|
|
||||||
new HRElement(),
|
|
||||||
new ObjectCommonElement(_isolate, _function, _retainedSizes,
|
|
||||||
_reachableSizes, _references, _retainingPaths, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new BRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = _createMembers(),
|
|
||||||
new HRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..children = _function.location == null
|
|
||||||
? const []
|
|
||||||
: [
|
|
||||||
new SourceInsetElement(_isolate, _function.location,
|
|
||||||
_scripts, _objects, _events,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
],
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createMenu() {
|
|
||||||
final menu = <Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
|
|
||||||
new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element
|
|
||||||
];
|
|
||||||
if (_library != null) {
|
|
||||||
menu.add(new NavLibraryMenuElement(_isolate, _library, queue: _r.queue)
|
|
||||||
.element);
|
|
||||||
} else if (_function.dartOwner is M.ClassRef) {
|
|
||||||
menu.add(new NavClassMenuElement(_isolate, _function.dartOwner,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element);
|
|
||||||
}
|
|
||||||
menu.addAll(<Element>[
|
|
||||||
navMenu(_function.name),
|
|
||||||
(new NavRefreshElement(queue: _r.queue)
|
|
||||||
..onRefresh.listen((e) {
|
|
||||||
e.element.disabled = true;
|
|
||||||
_refresh();
|
|
||||||
}))
|
|
||||||
.element,
|
|
||||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
|
||||||
]);
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createMembers() {
|
|
||||||
final members = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'kind',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()
|
|
||||||
..text = '${_function.isStatic ? "static " : ""}'
|
|
||||||
'${_function.isConst ? "const " : ""}'
|
|
||||||
'${_functionKindToString(_function.kind)}'
|
|
||||||
]
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'owner',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
_function.dartOwner == null
|
|
||||||
? (new SpanElement()..text = '...')
|
|
||||||
: anyRef(_isolate, _function.dartOwner, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
]
|
|
||||||
]
|
|
||||||
];
|
|
||||||
if (_function.field != null) {
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'script',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
new FieldRefElement(_isolate, _function.field, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'script',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
new SourceLinkElement(_isolate, _function.location, _scripts,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
if (_function.code != null) {
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'current code',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
new CodeRefElement(_isolate, _function.code, queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (_function.unoptimizedCode != null) {
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'unoptimized code',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
new CodeRefElement(_isolate, _function.unoptimizedCode,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new SpanElement()
|
|
||||||
..title = 'This count is used to determine when a function '
|
|
||||||
'will be optimized. It is a combination of call '
|
|
||||||
'counts and other factors.'
|
|
||||||
..text = ' (usage count: ${function.usageCounter})'
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..text = ' ');
|
|
||||||
|
|
||||||
if (_function.icDataArray != null) {
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'ic data array',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
new InstanceRefElement(_isolate, _function.icDataArray, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
members.addAll([
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'deoptimizations',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = '${_function.deoptimizations}'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'optimizable',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = _function.isOptimizable ? 'yes' : 'no'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'inlinable',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = _function.isInlinable ? 'yes' : 'no'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'intrinsic',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = _function.hasIntrinsic ? 'yes' : 'no'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'recognized',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = _function.isRecognized ? 'yes' : 'no'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'native',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = _function.isNative ? 'yes' : 'no'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'vm name',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = _function.vmName
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
return members;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _refresh() async {
|
|
||||||
_function = await _functions.get(_isolate, _function.id);
|
|
||||||
if (_function.dartOwner is M.LibraryRef) {
|
|
||||||
_library = _function.dartOwner;
|
|
||||||
} else if (_function.dartOwner is M.ClassRef) {
|
|
||||||
_library = (await _classes.get(_isolate, _function.dartOwner.id)).library;
|
|
||||||
}
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
static String _functionKindToString(M.FunctionKind kind) {
|
|
||||||
switch (kind) {
|
|
||||||
case M.FunctionKind.regular:
|
|
||||||
return 'regular';
|
|
||||||
case M.FunctionKind.closure:
|
|
||||||
return 'closure';
|
|
||||||
case M.FunctionKind.implicitClosure:
|
|
||||||
return 'implicit closure';
|
|
||||||
case M.FunctionKind.getter:
|
|
||||||
return 'getter';
|
|
||||||
case M.FunctionKind.setter:
|
|
||||||
return 'setter';
|
|
||||||
case M.FunctionKind.constructor:
|
|
||||||
return 'constructor';
|
|
||||||
case M.FunctionKind.implicitGetter:
|
|
||||||
return 'implicit getter';
|
|
||||||
case M.FunctionKind.implicitSetter:
|
|
||||||
return 'implicit setter';
|
|
||||||
case M.FunctionKind.implicitStaticGetter:
|
|
||||||
return 'implicit static getter';
|
|
||||||
case M.FunctionKind.fieldInitializer:
|
|
||||||
return 'field initializer';
|
|
||||||
case M.FunctionKind.irregexpFunction:
|
|
||||||
return 'irregexp function';
|
|
||||||
case M.FunctionKind.methodExtractor:
|
|
||||||
return 'method extractor';
|
|
||||||
case M.FunctionKind.noSuchMethodDispatcher:
|
|
||||||
return 'noSuchMethod dispatcher';
|
|
||||||
case M.FunctionKind.invokeFieldDispatcher:
|
|
||||||
return 'invokeField dispatcher';
|
|
||||||
case M.FunctionKind.collected:
|
|
||||||
return 'collected';
|
|
||||||
case M.FunctionKind.native:
|
|
||||||
return 'native';
|
|
||||||
case M.FunctionKind.ffiTrampoline:
|
|
||||||
return 'ffi trampoline';
|
|
||||||
case M.FunctionKind.stub:
|
|
||||||
return 'stub';
|
|
||||||
case M.FunctionKind.tag:
|
|
||||||
return 'tag';
|
|
||||||
case M.FunctionKind.dynamicInvocationForwarder:
|
|
||||||
return 'dynamic invocation forwarder';
|
|
||||||
}
|
|
||||||
throw new Exception('Unknown Functionkind ($kind)');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library general_error_element;
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
|
|
||||||
class GeneralErrorElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<GeneralErrorElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<GeneralErrorElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
String _message;
|
|
||||||
|
|
||||||
String get message => _message;
|
|
||||||
|
|
||||||
set message(String value) => _message = _r.checkAndReact(_message, value);
|
|
||||||
|
|
||||||
factory GeneralErrorElement(M.NotificationRepository notifications,
|
|
||||||
{String message = '', RenderingQueue queue}) {
|
|
||||||
assert(notifications != null);
|
|
||||||
assert(message != null);
|
|
||||||
GeneralErrorElement e = new GeneralErrorElement.created();
|
|
||||||
e._r = new RenderingScheduler<GeneralErrorElement>(e, queue: queue);
|
|
||||||
e._message = message;
|
|
||||||
e._notifications = notifications;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
GeneralErrorElement.created() : super.created('general-error');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
children = <Element>[];
|
|
||||||
_r.disable(notify: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
children = <Element>[
|
|
||||||
navBar(<Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
|
||||||
]),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered']
|
|
||||||
..children = <Element>[
|
|
||||||
new HeadingElement.h1()..text = 'Error',
|
|
||||||
new BRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['well']
|
|
||||||
..text = message
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,316 +0,0 @@
|
||||||
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library heap_map_element;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:math';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/service.dart' as S;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/refresh.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/vm_menu.dart';
|
|
||||||
|
|
||||||
class HeapMapElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<HeapMapElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<HeapMapElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.VM _vm;
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
M.VMRef get vm => _vm;
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.NotificationRepository get notifications => _notifications;
|
|
||||||
|
|
||||||
factory HeapMapElement(M.VM vm, M.IsolateRef isolate,
|
|
||||||
M.EventRepository events, M.NotificationRepository notifications,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(vm != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
assert(notifications != null);
|
|
||||||
HeapMapElement e = new HeapMapElement.created();
|
|
||||||
e._r = new RenderingScheduler<HeapMapElement>(e, queue: queue);
|
|
||||||
e._vm = vm;
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
e._notifications = notifications;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
HeapMapElement.created() : super.created('heap-map');
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
_refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
CanvasElement _canvas;
|
|
||||||
var _fragmentationData;
|
|
||||||
double _pageHeight;
|
|
||||||
final _classIdToColor = {};
|
|
||||||
final _colorToClassId = {};
|
|
||||||
final _classIdToName = {};
|
|
||||||
|
|
||||||
static final _freeColor = [255, 255, 255, 255];
|
|
||||||
static final _pageSeparationColor = [0, 0, 0, 255];
|
|
||||||
static const _PAGE_SEPARATION_HEIGHT = 4;
|
|
||||||
// Many browsers will not display a very tall canvas.
|
|
||||||
// TODO(koda): Improve interface for huge heaps.
|
|
||||||
static const _MAX_CANVAS_HEIGHT = 6000;
|
|
||||||
|
|
||||||
String _status;
|
|
||||||
S.ServiceMap _fragmentation;
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
if (_canvas == null) {
|
|
||||||
_canvas = new CanvasElement()
|
|
||||||
..width = 1
|
|
||||||
..height = 1
|
|
||||||
..onMouseMove.listen(_handleMouseMove);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set hover text to describe the object under the cursor.
|
|
||||||
_canvas.title = _status;
|
|
||||||
|
|
||||||
children = <Element>[
|
|
||||||
navBar(<Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
|
|
||||||
new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
|
|
||||||
navMenu('heap map'),
|
|
||||||
(new NavRefreshElement(label: 'Mark-Compact', queue: _r.queue)
|
|
||||||
..onRefresh.listen((_) => _refresh(gc: "mark-compact")))
|
|
||||||
.element,
|
|
||||||
(new NavRefreshElement(label: 'Mark-Sweep', queue: _r.queue)
|
|
||||||
..onRefresh.listen((_) => _refresh(gc: "mark-sweep")))
|
|
||||||
.element,
|
|
||||||
(new NavRefreshElement(label: 'Scavenge', queue: _r.queue)
|
|
||||||
..onRefresh.listen((_) => _refresh(gc: "scavenge")))
|
|
||||||
.element,
|
|
||||||
(new NavRefreshElement(queue: _r.queue)
|
|
||||||
..onRefresh.listen((_) => _refresh()))
|
|
||||||
.element,
|
|
||||||
(new NavNotifyElement(_notifications, queue: _r.queue)).element
|
|
||||||
]),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered-big']
|
|
||||||
..children = <Element>[
|
|
||||||
new HeadingElement.h2()..text = _status,
|
|
||||||
new HRElement(),
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['flex-row']
|
|
||||||
..children = <Element>[_canvas]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode color as single integer, to enable using it as a map key.
|
|
||||||
int _packColor(Iterable<int> color) {
|
|
||||||
int packed = 0;
|
|
||||||
for (var component in color) {
|
|
||||||
packed = packed * 256 + component;
|
|
||||||
}
|
|
||||||
return packed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _addClass(int classId, String name, Iterable<int> color) {
|
|
||||||
_classIdToName[classId] = name.split('@')[0];
|
|
||||||
_classIdToColor[classId] = color;
|
|
||||||
_colorToClassId[_packColor(color)] = classId;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateClassList(classList, int freeClassId) {
|
|
||||||
for (var member in classList['classes']) {
|
|
||||||
if (member is! S.Class) {
|
|
||||||
// TODO(turnidge): The printing for some of these non-class
|
|
||||||
// members is broken. Fix this:
|
|
||||||
//
|
|
||||||
// Logger.root.info('$member');
|
|
||||||
print('Ignoring non-class in class list');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var classId = int.parse(member.id.split('/').last);
|
|
||||||
var color = _classIdToRGBA(classId);
|
|
||||||
_addClass(classId, member.name, color);
|
|
||||||
}
|
|
||||||
_addClass(freeClassId, 'Free', _freeColor);
|
|
||||||
_addClass(0, '', _pageSeparationColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterable<int> _classIdToRGBA(int classId) {
|
|
||||||
// TODO(koda): Pick random hue, but fixed saturation and value.
|
|
||||||
var rng = new Random(classId);
|
|
||||||
return [rng.nextInt(128), rng.nextInt(128), rng.nextInt(128), 255];
|
|
||||||
}
|
|
||||||
|
|
||||||
String _classNameAt(Point<num> point) {
|
|
||||||
var color = new PixelReference(_fragmentationData, point).color;
|
|
||||||
return _classIdToName[_colorToClassId[_packColor(color)]];
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectInfo _objectAt(Point<num> point) {
|
|
||||||
if (_fragmentation == null || _canvas == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var pagePixels = _pageHeight * _fragmentationData.width;
|
|
||||||
var index = new PixelReference(_fragmentationData, point).index;
|
|
||||||
var pageIndex = index ~/ pagePixels;
|
|
||||||
num pageOffset = index % pagePixels;
|
|
||||||
var pages = _fragmentation['pages'];
|
|
||||||
if (pageIndex < 0 || pageIndex >= pages.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// Scan the page to find start and size.
|
|
||||||
var page = pages[pageIndex];
|
|
||||||
var objects = page['objects'];
|
|
||||||
var offset = 0;
|
|
||||||
var size = 0;
|
|
||||||
for (var i = 0; i < objects.length; i += 2) {
|
|
||||||
size = objects[i];
|
|
||||||
offset += size;
|
|
||||||
if (offset > pageOffset) {
|
|
||||||
pageOffset = offset - size;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new ObjectInfo(
|
|
||||||
int.parse(page['objectStart']) +
|
|
||||||
pageOffset * _fragmentation['unitSizeBytes'],
|
|
||||||
size * _fragmentation['unitSizeBytes']);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleMouseMove(MouseEvent event) {
|
|
||||||
var info = _objectAt(event.offset);
|
|
||||||
if (info == null) {
|
|
||||||
_status = '';
|
|
||||||
_r.dirty();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var addressString = '${info.size}B @ 0x${info.address.toRadixString(16)}';
|
|
||||||
var className = _classNameAt(event.offset);
|
|
||||||
_status = (className == '') ? '-' : '$className $addressString';
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateFragmentationData() {
|
|
||||||
if (_fragmentation == null || _canvas == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_updateClassList(
|
|
||||||
_fragmentation['classList'], _fragmentation['freeClassId']);
|
|
||||||
var pages = _fragmentation['pages'];
|
|
||||||
var width = max(_canvas.parent.client.width, 1);
|
|
||||||
_pageHeight = _PAGE_SEPARATION_HEIGHT +
|
|
||||||
_fragmentation['pageSizeBytes'] ~/
|
|
||||||
_fragmentation['unitSizeBytes'] ~/
|
|
||||||
width;
|
|
||||||
var height = min(_pageHeight * pages.length, _MAX_CANVAS_HEIGHT);
|
|
||||||
_fragmentationData = _canvas.context2D.createImageData(width, height);
|
|
||||||
_canvas.width = _fragmentationData.width;
|
|
||||||
_canvas.height = _fragmentationData.height;
|
|
||||||
_renderPages(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Renders and draws asynchronously, one page at a time to avoid
|
|
||||||
// blocking the UI.
|
|
||||||
void _renderPages(int startPage) {
|
|
||||||
var pages = _fragmentation['pages'];
|
|
||||||
_status = 'Loaded $startPage of ${pages.length} pages';
|
|
||||||
_r.dirty();
|
|
||||||
var startY = (startPage * _pageHeight).round();
|
|
||||||
var endY = startY + _pageHeight.round();
|
|
||||||
if (startPage >= pages.length || endY > _fragmentationData.height) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var pixel = new PixelReference(_fragmentationData, new Point(0, startY));
|
|
||||||
var objects = pages[startPage]['objects'];
|
|
||||||
for (var i = 0; i < objects.length; i += 2) {
|
|
||||||
var count = objects[i];
|
|
||||||
var classId = objects[i + 1];
|
|
||||||
var color = _classIdToColor[classId];
|
|
||||||
while (count-- > 0) {
|
|
||||||
pixel.color = color;
|
|
||||||
pixel = pixel.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (pixel.point.y < endY) {
|
|
||||||
pixel.color = _pageSeparationColor;
|
|
||||||
pixel = pixel.next();
|
|
||||||
}
|
|
||||||
_canvas.context2D.putImageData(
|
|
||||||
_fragmentationData, 0, 0, 0, startY, _fragmentationData.width, endY);
|
|
||||||
// Continue with the next page, asynchronously.
|
|
||||||
new Future(() {
|
|
||||||
_renderPages(startPage + 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _refresh({String gc}) {
|
|
||||||
final isolate = _isolate as S.Isolate;
|
|
||||||
var params = {};
|
|
||||||
if (gc != null) {
|
|
||||||
params['gc'] = gc;
|
|
||||||
}
|
|
||||||
return isolate.invokeRpc('_getHeapMap', params).then((serviceObject) {
|
|
||||||
S.ServiceMap response = serviceObject;
|
|
||||||
assert(response['type'] == 'HeapMap');
|
|
||||||
_fragmentation = response;
|
|
||||||
_updateFragmentationData();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A reference to a particular pixel of ImageData.
|
|
||||||
class PixelReference {
|
|
||||||
final _data;
|
|
||||||
var _dataIndex;
|
|
||||||
static const NUM_COLOR_COMPONENTS = 4;
|
|
||||||
|
|
||||||
PixelReference(ImageData data, Point<num> point)
|
|
||||||
: _data = data,
|
|
||||||
_dataIndex = (point.y * data.width + point.x) * NUM_COLOR_COMPONENTS;
|
|
||||||
|
|
||||||
PixelReference._fromDataIndex(this._data, this._dataIndex);
|
|
||||||
|
|
||||||
Point<num> get point => new Point(index % _data.width, index ~/ _data.width);
|
|
||||||
|
|
||||||
void set color(Iterable<int> color) {
|
|
||||||
_data.data.setRange(_dataIndex, _dataIndex + NUM_COLOR_COMPONENTS, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterable<int> get color =>
|
|
||||||
_data.data.getRange(_dataIndex, _dataIndex + NUM_COLOR_COMPONENTS);
|
|
||||||
|
|
||||||
// Returns the next pixel in row-major order.
|
|
||||||
PixelReference next() => new PixelReference._fromDataIndex(
|
|
||||||
_data, _dataIndex + NUM_COLOR_COMPONENTS);
|
|
||||||
|
|
||||||
// The row-major index of this pixel.
|
|
||||||
int get index => _dataIndex ~/ NUM_COLOR_COMPONENTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
class ObjectInfo {
|
|
||||||
final address;
|
|
||||||
final size;
|
|
||||||
ObjectInfo(this.address, this.size);
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,100 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/class_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/code_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/context_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/error_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/field_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/function_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/uris.dart';
|
|
||||||
import 'package:observatory_2/src/elements/icdata_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/instance_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/library_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/local_var_descriptors_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/megamorphiccache_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/objectpool_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/pc_descriptors_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/script_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/sentinel_value.dart';
|
|
||||||
import 'package:observatory_2/src/elements/singletargetcache_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/subtypetestcache_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/type_arguments_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/unknown_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/unlinkedcall_ref.dart';
|
|
||||||
|
|
||||||
Element anyRef(M.IsolateRef isolate, ref, M.ObjectRepository objects,
|
|
||||||
{RenderingQueue queue, bool expandable = true}) {
|
|
||||||
if (ref == null) {
|
|
||||||
return new SpanElement()..text = "???";
|
|
||||||
}
|
|
||||||
if (ref is M.Guarded) {
|
|
||||||
if (ref.isSentinel) {
|
|
||||||
return anyRef(isolate, ref.asSentinel, objects,
|
|
||||||
queue: queue, expandable: expandable);
|
|
||||||
} else {
|
|
||||||
return anyRef(isolate, ref.asValue, objects,
|
|
||||||
queue: queue, expandable: expandable);
|
|
||||||
}
|
|
||||||
} else if (ref is M.ObjectRef) {
|
|
||||||
if (ref is M.ClassRef) {
|
|
||||||
return new ClassRefElement(isolate, ref, queue: queue).element;
|
|
||||||
} else if (ref is M.CodeRef) {
|
|
||||||
return new CodeRefElement(isolate, ref, queue: queue).element;
|
|
||||||
} else if (ref is M.ContextRef) {
|
|
||||||
return new ContextRefElement(isolate, ref, objects,
|
|
||||||
queue: queue, expandable: expandable)
|
|
||||||
.element;
|
|
||||||
} else if (ref is M.Error) {
|
|
||||||
return new ErrorRefElement(ref, queue: queue).element;
|
|
||||||
} else if (ref is M.FieldRef) {
|
|
||||||
return new FieldRefElement(isolate, ref, objects,
|
|
||||||
queue: queue, expandable: expandable)
|
|
||||||
.element;
|
|
||||||
} else if (ref is M.FunctionRef) {
|
|
||||||
return new FunctionRefElement(isolate, ref, queue: queue).element;
|
|
||||||
} else if (ref is M.ICDataRef) {
|
|
||||||
return new ICDataRefElement(isolate, ref, queue: queue).element;
|
|
||||||
} else if (ref is M.InstanceRef) {
|
|
||||||
return new InstanceRefElement(isolate, ref, objects,
|
|
||||||
queue: queue, expandable: expandable)
|
|
||||||
.element;
|
|
||||||
} else if (ref is M.LibraryRef) {
|
|
||||||
return new LibraryRefElement(isolate, ref, queue: queue).element;
|
|
||||||
} else if (ref is M.LocalVarDescriptorsRef) {
|
|
||||||
return new LocalVarDescriptorsRefElement(isolate, ref, queue: queue)
|
|
||||||
.element;
|
|
||||||
} else if (ref is M.MegamorphicCacheRef) {
|
|
||||||
return new MegamorphicCacheRefElement(isolate, ref, queue: queue).element;
|
|
||||||
} else if (ref is M.ObjectPoolRef) {
|
|
||||||
return new ObjectPoolRefElement(isolate, ref, queue: queue).element;
|
|
||||||
} else if (ref is M.PcDescriptorsRef) {
|
|
||||||
return new PcDescriptorsRefElement(isolate, ref, queue: queue).element;
|
|
||||||
} else if (ref is M.ScriptRef) {
|
|
||||||
return new ScriptRefElement(isolate, ref, queue: queue).element;
|
|
||||||
} else if (ref is M.SingleTargetCacheRef) {
|
|
||||||
return new SingleTargetCacheRefElement(isolate, ref, queue: queue)
|
|
||||||
.element;
|
|
||||||
} else if (ref is M.SubtypeTestCacheRef) {
|
|
||||||
return new SubtypeTestCacheRefElement(isolate, ref, queue: queue).element;
|
|
||||||
} else if (ref is M.TypeArgumentsRef) {
|
|
||||||
return new TypeArgumentsRefElement(isolate, ref, queue: queue).element;
|
|
||||||
} else if (ref is M.UnknownObjectRef) {
|
|
||||||
return new UnknownObjectRefElement(isolate, ref, queue: queue).element;
|
|
||||||
} else if (ref is M.UnlinkedCallRef) {
|
|
||||||
return new UnlinkedCallRefElement(isolate, ref, queue: queue).element;
|
|
||||||
} else {
|
|
||||||
return new AnchorElement(href: Uris.inspect(isolate, object: ref))
|
|
||||||
..text = 'object';
|
|
||||||
}
|
|
||||||
} else if (ref is M.Sentinel) {
|
|
||||||
return new SentinelValueElement(ref, queue: queue).element;
|
|
||||||
} else if (ref is num || ref is String) {
|
|
||||||
return new SpanElement()..text = ref.toString();
|
|
||||||
}
|
|
||||||
throw new Exception('Unknown ref type (${ref.runtimeType})');
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
|
|
||||||
HtmlElement element(CustomElement e) => e.element;
|
|
||||||
|
|
||||||
class CustomElement {
|
|
||||||
static Expando reverseElements = new Expando();
|
|
||||||
static CustomElement reverse(HtmlElement element) => reverseElements[element];
|
|
||||||
|
|
||||||
static List<CustomElement> toBeAttached = <CustomElement>[];
|
|
||||||
static void drainAttached() {
|
|
||||||
// Send 'attached' to elements that have been attached to the document.
|
|
||||||
bool fired = false;
|
|
||||||
var connectedElements = toBeAttached
|
|
||||||
.where((CustomElement element) => element.element.isConnected)
|
|
||||||
.toList();
|
|
||||||
for (CustomElement element in connectedElements) {
|
|
||||||
toBeAttached.remove(element);
|
|
||||||
element.attached();
|
|
||||||
fired = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toBeAttached.isEmpty) {
|
|
||||||
return; // Done.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fired) {
|
|
||||||
// The 'attached' events above may have scheduled microtasks that will
|
|
||||||
// will add more CustomElements to be document, e.g. 'render'.
|
|
||||||
scheduleMicrotask(() => drainAttached());
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!toBeAttached.isEmpty) {
|
|
||||||
// Either this element will never be attached or it will be attached
|
|
||||||
// after a turn of the outer event loop. Fire 'attached' in case it is
|
|
||||||
// the latter, since firing it out of order is preferable to not firing
|
|
||||||
// it at all.
|
|
||||||
CustomElement element = toBeAttached.removeLast();
|
|
||||||
print("Warning: created but not in document: $element");
|
|
||||||
element.attached();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final HtmlElement element;
|
|
||||||
CustomElement.created(String elementClass)
|
|
||||||
: element = document.createElement("shadow") {
|
|
||||||
reverseElements[element] = this;
|
|
||||||
element.classes = [elementClass];
|
|
||||||
|
|
||||||
if (toBeAttached.isEmpty) {
|
|
||||||
scheduleMicrotask(() => drainAttached());
|
|
||||||
}
|
|
||||||
toBeAttached.add(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void attached() {}
|
|
||||||
void detached() {}
|
|
||||||
|
|
||||||
Element get parent => element.parent;
|
|
||||||
|
|
||||||
List<Element> get children => element.children;
|
|
||||||
set children(List<Element> c) => element.children = c;
|
|
||||||
|
|
||||||
CssClassSet get classes => element.classes;
|
|
||||||
set classes(dynamic c) => element.classes = c;
|
|
||||||
|
|
||||||
String get title => element.title;
|
|
||||||
set title(String t) => element.title = t;
|
|
||||||
|
|
||||||
String get text => element.text;
|
|
||||||
set text(String t) => element.text = t;
|
|
||||||
|
|
||||||
CssStyleDeclaration get style => element.style;
|
|
||||||
|
|
||||||
ElementStream<MouseEvent> get onClick => element.onClick;
|
|
||||||
|
|
||||||
Rectangle getBoundingClientRect() => element.getBoundingClientRect();
|
|
||||||
|
|
||||||
List<Node> getElementsByClassName(String c) =>
|
|
||||||
element.getElementsByClassName(c);
|
|
||||||
|
|
||||||
void scrollIntoView() => element.scrollIntoView();
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
|
|
||||||
Element navBar(Iterable<Element> content) {
|
|
||||||
assert(content != null);
|
|
||||||
return document.createElement('nav')
|
|
||||||
..classes = ['nav-bar']
|
|
||||||
..children = <Element>[
|
|
||||||
new UListElement()..children = content,
|
|
||||||
];
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
|
|
||||||
navMenu(String label, {String link, Iterable<Element> content = const []}) {
|
|
||||||
assert(label != null);
|
|
||||||
assert(content != null);
|
|
||||||
return new LIElement()
|
|
||||||
..classes = ['nav-menu']
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['nav-menu_label']
|
|
||||||
..children = <Element>[
|
|
||||||
new AnchorElement(href: link)..text = label,
|
|
||||||
new UListElement()..children = content
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:collection';
|
|
||||||
import 'dart:async';
|
|
||||||
|
|
||||||
/// A generic rendering task that can be scheduled.
|
|
||||||
abstract class RenderingTask {
|
|
||||||
/// Rendering synchronous callback.
|
|
||||||
void render();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A generic synchronization system for rendering operations.
|
|
||||||
abstract class RenderingBarrier {
|
|
||||||
/// Future to the next synchronization barrier (ms from application start).
|
|
||||||
Future<num> get next;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Synchronization system based on the AnimationFrame.
|
|
||||||
class NextAnimationFrameBarrier implements RenderingBarrier {
|
|
||||||
Future<num> get next => window.animationFrame;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// MOCK synchronization system for manual barrier triggering.
|
|
||||||
class RenderingBarrierMock implements RenderingBarrier {
|
|
||||||
final StreamController<num> _stream = new StreamController<num>.broadcast();
|
|
||||||
num _ms = 0;
|
|
||||||
|
|
||||||
Future<num> get next => _stream.stream.first;
|
|
||||||
|
|
||||||
/// Trigger the next barrier with an optional numer of ms elapsed.
|
|
||||||
void triggerRenderingBarrier({num step = 20}) {
|
|
||||||
assert(step != null);
|
|
||||||
_stream.add(_ms += step);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// RenderingTask queuing and synchronization system.
|
|
||||||
class RenderingQueue {
|
|
||||||
final RenderingBarrier _barrier;
|
|
||||||
final Queue<RenderingTask> _queue = new Queue<RenderingTask>();
|
|
||||||
|
|
||||||
bool get isEmpty => _queue.isEmpty;
|
|
||||||
bool get isNotEmpty => _queue.isNotEmpty;
|
|
||||||
|
|
||||||
/// Creates a RenderingQueue with the default synchronization barrier.
|
|
||||||
RenderingQueue() : this.fromBarrier(new NextAnimationFrameBarrier());
|
|
||||||
|
|
||||||
/// Creates a RenderingQueue with a custom synchronization barrier.
|
|
||||||
RenderingQueue.fromBarrier(this._barrier) {
|
|
||||||
assert(this._barrier != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a task to the queue.
|
|
||||||
/// If the current rendering phase is running it will be executed during this
|
|
||||||
/// rendering cycle, otherwise it will be queued for the next one.
|
|
||||||
void enqueue(RenderingTask r, {bool waitForBarrier = true}) {
|
|
||||||
assert(r != null);
|
|
||||||
final wasEmpty = _queue.isEmpty;
|
|
||||||
_queue.addLast(r);
|
|
||||||
// If no task are in the queue there is no rendering phase scheduled.
|
|
||||||
if (wasEmpty) {
|
|
||||||
if (waitForBarrier) {
|
|
||||||
_render();
|
|
||||||
} else {
|
|
||||||
// We schedule the _renderLoop as a microtask to allow the
|
|
||||||
// scheduleRendering method to terminate, due to the fact that it is
|
|
||||||
// generally invoked from inside a HtmlElement.attached method
|
|
||||||
scheduleMicrotask(_renderLoop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _render() async {
|
|
||||||
await _barrier.next;
|
|
||||||
_renderLoop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _renderLoop() {
|
|
||||||
while (_queue.isNotEmpty) {
|
|
||||||
_queue.first.render();
|
|
||||||
_queue.removeFirst();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,141 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_queue.dart';
|
|
||||||
export 'package:observatory_2/src/elements/helpers/rendering_queue.dart';
|
|
||||||
|
|
||||||
/// A generic renderable object.
|
|
||||||
abstract class Renderable {
|
|
||||||
void render();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Event related to a Renderable rendering phase.
|
|
||||||
class RenderedEvent<T> {
|
|
||||||
/// Renderable to which the event is related
|
|
||||||
final T element;
|
|
||||||
|
|
||||||
/// Is another rendering scheduled for this element.
|
|
||||||
final bool otherRenderScheduled;
|
|
||||||
|
|
||||||
RenderedEvent(this.element, this.otherRenderScheduled) {
|
|
||||||
assert(element != null);
|
|
||||||
assert(otherRenderScheduled != null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Scheduler for rendering operations.
|
|
||||||
class RenderingScheduler<T extends Renderable> implements RenderingTask {
|
|
||||||
bool _enabled = false;
|
|
||||||
bool _dirty = false;
|
|
||||||
bool _renderingScheduled = false;
|
|
||||||
bool _notificationScheduled = false;
|
|
||||||
bool _waitForBarrier = false;
|
|
||||||
|
|
||||||
/// Element managed by this scheduler.
|
|
||||||
final T element;
|
|
||||||
|
|
||||||
/// Queue used for rendering operations.
|
|
||||||
final RenderingQueue queue;
|
|
||||||
|
|
||||||
final List<Future> _wait = <Future>[];
|
|
||||||
|
|
||||||
/// Does the element need a new rendering cycle.
|
|
||||||
bool get isDirty => _dirty;
|
|
||||||
|
|
||||||
/// Is the scheduler enabled.
|
|
||||||
bool get isEnabled => _enabled;
|
|
||||||
|
|
||||||
final StreamController<RenderedEvent<T>> _onRendered =
|
|
||||||
new StreamController<RenderedEvent<T>>.broadcast();
|
|
||||||
Stream<RenderedEvent<T>> get onRendered => _onRendered.stream;
|
|
||||||
|
|
||||||
/// Creates a new scheduler for an element.
|
|
||||||
/// If no queue is provided it will create a new default configured queue.
|
|
||||||
factory RenderingScheduler(T element, {RenderingQueue queue}) {
|
|
||||||
assert(element != null);
|
|
||||||
if (queue == null) {
|
|
||||||
queue = new RenderingQueue();
|
|
||||||
}
|
|
||||||
return new RenderingScheduler<T>._(element, queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderingScheduler._(this.element, this.queue);
|
|
||||||
|
|
||||||
/// Enable the scheduler.
|
|
||||||
/// New dirty or schedule request will be considered.
|
|
||||||
void enable() {
|
|
||||||
if (_enabled) return;
|
|
||||||
_enabled = true;
|
|
||||||
scheduleRendering();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Disable the scheduler.
|
|
||||||
/// New dirty or schedule request will be discarded.
|
|
||||||
/// [optional] notify: send a final RenderEvent.
|
|
||||||
void disable({bool notify = false}) {
|
|
||||||
assert(notify != null);
|
|
||||||
if (!_enabled) return;
|
|
||||||
_enabled = false;
|
|
||||||
if (notify) scheduleNotification();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the object as dirty. A rendering will be scheduled.
|
|
||||||
void dirty() {
|
|
||||||
if (_dirty) return;
|
|
||||||
_dirty = true;
|
|
||||||
scheduleRendering();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks for modification during attribute set.
|
|
||||||
/// If value changes a new rendering is scheduled.
|
|
||||||
/// set attr(T v) => _attr = _r.checkAndReact(_attr, v);
|
|
||||||
T checkAndReact<T>(T oldValue, T newValue) {
|
|
||||||
if (oldValue != newValue)
|
|
||||||
dirty();
|
|
||||||
else
|
|
||||||
scheduleNotification();
|
|
||||||
return newValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Schedules a new rendering phase.
|
|
||||||
void scheduleRendering() {
|
|
||||||
if (_renderingScheduled) return;
|
|
||||||
if (!_enabled) return;
|
|
||||||
queue.enqueue(this, waitForBarrier: _waitForBarrier);
|
|
||||||
_waitForBarrier = true;
|
|
||||||
_renderingScheduled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Renders the element (if the scheduler is enabled).
|
|
||||||
/// It will clear the dirty flag.
|
|
||||||
void render() {
|
|
||||||
_renderingScheduled = false;
|
|
||||||
if (!_enabled) return;
|
|
||||||
_dirty = false;
|
|
||||||
_wait.clear();
|
|
||||||
element.render();
|
|
||||||
scheduleNotification();
|
|
||||||
if (_dirty) scheduleRendering();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Schedules a notification.
|
|
||||||
void scheduleNotification() {
|
|
||||||
if (_notificationScheduled) return;
|
|
||||||
_notify();
|
|
||||||
_notificationScheduled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void waitFor(Iterable<Future> it) {
|
|
||||||
_wait.addAll(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _notify() async {
|
|
||||||
await Future.wait(_wait);
|
|
||||||
_wait.clear();
|
|
||||||
_onRendered.add(new RenderedEvent<T>(element, _dirty));
|
|
||||||
_notificationScheduled = false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,97 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
|
|
||||||
HtmlElement element(CustomElement e) => e.element;
|
|
||||||
|
|
||||||
class CustomElement {
|
|
||||||
static Expando reverseElements = new Expando();
|
|
||||||
static CustomElement reverse(HtmlElement element) => reverseElements[element];
|
|
||||||
|
|
||||||
static List<CustomElement> toBeAttached = <CustomElement>[];
|
|
||||||
static void drainAttached() {
|
|
||||||
// Send 'attached' to elements that have been attached to the document.
|
|
||||||
bool fired = false;
|
|
||||||
var connectedElements = toBeAttached
|
|
||||||
.where((CustomElement element) => element.element.isConnected)
|
|
||||||
.toList();
|
|
||||||
for (CustomElement element in connectedElements) {
|
|
||||||
toBeAttached.remove(element);
|
|
||||||
element.attached();
|
|
||||||
fired = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toBeAttached.isEmpty) {
|
|
||||||
return; // Done.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fired) {
|
|
||||||
// The 'attached' events above may have scheduled microtasks that will
|
|
||||||
// will add more CustomElements to be document, e.g. 'render'.
|
|
||||||
scheduleMicrotask(() => drainAttached());
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!toBeAttached.isEmpty) {
|
|
||||||
// Either this element will never be attached or it will be attached
|
|
||||||
// after a turn of the outer event loop. Fire 'attached' in case it is
|
|
||||||
// the latter, since firing it out of order is preferable to not firing
|
|
||||||
// it at all.
|
|
||||||
CustomElement element = toBeAttached.removeLast();
|
|
||||||
print("Warning: created but not in document: $element");
|
|
||||||
element.attached();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final HtmlElement element;
|
|
||||||
CustomElement.created(Tag tag) : element = document.createElement("shadow") {
|
|
||||||
reverseElements[element] = this;
|
|
||||||
element.classes = [tag.name];
|
|
||||||
|
|
||||||
if (toBeAttached.isEmpty) {
|
|
||||||
scheduleMicrotask(() => drainAttached());
|
|
||||||
}
|
|
||||||
toBeAttached.add(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void attached() {}
|
|
||||||
void detached() {}
|
|
||||||
|
|
||||||
Element get parent => element.parent;
|
|
||||||
|
|
||||||
List<Element> get children => element.children;
|
|
||||||
set children(List<Element> c) => element.children = c;
|
|
||||||
|
|
||||||
CssClassSet get classes => element.classes;
|
|
||||||
set classes(dynamic c) => element.classes = c;
|
|
||||||
|
|
||||||
String get title => element.title;
|
|
||||||
set title(String t) => element.title = t;
|
|
||||||
|
|
||||||
String get text => element.text;
|
|
||||||
set text(String t) => element.text = t;
|
|
||||||
|
|
||||||
CssStyleDeclaration get style => element.style;
|
|
||||||
|
|
||||||
ElementStream<MouseEvent> get onClick => element.onClick;
|
|
||||||
|
|
||||||
Rectangle getBoundingClientRect() => element.getBoundingClientRect();
|
|
||||||
|
|
||||||
List<Node> getElementsByClassName(String c) =>
|
|
||||||
element.getElementsByClassName(c);
|
|
||||||
|
|
||||||
void scrollIntoView() => element.scrollIntoView();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Utility class for Custom Tags registration.
|
|
||||||
class Tag<T extends CustomElement> {
|
|
||||||
/// Tag name.
|
|
||||||
final String name;
|
|
||||||
|
|
||||||
/// Dependent tags that need to be registered for this tag to work properly.
|
|
||||||
final Iterable<Tag> dependencies;
|
|
||||||
|
|
||||||
const Tag(this.name, {this.dependencies = const []});
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
|
|
||||||
/// Utility class for URIs generation.
|
|
||||||
abstract class Uris {
|
|
||||||
static String _isolatePage(String path, M.IsolateRef isolate,
|
|
||||||
{M.ObjectRef object}) {
|
|
||||||
final parameters = {'isolateId': isolate.id};
|
|
||||||
if (object != null) parameters['objectId'] = object.id;
|
|
||||||
return '#' + new Uri(path: path, queryParameters: parameters).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
static String allocationProfiler(M.IsolateRef isolate) =>
|
|
||||||
_isolatePage('/allocation-profiler', isolate);
|
|
||||||
static String classTree(M.IsolateRef isolate) =>
|
|
||||||
_isolatePage('/class-tree', isolate);
|
|
||||||
static String cpuProfiler(M.IsolateRef isolate) =>
|
|
||||||
_isolatePage('/profiler', isolate);
|
|
||||||
static String cpuProfilerTable(M.IsolateRef isolate) =>
|
|
||||||
_isolatePage('/profiler-table', isolate);
|
|
||||||
static String debugger(M.IsolateRef isolate) =>
|
|
||||||
_isolatePage('/debugger', isolate);
|
|
||||||
static String flags() => '#/flags';
|
|
||||||
static String heapMap(M.IsolateRef isolate) =>
|
|
||||||
_isolatePage('/heap-map', isolate);
|
|
||||||
static String heapSnapshot(M.IsolateRef isolate) =>
|
|
||||||
_isolatePage('/heap-snapshot', isolate);
|
|
||||||
static String inspect(M.IsolateRef isolate, {M.ObjectRef object, int pos}) {
|
|
||||||
if (pos == null) {
|
|
||||||
return _isolatePage('/inspect', isolate, object: object);
|
|
||||||
}
|
|
||||||
return _isolatePage('/inspect', isolate, object: object) + '---pos=${pos}';
|
|
||||||
}
|
|
||||||
|
|
||||||
static String logging(M.IsolateRef isolate) =>
|
|
||||||
_isolatePage('/logging', isolate);
|
|
||||||
static String metrics(M.IsolateRef isolate) =>
|
|
||||||
_isolatePage('/metrics', isolate);
|
|
||||||
static String nativeMemory() => '#/native-memory-profile';
|
|
||||||
static String processSnapshot() => '#/process-snapshot';
|
|
||||||
static String objectStore(M.IsolateRef isolate) =>
|
|
||||||
_isolatePage('/object-store', isolate);
|
|
||||||
static String persistentHandles(M.IsolateRef isolate) =>
|
|
||||||
_isolatePage('/persistent-handles', isolate);
|
|
||||||
static String ports(M.IsolateRef isolate) => _isolatePage('/ports', isolate);
|
|
||||||
static String timeline() => '#/timeline';
|
|
||||||
static String vm() => '#/vm';
|
|
||||||
static String vmConnect() => '#/vm-connect';
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M show IsolateRef, ICDataRef;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/uris.dart';
|
|
||||||
|
|
||||||
class ICDataRefElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<ICDataRefElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<ICDataRefElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.ICDataRef _icdata;
|
|
||||||
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.ICDataRef get icdata => _icdata;
|
|
||||||
|
|
||||||
factory ICDataRefElement(M.IsolateRef isolate, M.ICDataRef icdata,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(icdata != null);
|
|
||||||
ICDataRefElement e = new ICDataRefElement.created();
|
|
||||||
e._r = new RenderingScheduler<ICDataRefElement>(e, queue: queue);
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._icdata = icdata;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
ICDataRefElement.created() : super.created('icdata-ref');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
children = <Element>[
|
|
||||||
new AnchorElement(href: Uris.inspect(_isolate, object: _icdata))
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['emphasize']
|
|
||||||
..text = 'ICData',
|
|
||||||
new SpanElement()..text = ' (${_icdata.selector})'
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,185 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/any_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/refresh.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/vm_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/object_common.dart';
|
|
||||||
|
|
||||||
class ICDataViewElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<ICDataViewElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<ICDataViewElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.VM _vm;
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
M.ICData _icdata;
|
|
||||||
M.ICDataRepository _icdatas;
|
|
||||||
M.RetainedSizeRepository _retainedSizes;
|
|
||||||
M.ReachableSizeRepository _reachableSizes;
|
|
||||||
M.InboundReferencesRepository _references;
|
|
||||||
M.RetainingPathRepository _retainingPaths;
|
|
||||||
M.ObjectRepository _objects;
|
|
||||||
|
|
||||||
M.VMRef get vm => _vm;
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.NotificationRepository get notifications => _notifications;
|
|
||||||
M.ICData get icdata => _icdata;
|
|
||||||
|
|
||||||
factory ICDataViewElement(
|
|
||||||
M.VM vm,
|
|
||||||
M.IsolateRef isolate,
|
|
||||||
M.ICData icdata,
|
|
||||||
M.EventRepository events,
|
|
||||||
M.NotificationRepository notifications,
|
|
||||||
M.ICDataRepository icdatas,
|
|
||||||
M.RetainedSizeRepository retainedSizes,
|
|
||||||
M.ReachableSizeRepository reachableSizes,
|
|
||||||
M.InboundReferencesRepository references,
|
|
||||||
M.RetainingPathRepository retainingPaths,
|
|
||||||
M.ObjectRepository objects,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(vm != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
assert(notifications != null);
|
|
||||||
assert(icdata != null);
|
|
||||||
assert(icdatas != null);
|
|
||||||
assert(retainedSizes != null);
|
|
||||||
assert(reachableSizes != null);
|
|
||||||
assert(references != null);
|
|
||||||
assert(retainingPaths != null);
|
|
||||||
assert(objects != null);
|
|
||||||
ICDataViewElement e = new ICDataViewElement.created();
|
|
||||||
e._r = new RenderingScheduler<ICDataViewElement>(e, queue: queue);
|
|
||||||
e._vm = vm;
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
e._notifications = notifications;
|
|
||||||
e._icdata = icdata;
|
|
||||||
e._icdatas = icdatas;
|
|
||||||
e._retainedSizes = retainedSizes;
|
|
||||||
e._reachableSizes = reachableSizes;
|
|
||||||
e._references = references;
|
|
||||||
e._retainingPaths = retainingPaths;
|
|
||||||
e._objects = objects;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
ICDataViewElement.created() : super.created('icdata-view');
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
children = <Element>[
|
|
||||||
navBar(<Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
|
|
||||||
new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
|
|
||||||
navMenu('icdata'),
|
|
||||||
(new NavRefreshElement(queue: _r.queue)
|
|
||||||
..onRefresh.listen((e) async {
|
|
||||||
e.element.disabled = true;
|
|
||||||
_icdata = await _icdatas.get(_isolate, _icdata.id);
|
|
||||||
_r.dirty();
|
|
||||||
}))
|
|
||||||
.element,
|
|
||||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
|
||||||
]),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered-big']
|
|
||||||
..children = <Element>[
|
|
||||||
new HeadingElement.h2()..text = 'ICData',
|
|
||||||
new HRElement(),
|
|
||||||
new ObjectCommonElement(_isolate, _icdata, _retainedSizes,
|
|
||||||
_reachableSizes, _references, _retainingPaths, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'selector',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = _icdata.selector
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'owner',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
_icdata.dartOwner == null
|
|
||||||
? (new SpanElement()..text = '<none>')
|
|
||||||
: anyRef(_isolate, _icdata.dartOwner, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
]
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'argumentsDescriptor',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
_icdata.argumentsDescriptor == null
|
|
||||||
? (new SpanElement()..text = '<none>')
|
|
||||||
: anyRef(
|
|
||||||
_isolate, _icdata.argumentsDescriptor, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
]
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'entries',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
_icdata.entries == null
|
|
||||||
? (new SpanElement()..text = '<none>')
|
|
||||||
: anyRef(_isolate, _icdata.entries, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
]
|
|
||||||
]
|
|
||||||
],
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 23 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 375 B |
|
@ -1,117 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/curly_block.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/any_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
|
|
||||||
class InboundReferencesElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<InboundReferencesElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<InboundReferencesElement>> get onRendered =>
|
|
||||||
_r.onRendered;
|
|
||||||
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.ObjectRef _object;
|
|
||||||
M.InboundReferencesRepository _references;
|
|
||||||
M.ObjectRepository _objects;
|
|
||||||
M.InboundReferences _inbounds;
|
|
||||||
bool _expanded = false;
|
|
||||||
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.ObjectRef get object => _object;
|
|
||||||
|
|
||||||
factory InboundReferencesElement(M.IsolateRef isolate, M.ObjectRef object,
|
|
||||||
M.InboundReferencesRepository references, M.ObjectRepository objects,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(object != null);
|
|
||||||
assert(references != null);
|
|
||||||
assert(objects != null);
|
|
||||||
InboundReferencesElement e = new InboundReferencesElement.created();
|
|
||||||
e._r = new RenderingScheduler<InboundReferencesElement>(e, queue: queue);
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._object = object;
|
|
||||||
e._references = references;
|
|
||||||
e._objects = objects;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
InboundReferencesElement.created() : super.created('inbound-references');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
children = <Element>[];
|
|
||||||
_r.disable(notify: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
final curlyBlock =
|
|
||||||
new CurlyBlockElement(expanded: _expanded, queue: _r.queue)
|
|
||||||
..content = _createContent()
|
|
||||||
..onToggle.listen((e) async {
|
|
||||||
_expanded = e.control.expanded;
|
|
||||||
if (_expanded) {
|
|
||||||
e.control.disabled = true;
|
|
||||||
await _refresh();
|
|
||||||
e.control.disabled = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
children = <Element>[curlyBlock.element];
|
|
||||||
_r.waitFor([curlyBlock.onRendered.first]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _refresh() async {
|
|
||||||
_inbounds = await _references.get(_isolate, _object.id);
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createContent() {
|
|
||||||
if (_inbounds == null) {
|
|
||||||
return const [];
|
|
||||||
}
|
|
||||||
return _inbounds.elements.map<Element>(_createItem).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
Element _createItem(M.InboundReference reference) {
|
|
||||||
final content = <Element>[];
|
|
||||||
|
|
||||||
if (reference.parentField != null) {
|
|
||||||
content.addAll([
|
|
||||||
new SpanElement()..text = 'referenced by ',
|
|
||||||
anyRef(_isolate, reference.parentField, _objects, queue: _r.queue),
|
|
||||||
new SpanElement()..text = ' of '
|
|
||||||
]);
|
|
||||||
} else if (reference.parentListIndex != null) {
|
|
||||||
content.add(new SpanElement()
|
|
||||||
..text = 'referenced by [ ${reference.parentListIndex} ] of ');
|
|
||||||
} else if (reference.parentWordOffset != null) {
|
|
||||||
content.add(new SpanElement()
|
|
||||||
..text = 'referenced by offset ${reference.parentWordOffset} of ');
|
|
||||||
}
|
|
||||||
|
|
||||||
content.addAll([
|
|
||||||
anyRef(_isolate, reference.source, _objects, queue: _r.queue),
|
|
||||||
new InboundReferencesElement(
|
|
||||||
_isolate, reference.source, _references, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]);
|
|
||||||
|
|
||||||
return new DivElement()
|
|
||||||
..classes = ['indent']
|
|
||||||
..children = content;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,366 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/curly_block.dart';
|
|
||||||
import 'package:observatory_2/src/elements/field_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/any_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/uris.dart';
|
|
||||||
import 'package:observatory_2/utils.dart';
|
|
||||||
|
|
||||||
class InstanceRefElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<InstanceRefElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<InstanceRefElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.InstanceRef _instance;
|
|
||||||
M.ObjectRepository _objects;
|
|
||||||
M.Instance _loadedInstance;
|
|
||||||
bool _expandable;
|
|
||||||
bool _expanded = false;
|
|
||||||
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.InstanceRef get instance => _instance;
|
|
||||||
|
|
||||||
factory InstanceRefElement(
|
|
||||||
M.IsolateRef isolate, M.InstanceRef instance, M.ObjectRepository objects,
|
|
||||||
{RenderingQueue queue, bool expandable = true}) {
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(instance != null);
|
|
||||||
assert(objects != null);
|
|
||||||
InstanceRefElement e = new InstanceRefElement.created();
|
|
||||||
e._r = new RenderingScheduler<InstanceRefElement>(e, queue: queue);
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._instance = instance;
|
|
||||||
e._objects = objects;
|
|
||||||
e._expandable = expandable;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
InstanceRefElement.created() : super.created('instance-ref');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
children = <Element>[];
|
|
||||||
_r.disable(notify: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
final content = _createLink();
|
|
||||||
|
|
||||||
if (_expandable && _hasValue()) {
|
|
||||||
content.addAll([
|
|
||||||
new SpanElement()..text = ' ',
|
|
||||||
(new CurlyBlockElement(expanded: _expanded, queue: _r.queue)
|
|
||||||
..content = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['indent']
|
|
||||||
..children = _createValue()
|
|
||||||
]
|
|
||||||
..onToggle.listen((e) async {
|
|
||||||
_expanded = e.control.expanded;
|
|
||||||
if (_expanded) {
|
|
||||||
e.control.disabled = true;
|
|
||||||
await _refresh();
|
|
||||||
e.control.disabled = false;
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.element
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
children = content;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _refresh() async {
|
|
||||||
_loadedInstance = await _objects.get(_isolate, _instance.id);
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createShowMoreButton() {
|
|
||||||
if (_loadedInstance.count == null) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
final count = _loadedInstance.count;
|
|
||||||
final button = new ButtonElement()..text = 'show next ${count}';
|
|
||||||
button.onClick.listen((_) async {
|
|
||||||
button.disabled = true;
|
|
||||||
_loadedInstance = await _objects.get(_isolate, _instance.id);
|
|
||||||
_r.dirty();
|
|
||||||
});
|
|
||||||
return [button];
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createLink() {
|
|
||||||
switch (_instance.kind) {
|
|
||||||
case M.InstanceKind.vNull:
|
|
||||||
case M.InstanceKind.bool:
|
|
||||||
case M.InstanceKind.int:
|
|
||||||
case M.InstanceKind.double:
|
|
||||||
case M.InstanceKind.float32x4:
|
|
||||||
case M.InstanceKind.float64x2:
|
|
||||||
case M.InstanceKind.int32x4:
|
|
||||||
return [
|
|
||||||
new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
|
|
||||||
..text = _instance.valueAsString
|
|
||||||
];
|
|
||||||
case M.InstanceKind.string:
|
|
||||||
return [
|
|
||||||
new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
|
|
||||||
..text = Utils.formatStringAsLiteral(
|
|
||||||
_instance.valueAsString, _instance.valueAsStringIsTruncated)
|
|
||||||
];
|
|
||||||
case M.InstanceKind.type:
|
|
||||||
case M.InstanceKind.functionType:
|
|
||||||
case M.InstanceKind.typeParameter:
|
|
||||||
case M.InstanceKind.recordType:
|
|
||||||
return [
|
|
||||||
new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
|
|
||||||
..text = _instance.name
|
|
||||||
];
|
|
||||||
case M.InstanceKind.closure:
|
|
||||||
return [
|
|
||||||
new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['emphasize']
|
|
||||||
..text = 'Closure',
|
|
||||||
new SpanElement()..text = ' (${_instance.closureFunction.name})'
|
|
||||||
]
|
|
||||||
];
|
|
||||||
case M.InstanceKind.regExp:
|
|
||||||
return [
|
|
||||||
new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['emphasize']
|
|
||||||
..text = _instance.clazz.name,
|
|
||||||
new SpanElement()..text = ' (${_instance.pattern.valueAsString})'
|
|
||||||
]
|
|
||||||
];
|
|
||||||
case M.InstanceKind.stackTrace:
|
|
||||||
return [
|
|
||||||
new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['emphasize']
|
|
||||||
..text = _instance.clazz.name,
|
|
||||||
]
|
|
||||||
];
|
|
||||||
case M.InstanceKind.plainInstance:
|
|
||||||
return [
|
|
||||||
new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
|
|
||||||
..classes = ['emphasize']
|
|
||||||
..text = _instance.clazz.name
|
|
||||||
];
|
|
||||||
case M.InstanceKind.list:
|
|
||||||
case M.InstanceKind.map:
|
|
||||||
case M.InstanceKind.set:
|
|
||||||
case M.InstanceKind.uint8ClampedList:
|
|
||||||
case M.InstanceKind.uint8List:
|
|
||||||
case M.InstanceKind.uint16List:
|
|
||||||
case M.InstanceKind.uint32List:
|
|
||||||
case M.InstanceKind.uint64List:
|
|
||||||
case M.InstanceKind.int8List:
|
|
||||||
case M.InstanceKind.int16List:
|
|
||||||
case M.InstanceKind.int32List:
|
|
||||||
case M.InstanceKind.int64List:
|
|
||||||
case M.InstanceKind.float32List:
|
|
||||||
case M.InstanceKind.float64List:
|
|
||||||
case M.InstanceKind.int32x4List:
|
|
||||||
case M.InstanceKind.float32x4List:
|
|
||||||
case M.InstanceKind.float64x2List:
|
|
||||||
return [
|
|
||||||
new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['emphasize']
|
|
||||||
..text = _instance.clazz.name,
|
|
||||||
new SpanElement()..text = ' (${_instance.length})'
|
|
||||||
]
|
|
||||||
];
|
|
||||||
case M.InstanceKind.mirrorReference:
|
|
||||||
case M.InstanceKind.weakProperty:
|
|
||||||
case M.InstanceKind.finalizer:
|
|
||||||
case M.InstanceKind.weakReference:
|
|
||||||
case M.InstanceKind.record:
|
|
||||||
return [
|
|
||||||
new AnchorElement(href: Uris.inspect(_isolate, object: _instance))
|
|
||||||
..classes = ['emphasize']
|
|
||||||
..text = _instance.clazz.name
|
|
||||||
];
|
|
||||||
}
|
|
||||||
throw new Exception('Unknown InstanceKind: ${_instance.kind}');
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _hasValue() {
|
|
||||||
switch (_instance.kind) {
|
|
||||||
case M.InstanceKind.closure:
|
|
||||||
case M.InstanceKind.plainInstance:
|
|
||||||
case M.InstanceKind.mirrorReference:
|
|
||||||
case M.InstanceKind.stackTrace:
|
|
||||||
case M.InstanceKind.weakReference:
|
|
||||||
case M.InstanceKind.weakProperty:
|
|
||||||
case M.InstanceKind.recordType:
|
|
||||||
return true;
|
|
||||||
case M.InstanceKind.list:
|
|
||||||
case M.InstanceKind.map:
|
|
||||||
case M.InstanceKind.set:
|
|
||||||
case M.InstanceKind.uint8ClampedList:
|
|
||||||
case M.InstanceKind.uint8List:
|
|
||||||
case M.InstanceKind.uint16List:
|
|
||||||
case M.InstanceKind.uint32List:
|
|
||||||
case M.InstanceKind.uint64List:
|
|
||||||
case M.InstanceKind.int8List:
|
|
||||||
case M.InstanceKind.int16List:
|
|
||||||
case M.InstanceKind.int32List:
|
|
||||||
case M.InstanceKind.int64List:
|
|
||||||
case M.InstanceKind.float32List:
|
|
||||||
case M.InstanceKind.float64List:
|
|
||||||
case M.InstanceKind.int32x4List:
|
|
||||||
case M.InstanceKind.float32x4List:
|
|
||||||
case M.InstanceKind.float64x2List:
|
|
||||||
return _instance.length > 0;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createValue() {
|
|
||||||
if (_loadedInstance == null) {
|
|
||||||
return [new SpanElement()..text = 'Loading...'];
|
|
||||||
}
|
|
||||||
switch (_instance.kind) {
|
|
||||||
case M.InstanceKind.closure:
|
|
||||||
{
|
|
||||||
var members = <Element>[];
|
|
||||||
if (_loadedInstance.closureFunction != null) {
|
|
||||||
members.add(new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = 'function = ',
|
|
||||||
anyRef(_isolate, _loadedInstance.closureFunction, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (_loadedInstance.closureContext != null) {
|
|
||||||
members.add(new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = 'context = ',
|
|
||||||
anyRef(_isolate, _loadedInstance.closureContext, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
return members;
|
|
||||||
}
|
|
||||||
case M.InstanceKind.plainInstance:
|
|
||||||
case M.InstanceKind.receivePort:
|
|
||||||
return _loadedInstance.fields
|
|
||||||
.map<Element>((f) => new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new FieldRefElement(_isolate, f.decl, _objects, queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new SpanElement()..text = ' = ',
|
|
||||||
anyRef(_isolate, f.value, _objects, queue: _r.queue)
|
|
||||||
])
|
|
||||||
.toList();
|
|
||||||
case M.InstanceKind.list:
|
|
||||||
var index = 0;
|
|
||||||
return _loadedInstance.elements
|
|
||||||
.map<Element>((element) => new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = '[ ${index++} ] : ',
|
|
||||||
anyRef(_isolate, element, _objects, queue: _r.queue)
|
|
||||||
])
|
|
||||||
.toList()
|
|
||||||
..addAll(_createShowMoreButton());
|
|
||||||
case M.InstanceKind.map:
|
|
||||||
return _loadedInstance.associations
|
|
||||||
.map<Element>((association) => new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = '[ ',
|
|
||||||
anyRef(_isolate, association.key, _objects, queue: _r.queue),
|
|
||||||
new SpanElement()..text = ' ] : ',
|
|
||||||
anyRef(_isolate, association.value, _objects, queue: _r.queue)
|
|
||||||
])
|
|
||||||
.toList()
|
|
||||||
..addAll(_createShowMoreButton());
|
|
||||||
case M.InstanceKind.set:
|
|
||||||
return _loadedInstance.elements
|
|
||||||
.map<Element>((element) => new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
anyRef(_isolate, element, _objects, queue: _r.queue)
|
|
||||||
])
|
|
||||||
.toList()
|
|
||||||
..addAll(_createShowMoreButton());
|
|
||||||
case M.InstanceKind.uint8ClampedList:
|
|
||||||
case M.InstanceKind.uint8List:
|
|
||||||
case M.InstanceKind.uint16List:
|
|
||||||
case M.InstanceKind.uint32List:
|
|
||||||
case M.InstanceKind.uint64List:
|
|
||||||
case M.InstanceKind.int8List:
|
|
||||||
case M.InstanceKind.int16List:
|
|
||||||
case M.InstanceKind.int32List:
|
|
||||||
case M.InstanceKind.int64List:
|
|
||||||
case M.InstanceKind.float32List:
|
|
||||||
case M.InstanceKind.float64List:
|
|
||||||
case M.InstanceKind.int32x4List:
|
|
||||||
case M.InstanceKind.float32x4List:
|
|
||||||
case M.InstanceKind.float64x2List:
|
|
||||||
var index = 0;
|
|
||||||
return _loadedInstance.typedElements
|
|
||||||
.map<Element>((e) => new DivElement()..text = '[ ${index++} ] : $e')
|
|
||||||
.toList()
|
|
||||||
..addAll(_createShowMoreButton());
|
|
||||||
case M.InstanceKind.mirrorReference:
|
|
||||||
return [
|
|
||||||
new SpanElement()..text = '<referent> : ',
|
|
||||||
anyRef(_isolate, _loadedInstance.referent, _objects, queue: _r.queue)
|
|
||||||
];
|
|
||||||
case M.InstanceKind.stackTrace:
|
|
||||||
return [
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['stackTraceBox']
|
|
||||||
..text = _instance.valueAsString
|
|
||||||
];
|
|
||||||
case M.InstanceKind.weakReference:
|
|
||||||
return [
|
|
||||||
new SpanElement()..text = '<target> : ',
|
|
||||||
anyRef(_isolate, _loadedInstance.target, _objects, queue: _r.queue)
|
|
||||||
];
|
|
||||||
case M.InstanceKind.weakProperty:
|
|
||||||
return [
|
|
||||||
new SpanElement()..text = '<key> : ',
|
|
||||||
anyRef(_isolate, _loadedInstance.key, _objects, queue: _r.queue),
|
|
||||||
new BRElement(),
|
|
||||||
new SpanElement()..text = '<value> : ',
|
|
||||||
anyRef(_isolate, _loadedInstance.value, _objects, queue: _r.queue),
|
|
||||||
];
|
|
||||||
case M.InstanceKind.recordType:
|
|
||||||
final fields = _loadedInstance.fields.toList();
|
|
||||||
return [
|
|
||||||
for (int i = 0; i < fields.length; ++i) ...[
|
|
||||||
new SpanElement()..text = '${fields[i].name} = ',
|
|
||||||
new InstanceRefElement(_isolate, fields[i].value.asValue, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
if (i + 1 != fields.length) new BRElement(),
|
|
||||||
]
|
|
||||||
];
|
|
||||||
default:
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,523 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library instance_view_element;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/curly_block.dart';
|
|
||||||
import 'package:observatory_2/src/elements/eval_box.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/any_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/instance_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/class_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/library_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/refresh.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/vm_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/object_common.dart';
|
|
||||||
import 'package:observatory_2/src/elements/source_inset.dart';
|
|
||||||
import 'package:observatory_2/utils.dart';
|
|
||||||
|
|
||||||
class InstanceViewElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<InstanceViewElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<InstanceViewElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.VM _vm;
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
M.Instance _instance;
|
|
||||||
M.LibraryRef _library;
|
|
||||||
M.ObjectRepository _objects;
|
|
||||||
M.ClassRepository _classes;
|
|
||||||
M.RetainedSizeRepository _retainedSizes;
|
|
||||||
M.ReachableSizeRepository _reachableSizes;
|
|
||||||
M.InboundReferencesRepository _references;
|
|
||||||
M.RetainingPathRepository _retainingPaths;
|
|
||||||
M.ScriptRepository _scripts;
|
|
||||||
M.EvalRepository _eval;
|
|
||||||
M.TypeArguments _typeArguments;
|
|
||||||
M.TypeArgumentsRepository _arguments;
|
|
||||||
M.BreakpointRepository _breakpoints;
|
|
||||||
M.FunctionRepository _functions;
|
|
||||||
M.SourceLocation _location;
|
|
||||||
|
|
||||||
M.VMRef get vm => _vm;
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.NotificationRepository get notifications => _notifications;
|
|
||||||
M.Instance get instance => _instance;
|
|
||||||
|
|
||||||
factory InstanceViewElement(
|
|
||||||
M.VM vm,
|
|
||||||
M.IsolateRef isolate,
|
|
||||||
M.Instance instance,
|
|
||||||
M.EventRepository events,
|
|
||||||
M.NotificationRepository notifications,
|
|
||||||
M.ObjectRepository objects,
|
|
||||||
M.ClassRepository classes,
|
|
||||||
M.RetainedSizeRepository retainedSizes,
|
|
||||||
M.ReachableSizeRepository reachableSizes,
|
|
||||||
M.InboundReferencesRepository references,
|
|
||||||
M.RetainingPathRepository retainingPaths,
|
|
||||||
M.ScriptRepository scripts,
|
|
||||||
M.EvalRepository eval,
|
|
||||||
M.TypeArgumentsRepository arguments,
|
|
||||||
M.BreakpointRepository breakpoints,
|
|
||||||
M.FunctionRepository functions,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(vm != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
assert(notifications != null);
|
|
||||||
assert(instance != null);
|
|
||||||
assert(objects != null);
|
|
||||||
assert(classes != null);
|
|
||||||
assert(retainedSizes != null);
|
|
||||||
assert(reachableSizes != null);
|
|
||||||
assert(references != null);
|
|
||||||
assert(retainingPaths != null);
|
|
||||||
assert(scripts != null);
|
|
||||||
assert(eval != null);
|
|
||||||
assert(arguments != null);
|
|
||||||
assert(breakpoints != null);
|
|
||||||
assert(functions != null);
|
|
||||||
InstanceViewElement e = new InstanceViewElement.created();
|
|
||||||
e._r = new RenderingScheduler<InstanceViewElement>(e, queue: queue);
|
|
||||||
e._vm = vm;
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
e._notifications = notifications;
|
|
||||||
e._instance = instance;
|
|
||||||
e._objects = objects;
|
|
||||||
e._classes = classes;
|
|
||||||
e._retainedSizes = retainedSizes;
|
|
||||||
e._reachableSizes = reachableSizes;
|
|
||||||
e._references = references;
|
|
||||||
e._retainingPaths = retainingPaths;
|
|
||||||
e._scripts = scripts;
|
|
||||||
e._eval = eval;
|
|
||||||
e._arguments = arguments;
|
|
||||||
e._breakpoints = breakpoints;
|
|
||||||
e._functions = functions;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
InstanceViewElement.created() : super.created('instance-view');
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
_loadExtraData();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
final content = <Element>[
|
|
||||||
new HeadingElement.h2()
|
|
||||||
..text = M.isAbstractType(_instance.kind)
|
|
||||||
? 'type ${_instance.name}'
|
|
||||||
: 'instance of ${_instance.clazz.name}',
|
|
||||||
new HRElement(),
|
|
||||||
new ObjectCommonElement(_isolate, _instance, _retainedSizes,
|
|
||||||
_reachableSizes, _references, _retainingPaths, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new BRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = _createMembers(),
|
|
||||||
new HRElement(),
|
|
||||||
new EvalBoxElement(_isolate, _instance, _objects, _eval,
|
|
||||||
quickExpressions: const ['toString()', 'runtimeType'],
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
];
|
|
||||||
if (_location != null) {
|
|
||||||
content.addAll([
|
|
||||||
new HRElement(),
|
|
||||||
new SourceInsetElement(_isolate, _location, _scripts, _objects, _events,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
children = <Element>[
|
|
||||||
navBar(_createMenu()),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered-big']
|
|
||||||
..children = content
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createMenu() {
|
|
||||||
final menu = <Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
|
|
||||||
new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element
|
|
||||||
];
|
|
||||||
if (_library != null) {
|
|
||||||
menu.add(new NavLibraryMenuElement(_isolate, _library, queue: _r.queue)
|
|
||||||
.element);
|
|
||||||
}
|
|
||||||
menu.addAll(<Element>[
|
|
||||||
new NavClassMenuElement(_isolate, _instance.clazz, queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
navMenu('instance'),
|
|
||||||
(new NavRefreshElement(queue: _r.queue)
|
|
||||||
..onRefresh.listen((e) {
|
|
||||||
e.element.disabled = true;
|
|
||||||
_refresh();
|
|
||||||
}))
|
|
||||||
.element,
|
|
||||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
|
||||||
]);
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
Element memberHalf(String cssClass, dynamic half) {
|
|
||||||
var result = new DivElement()..classes = [cssClass];
|
|
||||||
if (half is String) {
|
|
||||||
result.text = half;
|
|
||||||
} else {
|
|
||||||
result.children = <Element>[
|
|
||||||
anyRef(_isolate, half, _objects, queue: _r.queue)
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Element member(dynamic name, dynamic value) {
|
|
||||||
return new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
memberHalf('memberName', name),
|
|
||||||
memberHalf('memberValue', value),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createMembers() {
|
|
||||||
final members = <Element>[];
|
|
||||||
if (_instance.valueAsString != null) {
|
|
||||||
if (_instance.kind == M.InstanceKind.string) {
|
|
||||||
members.add(member(
|
|
||||||
'value as literal',
|
|
||||||
Utils.formatStringAsLiteral(
|
|
||||||
_instance.valueAsString, _instance.valueAsStringIsTruncated)));
|
|
||||||
} else {
|
|
||||||
members.add(member('value', _instance.valueAsString));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_instance.typeClass != null) {
|
|
||||||
members.add(member('type class', _instance.typeClass));
|
|
||||||
}
|
|
||||||
if (_typeArguments != null && _typeArguments.types.isNotEmpty) {
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'type arguments',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = ([new SpanElement()..text = '< ']
|
|
||||||
..addAll(_typeArguments.types.expand((type) => [
|
|
||||||
new InstanceRefElement(_isolate, type, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new SpanElement()..text = ', '
|
|
||||||
]))
|
|
||||||
..removeLast()
|
|
||||||
..add(new SpanElement()..text = ' >'))
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (_instance.parameterizedClass != null) {
|
|
||||||
members.add(member('parameterized class', _instance.parameterizedClass));
|
|
||||||
}
|
|
||||||
if (_instance.parameterIndex != null) {
|
|
||||||
members.add(member('parameter index', '${_instance.parameterIndex}'));
|
|
||||||
}
|
|
||||||
if (_instance.bound != null) {
|
|
||||||
members.add(member('bound', _instance.bound));
|
|
||||||
}
|
|
||||||
if (_instance.closureFunction != null) {
|
|
||||||
members.add(member('closure function', _instance.closureFunction));
|
|
||||||
}
|
|
||||||
if (_instance.closureContext != null) {
|
|
||||||
members.add(member('closure context', _instance.closureContext));
|
|
||||||
}
|
|
||||||
if (_instance.kind == M.InstanceKind.closure) {
|
|
||||||
ButtonElement btn;
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'closure breakpoint',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = <Element>[
|
|
||||||
btn = new ButtonElement()
|
|
||||||
..text = _instance.activationBreakpoint == null
|
|
||||||
? 'break on activation'
|
|
||||||
: 'remove'
|
|
||||||
..onClick.listen((_) {
|
|
||||||
btn.disabled = true;
|
|
||||||
_toggleBreakpoint();
|
|
||||||
})
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_instance.nativeFields != null && _instance.nativeFields.isNotEmpty) {
|
|
||||||
int i = 0;
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'native fields (${_instance.nativeFields.length})',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
(new CurlyBlockElement(
|
|
||||||
expanded: _instance.nativeFields.length <= 100,
|
|
||||||
queue: _r.queue)
|
|
||||||
..content = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = _instance.nativeFields
|
|
||||||
.map<Element>(
|
|
||||||
(f) => member('[ ${i++} ]', '[ ${f.value} ]'))
|
|
||||||
.toList()
|
|
||||||
])
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_instance.fields != null && _instance.fields.isNotEmpty) {
|
|
||||||
final fields = _instance.fields.toList();
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'fields (${fields.length})',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
(new CurlyBlockElement(
|
|
||||||
expanded: fields.length <= 100, queue: _r.queue)
|
|
||||||
..content = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = fields.map<Element>((f) {
|
|
||||||
final name = _instance.kind == M.InstanceKind.record
|
|
||||||
? f.name
|
|
||||||
: f.decl;
|
|
||||||
return member(name, f.value);
|
|
||||||
}).toList()
|
|
||||||
])
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_instance.elements != null && _instance.elements.isNotEmpty) {
|
|
||||||
final elements = _instance.elements.toList();
|
|
||||||
int i = 0;
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'elements (${_instance.length})',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = <Element>[
|
|
||||||
(new CurlyBlockElement(
|
|
||||||
expanded: elements.length <= 100, queue: _r.queue)
|
|
||||||
..content = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = elements
|
|
||||||
.map<Element>(
|
|
||||||
(element) => member('[ ${i++} ]', element))
|
|
||||||
.toList()
|
|
||||||
])
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
if (_instance.length != elements.length) {
|
|
||||||
members.add(member(
|
|
||||||
'...', '${_instance.length - elements.length} omitted elements'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_instance.associations != null && _instance.associations.isNotEmpty) {
|
|
||||||
final associations = _instance.associations.toList();
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'associations (${_instance.length})',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
(new CurlyBlockElement(
|
|
||||||
expanded: associations.length <= 100, queue: _r.queue)
|
|
||||||
..content = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = associations
|
|
||||||
.map<Element>((a) => new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = '[ ',
|
|
||||||
anyRef(_isolate, a.key, _objects,
|
|
||||||
queue: _r.queue),
|
|
||||||
new SpanElement()..text = ' ]',
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = <Element>[
|
|
||||||
anyRef(_isolate, a.value, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
]
|
|
||||||
])
|
|
||||||
.toList()
|
|
||||||
])
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
if (_instance.length != associations.length) {
|
|
||||||
members.add(member('...',
|
|
||||||
'${_instance.length - associations.length} omitted elements'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_instance.typedElements != null && _instance.typedElements.isNotEmpty) {
|
|
||||||
final typedElements = _instance.typedElements.toList();
|
|
||||||
int i = 0;
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'elements (${_instance.length})',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = <Element>[
|
|
||||||
(new CurlyBlockElement(
|
|
||||||
expanded: typedElements.length <= 100, queue: _r.queue)
|
|
||||||
..content = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = typedElements
|
|
||||||
.map<Element>((e) => member('[ ${i++} ]', '$e'))
|
|
||||||
.toList()
|
|
||||||
])
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
if (_instance.length != typedElements.length) {
|
|
||||||
members.add(member('...',
|
|
||||||
'${_instance.length - typedElements.length} omitted elements'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_instance.kind == M.InstanceKind.regExp) {
|
|
||||||
members.add(member('pattern', _instance.pattern));
|
|
||||||
members.add(
|
|
||||||
member('isCaseSensitive', _instance.isCaseSensitive ? 'yes' : 'no'));
|
|
||||||
members.add(member('isMultiLine', _instance.isMultiLine ? 'yes' : 'no'));
|
|
||||||
if (_instance.oneByteFunction != null) {
|
|
||||||
members.add(member('oneByteFunction', _instance.oneByteFunction));
|
|
||||||
}
|
|
||||||
if (_instance.twoByteFunction != null) {
|
|
||||||
members.add(member('twoByteFunction', _instance.twoByteFunction));
|
|
||||||
}
|
|
||||||
if (_instance.externalOneByteFunction != null) {
|
|
||||||
members.add(member(
|
|
||||||
'externalOneByteFunction', _instance.externalOneByteFunction));
|
|
||||||
}
|
|
||||||
if (_instance.externalTwoByteFunction != null) {
|
|
||||||
members.add(member(
|
|
||||||
'externalTwoByteFunction', _instance.externalTwoByteFunction));
|
|
||||||
}
|
|
||||||
if (_instance.oneByteBytecode != null) {
|
|
||||||
members.add(member('oneByteBytecode', _instance.oneByteBytecode));
|
|
||||||
}
|
|
||||||
if (_instance.twoByteBytecode != null) {
|
|
||||||
members.add(member('twoByteBytecode', _instance.twoByteBytecode));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_instance.kind == M.InstanceKind.mirrorReference) {
|
|
||||||
members.add(member('referent', _instance.referent));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_instance.kind == M.InstanceKind.weakReference) {
|
|
||||||
members.add(member('target', _instance.target));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_instance.kind == M.InstanceKind.weakProperty) {
|
|
||||||
members.add(member('key', _instance.key));
|
|
||||||
members.add(member('value', _instance.value));
|
|
||||||
}
|
|
||||||
|
|
||||||
return members;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _refresh() async {
|
|
||||||
_instance = await _objects.get(_isolate, _instance.id);
|
|
||||||
await _loadExtraData();
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _loadExtraData() async {
|
|
||||||
_library = (await _classes.get(_isolate, _instance.clazz.id)).library;
|
|
||||||
if (_instance.typeArguments != null) {
|
|
||||||
_typeArguments =
|
|
||||||
await _arguments.get(_isolate, _instance.typeArguments.id);
|
|
||||||
} else {
|
|
||||||
_typeArguments = null;
|
|
||||||
}
|
|
||||||
if (_instance.closureFunction != null) {
|
|
||||||
_location = (await _functions.get(_isolate, _instance.closureFunction.id))
|
|
||||||
.location;
|
|
||||||
} else if (_instance.typeClass != null) {
|
|
||||||
_location =
|
|
||||||
(await _classes.get(_isolate, _instance.typeClass.id)).location;
|
|
||||||
}
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _toggleBreakpoint() async {
|
|
||||||
if (_instance.activationBreakpoint == null) {
|
|
||||||
await _breakpoints.addOnActivation(_isolate, _instance);
|
|
||||||
} else {
|
|
||||||
await _breakpoints.remove(_isolate, _instance.activationBreakpoint);
|
|
||||||
}
|
|
||||||
await _refresh();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
|
|
||||||
class IsolateCounterChartElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<IsolateCounterChartElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<IsolateCounterChartElement>> get onRendered =>
|
|
||||||
_r.onRendered;
|
|
||||||
|
|
||||||
Map _counters;
|
|
||||||
StreamSubscription _subscription;
|
|
||||||
|
|
||||||
factory IsolateCounterChartElement(Map counters, {RenderingQueue queue}) {
|
|
||||||
assert(counters != null);
|
|
||||||
IsolateCounterChartElement e = new IsolateCounterChartElement.created();
|
|
||||||
e._r = new RenderingScheduler<IsolateCounterChartElement>(e, queue: queue);
|
|
||||||
e._counters = counters;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
IsolateCounterChartElement.created() : super.created('isolate-counter-chart');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
_subscription = window.onResize.listen((_) => _r.dirty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
children = <Element>[];
|
|
||||||
_r.disable(notify: true);
|
|
||||||
_subscription.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
var members = <Element>[];
|
|
||||||
_counters.forEach((key, value) {
|
|
||||||
members.add(new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = key,
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = value,
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = members
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,119 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/function_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/source_link.dart';
|
|
||||||
|
|
||||||
class IsolateLocationElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<IsolateLocationElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<IsolateLocationElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.Isolate _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
M.ScriptRepository _scripts;
|
|
||||||
StreamSubscription _debugSubscription;
|
|
||||||
StreamSubscription _isolateSubscription;
|
|
||||||
|
|
||||||
factory IsolateLocationElement(
|
|
||||||
M.Isolate isolate, M.EventRepository events, M.ScriptRepository scripts,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
assert(scripts != null);
|
|
||||||
IsolateLocationElement e = new IsolateLocationElement.created();
|
|
||||||
e._r = new RenderingScheduler<IsolateLocationElement>(e, queue: queue);
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
e._scripts = scripts;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
IsolateLocationElement.created() : super.created('isolate-location');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
_debugSubscription = _events.onDebugEvent.listen(_eventListener);
|
|
||||||
_isolateSubscription = _events.onIsolateEvent.listen(_eventListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
children = <Element>[];
|
|
||||||
_r.disable(notify: true);
|
|
||||||
_debugSubscription.cancel();
|
|
||||||
_isolateSubscription.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
switch (_isolate.status) {
|
|
||||||
case M.IsolateStatus.loading:
|
|
||||||
children = <Element>[new SpanElement()..text = 'not yet runnable'];
|
|
||||||
break;
|
|
||||||
case M.IsolateStatus.running:
|
|
||||||
children = <Element>[
|
|
||||||
new SpanElement()..text = 'at ',
|
|
||||||
new FunctionRefElement(
|
|
||||||
_isolate, M.topFrame(_isolate.pauseEvent).function,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new SpanElement()..text = ' (',
|
|
||||||
new SourceLinkElement(
|
|
||||||
_isolate, M.topFrame(_isolate.pauseEvent).location, _scripts,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new SpanElement()..text = ') '
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
case M.IsolateStatus.paused:
|
|
||||||
if (_isolate.pauseEvent is M.PauseStartEvent) {
|
|
||||||
children = <Element>[new SpanElement()..text = 'at isolate start'];
|
|
||||||
} else if (_isolate.pauseEvent is M.PauseExitEvent) {
|
|
||||||
children = <Element>[new SpanElement()..text = 'at isolate exit'];
|
|
||||||
} else if (_isolate.pauseEvent is M.NoneEvent) {
|
|
||||||
children = <Element>[new SpanElement()..text = 'not yet runnable'];
|
|
||||||
} else {
|
|
||||||
final content = <Element>[];
|
|
||||||
if (_isolate.pauseEvent is M.PauseBreakpointEvent) {
|
|
||||||
content.add(new SpanElement()..text = 'by breakpoint');
|
|
||||||
} else if (_isolate.pauseEvent is M.PauseExceptionEvent) {
|
|
||||||
content.add(new SpanElement()..text = 'by exception');
|
|
||||||
}
|
|
||||||
if (M.topFrame(_isolate.pauseEvent) != null) {
|
|
||||||
content.addAll([
|
|
||||||
new SpanElement()..text = ' at ',
|
|
||||||
new FunctionRefElement(
|
|
||||||
_isolate, M.topFrame(_isolate.pauseEvent).function,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new SpanElement()..text = ' (',
|
|
||||||
new SourceLinkElement(_isolate,
|
|
||||||
M.topFrame(_isolate.pauseEvent).location, _scripts,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new SpanElement()..text = ') '
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
children = content;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
children = const [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _eventListener(e) {
|
|
||||||
if (e.isolate.id == _isolate.id) {
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
|
|
||||||
class IsolateRunStateElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<IsolateRunStateElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<IsolateRunStateElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.Isolate _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
StreamSubscription _debugSubscription;
|
|
||||||
StreamSubscription _isolateSubscription;
|
|
||||||
|
|
||||||
factory IsolateRunStateElement(M.Isolate isolate, M.EventRepository events,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
IsolateRunStateElement e = new IsolateRunStateElement.created();
|
|
||||||
e._r = new RenderingScheduler<IsolateRunStateElement>(e, queue: queue);
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
IsolateRunStateElement.created() : super.created('isolate-run-state');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
_debugSubscription = _events.onDebugEvent.listen(_eventListener);
|
|
||||||
_isolateSubscription = _events.onIsolateEvent.listen(_eventListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
children = <Element>[];
|
|
||||||
_r.disable(notify: true);
|
|
||||||
_debugSubscription.cancel();
|
|
||||||
_isolateSubscription.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
switch (_isolate.status) {
|
|
||||||
case M.IsolateStatus.loading:
|
|
||||||
children = <Element>[new SpanElement()..text = 'loading... '];
|
|
||||||
break;
|
|
||||||
case M.IsolateStatus.running:
|
|
||||||
children = <Element>[new SpanElement()..text = 'running '];
|
|
||||||
break;
|
|
||||||
case M.IsolateStatus.idle:
|
|
||||||
children = <Element>[new SpanElement()..text = 'idle '];
|
|
||||||
break;
|
|
||||||
case M.IsolateStatus.paused:
|
|
||||||
children = <Element>[
|
|
||||||
new SpanElement()
|
|
||||||
..title = '${_isolate.pauseEvent.timestamp}'
|
|
||||||
..text = 'paused '
|
|
||||||
];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _eventListener(e) {
|
|
||||||
if (e.isolate.id == _isolate.id) {
|
|
||||||
_isolate = e.isolate;
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,177 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/utils.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/uris.dart';
|
|
||||||
import 'package:observatory_2/src/elements/isolate/counter_chart.dart';
|
|
||||||
|
|
||||||
class IsolateSharedSummaryElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<IsolateSharedSummaryElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<IsolateSharedSummaryElement>> get onRendered =>
|
|
||||||
_r.onRendered;
|
|
||||||
|
|
||||||
M.Isolate _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
StreamSubscription _isolateSubscription;
|
|
||||||
|
|
||||||
factory IsolateSharedSummaryElement(
|
|
||||||
M.Isolate isolate, M.EventRepository events,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
IsolateSharedSummaryElement e = new IsolateSharedSummaryElement.created();
|
|
||||||
e._r = new RenderingScheduler<IsolateSharedSummaryElement>(e, queue: queue);
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
IsolateSharedSummaryElement.created()
|
|
||||||
: super.created('isolate-shared-summary');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
_isolateSubscription = _events.onIsolateEvent.listen(_eventListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
children = <Element>[];
|
|
||||||
_r.disable(notify: true);
|
|
||||||
_isolateSubscription.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
final newHeapUsed = Utils.formatSize(_isolate.newSpace.used);
|
|
||||||
final newHeapCapacity = Utils.formatSize(_isolate.newSpace.capacity);
|
|
||||||
final oldHeapUsed = Utils.formatSize(_isolate.oldSpace.used);
|
|
||||||
final oldHeapCapacity = Utils.formatSize(_isolate.oldSpace.capacity);
|
|
||||||
final content = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['menu']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'new heap',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = '$newHeapUsed of $newHeapCapacity',
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'old heap',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = '$oldHeapUsed of $oldHeapCapacity',
|
|
||||||
]
|
|
||||||
],
|
|
||||||
new BRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = 'see ',
|
|
||||||
new AnchorElement(href: Uris.debugger(_isolate))
|
|
||||||
..text = 'debugger'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = 'see ',
|
|
||||||
new AnchorElement(href: Uris.classTree(_isolate))
|
|
||||||
..text = 'class hierarchy'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = 'see ',
|
|
||||||
new AnchorElement(href: Uris.cpuProfiler(_isolate))
|
|
||||||
..text = 'cpu profile'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = 'see ',
|
|
||||||
new AnchorElement(href: Uris.cpuProfilerTable(_isolate))
|
|
||||||
..text = 'cpu profile (table)'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = 'see ',
|
|
||||||
new AnchorElement(href: Uris.allocationProfiler(_isolate))
|
|
||||||
..text = 'allocation profile'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = 'see ',
|
|
||||||
new AnchorElement(href: Uris.heapSnapshot(_isolate))
|
|
||||||
..text = 'heap snapshot'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = 'see ',
|
|
||||||
new AnchorElement(href: Uris.heapMap(_isolate))..text = 'heap map'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = 'see ',
|
|
||||||
new AnchorElement(href: Uris.metrics(_isolate))..text = 'metrics'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = 'see ',
|
|
||||||
new AnchorElement(href: Uris.persistentHandles(_isolate))
|
|
||||||
..text = 'persistent handles'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = 'see ',
|
|
||||||
new AnchorElement(href: Uris.ports(_isolate))..text = 'ports'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = 'see ',
|
|
||||||
new AnchorElement(href: Uris.logging(_isolate))..text = 'logging'
|
|
||||||
]
|
|
||||||
],
|
|
||||||
new IsolateCounterChartElement(_isolate.counters, queue: _r.queue).element
|
|
||||||
];
|
|
||||||
if (_isolate.error != null) {
|
|
||||||
children = <Element>[
|
|
||||||
new PreElement()
|
|
||||||
..classes = ['errorBox']
|
|
||||||
..text = _isolate.error.message,
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['summary']
|
|
||||||
..children = content
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['summary']
|
|
||||||
..children = content
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _eventListener(e) {
|
|
||||||
if (e.isolate.id == _isolate.id) {
|
|
||||||
_isolate = e.isolate;
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,179 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/utils.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/uris.dart';
|
|
||||||
import 'package:observatory_2/src/elements/isolate_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/isolate/location.dart';
|
|
||||||
import 'package:observatory_2/src/elements/isolate/run_state.dart';
|
|
||||||
|
|
||||||
class IsolateSummaryElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<IsolateSummaryElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<IsolateSummaryElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
M.IsolateRepository _isolates;
|
|
||||||
M.ScriptRepository _scripts;
|
|
||||||
M.Isolate _loadedIsolate;
|
|
||||||
|
|
||||||
factory IsolateSummaryElement(
|
|
||||||
M.IsolateRef isolate,
|
|
||||||
M.IsolateRepository isolates,
|
|
||||||
M.EventRepository events,
|
|
||||||
M.ScriptRepository scripts,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(isolates != null);
|
|
||||||
assert(events != null);
|
|
||||||
assert(scripts != null);
|
|
||||||
IsolateSummaryElement e = new IsolateSummaryElement.created();
|
|
||||||
e._r = new RenderingScheduler<IsolateSummaryElement>(e, queue: queue);
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._isolates = isolates;
|
|
||||||
e._events = events;
|
|
||||||
e._scripts = scripts;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
IsolateSummaryElement.created() : super.created('isolate-summary');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
_load();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
children = <Element>[];
|
|
||||||
_r.disable(notify: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
if (_loadedIsolate == null) {
|
|
||||||
children = <Element>[
|
|
||||||
new SpanElement()..text = 'loading ',
|
|
||||||
new IsolateRefElement(_isolate, _events, queue: _r.queue).element
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
children = <Element>[
|
|
||||||
linkAndStatusRow(),
|
|
||||||
new BRElement(),
|
|
||||||
memoryRow(),
|
|
||||||
new BRElement(),
|
|
||||||
toolsRow(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Element linkAndStatusRow() {
|
|
||||||
return new DivElement()
|
|
||||||
..classes = ['flex-row-wrap']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['isolate-ref-container']
|
|
||||||
..children = <Element>[
|
|
||||||
new IsolateRefElement(_isolate, _events, queue: _r.queue).element
|
|
||||||
],
|
|
||||||
new DivElement()..style.flex = '1',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['flex-row', 'isolate-state-container']
|
|
||||||
..children = <Element>[
|
|
||||||
new IsolateRunStateElement(_isolate, _events, queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new IsolateLocationElement(_isolate, _events, _scripts,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new SpanElement()..text = ' [',
|
|
||||||
new AnchorElement(href: Uris.debugger(_isolate))..text = 'debug',
|
|
||||||
new SpanElement()..text = ']'
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
Element memoryRow() {
|
|
||||||
final isolate = _isolate as M.Isolate;
|
|
||||||
final newHeapUsed = Utils.formatSize(isolate.newSpace.used);
|
|
||||||
final newHeapCapacity = Utils.formatSize(isolate.newSpace.capacity);
|
|
||||||
final oldHeapUsed = Utils.formatSize(isolate.oldSpace.used);
|
|
||||||
final oldHeapCapacity = Utils.formatSize(isolate.oldSpace.capacity);
|
|
||||||
final heapUsed =
|
|
||||||
Utils.formatSize(isolate.newSpace.used + isolate.oldSpace.used);
|
|
||||||
final heapCapacity =
|
|
||||||
Utils.formatSize(isolate.newSpace.capacity + isolate.oldSpace.capacity);
|
|
||||||
return new DivElement()
|
|
||||||
..classes = ['flex-row-wrap-right']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..style.padding = '5px'
|
|
||||||
..text = 'new-space $newHeapUsed of $newHeapCapacity',
|
|
||||||
new DivElement()
|
|
||||||
..style.padding = '5px'
|
|
||||||
..text = '/',
|
|
||||||
new DivElement()
|
|
||||||
..style.padding = '5px'
|
|
||||||
..text = 'old-space $oldHeapUsed of $oldHeapCapacity',
|
|
||||||
new DivElement()
|
|
||||||
..style.padding = '5px'
|
|
||||||
..text = '/',
|
|
||||||
new DivElement()
|
|
||||||
..style.padding = '5px'
|
|
||||||
..text = 'heap $heapUsed of $heapCapacity',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
Element toolsRow() {
|
|
||||||
return new DivElement()
|
|
||||||
..classes = ['flex-row-spaced']
|
|
||||||
..children = <Element>[
|
|
||||||
new AnchorElement(href: Uris.debugger(_isolate))
|
|
||||||
..classes = ['flex-item-even']
|
|
||||||
..text = 'debugger',
|
|
||||||
new AnchorElement(href: Uris.classTree(_isolate))
|
|
||||||
..classes = ['flex-item-even']
|
|
||||||
..text = 'class hierarchy',
|
|
||||||
new AnchorElement(href: Uris.cpuProfiler(_isolate))
|
|
||||||
..classes = ['flex-item-even']
|
|
||||||
..text = 'cpu profile',
|
|
||||||
new AnchorElement(href: Uris.cpuProfilerTable(_isolate))
|
|
||||||
..classes = ['flex-item-even']
|
|
||||||
..text = 'cpu profile (table)',
|
|
||||||
new AnchorElement(href: Uris.allocationProfiler(_isolate))
|
|
||||||
..classes = ['flex-item-even']
|
|
||||||
..text = 'allocation profile',
|
|
||||||
new AnchorElement(href: Uris.heapSnapshot(_isolate))
|
|
||||||
..classes = ['flex-item-even']
|
|
||||||
..text = 'heap snapshot',
|
|
||||||
new AnchorElement(href: Uris.heapMap(_isolate))
|
|
||||||
..classes = ['flex-item-even']
|
|
||||||
..text = 'heap map',
|
|
||||||
new AnchorElement(href: Uris.metrics(_isolate))
|
|
||||||
..classes = ['flex-item-even']
|
|
||||||
..text = 'metrics',
|
|
||||||
new AnchorElement(href: Uris.persistentHandles(_isolate))
|
|
||||||
..classes = ['flex-item-even']
|
|
||||||
..text = 'persistent handles',
|
|
||||||
new AnchorElement(href: Uris.ports(_isolate))
|
|
||||||
..classes = ['flex-item-even']
|
|
||||||
..text = 'ports',
|
|
||||||
new AnchorElement(href: Uris.logging(_isolate))
|
|
||||||
..classes = ['flex-item-even']
|
|
||||||
..text = 'logging',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _load() async {
|
|
||||||
_loadedIsolate = await _isolates.get(_isolate);
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library isolate_reconnect_element;
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/uris.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
|
|
||||||
class IsolateReconnectElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<IsolateReconnectElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<IsolateReconnectElement>> get onRendered =>
|
|
||||||
_r.onRendered;
|
|
||||||
|
|
||||||
M.VM _vm;
|
|
||||||
String _missing;
|
|
||||||
Uri _uri;
|
|
||||||
M.EventRepository _events;
|
|
||||||
StreamSubscription _subscription;
|
|
||||||
|
|
||||||
M.VM get vm => _vm;
|
|
||||||
String get missing => _missing;
|
|
||||||
Uri get uri => _uri;
|
|
||||||
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
factory IsolateReconnectElement(M.VM vm, M.EventRepository events,
|
|
||||||
M.NotificationRepository notifications, String missing, Uri uri,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(vm != null);
|
|
||||||
assert(events != null);
|
|
||||||
assert(missing != null);
|
|
||||||
assert(uri != null);
|
|
||||||
assert(notifications != null);
|
|
||||||
IsolateReconnectElement e = new IsolateReconnectElement.created();
|
|
||||||
e._r = new RenderingScheduler<IsolateReconnectElement>(e, queue: queue);
|
|
||||||
e._vm = vm;
|
|
||||||
e._events = events;
|
|
||||||
e._missing = missing;
|
|
||||||
e._uri = uri;
|
|
||||||
e._notifications = notifications;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
IsolateReconnectElement.created() : super.created('isolate-reconnect');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_subscription = _events.onVMUpdate.listen((e) {
|
|
||||||
_vm = e.vm;
|
|
||||||
_r.dirty();
|
|
||||||
});
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
children = <Element>[];
|
|
||||||
_r.disable(notify: true);
|
|
||||||
_subscription.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
children = <Element>[
|
|
||||||
navBar(<Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
|
||||||
]),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered']
|
|
||||||
..children = <Element>[
|
|
||||||
new HeadingElement.h1()..text = 'Isolate $_missing no longer exists',
|
|
||||||
new HRElement(),
|
|
||||||
new BRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = (_vm.isolates.map<Element>((isolate) {
|
|
||||||
final query = new Map<String, dynamic>.from(_uri.queryParameters);
|
|
||||||
query['isolateId'] = isolate.id;
|
|
||||||
final href = new Uri(path: _uri.path, queryParameters: query);
|
|
||||||
return new DivElement()
|
|
||||||
..classes = ['memberItem', 'doubleSpaced']
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = 'Continue in ',
|
|
||||||
new AnchorElement(href: '#$href')
|
|
||||||
..classes = ['isolate-link']
|
|
||||||
..text = '${isolate.id} (${isolate.name})'
|
|
||||||
];
|
|
||||||
}).toList()
|
|
||||||
..add(new DivElement()
|
|
||||||
..classes = ['memberItem', 'doubleSpaced']
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = 'Go to ',
|
|
||||||
new AnchorElement(href: Uris.vm())..text = 'isolates summary',
|
|
||||||
]))
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library isolate_ref_element;
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M show IsolateRef, EventRepository;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/uris.dart';
|
|
||||||
|
|
||||||
class IsolateRefElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<IsolateRefElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<IsolateRefElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
StreamSubscription _updatesSubscription;
|
|
||||||
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
|
|
||||||
factory IsolateRefElement(M.IsolateRef isolate, M.EventRepository events,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
IsolateRefElement e = new IsolateRefElement.created();
|
|
||||||
e._r = new RenderingScheduler<IsolateRefElement>(e, queue: queue);
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
IsolateRefElement.created() : super.created('isolate-ref');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_updatesSubscription = _events.onIsolateUpdate
|
|
||||||
.where((e) => e.isolate.id == isolate.id)
|
|
||||||
.listen((e) {
|
|
||||||
_isolate = e.isolate;
|
|
||||||
_r.dirty();
|
|
||||||
});
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
children = <Element>[];
|
|
||||||
_r.disable(notify: true);
|
|
||||||
_updatesSubscription.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
final isolateType = isolate.isSystemIsolate ? 'System Isolate' : 'Isolate';
|
|
||||||
children = <Element>[
|
|
||||||
new AnchorElement(href: Uris.inspect(isolate))
|
|
||||||
..text = '$isolateType ${isolate.number} (${isolate.name})'
|
|
||||||
..classes = ['isolate-ref']
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,323 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library isolate_view_element;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/curly_block.dart';
|
|
||||||
import 'package:observatory_2/src/elements/eval_box.dart';
|
|
||||||
import 'package:observatory_2/src/elements/function_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/uris.dart';
|
|
||||||
import 'package:observatory_2/src/elements/isolate/location.dart';
|
|
||||||
import 'package:observatory_2/src/elements/isolate/run_state.dart';
|
|
||||||
import 'package:observatory_2/src/elements/isolate/shared_summary.dart';
|
|
||||||
import 'package:observatory_2/src/elements/library_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/refresh.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/reload.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/vm_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/script_inset.dart';
|
|
||||||
import 'package:observatory_2/src/elements/source_inset.dart';
|
|
||||||
|
|
||||||
class IsolateViewElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<IsolateViewElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<IsolateViewElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.VM _vm;
|
|
||||||
M.Isolate _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
M.IsolateRepository _isolates;
|
|
||||||
M.ScriptRepository _scripts;
|
|
||||||
M.FunctionRepository _functions;
|
|
||||||
M.LibraryRepository _libraries;
|
|
||||||
M.ObjectRepository _objects;
|
|
||||||
M.EvalRepository _eval;
|
|
||||||
M.ServiceFunction _function;
|
|
||||||
M.ScriptRef _rootScript;
|
|
||||||
StreamSubscription _subscription;
|
|
||||||
|
|
||||||
M.VMRef get vm => _vm;
|
|
||||||
M.Isolate get isolate => _isolate;
|
|
||||||
M.NotificationRepository get notifications => _notifications;
|
|
||||||
|
|
||||||
factory IsolateViewElement(
|
|
||||||
M.VM vm,
|
|
||||||
M.Isolate isolate,
|
|
||||||
M.EventRepository events,
|
|
||||||
M.NotificationRepository notifications,
|
|
||||||
M.IsolateRepository isolates,
|
|
||||||
M.ScriptRepository scripts,
|
|
||||||
M.FunctionRepository functions,
|
|
||||||
M.LibraryRepository libraries,
|
|
||||||
M.ObjectRepository objects,
|
|
||||||
M.EvalRepository eval,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(vm != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
assert(notifications != null);
|
|
||||||
assert(isolates != null);
|
|
||||||
assert(scripts != null);
|
|
||||||
assert(functions != null);
|
|
||||||
assert(objects != null);
|
|
||||||
assert(eval != null);
|
|
||||||
assert(libraries != null);
|
|
||||||
IsolateViewElement e = new IsolateViewElement.created();
|
|
||||||
e._r = new RenderingScheduler<IsolateViewElement>(e, queue: queue);
|
|
||||||
e._vm = vm;
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
e._notifications = notifications;
|
|
||||||
e._isolates = isolates;
|
|
||||||
e._scripts = scripts;
|
|
||||||
e._functions = functions;
|
|
||||||
e._objects = objects;
|
|
||||||
e._eval = eval;
|
|
||||||
e._libraries = libraries;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
IsolateViewElement.created() : super.created('isolate-view');
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
_loadExtraData();
|
|
||||||
_subscription = _events.onIsolateUpdate.listen((e) {
|
|
||||||
if (e.isolate.id == _isolate) {
|
|
||||||
_isolate = isolate;
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
_subscription.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
final uptime = new DateTime.now().difference(_isolate.startTime);
|
|
||||||
final libraries = _isolate.libraries.toList();
|
|
||||||
children = <Element>[
|
|
||||||
navBar(<Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
|
|
||||||
new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
|
|
||||||
(new NavReloadElement(_isolate, _isolates, _events, queue: _r.queue)
|
|
||||||
..onReload.listen((_) async {
|
|
||||||
_isolate = await _isolates.get(_isolate);
|
|
||||||
await _loadExtraData();
|
|
||||||
_r.dirty();
|
|
||||||
}))
|
|
||||||
.element,
|
|
||||||
(new NavRefreshElement(queue: _r.queue)
|
|
||||||
..onRefresh.listen((e) async {
|
|
||||||
e.element.disabled = true;
|
|
||||||
_isolate = await _isolates.get(_isolate);
|
|
||||||
await _loadExtraData();
|
|
||||||
_r.dirty();
|
|
||||||
}))
|
|
||||||
.element,
|
|
||||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
|
||||||
]),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered-big']
|
|
||||||
..children = <Element>[
|
|
||||||
new HeadingElement.h2()..text = 'Isolate ${_isolate.name}',
|
|
||||||
new BRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['flex-row']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()..style.flex = '1',
|
|
||||||
new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new IsolateRunStateElement(_isolate, _events, queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new IsolateLocationElement(_isolate, _events, _scripts,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new SpanElement()..text = ' [',
|
|
||||||
new AnchorElement(href: Uris.debugger(_isolate))
|
|
||||||
..text = 'debug',
|
|
||||||
new SpanElement()..text = ']'
|
|
||||||
]
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..children = _function != null
|
|
||||||
? [
|
|
||||||
new BRElement(),
|
|
||||||
(new SourceInsetElement(_isolate, _function.location,
|
|
||||||
_scripts, _objects, _events,
|
|
||||||
currentPos: M
|
|
||||||
.topFrame(isolate.pauseEvent)
|
|
||||||
.location
|
|
||||||
.tokenPos,
|
|
||||||
queue: _r.queue)
|
|
||||||
..classes = ['header_inset'])
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
: const [],
|
|
||||||
new HRElement(),
|
|
||||||
new IsolateSharedSummaryElement(_isolate, _events, queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new HRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'started at',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = '${_isolate.startTime}'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'uptime',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = '$uptime'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'root library',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = <Element>[
|
|
||||||
_isolate.rootLibrary == null
|
|
||||||
? (new SpanElement()..text = 'loading...')
|
|
||||||
: new LibraryRefElement(
|
|
||||||
_isolate, _isolate.rootLibrary,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = _isolate.entry != null
|
|
||||||
? [
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'entry',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = <Element>[
|
|
||||||
new FunctionRefElement(_isolate, _isolate.entry,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
]
|
|
||||||
: const [],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'isolate id',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = '${_isolate.number}'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'service protocol extensions',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = '${_isolate.extensionRPCs}'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'object store',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = <Element>[
|
|
||||||
new AnchorElement(href: Uris.objectStore(_isolate))
|
|
||||||
..text = 'object store'
|
|
||||||
]
|
|
||||||
],
|
|
||||||
new BRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'libraries (${libraries.length})',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = <Element>[
|
|
||||||
(new CurlyBlockElement(queue: _r.queue)
|
|
||||||
..content = libraries
|
|
||||||
.map<Element>((l) => new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new LibraryRefElement(_isolate, l,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
])
|
|
||||||
.toList())
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
],
|
|
||||||
],
|
|
||||||
new HRElement(),
|
|
||||||
new EvalBoxElement(_isolate, _isolate.rootLibrary, _objects, _eval,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new DivElement()
|
|
||||||
..children = _rootScript != null
|
|
||||||
? [
|
|
||||||
new HRElement(),
|
|
||||||
new ScriptInsetElement(
|
|
||||||
_isolate, _rootScript, _scripts, _objects, _events,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
: const [],
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _loadExtraData() async {
|
|
||||||
_function = null;
|
|
||||||
_rootScript = null;
|
|
||||||
final frame = M.topFrame(_isolate.pauseEvent);
|
|
||||||
if (frame != null) {
|
|
||||||
_function = await _functions.get(_isolate, frame.function.id);
|
|
||||||
}
|
|
||||||
if (_isolate.rootLibrary != null) {
|
|
||||||
final rootLibrary =
|
|
||||||
await _libraries.get(_isolate, _isolate.rootLibrary.id);
|
|
||||||
_rootScript = rootLibrary.rootScript;
|
|
||||||
}
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,145 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library json_view_element;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
|
|
||||||
class JSONViewElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<JSONViewElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<JSONViewElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
Map _map;
|
|
||||||
|
|
||||||
M.NotificationRepository get notifications => _notifications;
|
|
||||||
Map get map => _map;
|
|
||||||
|
|
||||||
factory JSONViewElement(Map map, M.NotificationRepository notifications,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(notifications != null);
|
|
||||||
assert(map != null);
|
|
||||||
JSONViewElement e = new JSONViewElement.created();
|
|
||||||
e._r = new RenderingScheduler<JSONViewElement>(e, queue: queue);
|
|
||||||
e._notifications = notifications;
|
|
||||||
e._map = map;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONViewElement.created() : super.created('json-view');
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
children = <Element>[
|
|
||||||
navBar(<Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
|
||||||
]),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered-big']
|
|
||||||
..children = <Element>[
|
|
||||||
new HeadingElement.h2()..text = 'Object',
|
|
||||||
new HRElement(),
|
|
||||||
new PreElement()..text = JSONPretty.stringify(_map),
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class JSONPretty {
|
|
||||||
JSONPretty._();
|
|
||||||
|
|
||||||
static String stringify(Map map) => new JSONPretty._()._stringify(map);
|
|
||||||
|
|
||||||
String _stringify(Map map) {
|
|
||||||
_buffer.clear();
|
|
||||||
_buffer.write('{\n');
|
|
||||||
_printMap(map, 0);
|
|
||||||
_buffer.write('}\n');
|
|
||||||
return _buffer.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _printMap(Map map, int depth) {
|
|
||||||
if (_seen.contains(map)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_seen.add(map);
|
|
||||||
for (var k in map.keys) {
|
|
||||||
var v = map[k];
|
|
||||||
if (v is Map) {
|
|
||||||
_writeIndent(depth);
|
|
||||||
_buffer.write('"$k": {\n');
|
|
||||||
_printMap(v, depth + 1);
|
|
||||||
_writeIndent(depth);
|
|
||||||
_buffer.write('}\n');
|
|
||||||
} else if (v is List) {
|
|
||||||
_writeIndent(depth);
|
|
||||||
_buffer.write('"$k": [\n');
|
|
||||||
_printList(v, depth + 1);
|
|
||||||
_writeIndent(depth);
|
|
||||||
_buffer.write(']\n');
|
|
||||||
} else {
|
|
||||||
_writeIndent(depth);
|
|
||||||
_buffer.write('"$k": $v');
|
|
||||||
_buffer.write('\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_seen.remove(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _printList(List list, int depth) {
|
|
||||||
if (_seen.contains(list)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_seen.add(list);
|
|
||||||
for (var v in list) {
|
|
||||||
if (v is Map) {
|
|
||||||
_writeIndent(depth);
|
|
||||||
_buffer.write('{\n');
|
|
||||||
_printMap(v, depth + 1);
|
|
||||||
_writeIndent(depth);
|
|
||||||
_buffer.write('}\n');
|
|
||||||
} else if (v is List) {
|
|
||||||
_writeIndent(depth);
|
|
||||||
_buffer.write('[\n');
|
|
||||||
_printList(v, depth + 1);
|
|
||||||
_writeIndent(depth);
|
|
||||||
_buffer.write(']\n');
|
|
||||||
} else {
|
|
||||||
_writeIndent(depth);
|
|
||||||
_buffer.write(v);
|
|
||||||
_buffer.write('\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_seen.remove(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _writeIndent(int depth) {
|
|
||||||
const tab = ' '; // 2 spaces.
|
|
||||||
_buffer.write(tab * depth);
|
|
||||||
}
|
|
||||||
|
|
||||||
final _buffer = new StringBuffer();
|
|
||||||
final _seen = new Set();
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library library_ref_element;
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M show IsolateRef, LibraryRef;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/uris.dart';
|
|
||||||
|
|
||||||
class LibraryRefElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<LibraryRefElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<LibraryRefElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.LibraryRef _library;
|
|
||||||
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.LibraryRef get library => _library;
|
|
||||||
|
|
||||||
factory LibraryRefElement(M.IsolateRef isolate, M.LibraryRef library,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(library != null);
|
|
||||||
LibraryRefElement e = new LibraryRefElement.created();
|
|
||||||
e._r = new RenderingScheduler<LibraryRefElement>(e, queue: queue);
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._library = library;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
LibraryRefElement.created() : super.created('library-ref');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
final name = _library.name;
|
|
||||||
children = <Element>[
|
|
||||||
new AnchorElement(href: Uris.inspect(_isolate, object: _library))
|
|
||||||
..text = (name == null || name.isEmpty) ? 'unnamed' : name
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,330 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library library_view_element;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/class_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/curly_block.dart';
|
|
||||||
import 'package:observatory_2/src/elements/eval_box.dart';
|
|
||||||
import 'package:observatory_2/src/elements/field_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/function_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/any_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/library_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/library_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/refresh.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/vm_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/object_common.dart';
|
|
||||||
import 'package:observatory_2/src/elements/script_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/script_inset.dart';
|
|
||||||
|
|
||||||
class LibraryViewElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<LibraryViewElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<LibraryViewElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.VM _vm;
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
M.Library _library;
|
|
||||||
M.LibraryRepository _libraries;
|
|
||||||
M.FieldRepository _fields;
|
|
||||||
M.RetainedSizeRepository _retainedSizes;
|
|
||||||
M.ReachableSizeRepository _reachableSizes;
|
|
||||||
M.InboundReferencesRepository _references;
|
|
||||||
M.RetainingPathRepository _retainingPaths;
|
|
||||||
M.ScriptRepository _scripts;
|
|
||||||
M.ObjectRepository _objects;
|
|
||||||
M.EvalRepository _eval;
|
|
||||||
Iterable<M.Field> _variables;
|
|
||||||
|
|
||||||
M.VMRef get vm => _vm;
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.NotificationRepository get notifications => _notifications;
|
|
||||||
M.Library get library => _library;
|
|
||||||
|
|
||||||
factory LibraryViewElement(
|
|
||||||
M.VM vm,
|
|
||||||
M.IsolateRef isolate,
|
|
||||||
M.Library library,
|
|
||||||
M.EventRepository events,
|
|
||||||
M.NotificationRepository notifications,
|
|
||||||
M.LibraryRepository libraries,
|
|
||||||
M.FieldRepository fields,
|
|
||||||
M.RetainedSizeRepository retainedSizes,
|
|
||||||
M.ReachableSizeRepository reachableSizes,
|
|
||||||
M.InboundReferencesRepository references,
|
|
||||||
M.RetainingPathRepository retainingPaths,
|
|
||||||
M.ScriptRepository scripts,
|
|
||||||
M.ObjectRepository objects,
|
|
||||||
M.EvalRepository eval,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(vm != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
assert(notifications != null);
|
|
||||||
assert(library != null);
|
|
||||||
assert(libraries != null);
|
|
||||||
assert(fields != null);
|
|
||||||
assert(retainedSizes != null);
|
|
||||||
assert(reachableSizes != null);
|
|
||||||
assert(references != null);
|
|
||||||
assert(retainingPaths != null);
|
|
||||||
assert(scripts != null);
|
|
||||||
assert(objects != null);
|
|
||||||
assert(eval != null);
|
|
||||||
LibraryViewElement e = new LibraryViewElement.created();
|
|
||||||
e._r = new RenderingScheduler<LibraryViewElement>(e, queue: queue);
|
|
||||||
e._vm = vm;
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
e._notifications = notifications;
|
|
||||||
e._library = library;
|
|
||||||
e._libraries = libraries;
|
|
||||||
e._fields = fields;
|
|
||||||
e._retainedSizes = retainedSizes;
|
|
||||||
e._reachableSizes = reachableSizes;
|
|
||||||
e._references = references;
|
|
||||||
e._retainingPaths = retainingPaths;
|
|
||||||
e._scripts = scripts;
|
|
||||||
e._objects = objects;
|
|
||||||
e._eval = eval;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
LibraryViewElement.created() : super.created('library-view');
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
_refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
final rootScript = library.rootScript;
|
|
||||||
|
|
||||||
children = <Element>[
|
|
||||||
navBar(<Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
|
|
||||||
new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
|
|
||||||
new NavLibraryMenuElement(_isolate, _library, queue: _r.queue).element,
|
|
||||||
(new NavRefreshElement(queue: _r.queue)
|
|
||||||
..onRefresh.listen((e) async {
|
|
||||||
e.element.disabled = true;
|
|
||||||
_refresh();
|
|
||||||
}))
|
|
||||||
.element,
|
|
||||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
|
||||||
]),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered-big']
|
|
||||||
..children = <Element>[
|
|
||||||
new HeadingElement.h2()..text = 'Library',
|
|
||||||
new HRElement(),
|
|
||||||
new ObjectCommonElement(_isolate, _library, _retainedSizes,
|
|
||||||
_reachableSizes, _references, _retainingPaths, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'uri',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = _library.uri
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'vm name',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = _library.vmName
|
|
||||||
]
|
|
||||||
],
|
|
||||||
new HRElement(),
|
|
||||||
new EvalBoxElement(_isolate, _library, _objects, _eval,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new HRElement(),
|
|
||||||
_createDependencies(),
|
|
||||||
new BRElement(),
|
|
||||||
_createScripts(),
|
|
||||||
new BRElement(),
|
|
||||||
_createClasses(),
|
|
||||||
new BRElement(),
|
|
||||||
_createVariables(),
|
|
||||||
new BRElement(),
|
|
||||||
_createFunctions(),
|
|
||||||
if (rootScript != null) ...[
|
|
||||||
new HRElement(),
|
|
||||||
new ScriptInsetElement(
|
|
||||||
_isolate, rootScript, _scripts, _objects, _events,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
],
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _refresh() async {
|
|
||||||
_library = await _libraries.get(_isolate, _library.id);
|
|
||||||
_variables = null;
|
|
||||||
_r.dirty();
|
|
||||||
_variables = await Future.wait(
|
|
||||||
_library.variables.map((field) => _fields.get(_isolate, field.id)));
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
Element _createDependencies() {
|
|
||||||
if (_library.dependencies.isEmpty) {
|
|
||||||
return new SpanElement();
|
|
||||||
}
|
|
||||||
final dependencies = _library.dependencies.toList();
|
|
||||||
return new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = 'dependencies (${dependencies.length}) ',
|
|
||||||
(new CurlyBlockElement(queue: _r.queue)
|
|
||||||
..content = dependencies
|
|
||||||
.map<Element>((d) => new DivElement()
|
|
||||||
..classes = ['indent']
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()
|
|
||||||
..text = d.isImport ? 'import ' : 'export ',
|
|
||||||
new LibraryRefElement(_isolate, d.target, queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new SpanElement()
|
|
||||||
..text = d.prefix == null ? '' : ' as ${d.prefix}',
|
|
||||||
new SpanElement()..text = d.isDeferred ? ' deferred' : '',
|
|
||||||
])
|
|
||||||
.toList())
|
|
||||||
.element
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
Element _createScripts() {
|
|
||||||
if (_library.scripts.isEmpty) {
|
|
||||||
return new SpanElement();
|
|
||||||
}
|
|
||||||
final scripts = _library.scripts.toList();
|
|
||||||
return new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = 'scripts (${scripts.length}) ',
|
|
||||||
(new CurlyBlockElement(queue: _r.queue)
|
|
||||||
..content = scripts
|
|
||||||
.map<Element>((s) => new DivElement()
|
|
||||||
..classes = ['indent']
|
|
||||||
..children = <Element>[
|
|
||||||
new ScriptRefElement(_isolate, s, queue: _r.queue).element
|
|
||||||
])
|
|
||||||
.toList())
|
|
||||||
.element
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
Element _createClasses() {
|
|
||||||
if (_library.classes.isEmpty) {
|
|
||||||
return new SpanElement();
|
|
||||||
}
|
|
||||||
final classes = _library.classes.toList();
|
|
||||||
return new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = 'classes (${classes.length}) ',
|
|
||||||
(new CurlyBlockElement(queue: _r.queue)
|
|
||||||
..content = classes
|
|
||||||
.map<Element>((c) => new DivElement()
|
|
||||||
..classes = ['indent']
|
|
||||||
..children = <Element>[
|
|
||||||
new ClassRefElement(_isolate, c, queue: _r.queue).element
|
|
||||||
])
|
|
||||||
.toList())
|
|
||||||
.element
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
Element _createVariables() {
|
|
||||||
if (_library.variables.isEmpty) {
|
|
||||||
return new SpanElement();
|
|
||||||
}
|
|
||||||
final variables = _library.variables.toList();
|
|
||||||
return new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = 'variables (${variables.length}) ',
|
|
||||||
(new CurlyBlockElement(queue: _r.queue)
|
|
||||||
..content = <Element>[
|
|
||||||
_variables == null
|
|
||||||
? (new SpanElement()..text = 'loading...')
|
|
||||||
: (new DivElement()
|
|
||||||
..classes = ['indent', 'memberList']
|
|
||||||
..children = _variables
|
|
||||||
.map<Element>((f) => new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
new FieldRefElement(_isolate, f, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = ' = ',
|
|
||||||
anyRef(_isolate, f.staticValue, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
]
|
|
||||||
])
|
|
||||||
.toList())
|
|
||||||
])
|
|
||||||
.element
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
Element _createFunctions() {
|
|
||||||
if (_library.functions.isEmpty) {
|
|
||||||
return new SpanElement();
|
|
||||||
}
|
|
||||||
final functions = _library.functions.toList();
|
|
||||||
return new DivElement()
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()..text = 'functions (${functions.length}) ',
|
|
||||||
(new CurlyBlockElement(queue: _r.queue)
|
|
||||||
..content = functions
|
|
||||||
.map<Element>((f) => new DivElement()
|
|
||||||
..classes = ['indent']
|
|
||||||
..children = <Element>[
|
|
||||||
new FunctionRefElement(_isolate, f, queue: _r.queue)
|
|
||||||
.element
|
|
||||||
])
|
|
||||||
.toList())
|
|
||||||
.element
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M
|
|
||||||
show IsolateRef, LocalVarDescriptorsRef;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/uris.dart';
|
|
||||||
|
|
||||||
class LocalVarDescriptorsRefElement extends CustomElement
|
|
||||||
implements Renderable {
|
|
||||||
RenderingScheduler<LocalVarDescriptorsRefElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<LocalVarDescriptorsRefElement>> get onRendered =>
|
|
||||||
_r.onRendered;
|
|
||||||
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.LocalVarDescriptorsRef _localVar;
|
|
||||||
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.LocalVarDescriptorsRef get localVar => _localVar;
|
|
||||||
|
|
||||||
factory LocalVarDescriptorsRefElement(
|
|
||||||
M.IsolateRef isolate, M.LocalVarDescriptorsRef localVar,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(localVar != null);
|
|
||||||
LocalVarDescriptorsRefElement e =
|
|
||||||
new LocalVarDescriptorsRefElement.created();
|
|
||||||
e._r =
|
|
||||||
new RenderingScheduler<LocalVarDescriptorsRefElement>(e, queue: queue);
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._localVar = localVar;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalVarDescriptorsRefElement.created() : super.created('var-ref');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
final text = (_localVar.name == null || _localVar.name == '')
|
|
||||||
? 'LocalVarDescriptors'
|
|
||||||
: _localVar.name;
|
|
||||||
children = <Element>[
|
|
||||||
new AnchorElement(href: Uris.inspect(_isolate, object: _localVar))
|
|
||||||
..text = text
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,113 +0,0 @@
|
||||||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library logging_page;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'package:logging/logging.dart';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/logging_list.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/refresh.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/vm_menu.dart';
|
|
||||||
|
|
||||||
class LoggingPageElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<LoggingPageElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<LoggingPageElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.VM _vm;
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
Level _level = Level.ALL;
|
|
||||||
|
|
||||||
M.VMRef get vm => _vm;
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.NotificationRepository get notifications => _notifications;
|
|
||||||
|
|
||||||
factory LoggingPageElement(M.VM vm, M.IsolateRef isolate,
|
|
||||||
M.EventRepository events, M.NotificationRepository notifications,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(vm != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
assert(notifications != null);
|
|
||||||
LoggingPageElement e = new LoggingPageElement.created();
|
|
||||||
e._r = new RenderingScheduler<LoggingPageElement>(e, queue: queue);
|
|
||||||
e._vm = vm;
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
e._notifications = notifications;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
LoggingPageElement.created() : super.created('logging-page');
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
LoggingListElement _logs;
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
_logs = _logs ?? new LoggingListElement(_isolate, _events);
|
|
||||||
_logs.level = _level;
|
|
||||||
children = <Element>[
|
|
||||||
navBar(<Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
|
|
||||||
new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
|
|
||||||
navMenu('logging'),
|
|
||||||
(new NavRefreshElement(label: 'clear', queue: _r.queue)
|
|
||||||
..onRefresh.listen((e) async {
|
|
||||||
e.element.disabled = true;
|
|
||||||
_logs = null;
|
|
||||||
_r.dirty();
|
|
||||||
}))
|
|
||||||
.element,
|
|
||||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
|
||||||
]),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered-big']
|
|
||||||
..children = <Element>[
|
|
||||||
new HeadingElement.h2()..text = 'Logging',
|
|
||||||
new SpanElement()..text = 'Show messages with severity ',
|
|
||||||
_createLevelSelector(),
|
|
||||||
new HRElement(),
|
|
||||||
_logs.element
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
Element _createLevelSelector() {
|
|
||||||
var s = new SelectElement()
|
|
||||||
..value = _level.name
|
|
||||||
..children = Level.LEVELS.map((level) {
|
|
||||||
return new OptionElement(value: level.name, selected: _level == level)
|
|
||||||
..text = level.name;
|
|
||||||
}).toList(growable: false);
|
|
||||||
s.onChange.listen((_) {
|
|
||||||
_level = Level.LEVELS[s.selectedIndex];
|
|
||||||
_r.dirty();
|
|
||||||
});
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,84 +0,0 @@
|
||||||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'package:logging/logging.dart';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/utils.dart';
|
|
||||||
|
|
||||||
class LoggingListElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<LoggingListElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<LoggingListElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
StreamSubscription _subscription;
|
|
||||||
Level _level = Level.ALL;
|
|
||||||
final _logs = <Map>[];
|
|
||||||
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
Level get level => _level;
|
|
||||||
|
|
||||||
set level(Level value) => _level = _r.checkAndReact(_level, value);
|
|
||||||
|
|
||||||
factory LoggingListElement(M.IsolateRef isolate, M.EventRepository events,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
LoggingListElement e = new LoggingListElement.created();
|
|
||||||
e._r = new RenderingScheduler<LoggingListElement>(e, queue: queue);
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
LoggingListElement.created() : super.created('logging-list');
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
_subscription = _events.onLoggingEvent.listen((e) {
|
|
||||||
if (e.isolate.id == _isolate.id) {
|
|
||||||
_logs.add(e.logRecord);
|
|
||||||
if (_shouldBeVisible(_logs.last)) {
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
_subscription.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
children = _logs
|
|
||||||
.where(_shouldBeVisible)
|
|
||||||
.map<Element>((logRecord) => new DivElement()
|
|
||||||
..classes = ['logItem', logRecord['level'].name]
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['level']
|
|
||||||
..text = logRecord['level'].name,
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['time']
|
|
||||||
..text = Utils.formatDateTime(logRecord['time']),
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['message']
|
|
||||||
..text = logRecord["message"].valueAsString
|
|
||||||
])
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _shouldBeVisible(Map record) => _level.compareTo(record['level']) <= 0;
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M
|
|
||||||
show IsolateRef, MegamorphicCacheRef;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/uris.dart';
|
|
||||||
|
|
||||||
class MegamorphicCacheRefElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<MegamorphicCacheRefElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<MegamorphicCacheRefElement>> get onRendered =>
|
|
||||||
_r.onRendered;
|
|
||||||
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.MegamorphicCacheRef _cache;
|
|
||||||
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.MegamorphicCacheRef get cache => _cache;
|
|
||||||
|
|
||||||
factory MegamorphicCacheRefElement(
|
|
||||||
M.IsolateRef isolate, M.MegamorphicCacheRef cache,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(cache != null);
|
|
||||||
MegamorphicCacheRefElement e = new MegamorphicCacheRefElement.created();
|
|
||||||
e._r = new RenderingScheduler<MegamorphicCacheRefElement>(e, queue: queue);
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._cache = cache;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
MegamorphicCacheRefElement.created() : super.created('megamorphic-cache-ref');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
children = <Element>[
|
|
||||||
new AnchorElement(href: Uris.inspect(_isolate, object: _cache))
|
|
||||||
..children = <Element>[
|
|
||||||
new SpanElement()
|
|
||||||
..classes = ['emphasize']
|
|
||||||
..text = 'MegamorphicCache',
|
|
||||||
new SpanElement()..text = ' (${_cache.selector})'
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,180 +0,0 @@
|
||||||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library megamorphiccache_view;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/any_ref.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/refresh.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/vm_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/object_common.dart';
|
|
||||||
|
|
||||||
class MegamorphicCacheViewElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<MegamorphicCacheViewElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<MegamorphicCacheViewElement>> get onRendered =>
|
|
||||||
_r.onRendered;
|
|
||||||
|
|
||||||
M.VM _vm;
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
M.MegamorphicCache _cache;
|
|
||||||
M.MegamorphicCacheRepository _caches;
|
|
||||||
M.RetainedSizeRepository _retainedSizes;
|
|
||||||
M.ReachableSizeRepository _reachableSizes;
|
|
||||||
M.InboundReferencesRepository _references;
|
|
||||||
M.RetainingPathRepository _retainingPaths;
|
|
||||||
M.ObjectRepository _objects;
|
|
||||||
|
|
||||||
M.VMRef get vm => _vm;
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.NotificationRepository get notifications => _notifications;
|
|
||||||
M.MegamorphicCache get cache => _cache;
|
|
||||||
|
|
||||||
factory MegamorphicCacheViewElement(
|
|
||||||
M.VM vm,
|
|
||||||
M.IsolateRef isolate,
|
|
||||||
M.MegamorphicCache cache,
|
|
||||||
M.EventRepository events,
|
|
||||||
M.NotificationRepository notifications,
|
|
||||||
M.MegamorphicCacheRepository caches,
|
|
||||||
M.RetainedSizeRepository retainedSizes,
|
|
||||||
M.ReachableSizeRepository reachableSizes,
|
|
||||||
M.InboundReferencesRepository references,
|
|
||||||
M.RetainingPathRepository retainingPaths,
|
|
||||||
M.ObjectRepository objects,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(vm != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
assert(notifications != null);
|
|
||||||
assert(cache != null);
|
|
||||||
assert(caches != null);
|
|
||||||
assert(retainedSizes != null);
|
|
||||||
assert(reachableSizes != null);
|
|
||||||
assert(references != null);
|
|
||||||
assert(retainingPaths != null);
|
|
||||||
assert(objects != null);
|
|
||||||
MegamorphicCacheViewElement e = new MegamorphicCacheViewElement.created();
|
|
||||||
e._r = new RenderingScheduler<MegamorphicCacheViewElement>(e, queue: queue);
|
|
||||||
e._vm = vm;
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
e._notifications = notifications;
|
|
||||||
e._cache = cache;
|
|
||||||
e._caches = caches;
|
|
||||||
e._retainedSizes = retainedSizes;
|
|
||||||
e._reachableSizes = reachableSizes;
|
|
||||||
e._references = references;
|
|
||||||
e._retainingPaths = retainingPaths;
|
|
||||||
e._objects = objects;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
MegamorphicCacheViewElement.created()
|
|
||||||
: super.created('megamorphiccache-view');
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
children = <Element>[
|
|
||||||
navBar(<Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
|
|
||||||
new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
|
|
||||||
navMenu('megamorphic inline cache'),
|
|
||||||
(new NavRefreshElement(queue: _r.queue)
|
|
||||||
..onRefresh.listen((e) async {
|
|
||||||
e.element.disabled = true;
|
|
||||||
_cache = await _caches.get(_isolate, _cache.id);
|
|
||||||
_r.dirty();
|
|
||||||
}))
|
|
||||||
.element,
|
|
||||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
|
||||||
]),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered-big']
|
|
||||||
..children = <Element>[
|
|
||||||
new HeadingElement.h2()..text = 'Megamorphic Cache',
|
|
||||||
new HRElement(),
|
|
||||||
new ObjectCommonElement(_isolate, _cache, _retainedSizes,
|
|
||||||
_reachableSizes, _references, _retainingPaths, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element,
|
|
||||||
new BRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'selector',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = '${_cache.selector}'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'mask',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = '${_cache.mask}'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'buckets',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
anyRef(_isolate, _cache.buckets, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
]
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'argumentsDescriptor',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..children = <Element>[
|
|
||||||
anyRef(_isolate, _cache.argumentsDescriptor, _objects,
|
|
||||||
queue: _r.queue)
|
|
||||||
]
|
|
||||||
]
|
|
||||||
],
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,168 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
|
|
||||||
class MetricDetailsElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<MetricDetailsElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<MetricDetailsElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.Metric _metric;
|
|
||||||
M.MetricRepository _metrics;
|
|
||||||
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.Metric get metric => _metric;
|
|
||||||
|
|
||||||
factory MetricDetailsElement(
|
|
||||||
M.IsolateRef isolate, M.Metric metric, M.MetricRepository metrics,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(metric != null);
|
|
||||||
assert(metrics != null);
|
|
||||||
MetricDetailsElement e = new MetricDetailsElement.created();
|
|
||||||
e._r = new RenderingScheduler<MetricDetailsElement>(e, queue: queue);
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._metric = metric;
|
|
||||||
e._metrics = metrics;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
MetricDetailsElement.created() : super.created('metric-details');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'name',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = _metric.name,
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'description',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = _metric.description,
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'refresh rate',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = _createRefreshRateSelect(),
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'buffer size',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = _createBufferSizeSelect(),
|
|
||||||
]
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createRefreshRateSelect() {
|
|
||||||
final current = _metrics.getSamplingRate(_isolate, _metric);
|
|
||||||
var s;
|
|
||||||
return [
|
|
||||||
s = new SelectElement()
|
|
||||||
..value = _rateToString(current)
|
|
||||||
..children = M.MetricSamplingRate.values.map((rate) {
|
|
||||||
return new OptionElement(
|
|
||||||
value: _rateToString(current), selected: current == rate)
|
|
||||||
..text = _rateToString(rate);
|
|
||||||
}).toList(growable: false)
|
|
||||||
..onChange.listen((_) {
|
|
||||||
_metrics.setSamplingRate(
|
|
||||||
_isolate, _metric, M.MetricSamplingRate.values[s.selectedIndex]);
|
|
||||||
_r.dirty();
|
|
||||||
})
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createBufferSizeSelect() {
|
|
||||||
final current = _metrics.getBufferSize(_isolate, _metric);
|
|
||||||
var s;
|
|
||||||
return [
|
|
||||||
s = new SelectElement()
|
|
||||||
..value = _sizeToString(current)
|
|
||||||
..children = M.MetricBufferSize.values.map((rate) {
|
|
||||||
return new OptionElement(
|
|
||||||
value: _sizeToString(current), selected: current == rate)
|
|
||||||
..text = _sizeToString(rate);
|
|
||||||
}).toList(growable: false)
|
|
||||||
..onChange.listen((_) {
|
|
||||||
_metrics.setBufferSize(
|
|
||||||
_isolate, _metric, M.MetricBufferSize.values[s.selectedIndex]);
|
|
||||||
_r.dirty();
|
|
||||||
})
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
static String _rateToString(M.MetricSamplingRate rate) {
|
|
||||||
switch (rate) {
|
|
||||||
case M.MetricSamplingRate.off:
|
|
||||||
return 'Never';
|
|
||||||
case M.MetricSamplingRate.e100ms:
|
|
||||||
return 'Ten times per second';
|
|
||||||
case M.MetricSamplingRate.e1s:
|
|
||||||
return 'Once a second';
|
|
||||||
case M.MetricSamplingRate.e2s:
|
|
||||||
return 'Every two seconds';
|
|
||||||
case M.MetricSamplingRate.e4s:
|
|
||||||
return 'Every four seconds';
|
|
||||||
case M.MetricSamplingRate.e8s:
|
|
||||||
return 'Every eight seconds';
|
|
||||||
}
|
|
||||||
throw new Exception('Unknown MetricSamplingRate ($rate)');
|
|
||||||
}
|
|
||||||
|
|
||||||
static String _sizeToString(M.MetricBufferSize size) {
|
|
||||||
switch (size) {
|
|
||||||
case M.MetricBufferSize.n10samples:
|
|
||||||
return '10';
|
|
||||||
case M.MetricBufferSize.n100samples:
|
|
||||||
return '100';
|
|
||||||
case M.MetricBufferSize.n1000samples:
|
|
||||||
return '1000';
|
|
||||||
}
|
|
||||||
throw new Exception('Unknown MetricSamplingRate ($size)');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,117 +0,0 @@
|
||||||
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:html';
|
|
||||||
import 'dart:async';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
|
|
||||||
class MetricGraphElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<MetricGraphElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<MetricGraphElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.Metric _metric;
|
|
||||||
M.MetricRepository _metrics;
|
|
||||||
Timer _timer;
|
|
||||||
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.Metric get metric => _metric;
|
|
||||||
|
|
||||||
factory MetricGraphElement(
|
|
||||||
M.IsolateRef isolate, M.Metric metric, M.MetricRepository metrics,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(metric != null);
|
|
||||||
assert(metrics != null);
|
|
||||||
MetricGraphElement e = new MetricGraphElement.created();
|
|
||||||
e._r = new RenderingScheduler<MetricGraphElement>(e, queue: queue);
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._metric = metric;
|
|
||||||
e._metrics = metrics;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
MetricGraphElement.created() : super.created('metric-graph');
|
|
||||||
|
|
||||||
@override
|
|
||||||
void attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
_timer = new Timer.periodic(const Duration(seconds: 1), (_) => _r.dirty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
_timer.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
final min = _metrics.getMinValue(_isolate, _metric);
|
|
||||||
final max = _metrics.getMaxValue(_isolate, _metric);
|
|
||||||
final rows = _metrics
|
|
||||||
.getSamples(_isolate, _metric)
|
|
||||||
.map((s) => [s.time.millisecondsSinceEpoch, s.value])
|
|
||||||
.toList();
|
|
||||||
final current = rows.last.last;
|
|
||||||
|
|
||||||
var message = 'current: $current';
|
|
||||||
if (min != null) {
|
|
||||||
message = 'min: $min, $message';
|
|
||||||
}
|
|
||||||
if (max != null) {
|
|
||||||
message = message + ', max: $max';
|
|
||||||
}
|
|
||||||
|
|
||||||
final host = new DivElement();
|
|
||||||
children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = min == null
|
|
||||||
? const []
|
|
||||||
: [
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'min',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = '$min'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'current',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = '$current'
|
|
||||||
],
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = max == null
|
|
||||||
? const []
|
|
||||||
: [
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'max',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..text = '$max'
|
|
||||||
]
|
|
||||||
],
|
|
||||||
];
|
|
||||||
if (rows.length <= 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,159 +0,0 @@
|
||||||
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
library metrics;
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:html';
|
|
||||||
import 'package:observatory_2/models.dart' as M;
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_bar.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/nav_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/rendering_scheduler.dart';
|
|
||||||
import 'package:observatory_2/src/elements/helpers/custom_element.dart';
|
|
||||||
import 'package:observatory_2/src/elements/metric/details.dart';
|
|
||||||
import 'package:observatory_2/src/elements/metric/graph.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/isolate_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/notify.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/refresh.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/top_menu.dart';
|
|
||||||
import 'package:observatory_2/src/elements/nav/vm_menu.dart';
|
|
||||||
|
|
||||||
class MetricsPageElement extends CustomElement implements Renderable {
|
|
||||||
RenderingScheduler<MetricsPageElement> _r;
|
|
||||||
|
|
||||||
Stream<RenderedEvent<MetricsPageElement>> get onRendered => _r.onRendered;
|
|
||||||
|
|
||||||
M.VM _vm;
|
|
||||||
M.IsolateRef _isolate;
|
|
||||||
M.EventRepository _events;
|
|
||||||
M.NotificationRepository _notifications;
|
|
||||||
M.MetricRepository _metrics;
|
|
||||||
List<M.Metric> _available;
|
|
||||||
M.Metric _selected;
|
|
||||||
|
|
||||||
M.VMRef get vm => _vm;
|
|
||||||
M.IsolateRef get isolate => _isolate;
|
|
||||||
M.NotificationRepository get notifications => _notifications;
|
|
||||||
|
|
||||||
factory MetricsPageElement(
|
|
||||||
M.VM vm,
|
|
||||||
M.IsolateRef isolate,
|
|
||||||
M.EventRepository events,
|
|
||||||
M.NotificationRepository notifications,
|
|
||||||
M.MetricRepository metrics,
|
|
||||||
{RenderingQueue queue}) {
|
|
||||||
assert(vm != null);
|
|
||||||
assert(isolate != null);
|
|
||||||
assert(events != null);
|
|
||||||
assert(notifications != null);
|
|
||||||
MetricsPageElement e = new MetricsPageElement.created();
|
|
||||||
e._r = new RenderingScheduler<MetricsPageElement>(e, queue: queue);
|
|
||||||
e._vm = vm;
|
|
||||||
e._isolate = isolate;
|
|
||||||
e._events = events;
|
|
||||||
e._notifications = notifications;
|
|
||||||
e._metrics = metrics;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
MetricsPageElement.created() : super.created('metrics-page');
|
|
||||||
|
|
||||||
@override
|
|
||||||
attached() {
|
|
||||||
super.attached();
|
|
||||||
_r.enable();
|
|
||||||
_refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
detached() {
|
|
||||||
super.detached();
|
|
||||||
_r.disable(notify: true);
|
|
||||||
children = <Element>[];
|
|
||||||
}
|
|
||||||
|
|
||||||
void render() {
|
|
||||||
children = <Element>[
|
|
||||||
navBar(<Element>[
|
|
||||||
new NavTopMenuElement(queue: _r.queue).element,
|
|
||||||
new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
|
|
||||||
new NavIsolateMenuElement(_isolate, _events, queue: _r.queue).element,
|
|
||||||
navMenu('metrics'),
|
|
||||||
(new NavRefreshElement(queue: _r.queue)
|
|
||||||
..onRefresh.listen((e) {
|
|
||||||
e.element.disabled = true;
|
|
||||||
_refresh();
|
|
||||||
}))
|
|
||||||
.element,
|
|
||||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
|
||||||
]),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['content-centered-big']
|
|
||||||
..children = <Element>[
|
|
||||||
new HeadingElement.h2()..text = 'Metrics',
|
|
||||||
new HRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberList']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberItem']
|
|
||||||
..children = <Element>[
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberName']
|
|
||||||
..text = 'Metric',
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['memberValue']
|
|
||||||
..children = _available == null
|
|
||||||
? [new SpanElement()..text = 'Loading..']
|
|
||||||
: _createMetricSelect()
|
|
||||||
]
|
|
||||||
],
|
|
||||||
new HRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..children = _selected == null
|
|
||||||
? const []
|
|
||||||
: [
|
|
||||||
new MetricDetailsElement(_isolate, _selected, _metrics,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
],
|
|
||||||
new HRElement(),
|
|
||||||
new DivElement()
|
|
||||||
..classes = ['graph']
|
|
||||||
..children = _selected == null
|
|
||||||
? const []
|
|
||||||
: [
|
|
||||||
new MetricGraphElement(_isolate, _selected, _metrics,
|
|
||||||
queue: _r.queue)
|
|
||||||
.element
|
|
||||||
]
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
Future _refresh() async {
|
|
||||||
_available = (await _metrics.list(_isolate)).toList();
|
|
||||||
if (!_available.contains(_selected)) {
|
|
||||||
_selected = _available.first;
|
|
||||||
}
|
|
||||||
_r.dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Element> _createMetricSelect() {
|
|
||||||
var s;
|
|
||||||
return [
|
|
||||||
s = new SelectElement()
|
|
||||||
..value = _selected.name
|
|
||||||
..children = _available.map((metric) {
|
|
||||||
return new OptionElement(
|
|
||||||
value: metric.name, selected: _selected == metric)
|
|
||||||
..text = metric.name;
|
|
||||||
}).toList(growable: false)
|
|
||||||
..onChange.listen((_) {
|
|
||||||
_selected = _available[s.selectedIndex];
|
|
||||||
_r.dirty();
|
|
||||||
})
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue