[ VM ] Remove dead fork of Observatory from runtime/
Observatory was moved to pkg/observatory in a previous CL. TEST=N/A Change-Id: Ie0e7cb71799408cefe30d2c72b4dfd7bd43747ce Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/450221 Auto-Submit: Ben Konyi <bkonyi@google.com> Commit-Queue: Ben Konyi <bkonyi@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
+1
-1
@@ -317,4 +317,4 @@ dependency_overrides:
|
||||
# observatory needs a migration to language version 3.6 before it can be
|
||||
# included in the workspace. (If it is not deleted before)
|
||||
observatory:
|
||||
path: runtime/observatory
|
||||
path: pkg/observatory
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
bootstrap_css
|
||||
out
|
||||
build
|
||||
.pub
|
||||
.idea
|
||||
.dart_tool
|
||||
@@ -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,22 +0,0 @@
|
||||
analyzer:
|
||||
errors:
|
||||
dead_code: info
|
||||
getter_not_subtype_setter_types: info
|
||||
missing_enum_constant_in_switch: info
|
||||
unused_element: info
|
||||
unused_field: info
|
||||
unused_import: info
|
||||
unused_local_variable: info
|
||||
|
||||
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/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 dynResponse) {
|
||||
if (dynResponse is String) {
|
||||
final response = json.decode(dynResponse);
|
||||
if (response['id'] == 1) {
|
||||
getVM.complete(response['result']['isolates'][0]['id']);
|
||||
}
|
||||
} else if (dynResponse is List<int>) {
|
||||
final response = new Uint8List.fromList(dynResponse);
|
||||
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] as int);
|
||||
}
|
||||
}
|
||||
|
||||
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 'dart:io';
|
||||
|
||||
import 'package:observatory/service_io.dart';
|
||||
|
||||
// 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 as VM;
|
||||
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/models.dart' as M;
|
||||
import 'package:observatory/service.dart' as S;
|
||||
|
||||
part 'src/allocation_profile/allocation_profile.dart';
|
||||
@@ -1,31 +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:js_interop';
|
||||
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:stack_trace/stack_trace.dart';
|
||||
import 'package:web/web.dart' as web;
|
||||
|
||||
import 'package:observatory/elements.dart';
|
||||
import 'package:observatory/event.dart';
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/repositories.dart';
|
||||
import 'package:observatory/service_html.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
import 'package:observatory/tracer.dart';
|
||||
|
||||
export 'package:observatory/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,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 debugger;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/service.dart';
|
||||
|
||||
part 'src/debugger/debugger.dart';
|
||||
part 'src/debugger/debugger_location.dart';
|
||||
@@ -1,97 +0,0 @@
|
||||
library observatory_elements;
|
||||
|
||||
export 'package:observatory/src/elements/allocation_profile.dart';
|
||||
export 'package:observatory/src/elements/class_allocation_profile.dart';
|
||||
export 'package:observatory/src/elements/class_instances.dart';
|
||||
export 'package:observatory/src/elements/class_ref.dart';
|
||||
export 'package:observatory/src/elements/class_tree.dart';
|
||||
export 'package:observatory/src/elements/class_view.dart';
|
||||
export 'package:observatory/src/elements/code_ref.dart';
|
||||
export 'package:observatory/src/elements/code_view.dart';
|
||||
export 'package:observatory/src/elements/containers/search_bar.dart';
|
||||
export 'package:observatory/src/elements/containers/virtual_collection.dart';
|
||||
export 'package:observatory/src/elements/containers/virtual_tree.dart';
|
||||
export 'package:observatory/src/elements/context_ref.dart';
|
||||
export 'package:observatory/src/elements/context_view.dart';
|
||||
export 'package:observatory/src/elements/cpu_profile.dart';
|
||||
export 'package:observatory/src/elements/cpu_profile/virtual_tree.dart';
|
||||
export 'package:observatory/src/elements/cpu_profile_table.dart';
|
||||
export 'package:observatory/src/elements/curly_block.dart';
|
||||
export 'package:observatory/src/elements/debugger.dart';
|
||||
export 'package:observatory/src/elements/error_ref.dart';
|
||||
export 'package:observatory/src/elements/error_view.dart';
|
||||
export 'package:observatory/src/elements/eval_box.dart';
|
||||
export 'package:observatory/src/elements/field_ref.dart';
|
||||
export 'package:observatory/src/elements/field_view.dart';
|
||||
export 'package:observatory/src/elements/flag_list.dart';
|
||||
export 'package:observatory/src/elements/function_ref.dart';
|
||||
export 'package:observatory/src/elements/function_view.dart';
|
||||
export 'package:observatory/src/elements/general_error.dart';
|
||||
export 'package:observatory/src/elements/heap_map.dart';
|
||||
export 'package:observatory/src/elements/heap_snapshot.dart';
|
||||
export 'package:observatory/src/elements/process_snapshot.dart';
|
||||
export 'package:observatory/src/elements/helpers/rendering_queue.dart';
|
||||
export 'package:observatory/src/elements/icdata_ref.dart';
|
||||
export 'package:observatory/src/elements/icdata_view.dart';
|
||||
export 'package:observatory/src/elements/instance_ref.dart';
|
||||
export 'package:observatory/src/elements/instance_view.dart';
|
||||
export 'package:observatory/src/elements/isolate/counter_chart.dart';
|
||||
export 'package:observatory/src/elements/isolate/location.dart';
|
||||
export 'package:observatory/src/elements/isolate/run_state.dart';
|
||||
export 'package:observatory/src/elements/isolate/shared_summary.dart';
|
||||
export 'package:observatory/src/elements/isolate/summary.dart';
|
||||
export 'package:observatory/src/elements/isolate_reconnect.dart';
|
||||
export 'package:observatory/src/elements/isolate_ref.dart';
|
||||
export 'package:observatory/src/elements/isolate_view.dart';
|
||||
export 'package:observatory/src/elements/json_view.dart';
|
||||
export 'package:observatory/src/elements/library_ref.dart';
|
||||
export 'package:observatory/src/elements/library_view.dart';
|
||||
export 'package:observatory/src/elements/local_var_descriptors_ref.dart';
|
||||
export 'package:observatory/src/elements/logging.dart';
|
||||
export 'package:observatory/src/elements/megamorphiccache_ref.dart';
|
||||
export 'package:observatory/src/elements/megamorphiccache_view.dart';
|
||||
export 'package:observatory/src/elements/metric/details.dart';
|
||||
export 'package:observatory/src/elements/metric/graph.dart';
|
||||
export 'package:observatory/src/elements/metrics.dart';
|
||||
export 'package:observatory/src/elements/native_memory_profiler.dart';
|
||||
export 'package:observatory/src/elements/nav/class_menu.dart';
|
||||
export 'package:observatory/src/elements/nav/isolate_menu.dart';
|
||||
export 'package:observatory/src/elements/nav/library_menu.dart';
|
||||
export 'package:observatory/src/elements/nav/menu_item.dart';
|
||||
export 'package:observatory/src/elements/nav/notify.dart';
|
||||
export 'package:observatory/src/elements/nav/notify_event.dart';
|
||||
export 'package:observatory/src/elements/nav/notify_exception.dart';
|
||||
export 'package:observatory/src/elements/nav/refresh.dart';
|
||||
export 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
export 'package:observatory/src/elements/nav/vm_menu.dart';
|
||||
export 'package:observatory/src/elements/object_common.dart';
|
||||
export 'package:observatory/src/elements/object_view.dart';
|
||||
export 'package:observatory/src/elements/objectpool_ref.dart';
|
||||
export 'package:observatory/src/elements/objectpool_view.dart';
|
||||
export 'package:observatory/src/elements/objectstore_view.dart';
|
||||
export 'package:observatory/src/elements/observatory_application.dart';
|
||||
export 'package:observatory/src/elements/pc_descriptors_ref.dart';
|
||||
export 'package:observatory/src/elements/persistent_handles.dart';
|
||||
export 'package:observatory/src/elements/ports.dart';
|
||||
export 'package:observatory/src/elements/sample_buffer_control.dart';
|
||||
export 'package:observatory/src/elements/script_inset.dart';
|
||||
export 'package:observatory/src/elements/script_ref.dart';
|
||||
export 'package:observatory/src/elements/script_view.dart';
|
||||
export 'package:observatory/src/elements/sentinel_value.dart';
|
||||
export 'package:observatory/src/elements/sentinel_view.dart';
|
||||
export 'package:observatory/src/elements/singletargetcache_ref.dart';
|
||||
export 'package:observatory/src/elements/singletargetcache_view.dart';
|
||||
export 'package:observatory/src/elements/source_inset.dart';
|
||||
export 'package:observatory/src/elements/source_link.dart';
|
||||
export 'package:observatory/src/elements/stack_trace_tree_config.dart';
|
||||
export 'package:observatory/src/elements/strongly_reachable_instances.dart';
|
||||
export 'package:observatory/src/elements/subtypetestcache_ref.dart';
|
||||
export 'package:observatory/src/elements/subtypetestcache_view.dart';
|
||||
export 'package:observatory/src/elements/timeline_page.dart';
|
||||
export 'package:observatory/src/elements/type_arguments_ref.dart';
|
||||
export 'package:observatory/src/elements/unknown_ref.dart';
|
||||
export 'package:observatory/src/elements/unlinkedcall_ref.dart';
|
||||
export 'package:observatory/src/elements/unlinkedcall_view.dart';
|
||||
export 'package:observatory/src/elements/vm_connect.dart';
|
||||
export 'package:observatory/src/elements/vm_connect_target.dart';
|
||||
export 'package:observatory/src/elements/vm_view.dart';
|
||||
@@ -1,281 +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/models.dart' as M;
|
||||
import 'package:observatory/service.dart' as S;
|
||||
|
||||
class VMUpdateEvent implements M.VMUpdateEvent {
|
||||
final DateTime timestamp;
|
||||
final M.VMRef vm;
|
||||
VMUpdateEvent(this.timestamp, this.vm);
|
||||
}
|
||||
|
||||
class IsolateStartEvent implements M.IsolateStartEvent {
|
||||
final DateTime timestamp;
|
||||
final M.IsolateRef isolate;
|
||||
IsolateStartEvent(this.timestamp, this.isolate);
|
||||
}
|
||||
|
||||
class IsolateRunnableEvent implements M.IsolateRunnableEvent {
|
||||
final DateTime timestamp;
|
||||
final M.IsolateRef isolate;
|
||||
IsolateRunnableEvent(this.timestamp, this.isolate);
|
||||
}
|
||||
|
||||
class IsolateExitEvent implements M.IsolateExitEvent {
|
||||
final DateTime timestamp;
|
||||
final M.IsolateRef isolate;
|
||||
IsolateExitEvent(this.timestamp, this.isolate);
|
||||
}
|
||||
|
||||
class IsolateUpdateEvent implements M.IsolateUpdateEvent {
|
||||
final DateTime timestamp;
|
||||
final M.IsolateRef isolate;
|
||||
IsolateUpdateEvent(this.timestamp, this.isolate);
|
||||
}
|
||||
|
||||
class IsolateReloadEvent implements M.IsolateReloadEvent {
|
||||
final DateTime timestamp;
|
||||
final M.IsolateRef isolate;
|
||||
final M.ErrorRef error;
|
||||
IsolateReloadEvent(this.timestamp, this.isolate, this.error);
|
||||
}
|
||||
|
||||
class ServiceExtensionAddedEvent implements M.ServiceExtensionAddedEvent {
|
||||
final DateTime timestamp;
|
||||
final M.IsolateRef isolate;
|
||||
final String extensionRPC;
|
||||
ServiceExtensionAddedEvent(this.timestamp, this.isolate, this.extensionRPC);
|
||||
}
|
||||
|
||||
class DebuggerSettingsUpdateEvent implements M.DebuggerSettingsUpdateEvent {
|
||||
final DateTime timestamp;
|
||||
final M.IsolateRef isolate;
|
||||
DebuggerSettingsUpdateEvent(this.timestamp, this.isolate);
|
||||
}
|
||||
|
||||
class PauseStartEvent implements M.PauseStartEvent {
|
||||
final DateTime timestamp;
|
||||
final M.IsolateRef isolate;
|
||||
PauseStartEvent(this.timestamp, this.isolate);
|
||||
}
|
||||
|
||||
class PauseExitEvent implements M.PauseExitEvent {
|
||||
final DateTime timestamp;
|
||||
final M.IsolateRef isolate;
|
||||
PauseExitEvent(this.timestamp, this.isolate);
|
||||
}
|
||||
|
||||
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) {}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
class ResumeEvent implements M.ResumeEvent {
|
||||
final DateTime timestamp;
|
||||
final M.IsolateRef isolate;
|
||||
final M.Frame? topFrame;
|
||||
ResumeEvent(this.timestamp, this.isolate, this.topFrame);
|
||||
}
|
||||
|
||||
class BreakpointAddedEvent implements M.BreakpointAddedEvent {
|
||||
final DateTime timestamp;
|
||||
final M.IsolateRef isolate;
|
||||
final M.Breakpoint breakpoint;
|
||||
BreakpointAddedEvent(this.timestamp, this.isolate, this.breakpoint);
|
||||
}
|
||||
|
||||
class BreakpointResolvedEvent implements M.BreakpointResolvedEvent {
|
||||
final DateTime timestamp;
|
||||
final M.IsolateRef isolate;
|
||||
final M.Breakpoint breakpoint;
|
||||
BreakpointResolvedEvent(this.timestamp, this.isolate, this.breakpoint);
|
||||
}
|
||||
|
||||
class BreakpointRemovedEvent implements M.BreakpointRemovedEvent {
|
||||
final DateTime timestamp;
|
||||
final M.IsolateRef isolate;
|
||||
final M.Breakpoint breakpoint;
|
||||
BreakpointRemovedEvent(this.timestamp, this.isolate, this.breakpoint);
|
||||
}
|
||||
|
||||
class InspectEvent implements M.InspectEvent {
|
||||
final DateTime timestamp;
|
||||
final M.IsolateRef isolate;
|
||||
final M.InstanceRef inspectee;
|
||||
InspectEvent(this.timestamp, this.isolate, this.inspectee);
|
||||
}
|
||||
|
||||
class NoneEvent implements M.NoneEvent {
|
||||
final DateTime timestamp;
|
||||
final M.IsolateRef isolate;
|
||||
NoneEvent(this.timestamp, this.isolate);
|
||||
}
|
||||
|
||||
class GCEvent implements M.GCEvent {
|
||||
final DateTime timestamp;
|
||||
final M.IsolateRef isolate;
|
||||
GCEvent(this.timestamp, this.isolate);
|
||||
}
|
||||
|
||||
class LoggingEvent implements M.LoggingEvent {
|
||||
final DateTime timestamp;
|
||||
final M.IsolateRef isolate;
|
||||
final Map logRecord;
|
||||
LoggingEvent(this.timestamp, this.isolate, this.logRecord);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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) {}
|
||||
}
|
||||
|
||||
class ConnectionClosedEvent implements M.ConnectionClosedEvent {
|
||||
final DateTime timestamp;
|
||||
final String reason;
|
||||
ConnectionClosedEvent(this.timestamp, this.reason);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
class ServiceUnregisteredEvent implements M.ServiceUnregisteredEvent {
|
||||
final DateTime timestamp;
|
||||
final String service;
|
||||
final String method;
|
||||
ServiceUnregisteredEvent(this.timestamp, this.service, this.method);
|
||||
}
|
||||
|
||||
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,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.
|
||||
|
||||
library models;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:observatory/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,58 +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 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/allocation_profile.dart';
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/object_graph.dart';
|
||||
import 'package:observatory/sample_profile.dart';
|
||||
import 'package:observatory/service.dart' as S;
|
||||
import 'package:observatory/service_common.dart' as SC;
|
||||
import 'package:observatory/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,15 +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/models.dart' as M;
|
||||
import 'package:observatory/service.dart';
|
||||
import 'package:observatory/utils.dart';
|
||||
|
||||
part 'src/sample_profile/sample_profile.dart';
|
||||
@@ -1,21 +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:math' as math;
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import 'package:observatory/event.dart' show createEventFromServiceEvent;
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/object_graph.dart';
|
||||
import 'package:observatory/sample_profile.dart';
|
||||
import 'package:observatory/service_common.dart';
|
||||
import 'package:observatory/tracer.dart';
|
||||
|
||||
part 'src/service/object.dart';
|
||||
@@ -1,339 +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/models.dart' as M;
|
||||
import 'package:observatory/service.dart';
|
||||
|
||||
// Export the service library.
|
||||
export 'package:observatory/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.
|
||||
late String name;
|
||||
// Network address of VM.
|
||||
late String networkAddress;
|
||||
|
||||
WebSocketVMTarget(this.networkAddress) {
|
||||
name = networkAddress;
|
||||
}
|
||||
|
||||
WebSocketVMTarget.fromMap(Map json) {
|
||||
lastConnectionTime = json['lastConnectionTime'];
|
||||
chrome = json['chrome'];
|
||||
networkAddress = json['networkAddress'];
|
||||
name = json['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 [package:web/web.dart].
|
||||
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);
|
||||
|
||||
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,79 +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:typed_data';
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/service_common.dart';
|
||||
|
||||
// Export the service library.
|
||||
export 'package:observatory/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 package:web/web.dart 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) {
|
||||
final Uint8List result = (fileReader.result as ByteBuffer).asUint8List();
|
||||
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,57 +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/service_common.dart';
|
||||
|
||||
// Export the service library.
|
||||
export 'package:observatory/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() {
|
||||
_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,91 +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 as ClassHeapStats);
|
||||
} else {
|
||||
result.add(member as ClassHeapStats);
|
||||
}
|
||||
});
|
||||
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,291 +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 late 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>[];
|
||||
late 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() {
|
||||
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 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<String, String> 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) {
|
||||
if (currentPage == page) {
|
||||
// Already installed.
|
||||
return;
|
||||
}
|
||||
if (currentPage != null) {
|
||||
Logger.root.info('Uninstalling page: $currentPage');
|
||||
currentPage!.onUninstall();
|
||||
// Clear children.
|
||||
rootElement.removeChildren();
|
||||
}
|
||||
Logger.root.info('Installing page: $page');
|
||||
try {
|
||||
page.onInstall();
|
||||
} catch (e) {
|
||||
Logger.root.severe('Failed to install page: $e');
|
||||
}
|
||||
// Add new page.
|
||||
rootElement.appendChild(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) {
|
||||
web.window.onPopState.listen(_onBrowserNavigation);
|
||||
// Determine initial application path.
|
||||
var applicationPath = '${web.window.location.hash}';
|
||||
if ((web.window.location.hash == '') || (web.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.
|
||||
web.window.history.replaceState(
|
||||
applicationPath.toJS, web.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(web.PopStateEvent event) {
|
||||
_updateApplicationLocation(web.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) {
|
||||
web.window.history.pushState(url.toJS, web.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.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<String, dynamic>.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(web.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.
|
||||
web.HTMLElement target = event.currentTarget as web.HTMLElement;
|
||||
go(target.attributes.getNamedItem('href')!.value);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -1,953 +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;
|
||||
|
||||
final _allocationProfileRepository = new AllocationProfileRepository();
|
||||
final _breakpointRepository = new BreakpointRepository();
|
||||
final _classRepository = new ClassRepository();
|
||||
final _classSampleProfileRepository = new ClassSampleProfileRepository();
|
||||
final _contextRepository = new ContextRepository();
|
||||
final _evalRepository = new EvalRepository();
|
||||
final _fieldRepository = new FieldRepository();
|
||||
final _functionRepository = new FunctionRepository();
|
||||
final _heapSnapshotRepository = new HeapSnapshotRepository();
|
||||
final _icdataRepository = new ICDataRepository();
|
||||
final _inboundReferencesRepository = new InboundReferencesRepository();
|
||||
final _isolateSampleProfileRepository = new IsolateSampleProfileRepository();
|
||||
final _libraryRepository = new LibraryRepository();
|
||||
final _megamorphicCacheRepository = new MegamorphicCacheRepository();
|
||||
final _metricRepository = new MetricRepository();
|
||||
final _nativeMemorySampleProfileRepository =
|
||||
new NativeMemorySampleProfileRepository();
|
||||
final _objectPoolRepository = new ObjectPoolRepository();
|
||||
final _objectRepository = new ObjectRepository();
|
||||
final _objectstoreRepository = new ObjectStoreRepository();
|
||||
final _persistentHandlesRepository = new PersistentHandlesRepository();
|
||||
final _portsRepository = new PortsRepository();
|
||||
final _scriptRepository = new ScriptRepository();
|
||||
final _singleTargetCacheRepository = new SingleTargetCacheRepository();
|
||||
final _stronglyReachangleInstancesRepository =
|
||||
new StronglyReachableInstancesRepository();
|
||||
final _subtypeTestCacheRepository = new SubtypeTestCacheRepository();
|
||||
final _timelineRepository = new TimelineRepository();
|
||||
final _typeArgumentsRepository = new TypeArgumentsRepository();
|
||||
final _unlinkedCallRepository = new UnlinkedCallRepository();
|
||||
final _vmrepository = new VMRepository();
|
||||
|
||||
class IsolateNotFound implements Exception {
|
||||
String isolateId;
|
||||
IsolateNotFound(this.isolateId);
|
||||
String toString() => "IsolateNotFound: $isolateId";
|
||||
}
|
||||
|
||||
RetainedSizeRepository _retainedSizeRepository = new RetainedSizeRepository();
|
||||
ReachableSizeRepository _reachableSizeRepository =
|
||||
new ReachableSizeRepository();
|
||||
RetainingPathRepository _retainingPathRepository =
|
||||
new RetainingPathRepository();
|
||||
|
||||
/// A [Page] controls the user interface of Observatory. At any given time
|
||||
/// one page will be the current page. Pages are registered at startup.
|
||||
/// When the user navigates within the application, each page is asked if it
|
||||
/// can handle the current location, the first page to say yes, wins.
|
||||
abstract class Page {
|
||||
final ObservatoryApplication app;
|
||||
final Map<String, String> internalArguments = <String, String>{};
|
||||
web.HTMLElement? element;
|
||||
|
||||
Page(this.app);
|
||||
|
||||
/// Called when the page is installed, this callback must initialize
|
||||
/// [element].
|
||||
void onInstall();
|
||||
|
||||
/// Called when the page is uninstalled, this callback must clear
|
||||
/// [element].
|
||||
void onUninstall() {
|
||||
element = null;
|
||||
}
|
||||
|
||||
/// Called when the page should update its state based on [uri].
|
||||
void visit(Uri uri, Map<String, String> internalArguments) {
|
||||
this.internalArguments.clear();
|
||||
this.internalArguments.addAll(internalArguments);
|
||||
_visit(uri);
|
||||
}
|
||||
|
||||
// Overridden by subclasses.
|
||||
void _visit(Uri uri);
|
||||
|
||||
/// Called to test whether this page can visit [uri].
|
||||
bool canVisit(Uri uri);
|
||||
}
|
||||
|
||||
/// A [MatchingPage] matches a single uri path.
|
||||
abstract class MatchingPage extends Page {
|
||||
final String path;
|
||||
MatchingPage(this.path, app) : super(app);
|
||||
|
||||
void _visit(Uri uri) {
|
||||
assert(canVisit(uri));
|
||||
}
|
||||
|
||||
Future<Isolate> getIsolate(Uri uri) {
|
||||
var isolateId = uri.queryParameters['isolateId'];
|
||||
return app.vm.getIsolate(isolateId!).then((isolate) {
|
||||
return isolate;
|
||||
});
|
||||
}
|
||||
|
||||
EditorRepository getEditor(Uri uri) {
|
||||
final editor = uri.queryParameters['editor'];
|
||||
return new EditorRepository(app.vm, editor: editor);
|
||||
}
|
||||
|
||||
bool canVisit(Uri uri) => uri.path == path;
|
||||
}
|
||||
|
||||
/// A [SimplePage] matches a single uri path and displays a single element.
|
||||
abstract class SimplePage extends MatchingPage {
|
||||
final String elementTagName;
|
||||
SimplePage(String path, this.elementTagName, app) : super(path, app);
|
||||
|
||||
void onInstall() {
|
||||
if (element == null) {
|
||||
element = createElement();
|
||||
}
|
||||
}
|
||||
|
||||
web.HTMLElement createElement();
|
||||
}
|
||||
|
||||
/// Error page for unrecognized paths.
|
||||
class ErrorPage extends Page {
|
||||
ErrorPage(app) : super(app);
|
||||
|
||||
void onInstall() {
|
||||
if (element == null) {
|
||||
// Lazily create page.
|
||||
element =
|
||||
new GeneralErrorElement(app.notifications, queue: app.queue).element;
|
||||
}
|
||||
}
|
||||
|
||||
void _visit(Uri uri) {
|
||||
assert(element != null);
|
||||
assert(canVisit(uri));
|
||||
|
||||
(element as GeneralErrorElement).message = "Path '${uri.path}' not found";
|
||||
}
|
||||
|
||||
/// Catch all.
|
||||
bool canVisit(Uri uri) => true;
|
||||
}
|
||||
|
||||
/// Top-level vm info page.
|
||||
class VMPage extends MatchingPage {
|
||||
VMPage(app) : super('vm', app);
|
||||
|
||||
final web.HTMLDivElement container = web.HTMLDivElement();
|
||||
|
||||
void onInstall() {
|
||||
if (element == null) {
|
||||
element = container;
|
||||
}
|
||||
assert(element != null);
|
||||
}
|
||||
|
||||
void _visit(Uri uri) {
|
||||
super._visit(uri);
|
||||
app.vm.reload().then((serviceObject) {
|
||||
VM vm = serviceObject as VM;
|
||||
container.removeChildren();
|
||||
final vmview = new VMViewElement(
|
||||
vm,
|
||||
_vmrepository,
|
||||
app.events,
|
||||
app.notifications,
|
||||
new IsolateRepository(app.vm),
|
||||
new IsolateGroupRepository(),
|
||||
_scriptRepository,
|
||||
queue: app.queue)
|
||||
.element;
|
||||
container.appendChild(vmview);
|
||||
}).catchError((e, stack) {
|
||||
Logger.root.severe('VMPage visit error: $e');
|
||||
Logger.root.severe('Stack: $stack');
|
||||
// Reroute to vm-connect.
|
||||
app.locationManager.go(Uris.vmConnect());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class FlagsPage extends SimplePage {
|
||||
FlagsPage(app) : super('flags', 'flag-list', app);
|
||||
|
||||
@override
|
||||
web.HTMLElement createElement() => new FlagListElement(
|
||||
app.vm, app.events, new FlagsRepository(app.vm), app.notifications,
|
||||
queue: app.queue)
|
||||
.element;
|
||||
|
||||
void _visit(Uri uri) {
|
||||
super._visit(uri);
|
||||
}
|
||||
}
|
||||
|
||||
class NativeMemoryProfilerPage extends SimplePage {
|
||||
NativeMemoryProfilerPage(app)
|
||||
: super('native-memory-profile', 'native-memory-profile', app);
|
||||
|
||||
@override
|
||||
web.HTMLElement createElement() => NativeMemoryProfileElement(app.vm,
|
||||
app.events, app.notifications, _nativeMemorySampleProfileRepository,
|
||||
queue: app.queue)
|
||||
.element;
|
||||
|
||||
void _visit(Uri uri) {
|
||||
super._visit(uri);
|
||||
}
|
||||
}
|
||||
|
||||
class InspectPage extends MatchingPage {
|
||||
InspectPage(app) : super('inspect', app);
|
||||
|
||||
final web.HTMLDivElement container = web.HTMLDivElement();
|
||||
|
||||
void _visit(Uri uri) {
|
||||
super._visit(uri);
|
||||
getIsolate(uri).then((isolate) {
|
||||
var objectId = uri.queryParameters['objectId'];
|
||||
if (objectId == null) {
|
||||
isolate.reload().then(_visitObject);
|
||||
} else {
|
||||
isolate.getObject(objectId).then(_visitObject);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void onInstall() {
|
||||
if (element == null) {
|
||||
element = container;
|
||||
}
|
||||
assert(element != null);
|
||||
}
|
||||
|
||||
Future _visitObject(obj) async {
|
||||
container.removeChildren();
|
||||
await obj.reload();
|
||||
if (obj is Class) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new ClassViewElement(
|
||||
app.vm,
|
||||
obj.isolate as Isolate,
|
||||
obj,
|
||||
app.events,
|
||||
app.notifications,
|
||||
_classRepository,
|
||||
_retainedSizeRepository,
|
||||
_reachableSizeRepository,
|
||||
_inboundReferencesRepository,
|
||||
_retainingPathRepository,
|
||||
_fieldRepository,
|
||||
_scriptRepository,
|
||||
_objectRepository,
|
||||
_evalRepository,
|
||||
_stronglyReachangleInstancesRepository,
|
||||
_classSampleProfileRepository,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
} else if (obj is Code) {
|
||||
await obj.loadScript();
|
||||
container.removeChildren();
|
||||
container.appendChild(new CodeViewElement(
|
||||
app.vm,
|
||||
obj.isolate as Isolate,
|
||||
obj,
|
||||
app.events,
|
||||
app.notifications,
|
||||
_retainedSizeRepository,
|
||||
_reachableSizeRepository,
|
||||
_inboundReferencesRepository,
|
||||
_retainingPathRepository,
|
||||
_objectRepository,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
} else if (obj is Context) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new ContextViewElement(
|
||||
app.vm,
|
||||
obj.isolate as Isolate,
|
||||
obj,
|
||||
app.events,
|
||||
app.notifications,
|
||||
_contextRepository,
|
||||
_retainedSizeRepository,
|
||||
_reachableSizeRepository,
|
||||
_inboundReferencesRepository,
|
||||
_retainingPathRepository,
|
||||
_objectRepository,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
} else if (obj is DartError) {
|
||||
container.removeChildren();
|
||||
container.appendChild(
|
||||
new ErrorViewElement(app.notifications, obj, queue: app.queue)
|
||||
.element);
|
||||
} else if (obj is Field) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new FieldViewElement(
|
||||
app.vm,
|
||||
obj.isolate as Isolate,
|
||||
obj,
|
||||
app.events,
|
||||
app.notifications,
|
||||
_fieldRepository,
|
||||
_classRepository,
|
||||
_retainedSizeRepository,
|
||||
_reachableSizeRepository,
|
||||
_inboundReferencesRepository,
|
||||
_retainingPathRepository,
|
||||
_scriptRepository,
|
||||
_objectRepository,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
} else if (obj is Instance) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new InstanceViewElement(
|
||||
app.vm,
|
||||
obj.isolate as Isolate,
|
||||
obj,
|
||||
app.events,
|
||||
app.notifications,
|
||||
_objectRepository,
|
||||
_classRepository,
|
||||
_retainedSizeRepository,
|
||||
_reachableSizeRepository,
|
||||
_inboundReferencesRepository,
|
||||
_retainingPathRepository,
|
||||
_scriptRepository,
|
||||
_evalRepository,
|
||||
_typeArgumentsRepository,
|
||||
_breakpointRepository,
|
||||
_functionRepository,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
} else if (obj is Isolate) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new IsolateViewElement(
|
||||
app.vm,
|
||||
obj,
|
||||
app.events,
|
||||
app.notifications,
|
||||
new IsolateRepository(app.vm),
|
||||
_scriptRepository,
|
||||
_functionRepository,
|
||||
_libraryRepository,
|
||||
_objectRepository,
|
||||
_evalRepository,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
} else if (obj is ServiceFunction) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new FunctionViewElement(
|
||||
app.vm,
|
||||
obj.isolate as Isolate,
|
||||
obj,
|
||||
app.events,
|
||||
app.notifications,
|
||||
_functionRepository,
|
||||
_classRepository,
|
||||
_retainedSizeRepository,
|
||||
_reachableSizeRepository,
|
||||
_inboundReferencesRepository,
|
||||
_retainingPathRepository,
|
||||
_scriptRepository,
|
||||
_objectRepository,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
} else if (obj is ICData) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new ICDataViewElement(
|
||||
app.vm,
|
||||
obj.isolate as Isolate,
|
||||
obj,
|
||||
app.events,
|
||||
app.notifications,
|
||||
_icdataRepository,
|
||||
_retainedSizeRepository,
|
||||
_reachableSizeRepository,
|
||||
_inboundReferencesRepository,
|
||||
_retainingPathRepository,
|
||||
_objectRepository,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
} else if (obj is SingleTargetCache) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new SingleTargetCacheViewElement(
|
||||
app.vm,
|
||||
obj.isolate as Isolate,
|
||||
obj,
|
||||
app.events,
|
||||
app.notifications,
|
||||
_singleTargetCacheRepository,
|
||||
_retainedSizeRepository,
|
||||
_reachableSizeRepository,
|
||||
_inboundReferencesRepository,
|
||||
_retainingPathRepository,
|
||||
_objectRepository,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
} else if (obj is SubtypeTestCache) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new SubtypeTestCacheViewElement(
|
||||
app.vm,
|
||||
obj.isolate as Isolate,
|
||||
obj,
|
||||
app.events,
|
||||
app.notifications,
|
||||
_subtypeTestCacheRepository,
|
||||
_retainedSizeRepository,
|
||||
_reachableSizeRepository,
|
||||
_inboundReferencesRepository,
|
||||
_retainingPathRepository,
|
||||
_objectRepository,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
} else if (obj is UnlinkedCall) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new UnlinkedCallViewElement(
|
||||
app.vm,
|
||||
obj.isolate as Isolate,
|
||||
obj,
|
||||
app.events,
|
||||
app.notifications,
|
||||
_unlinkedCallRepository,
|
||||
_retainedSizeRepository,
|
||||
_reachableSizeRepository,
|
||||
_inboundReferencesRepository,
|
||||
_retainingPathRepository,
|
||||
_objectRepository,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
} else if (obj is Library) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new LibraryViewElement(
|
||||
app.vm,
|
||||
obj.isolate as Isolate,
|
||||
obj,
|
||||
app.events,
|
||||
app.notifications,
|
||||
_libraryRepository,
|
||||
_fieldRepository,
|
||||
_retainedSizeRepository,
|
||||
_reachableSizeRepository,
|
||||
_inboundReferencesRepository,
|
||||
_retainingPathRepository,
|
||||
_scriptRepository,
|
||||
_objectRepository,
|
||||
_evalRepository,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
} else if (obj is MegamorphicCache) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new MegamorphicCacheViewElement(
|
||||
app.vm,
|
||||
obj.isolate as Isolate,
|
||||
obj,
|
||||
app.events,
|
||||
app.notifications,
|
||||
_megamorphicCacheRepository,
|
||||
_retainedSizeRepository,
|
||||
_reachableSizeRepository,
|
||||
_inboundReferencesRepository,
|
||||
_retainingPathRepository,
|
||||
_objectRepository,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
} else if (obj is ObjectPool) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new ObjectPoolViewElement(
|
||||
app.vm,
|
||||
obj.isolate as Isolate,
|
||||
obj,
|
||||
app.events,
|
||||
app.notifications,
|
||||
_objectPoolRepository,
|
||||
_retainedSizeRepository,
|
||||
_reachableSizeRepository,
|
||||
_inboundReferencesRepository,
|
||||
_retainingPathRepository,
|
||||
_objectRepository,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
} else if (obj is Script) {
|
||||
var pos;
|
||||
if (app.locationManager.internalArguments['pos'] != null) {
|
||||
try {
|
||||
pos = int.parse(app.locationManager.internalArguments['pos']!);
|
||||
} catch (_) {}
|
||||
}
|
||||
container.removeChildren();
|
||||
container.appendChild(new ScriptViewElement(
|
||||
app.vm,
|
||||
obj.isolate as Isolate,
|
||||
obj,
|
||||
app.events,
|
||||
app.notifications,
|
||||
_scriptRepository,
|
||||
_retainedSizeRepository,
|
||||
_reachableSizeRepository,
|
||||
_inboundReferencesRepository,
|
||||
_retainingPathRepository,
|
||||
_objectRepository,
|
||||
pos: pos,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
} else if (obj is HeapObject) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new ObjectViewElement(
|
||||
app.vm,
|
||||
obj.isolate as Isolate,
|
||||
obj,
|
||||
app.events,
|
||||
app.notifications,
|
||||
_objectRepository,
|
||||
_retainedSizeRepository,
|
||||
_reachableSizeRepository,
|
||||
_inboundReferencesRepository,
|
||||
_retainingPathRepository,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
} else if (obj is Sentinel) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new SentinelViewElement(app.vm,
|
||||
obj.isolate as Isolate, obj, app.events, app.notifications,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
} else {
|
||||
container.removeChildren();
|
||||
container.appendChild(
|
||||
new JSONViewElement(obj, app.notifications, queue: app.queue)
|
||||
.element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Class tree page.
|
||||
class ClassTreePage extends SimplePage {
|
||||
ClassTreePage(app) : super('class-tree', 'class-tree', app);
|
||||
|
||||
final web.HTMLDivElement container = web.HTMLDivElement();
|
||||
|
||||
@override
|
||||
web.HTMLElement createElement() => container;
|
||||
|
||||
void _visit(Uri uri) {
|
||||
super._visit(uri);
|
||||
getIsolate(uri).then((isolate) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new ClassTreeElement(
|
||||
app.vm, isolate, app.events, app.notifications, _classRepository)
|
||||
.element);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class DebuggerPage extends MatchingPage {
|
||||
DebuggerPage(app) : super('debugger', app);
|
||||
|
||||
final web.HTMLDivElement container = web.HTMLDivElement();
|
||||
|
||||
void _visit(Uri uri) {
|
||||
super._visit(uri);
|
||||
getIsolate(uri).then((isolate) async {
|
||||
container.removeChildren();
|
||||
container.appendChild(new DebuggerPageElement(
|
||||
isolate, _objectRepository, _scriptRepository, app.events)
|
||||
.element);
|
||||
});
|
||||
}
|
||||
|
||||
void onInstall() {
|
||||
if (element == null) {
|
||||
element = container;
|
||||
}
|
||||
assert(element != null);
|
||||
}
|
||||
|
||||
@override
|
||||
void onUninstall() {
|
||||
super.onUninstall();
|
||||
}
|
||||
}
|
||||
|
||||
class ObjectStorePage extends MatchingPage {
|
||||
ObjectStorePage(app) : super('object-store', app);
|
||||
|
||||
final web.HTMLDivElement container = web.HTMLDivElement();
|
||||
|
||||
void _visit(Uri uri) {
|
||||
super._visit(uri);
|
||||
getIsolate(uri).then((isolate) async {
|
||||
container.removeChildren();
|
||||
container.appendChild(new ObjectStoreViewElement(
|
||||
isolate.vm,
|
||||
isolate,
|
||||
app.events,
|
||||
app.notifications,
|
||||
_objectstoreRepository,
|
||||
_objectRepository)
|
||||
.element);
|
||||
});
|
||||
}
|
||||
|
||||
void onInstall() {
|
||||
if (element == null) {
|
||||
element = container;
|
||||
}
|
||||
assert(element != null);
|
||||
}
|
||||
}
|
||||
|
||||
class CpuProfilerPage extends MatchingPage {
|
||||
CpuProfilerPage(app) : super('profiler', app);
|
||||
|
||||
final web.HTMLDivElement container = web.HTMLDivElement();
|
||||
|
||||
void _visit(Uri uri) {
|
||||
super._visit(uri);
|
||||
getIsolate(uri).then((isolate) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new CpuProfileElement(isolate.vm, isolate,
|
||||
app.events, app.notifications, _isolateSampleProfileRepository)
|
||||
.element);
|
||||
});
|
||||
}
|
||||
|
||||
void onInstall() {
|
||||
if (element == null) {
|
||||
element = container;
|
||||
}
|
||||
assert(element != null);
|
||||
}
|
||||
}
|
||||
|
||||
class TableCpuProfilerPage extends MatchingPage {
|
||||
TableCpuProfilerPage(app) : super('profiler-table', app);
|
||||
|
||||
final web.HTMLDivElement container = web.HTMLDivElement();
|
||||
|
||||
void _visit(Uri uri) {
|
||||
super._visit(uri);
|
||||
getIsolate(uri).then((isolate) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new CpuProfileTableElement(isolate.vm, isolate,
|
||||
app.events, app.notifications, _isolateSampleProfileRepository)
|
||||
.element);
|
||||
});
|
||||
}
|
||||
|
||||
void onInstall() {
|
||||
if (element == null) {
|
||||
element = container;
|
||||
}
|
||||
assert(element != null);
|
||||
}
|
||||
}
|
||||
|
||||
class AllocationProfilerPage extends MatchingPage {
|
||||
AllocationProfilerPage(app) : super('allocation-profiler', app);
|
||||
|
||||
final web.HTMLDivElement container = web.HTMLDivElement();
|
||||
|
||||
void _visit(Uri uri) {
|
||||
super._visit(uri);
|
||||
getIsolate(uri).then((isolate) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new AllocationProfileElement(isolate.vm, isolate,
|
||||
app.events, app.notifications, _allocationProfileRepository,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
});
|
||||
}
|
||||
|
||||
void onInstall() {
|
||||
if (element == null) {
|
||||
element = container;
|
||||
}
|
||||
app.startGCEventListener();
|
||||
}
|
||||
|
||||
@override
|
||||
void onUninstall() {
|
||||
super.onUninstall();
|
||||
app.stopGCEventListener();
|
||||
}
|
||||
}
|
||||
|
||||
class PortsPage extends MatchingPage {
|
||||
PortsPage(app) : super('ports', app);
|
||||
|
||||
final web.HTMLDivElement container = web.HTMLDivElement();
|
||||
|
||||
void _visit(Uri uri) {
|
||||
super._visit(uri);
|
||||
getIsolate(uri).then((isolate) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new PortsElement(isolate.vm, isolate, app.events,
|
||||
app.notifications, _portsRepository, _objectRepository,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
});
|
||||
}
|
||||
|
||||
void onInstall() {
|
||||
if (element == null) {
|
||||
element = container;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PersistentHandlesPage extends MatchingPage {
|
||||
PersistentHandlesPage(app) : super('persistent-handles', app);
|
||||
|
||||
final web.HTMLDivElement container = web.HTMLDivElement();
|
||||
|
||||
void _visit(Uri uri) {
|
||||
super._visit(uri);
|
||||
getIsolate(uri).then((isolate) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new PersistentHandlesPageElement(
|
||||
isolate.vm,
|
||||
isolate,
|
||||
app.events,
|
||||
app.notifications,
|
||||
_persistentHandlesRepository,
|
||||
_objectRepository,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
});
|
||||
}
|
||||
|
||||
void onInstall() {
|
||||
if (element == null) {
|
||||
element = container;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HeapMapPage extends MatchingPage {
|
||||
HeapMapPage(app) : super('heap-map', app);
|
||||
|
||||
final web.HTMLDivElement container = web.HTMLDivElement();
|
||||
|
||||
void _visit(Uri uri) {
|
||||
super._visit(uri);
|
||||
getIsolate(uri).then((isolate) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new HeapMapElement(
|
||||
isolate.vm, isolate, app.events, app.notifications,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
});
|
||||
}
|
||||
|
||||
void onInstall() {
|
||||
if (element == null) {
|
||||
element = container;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HeapSnapshotPage extends MatchingPage {
|
||||
HeapSnapshotPage(app) : super('heap-snapshot', app);
|
||||
|
||||
final web.HTMLDivElement container = web.HTMLDivElement();
|
||||
|
||||
void _visit(Uri uri) {
|
||||
super._visit(uri);
|
||||
getIsolate(uri).then((isolate) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new HeapSnapshotElement(
|
||||
isolate.vm,
|
||||
isolate,
|
||||
app.events,
|
||||
app.notifications,
|
||||
_heapSnapshotRepository,
|
||||
_objectRepository,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
});
|
||||
}
|
||||
|
||||
void onInstall() {
|
||||
if (element == null) {
|
||||
element = container;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LoggingPage extends MatchingPage {
|
||||
LoggingPage(app) : super('logging', app);
|
||||
|
||||
final web.HTMLDivElement container = web.HTMLDivElement();
|
||||
|
||||
@override
|
||||
void onInstall() {
|
||||
element = container;
|
||||
app.startLoggingEventListener();
|
||||
}
|
||||
|
||||
@override
|
||||
void onUninstall() {
|
||||
super.onUninstall();
|
||||
app.stopLoggingEventListener();
|
||||
}
|
||||
|
||||
void _visit(Uri uri) {
|
||||
assert(element != null);
|
||||
assert(canVisit(uri));
|
||||
getIsolate(uri).then((isolate) {
|
||||
container.removeChildren();
|
||||
container.appendChild(new LoggingPageElement(
|
||||
app.vm, isolate, app.events, app.notifications,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ErrorViewPage extends Page {
|
||||
ErrorViewPage(app) : super(app);
|
||||
|
||||
void onInstall() {
|
||||
element = new ErrorViewElement(
|
||||
app.notifications, app.lastErrorOrException as DartError,
|
||||
queue: app.queue)
|
||||
.element;
|
||||
}
|
||||
|
||||
void _visit(Uri uri) {
|
||||
assert(element != null);
|
||||
assert(canVisit(uri));
|
||||
}
|
||||
|
||||
// TODO(turnidge): How to test this page?
|
||||
bool canVisit(Uri uri) => uri.path == 'error';
|
||||
}
|
||||
|
||||
class VMConnectPage extends Page {
|
||||
VMConnectPage(app) : super(app);
|
||||
|
||||
void onInstall() {
|
||||
if (element == null) {
|
||||
element = new VMConnectElement(ObservatoryApplication.app.targets,
|
||||
ObservatoryApplication.app.notifications,
|
||||
queue: ObservatoryApplication.app.queue)
|
||||
.element;
|
||||
}
|
||||
assert(element != null);
|
||||
}
|
||||
|
||||
void _visit(Uri uri) {
|
||||
assert(element != null);
|
||||
assert(canVisit(uri));
|
||||
}
|
||||
|
||||
bool canVisit(Uri uri) => uri.path == 'vm-connect';
|
||||
}
|
||||
|
||||
class IsolateReconnectPage extends Page {
|
||||
IsolateReconnectPage(app) : super(app);
|
||||
|
||||
final web.HTMLDivElement container = web.HTMLDivElement();
|
||||
|
||||
void onInstall() {
|
||||
element = container;
|
||||
}
|
||||
|
||||
void _visit(Uri uri) {
|
||||
app.vm.reload();
|
||||
container.removeChildren();
|
||||
container.appendChild(new IsolateReconnectElement(
|
||||
app.vm,
|
||||
app.events,
|
||||
app.notifications,
|
||||
uri.queryParameters['isolateId']!,
|
||||
Uri.parse(uri.queryParameters['originalUri']!))
|
||||
.element);
|
||||
assert(element != null);
|
||||
assert(canVisit(uri));
|
||||
}
|
||||
|
||||
bool canVisit(Uri uri) => uri.path == 'isolate-reconnect';
|
||||
}
|
||||
|
||||
class MetricsPage extends MatchingPage {
|
||||
MetricsPage(app) : super('metrics', app);
|
||||
|
||||
final web.HTMLDivElement container = web.HTMLDivElement();
|
||||
|
||||
Isolate? lastIsolate;
|
||||
|
||||
void _visit(Uri uri) {
|
||||
super._visit(uri);
|
||||
getIsolate(uri).then((isolate) async {
|
||||
lastIsolate = isolate;
|
||||
await _metricRepository.startSampling(isolate);
|
||||
container.removeChildren();
|
||||
container.appendChild(new MetricsPageElement(isolate.vm, isolate,
|
||||
app.events, app.notifications, _metricRepository,
|
||||
queue: app.queue)
|
||||
.element);
|
||||
});
|
||||
}
|
||||
|
||||
void onInstall() {
|
||||
if (element == null) {
|
||||
element = container;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onUninstall() {
|
||||
super.onUninstall();
|
||||
_metricRepository.stopSampling(lastIsolate!);
|
||||
}
|
||||
}
|
||||
|
||||
class TimelinePage extends Page {
|
||||
TimelinePage(app) : super(app);
|
||||
|
||||
void onInstall() {
|
||||
element = new TimelinePageElement(
|
||||
app.vm, _timelineRepository, app.events, app.notifications,
|
||||
queue: app.queue)
|
||||
.element;
|
||||
}
|
||||
|
||||
void _visit(Uri uri) {
|
||||
assert(canVisit(uri));
|
||||
}
|
||||
|
||||
bool canVisit(Uri uri) => uri.path == 'timeline';
|
||||
}
|
||||
|
||||
class ProcessSnapshotPage extends Page {
|
||||
ProcessSnapshotPage(app) : super(app);
|
||||
|
||||
void onInstall() {
|
||||
element = new ProcessSnapshotElement(app.vm, app.events, app.notifications,
|
||||
queue: app.queue)
|
||||
.element;
|
||||
}
|
||||
|
||||
void _visit(Uri uri) {
|
||||
assert(canVisit(uri));
|
||||
}
|
||||
|
||||
bool canVisit(Uri uri) => uri.path == 'process-snapshot';
|
||||
}
|
||||
@@ -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 web.Storage _storage = web.window.localStorage;
|
||||
|
||||
/// Associated [value] with [key]. [value] must be JSON encodable.
|
||||
static void set(String key, dynamic value) {
|
||||
_storage.setItem(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.getItem(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,279 +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) {
|
||||
_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';
|
||||
}
|
||||
|
||||
String 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 as Command;
|
||||
return '${parent.fullName} $name';
|
||||
}
|
||||
}
|
||||
|
||||
String 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,452 +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] as Frame?;
|
||||
}
|
||||
|
||||
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 {
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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) ?? '';
|
||||
if (lineStr.endsWith(':')) {
|
||||
lineStr = lineStr.substring(0, lineStr.length - 1);
|
||||
lineStrComplete = true;
|
||||
}
|
||||
colStr = match.group(2) ?? '';
|
||||
} else {
|
||||
// CASE 2: We have matched a prefix of (scriptName:)(lineStr)(:colStr)
|
||||
scriptName = match.group(1) ?? '';
|
||||
if (scriptName.endsWith(':')) {
|
||||
scriptName = scriptName.substring(0, scriptName.length - 1);
|
||||
scriptNameComplete = true;
|
||||
}
|
||||
lineStr = match.group(2) ?? '';
|
||||
colStr = match.group(3) ?? '';
|
||||
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) as ServiceMap;
|
||||
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,581 +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 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/class_ref.dart';
|
||||
import 'package:observatory/src/elements/containers/virtual_collection.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_menu.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/nav/isolate_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/refresh.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/vm_menu.dart';
|
||||
import 'package:observatory/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 {
|
||||
late RenderingScheduler<AllocationProfileElement> _r;
|
||||
|
||||
Stream<RenderedEvent<AllocationProfileElement>> get onRendered =>
|
||||
_r.onRendered;
|
||||
|
||||
late M.VM _vm;
|
||||
late M.IsolateRef _isolate;
|
||||
late M.EventRepository _events;
|
||||
late M.NotificationRepository _notifications;
|
||||
late M.AllocationProfileRepository _repository;
|
||||
M.AllocationProfile? _profile;
|
||||
bool _autoRefresh = false;
|
||||
bool _isCompacted = false;
|
||||
late 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}) {
|
||||
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);
|
||||
removeChildren();
|
||||
_gcSubscription.cancel();
|
||||
}
|
||||
|
||||
void render() {
|
||||
children = <HTMLElement>[
|
||||
navBar(<HTMLElement>[
|
||||
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 HTMLDivElement()
|
||||
..className = 'nav-option'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLInputElement()
|
||||
..id = 'allocation-profile-auto-refresh'
|
||||
..type = 'checkbox'
|
||||
..checked = _autoRefresh
|
||||
..onChange.listen((_) => _autoRefresh = !_autoRefresh),
|
||||
new HTMLLabelElement()
|
||||
..htmlFor = 'allocation-profile-auto-refresh'
|
||||
..textContent = 'Auto-refresh on GC'
|
||||
]),
|
||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'content-centered-big'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLHeadingElement.h2()..textContent = 'Allocation Profile',
|
||||
new HTMLHRElement()
|
||||
])
|
||||
];
|
||||
if (_profile == null) {
|
||||
appendChild(new HTMLDivElement()
|
||||
..className = 'content-centered-big'
|
||||
..append(new HTMLHeadingElement.h2()..textContent = 'Loading...'));
|
||||
} else {
|
||||
appendChildren([
|
||||
new HTMLDivElement()
|
||||
..className = 'content-centered-big'
|
||||
..appendChildren(_isCompacted
|
||||
? []
|
||||
: [
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..append(
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'last forced GC at',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = _profile!.lastServiceGC == null
|
||||
? '---'
|
||||
: '${_profile!.lastServiceGC}'
|
||||
]),
|
||||
),
|
||||
new HTMLHRElement(),
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'content-centered-big compactable'
|
||||
..appendChildren([
|
||||
new HTMLDivElement()
|
||||
..className = 'heap-space left'
|
||||
..appendChildren(_isCompacted
|
||||
? [
|
||||
new HTMLHeadingElement.h2()
|
||||
..textContent = 'New Generation '
|
||||
'(${_usedCaption(_profile!.newSpace)})',
|
||||
]
|
||||
: [
|
||||
new HTMLHeadingElement.h2()
|
||||
..textContent = 'New Generation',
|
||||
new HTMLBRElement(),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(
|
||||
_createSpaceMembers(_profile!.newSpace)),
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'heap-space left'
|
||||
..appendChildren(_isCompacted
|
||||
? [
|
||||
new HTMLHeadingElement.h2()
|
||||
..textContent = 'Old Generation '
|
||||
'(${_usedCaption(_profile!.oldSpace)})',
|
||||
]
|
||||
: [
|
||||
new HTMLHeadingElement.h2()
|
||||
..textContent = 'Old Generation',
|
||||
new HTMLBRElement(),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(
|
||||
_createSpaceMembers(_profile!.oldSpace)),
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'heap-space left'
|
||||
..appendChildren(_isCompacted
|
||||
? [
|
||||
new HTMLHeadingElement.h2()
|
||||
..textContent = 'Total '
|
||||
'(${_usedCaption(_profile!.totalSpace)})',
|
||||
]
|
||||
: [
|
||||
new HTMLHeadingElement.h2()..textContent = 'Total',
|
||||
new HTMLBRElement(),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(
|
||||
_createSpaceMembers(_profile!.totalSpace)),
|
||||
]),
|
||||
new HTMLButtonElement()
|
||||
..className = 'compact'
|
||||
..textContent = _isCompacted ? 'expand ▼' : 'compact ▲'
|
||||
..onClick.listen((_) {
|
||||
_isCompacted = !_isCompacted;
|
||||
_r.dirty();
|
||||
}),
|
||||
new HTMLHRElement()
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = _isCompacted ? 'collection expanded' : 'collection'
|
||||
..appendChild(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 HTMLDivElement()
|
||||
..className = 'collection-item'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()
|
||||
..className = 'bytes'
|
||||
..textContent = '0B',
|
||||
new HTMLSpanElement()
|
||||
..className = 'bytes'
|
||||
..textContent = '0B',
|
||||
new HTMLSpanElement()
|
||||
..className = 'bytes'
|
||||
..textContent = '0B',
|
||||
new HTMLSpanElement()
|
||||
..className = 'instances'
|
||||
..textContent = '0',
|
||||
new HTMLSpanElement()
|
||||
..className = 'bytes'
|
||||
..textContent = '0B',
|
||||
new HTMLSpanElement()
|
||||
..className = 'bytes'
|
||||
..textContent = '0B',
|
||||
new HTMLSpanElement()
|
||||
..className = 'bytes'
|
||||
..textContent = '0B',
|
||||
new HTMLSpanElement()
|
||||
..className = 'instances'
|
||||
..textContent = '0',
|
||||
new HTMLSpanElement()
|
||||
..className = 'bytes'
|
||||
..textContent = '0B',
|
||||
new HTMLSpanElement()
|
||||
..className = 'bytes'
|
||||
..textContent = '0B',
|
||||
new HTMLSpanElement()
|
||||
..className = 'bytes'
|
||||
..textContent = '0B',
|
||||
new HTMLSpanElement()
|
||||
..className = 'instances'
|
||||
..textContent = '0',
|
||||
new HTMLSpanElement()..className = 'name'
|
||||
]);
|
||||
|
||||
List<HTMLElement> _createCollectionHeader() => [
|
||||
new HTMLDivElement()
|
||||
..className = 'collection-item'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()
|
||||
..className = 'group'
|
||||
..textContent = 'New Generation',
|
||||
new HTMLSpanElement()
|
||||
..className = 'group'
|
||||
..textContent = 'Old Generation',
|
||||
new HTMLSpanElement()
|
||||
..className = 'group'
|
||||
..textContent = 'Total',
|
||||
new HTMLSpanElement()
|
||||
..className = 'group'
|
||||
..textContent = '',
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'collection-item'
|
||||
..appendChildren(<Node>[
|
||||
_createHeaderButton('bytes', 'Internal',
|
||||
_SortingField.newInternalSize, _SortingDirection.descending),
|
||||
_createHeaderButton('bytes', 'External',
|
||||
_SortingField.newExternalSize, _SortingDirection.descending),
|
||||
_createHeaderButton('bytes', 'Size', _SortingField.newSize,
|
||||
_SortingDirection.descending),
|
||||
_createHeaderButton('instances', 'Instances',
|
||||
_SortingField.newInstances, _SortingDirection.descending),
|
||||
_createHeaderButton('bytes', 'Internal',
|
||||
_SortingField.oldInternalSize, _SortingDirection.descending),
|
||||
_createHeaderButton('bytes', 'External',
|
||||
_SortingField.oldExternalSize, _SortingDirection.descending),
|
||||
_createHeaderButton('bytes', 'Size', _SortingField.oldSize,
|
||||
_SortingDirection.descending),
|
||||
_createHeaderButton('instances', 'Instances',
|
||||
_SortingField.oldInstances, _SortingDirection.descending),
|
||||
_createHeaderButton('bytes', 'Internal', _SortingField.internalSize,
|
||||
_SortingDirection.descending),
|
||||
_createHeaderButton('bytes', 'External', _SortingField.externalSize,
|
||||
_SortingDirection.descending),
|
||||
_createHeaderButton('bytes', 'Size', _SortingField.size,
|
||||
_SortingDirection.descending),
|
||||
_createHeaderButton('instances', 'Instances',
|
||||
_SortingField.instances, _SortingDirection.descending),
|
||||
_createHeaderButton('name', 'Class', _SortingField.className,
|
||||
_SortingDirection.ascending)
|
||||
])
|
||||
];
|
||||
|
||||
HTMLButtonElement _createHeaderButton(String className, String text,
|
||||
_SortingField field, _SortingDirection direction) =>
|
||||
new HTMLButtonElement()
|
||||
..className = className
|
||||
..textContent = _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(HTMLElement e, itemDynamic, index) {
|
||||
M.ClassHeapStats item = itemDynamic;
|
||||
e.childNodes.item(0)!.textContent =
|
||||
Utils.formatSize(_getNewInternalSize(item));
|
||||
e.childNodes.item(1)!.textContent =
|
||||
Utils.formatSize(_getNewExternalSize(item));
|
||||
e.childNodes.item(2)!.textContent = Utils.formatSize(_getNewSize(item));
|
||||
e.childNodes.item(3)!.textContent = '${_getNewInstances(item)}';
|
||||
e.childNodes.item(4)!.textContent =
|
||||
Utils.formatSize(_getOldInternalSize(item));
|
||||
e.childNodes.item(5)!.textContent =
|
||||
Utils.formatSize(_getOldExternalSize(item));
|
||||
e.childNodes.item(6)!.textContent = Utils.formatSize(_getOldSize(item));
|
||||
e.childNodes.item(7)!.textContent = '${_getOldInstances(item)}';
|
||||
e.childNodes.item(8)!.textContent =
|
||||
Utils.formatSize(_getInternalSize(item));
|
||||
e.childNodes.item(9)!.textContent =
|
||||
Utils.formatSize(_getExternalSize(item));
|
||||
e.childNodes.item(10)!.textContent = Utils.formatSize(_getSize(item));
|
||||
e.childNodes.item(11)!.textContent = '${_getInstances(item)}';
|
||||
final old = e.childNodes.item(12)!;
|
||||
e.insertBefore(
|
||||
old,
|
||||
e.appendChild(
|
||||
(new ClassRefElement(_isolate, item.clazz!, queue: _r.queue).element
|
||||
..className = 'name')));
|
||||
e.removeChild(old);
|
||||
}
|
||||
|
||||
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<HTMLElement> _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';
|
||||
return [
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'used',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = used
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'external',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = ext
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'collections',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = collections
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'average collection time',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = 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';
|
||||
HTMLAnchorElement tl = document.createElement('a') as HTMLAnchorElement;
|
||||
tl
|
||||
..attributes.setNamedItem(document.createAttribute('href')
|
||||
..value = 'data:text/plain;charset=utf-8,' +
|
||||
Uri.encodeComponent(header +
|
||||
(_profile!.members.toList()..sort(_createSorter()))
|
||||
.map(_csvOut)
|
||||
.join('\n')))
|
||||
..attributes.setNamedItem(
|
||||
document.createAttribute('download')..value = '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,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:async';
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/cpu_profile/virtual_tree.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/sample_buffer_control.dart';
|
||||
import 'package:observatory/src/elements/stack_trace_tree_config.dart';
|
||||
|
||||
class ClassAllocationProfileElement extends CustomElement
|
||||
implements Renderable {
|
||||
late RenderingScheduler<ClassAllocationProfileElement> _r;
|
||||
|
||||
Stream<RenderedEvent<ClassAllocationProfileElement>> get onRendered =>
|
||||
_r.onRendered;
|
||||
|
||||
late M.VM _vm;
|
||||
late M.IsolateRef _isolate;
|
||||
late M.Class _cls;
|
||||
late M.ClassSampleProfileRepository _profiles;
|
||||
late Stream<M.SampleProfileLoadingProgressEvent> _progressStream;
|
||||
M.SampleProfileLoadingProgress? _progress;
|
||||
late M.SampleProfileTag _tag = M.SampleProfileTag.none;
|
||||
late ProfileTreeMode _mode = ProfileTreeMode.function;
|
||||
late 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}) {
|
||||
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();
|
||||
removeChildren();
|
||||
_r.disable(notify: true);
|
||||
}
|
||||
|
||||
void render() {
|
||||
if (_progress == null) {
|
||||
removeChildren();
|
||||
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) {
|
||||
late CpuProfileVirtualTreeElement tree;
|
||||
content.addAll([
|
||||
new HTMLBRElement(),
|
||||
(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 HTMLBRElement(),
|
||||
(tree = new CpuProfileVirtualTreeElement(_isolate, _progress!.profile,
|
||||
queue: _r.queue))
|
||||
.element
|
||||
]);
|
||||
}
|
||||
removeChildren();
|
||||
appendChildren(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,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.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/helpers/any_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/sentinel_value.dart';
|
||||
import 'package:observatory/src/elements/strongly_reachable_instances.dart';
|
||||
import 'package:observatory/utils.dart';
|
||||
|
||||
class ClassInstancesElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<ClassInstancesElement> _r;
|
||||
|
||||
Stream<RenderedEvent<ClassInstancesElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.IsolateRef _isolate;
|
||||
late M.Class _cls;
|
||||
late M.RetainedSizeRepository _retainedSizes;
|
||||
late M.ReachableSizeRepository _reachableSizes;
|
||||
late M.StronglyReachableInstancesRepository _stronglyReachableInstances;
|
||||
late 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}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
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);
|
||||
removeChildren();
|
||||
appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'currently allocated',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = 'count ${instanceCount} (shallow size ${size})'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'strongly reachable ',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChild(_strong!.element)
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'all direct instances'
|
||||
..title = 'All instances whose class is exactly this class',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(_createAllInstances())
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'all instances of subclasses'
|
||||
..title =
|
||||
'All instances whose class is a subclass of this class',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(_createAllSubclassInstances())
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'all instances of implementors'
|
||||
..title =
|
||||
'All instances whose class implements the implicit interface of this class',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(_createAllImplementorInstances())
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..title = 'Space reachable from this object, '
|
||||
'excluding class references'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'Reachable size ',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(_createReachableSizeValue())
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..title = 'Space that would be reclaimed if references to this '
|
||||
'object were replaced with null'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'Retained size ',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(_createRetainedSizeValue())
|
||||
]),
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
List<HTMLElement> _createAllInstances() {
|
||||
final content = <HTMLElement>[];
|
||||
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 HTMLSpanElement()..textContent = '...');
|
||||
}
|
||||
final button = new HTMLButtonElement()
|
||||
..className = 'reachable_size'
|
||||
..disabled = _loadingAllInstances
|
||||
..textContent = '↺';
|
||||
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<HTMLElement> _createAllSubclassInstances() {
|
||||
final content = <HTMLElement>[];
|
||||
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 HTMLSpanElement()..textContent = '...');
|
||||
}
|
||||
final button = new HTMLButtonElement()
|
||||
..className = 'reachable_size'
|
||||
..disabled = _loadingAllSubclassInstances
|
||||
..textContent = '↺';
|
||||
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<HTMLElement> _createAllImplementorInstances() {
|
||||
final content = <HTMLElement>[];
|
||||
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 HTMLSpanElement()..textContent = '...');
|
||||
}
|
||||
final button = new HTMLButtonElement()
|
||||
..className = 'reachable_size'
|
||||
..disabled = _loadingAllImplementorInstances
|
||||
..textContent = '↺';
|
||||
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<HTMLElement> _createReachableSizeValue() {
|
||||
final content = <HTMLElement>[];
|
||||
if (_reachableSize != null) {
|
||||
if (_reachableSize!.isSentinel) {
|
||||
content.add(new SentinelValueElement(_reachableSize!.asSentinel!,
|
||||
queue: _r.queue)
|
||||
.element);
|
||||
} else {
|
||||
content.add(new HTMLSpanElement()
|
||||
..textContent = Utils.formatSize(
|
||||
int.parse(_reachableSize!.asValue!.valueAsString!)));
|
||||
}
|
||||
} else {
|
||||
content.add(new HTMLSpanElement()..textContent = '...');
|
||||
}
|
||||
final button = new HTMLButtonElement()
|
||||
..className = 'reachable_size'
|
||||
..disabled = _loadingReachableBytes
|
||||
..textContent = '↺';
|
||||
button.onClick.listen((_) async {
|
||||
button.disabled = true;
|
||||
_loadingReachableBytes = true;
|
||||
_reachableSize = await _reachableSizes.get(_isolate, _cls.id!);
|
||||
_loadingReachableBytes = false;
|
||||
_r.dirty();
|
||||
});
|
||||
content.add(button);
|
||||
return content;
|
||||
}
|
||||
|
||||
List<HTMLElement> _createRetainedSizeValue() {
|
||||
final content = <HTMLElement>[];
|
||||
if (_retainedSize != null) {
|
||||
if (_retainedSize!.isSentinel) {
|
||||
content.add(new SentinelValueElement(_retainedSize!.asSentinel!,
|
||||
queue: _r.queue)
|
||||
.element);
|
||||
} else {
|
||||
content.add(new HTMLSpanElement()
|
||||
..textContent = Utils.formatSize(
|
||||
int.parse(_retainedSize!.asValue!.valueAsString!)));
|
||||
}
|
||||
} else {
|
||||
content.add(new HTMLSpanElement()..textContent = '...');
|
||||
}
|
||||
final button = new HTMLButtonElement()
|
||||
..className = 'retained_size'
|
||||
..disabled = _loadingRetainedBytes
|
||||
..textContent = '↺';
|
||||
button.onClick.listen((_) async {
|
||||
button.disabled = true;
|
||||
_loadingRetainedBytes = true;
|
||||
_retainedSize = await _retainedSizes.get(_isolate, _cls.id!);
|
||||
_loadingRetainedBytes = false;
|
||||
_r.dirty();
|
||||
});
|
||||
content.add(button);
|
||||
return content;
|
||||
}
|
||||
}
|
||||
@@ -1,55 +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 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
|
||||
class ClassRefElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<ClassRefElement> _r;
|
||||
|
||||
Stream<RenderedEvent<ClassRefElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.IsolateRef _isolate;
|
||||
late M.ClassRef _class;
|
||||
|
||||
M.IsolateRef get isolate => _isolate;
|
||||
M.ClassRef get cls => _class;
|
||||
|
||||
factory ClassRefElement(M.IsolateRef isolate, M.ClassRef cls,
|
||||
{RenderingQueue? queue}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
void render() {
|
||||
removeChildren();
|
||||
appendChild(new HTMLAnchorElement()
|
||||
..href = Uris.inspect(_isolate, object: _class)
|
||||
..text = _class.name ?? '');
|
||||
}
|
||||
}
|
||||
@@ -1,191 +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:async';
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/class_ref.dart';
|
||||
import 'package:observatory/src/elements/containers/virtual_tree.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_menu.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/nav/isolate_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/vm_menu.dart';
|
||||
|
||||
class ClassTreeElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<ClassTreeElement> _r;
|
||||
|
||||
Stream<RenderedEvent<ClassTreeElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.VM _vm;
|
||||
late M.IsolateRef _isolate;
|
||||
late M.EventRepository _events;
|
||||
late M.NotificationRepository _notifications;
|
||||
late M.ClassRepository _classes;
|
||||
M.Class? _object;
|
||||
final _subclasses = <String, Iterable<M.Class>>{};
|
||||
final _mixins = <String, List<M.Instance>?>{};
|
||||
|
||||
factory ClassTreeElement(
|
||||
M.VM vm,
|
||||
M.IsolateRef isolate,
|
||||
M.EventRepository events,
|
||||
M.NotificationRepository notifications,
|
||||
M.ClassRepository classes,
|
||||
{RenderingQueue? queue}) {
|
||||
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 = <HTMLElement>[
|
||||
navBar(<HTMLElement>[
|
||||
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 HTMLDivElement()
|
||||
..className = 'content-centered'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLHeadingElement.h1()
|
||||
..textContent = 'Class Hierarchy (${_subclasses.length})',
|
||||
new HTMLBRElement(),
|
||||
new HTMLHRElement(),
|
||||
_object == null
|
||||
? (new HTMLHeadingElement.h2()..textContent = 'Loading...')
|
||||
: _createTree()
|
||||
])
|
||||
];
|
||||
}
|
||||
|
||||
HTMLElement _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 as M.Instance);
|
||||
});
|
||||
}
|
||||
|
||||
static HTMLElement _create(toggle) {
|
||||
return new HTMLDivElement()
|
||||
..className = 'class-tree-item'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..className = 'lines',
|
||||
new HTMLButtonElement()
|
||||
..className = 'expander'
|
||||
..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
|
||||
new HTMLSpanElement()..className = 'name'
|
||||
]);
|
||||
}
|
||||
|
||||
void _update(HTMLElement el, classDynamic, int index) {
|
||||
M.Class cls = classDynamic;
|
||||
virtualTreeUpdateLines(el.childNodes.item(0) as HTMLSpanElement, index);
|
||||
if (cls.subclasses!.isEmpty) {
|
||||
(el.children.item(1) as HTMLElement).textContent = '';
|
||||
} else {
|
||||
(el.children.item(1) as HTMLElement).textContent =
|
||||
_tree!.isExpanded(cls) ? '▼' : '►';
|
||||
}
|
||||
(el.children.item(2) as HTMLElement)
|
||||
..removeChildren()
|
||||
..appendChild(
|
||||
new ClassRefElement(_isolate, cls, queue: _r.queue).element);
|
||||
if (_mixins[cls.id] != null) {
|
||||
(el.children.item(2) as HTMLElement)
|
||||
.appendChildren(_createMixins(_mixins[cls.id]!));
|
||||
}
|
||||
}
|
||||
|
||||
bool _search(Pattern pattern, classDynamic) {
|
||||
M.Class cls = classDynamic;
|
||||
return cls.name!.contains(pattern);
|
||||
}
|
||||
|
||||
List<HTMLElement> _createMixins(List<M.Instance> types) {
|
||||
final children = types
|
||||
.expand((type) => <HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = ', ',
|
||||
type.typeClass == null
|
||||
? (new HTMLSpanElement()
|
||||
..textContent = type.name!.split('<').first)
|
||||
: new ClassRefElement(_isolate, type.typeClass!,
|
||||
queue: _r.queue)
|
||||
.element
|
||||
])
|
||||
.toList();
|
||||
children.first.textContent = ' with ';
|
||||
return children;
|
||||
}
|
||||
|
||||
Iterable<M.Class> _children(classDynamic) {
|
||||
M.Class cls = classDynamic;
|
||||
return _subclasses[cls.id]!;
|
||||
}
|
||||
}
|
||||
@@ -1,471 +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 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/class_allocation_profile.dart';
|
||||
import 'package:observatory/src/elements/class_instances.dart';
|
||||
import 'package:observatory/src/elements/class_ref.dart';
|
||||
import 'package:observatory/src/elements/curly_block.dart';
|
||||
import 'package:observatory/src/elements/error_ref.dart';
|
||||
import 'package:observatory/src/elements/eval_box.dart';
|
||||
import 'package:observatory/src/elements/field_ref.dart';
|
||||
import 'package:observatory/src/elements/function_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/any_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/instance_ref.dart';
|
||||
import 'package:observatory/src/elements/library_ref.dart';
|
||||
import 'package:observatory/src/elements/nav/class_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/isolate_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/refresh.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/vm_menu.dart';
|
||||
import 'package:observatory/src/elements/object_common.dart';
|
||||
import 'package:observatory/src/elements/source_inset.dart';
|
||||
import 'package:observatory/src/elements/source_link.dart';
|
||||
|
||||
class ClassViewElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<ClassViewElement> _r;
|
||||
|
||||
Stream<RenderedEvent<ClassViewElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.VM _vm;
|
||||
late M.IsolateRef _isolate;
|
||||
late M.EventRepository _events;
|
||||
late M.NotificationRepository _notifications;
|
||||
late M.Class _cls;
|
||||
late M.ClassRepository _classes;
|
||||
late M.RetainedSizeRepository _retainedSizes;
|
||||
late M.ReachableSizeRepository _reachableSizes;
|
||||
late M.InboundReferencesRepository _references;
|
||||
late M.RetainingPathRepository _retainingPaths;
|
||||
late M.StronglyReachableInstancesRepository _stronglyReachableInstances;
|
||||
late M.FieldRepository _fields;
|
||||
late M.ScriptRepository _scripts;
|
||||
late M.ObjectRepository _objects;
|
||||
late M.EvalRepository _eval;
|
||||
late 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}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
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 ';
|
||||
}
|
||||
setChildren(<HTMLElement>[
|
||||
navBar(<HTMLElement>[
|
||||
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 HTMLDivElement()
|
||||
..className = 'content-centered-big'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLHeadingElement.h2()
|
||||
..textContent = '$header class ${_cls.name}',
|
||||
new HTMLHRElement(),
|
||||
_common!.element,
|
||||
new HTMLBRElement(),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(_createMembers()),
|
||||
new HTMLDivElement()
|
||||
..appendChildren(_cls.error == null
|
||||
? const []
|
||||
: [
|
||||
new HTMLHRElement(),
|
||||
new ErrorRefElement(_cls.error!, queue: _r.queue).element
|
||||
]),
|
||||
new HTMLHRElement(),
|
||||
new EvalBoxElement(_isolate, _cls, _objects, _eval, queue: _r.queue)
|
||||
.element,
|
||||
new HTMLHRElement(),
|
||||
new HTMLHeadingElement.h2()..textContent = 'Fields & Functions',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(_createElements()),
|
||||
new HTMLHRElement(),
|
||||
new HTMLHeadingElement.h2()..textContent = 'Instances',
|
||||
new HTMLDivElement()..appendChild(_classInstances!.element),
|
||||
new HTMLHRElement(),
|
||||
new HTMLHeadingElement.h2()..textContent = 'Allocations',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'Tracing allocations? ',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(_cls.traceAllocations!
|
||||
? [
|
||||
new HTMLSpanElement()..textContent = 'Yes ',
|
||||
new HTMLButtonElement()
|
||||
..textContent = 'disable'
|
||||
..onClick.listen((e) async {
|
||||
(e.target as HTMLButtonElement).disabled = true;
|
||||
await _profiles.disable(_isolate, _cls);
|
||||
_loadProfile = true;
|
||||
_refresh();
|
||||
})
|
||||
]
|
||||
: [
|
||||
new HTMLSpanElement()..textContent = 'No ',
|
||||
new HTMLButtonElement()
|
||||
..textContent = 'enable'
|
||||
..onClick.listen((e) async {
|
||||
(e.target as HTMLButtonElement).disabled = true;
|
||||
await _profiles.enable(_isolate, _cls);
|
||||
_refresh();
|
||||
})
|
||||
])
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..appendChildren(_loadProfile
|
||||
? [
|
||||
new ClassAllocationProfileElement(
|
||||
_vm, _isolate, _cls, _profiles,
|
||||
queue: _r.queue)
|
||||
.element
|
||||
]
|
||||
: const []),
|
||||
new HTMLDivElement()
|
||||
..appendChildren(_cls.location != null
|
||||
? [
|
||||
new HTMLHRElement(),
|
||||
new SourceInsetElement(_isolate, _cls.location!, _scripts,
|
||||
_objects, _events,
|
||||
queue: _r.queue)
|
||||
.element
|
||||
]
|
||||
: const []),
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
bool? _fieldsExpanded;
|
||||
bool? _functionsExpanded;
|
||||
|
||||
List<HTMLElement> _createMembers() {
|
||||
final members = <HTMLElement>[];
|
||||
if (_cls.library != null) {
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'library',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new LibraryRefElement(_isolate, _cls.library!, queue: _r.queue)
|
||||
.element
|
||||
])
|
||||
]));
|
||||
}
|
||||
if (_cls.location != null) {
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'script',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new SourceLinkElement(_isolate, _cls.location!, _scripts,
|
||||
queue: _r.queue)
|
||||
.element
|
||||
])
|
||||
]));
|
||||
}
|
||||
if (_cls.superclass != null) {
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'superclass',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new ClassRefElement(_isolate, _cls.superclass!, queue: _r.queue)
|
||||
.element
|
||||
])
|
||||
]));
|
||||
}
|
||||
if (_cls.superType != null) {
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'supertype',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new InstanceRefElement(_isolate, _cls.superType!, _objects,
|
||||
queue: _r.queue)
|
||||
.element
|
||||
])
|
||||
]));
|
||||
}
|
||||
if (cls.mixin != null) {
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'mixin',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new InstanceRefElement(_isolate, _cls.mixin!, _objects,
|
||||
queue: _r.queue)
|
||||
.element
|
||||
])
|
||||
]));
|
||||
}
|
||||
if (_cls.subclasses!.length > 0) {
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'extended by',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren((_cls.subclasses!.expand((subcls) => <HTMLElement>[
|
||||
new ClassRefElement(_isolate, subcls, queue: _r.queue)
|
||||
.element,
|
||||
new HTMLSpanElement()..textContent = ', '
|
||||
])).toList()
|
||||
..removeLast())
|
||||
]));
|
||||
}
|
||||
|
||||
members.add(new HTMLBRElement());
|
||||
|
||||
if (_cls.interfaces!.length > 0) {
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'implements',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren((_cls.interfaces!.expand((interf) => <HTMLElement>[
|
||||
new InstanceRefElement(_isolate, interf, _objects,
|
||||
queue: _r.queue)
|
||||
.element,
|
||||
new HTMLSpanElement()..textContent = ', '
|
||||
])).toList()
|
||||
..removeLast())
|
||||
]));
|
||||
}
|
||||
if (_cls.name != _cls.vmName) {
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'vm name',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = '${_cls.vmName}'
|
||||
]));
|
||||
}
|
||||
return members;
|
||||
}
|
||||
|
||||
List<HTMLElement> _createElements() {
|
||||
final members = <HTMLElement>[];
|
||||
if (_classFields != null && _classFields!.isNotEmpty) {
|
||||
final fields = _classFields!.toList();
|
||||
_fieldsExpanded = _fieldsExpanded ?? (fields.length <= 8);
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'fields ${fields.length}',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(<HTMLElement>[
|
||||
(new CurlyBlockElement(expanded: _fieldsExpanded!)
|
||||
..onToggle
|
||||
.listen((e) => _fieldsExpanded = e.control.expanded)
|
||||
..content = <HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(
|
||||
fields.map<HTMLElement>((f) => new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new FieldRefElement(_isolate, f, _objects,
|
||||
queue: _r.queue)
|
||||
.element
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(f.staticValue == null
|
||||
? const []
|
||||
: [
|
||||
anyRef(
|
||||
_isolate, f.staticValue, _objects,
|
||||
queue: _r.queue)
|
||||
])
|
||||
])))
|
||||
])
|
||||
.element
|
||||
])
|
||||
]));
|
||||
}
|
||||
|
||||
if (_cls.functions!.isNotEmpty) {
|
||||
final functions = _cls.functions!.toList();
|
||||
_functionsExpanded = _functionsExpanded ?? (functions.length <= 8);
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'functions (${functions.length})',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChild((new CurlyBlockElement(expanded: _functionsExpanded!)
|
||||
..onToggle
|
||||
.listen((e) => _functionsExpanded = e.control.expanded)
|
||||
..content = (functions.map<HTMLElement>((f) =>
|
||||
new HTMLDivElement()
|
||||
..className = 'indent'
|
||||
..appendChild(
|
||||
new FunctionRefElement(_isolate, f, queue: _r.queue)
|
||||
.element))))
|
||||
.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,62 +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:async';
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M
|
||||
show IsolateRef, CodeRef, isSyntheticCode;
|
||||
|
||||
class CodeRefElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<CodeRefElement> _r;
|
||||
|
||||
Stream<RenderedEvent<CodeRefElement>> get onRendered => _r.onRendered;
|
||||
|
||||
M.IsolateRef? _isolate;
|
||||
late M.CodeRef _code;
|
||||
|
||||
M.IsolateRef get isolate => _isolate!;
|
||||
M.CodeRef get code => _code;
|
||||
|
||||
factory CodeRefElement(M.IsolateRef? isolate, M.CodeRef code,
|
||||
{RenderingQueue? queue}) {
|
||||
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();
|
||||
removeChildren();
|
||||
_r.disable(notify: true);
|
||||
}
|
||||
|
||||
void render() {
|
||||
setChildren(<HTMLElement>[
|
||||
new HTMLAnchorElement()
|
||||
..href = ((M.isSyntheticCode(_code.kind)) || (_isolate == null))
|
||||
? ''
|
||||
: Uris.inspect(_isolate!, object: _code)
|
||||
..textContent = _code.name ?? ''
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,607 +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 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/sample_profile.dart';
|
||||
import 'package:observatory/service.dart' as S;
|
||||
import 'package:observatory/src/elements/curly_block.dart';
|
||||
import 'package:observatory/src/elements/function_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/any_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_menu.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/nav/isolate_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/refresh.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/vm_menu.dart';
|
||||
import 'package:observatory/src/elements/object_common.dart';
|
||||
import 'package:observatory/src/elements/objectpool_ref.dart';
|
||||
import 'package:observatory/utils.dart';
|
||||
|
||||
import 'package:observatory/app.dart'
|
||||
show SortedTable, SortedTableColumn, SortedTableRow;
|
||||
|
||||
class DisassemblyTable extends SortedTable {
|
||||
DisassemblyTable(columns) : super(columns);
|
||||
}
|
||||
|
||||
class InlineTable extends SortedTable {
|
||||
InlineTable(columns) : super(columns);
|
||||
}
|
||||
|
||||
class CodeViewElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<CodeViewElement> _r;
|
||||
|
||||
Stream<RenderedEvent<CodeViewElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.VM _vm;
|
||||
late M.IsolateRef _isolate;
|
||||
late M.EventRepository _events;
|
||||
late M.NotificationRepository _notifications;
|
||||
late M.Code _code;
|
||||
late M.RetainedSizeRepository _retainedSizes;
|
||||
late M.ReachableSizeRepository _reachableSizes;
|
||||
late M.InboundReferencesRepository _references;
|
||||
late M.RetainingPathRepository _retainingPaths;
|
||||
late M.ObjectRepository _objects;
|
||||
late DisassemblyTable disassemblyTable;
|
||||
late 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}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
HTMLTableElement? _disassemblyTable;
|
||||
HTMLTableElement? _inlineRangeTable;
|
||||
HTMLTableSectionElement? _disassemblyTableBody;
|
||||
HTMLTableSectionElement? _inlineRangeTableBody;
|
||||
|
||||
void render() {
|
||||
if (_inlineRangeTable == null) {
|
||||
_inlineRangeTable = new HTMLTableElement()..className = 'table';
|
||||
_inlineRangeTable!.createTHead().insertRow().appendChildren(<HTMLElement>[
|
||||
new HTMLTableCellElement.th()
|
||||
..className = 'address'
|
||||
..textContent = 'Address Range',
|
||||
new HTMLTableCellElement.th()
|
||||
..className = 'tick'
|
||||
..textContent = 'Inclusive',
|
||||
new HTMLTableCellElement.th()
|
||||
..className = 'tick'
|
||||
..textContent = 'Exclusive',
|
||||
new HTMLTableCellElement.th()..textContent = 'Functions',
|
||||
]);
|
||||
_inlineRangeTableBody = _inlineRangeTable!.createTBody()
|
||||
..className = 'monospace';
|
||||
}
|
||||
if (_disassemblyTable == null) {
|
||||
_disassemblyTable = new HTMLTableElement()..className = 'table';
|
||||
_disassemblyTable!.createTHead().insertRow().appendChildren(<HTMLElement>[
|
||||
new HTMLTableCellElement.th()
|
||||
..className = 'address'
|
||||
..textContent = 'Address Range',
|
||||
new HTMLTableCellElement.th()
|
||||
..className = 'tick'
|
||||
..title = 'Ticks with PC on the stack'
|
||||
..textContent = 'Inclusive',
|
||||
new HTMLTableCellElement.th()
|
||||
..className = 'tick'
|
||||
..title = 'Ticks with PC at top of stack'
|
||||
..textContent = 'Exclusive',
|
||||
new HTMLTableCellElement.th()
|
||||
..className = 'disassembly'
|
||||
..textContent = 'Disassembly',
|
||||
new HTMLTableCellElement.th()
|
||||
..className = 'object'
|
||||
..textContent = 'Object',
|
||||
]);
|
||||
_disassemblyTableBody = _disassemblyTable!.createTBody()
|
||||
..className = 'monospace';
|
||||
}
|
||||
final inlinedFunctions = _code.inlinedFunctions!.toList();
|
||||
final S.Code code = _code as S.Code;
|
||||
setChildren(<HTMLElement>[
|
||||
navBar(<HTMLElement>[
|
||||
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 HTMLDivElement()
|
||||
..className = 'content-centered-big'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLHeadingElement.h1()
|
||||
..textContent = (M.isDartCode(_code.kind) && _code.isOptimized!)
|
||||
? 'Optimized code for ${_code.name}'
|
||||
: 'Code for ${_code.name}',
|
||||
new HTMLHRElement(),
|
||||
new ObjectCommonElement(_isolate, _code, _retainedSizes,
|
||||
_reachableSizes, _references, _retainingPaths, _objects,
|
||||
queue: _r.queue)
|
||||
.element,
|
||||
new HTMLBRElement(),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'Kind',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = _codeKindToString(_code.kind)
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(M.isDartCode(_code.kind)
|
||||
? const []
|
||||
: [
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'Optimized',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = _code.isOptimized! ? 'Yes' : 'No'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'Function',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChild(new FunctionRefElement(
|
||||
_isolate, _code.function!,
|
||||
queue: _r.queue)
|
||||
.element)
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(code.profile == null
|
||||
? const []
|
||||
: [
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'Inclusive',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent =
|
||||
'${code.profile!.formattedInclusiveTicks}'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(code.profile == null
|
||||
? const []
|
||||
: [
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'Exclusive',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent =
|
||||
'${code.profile!.formattedExclusiveTicks}'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'Object pool',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new ObjectPoolRefElement(_isolate, _code.objectPool!,
|
||||
queue: _r.queue)
|
||||
.element
|
||||
])
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(inlinedFunctions.isNotEmpty
|
||||
? const []
|
||||
: [
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent =
|
||||
'inlined functions (${inlinedFunctions.length})',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(<HTMLElement>[
|
||||
(new CurlyBlockElement(
|
||||
expanded: inlinedFunctions.length < 8,
|
||||
queue: _r.queue)
|
||||
..content = inlinedFunctions
|
||||
.map<HTMLElement>((f) =>
|
||||
new FunctionRefElement(_isolate, f,
|
||||
queue: _r.queue)
|
||||
.element)
|
||||
.toList())
|
||||
.element
|
||||
])
|
||||
]),
|
||||
new HTMLHRElement(),
|
||||
_inlineRangeTable!,
|
||||
new HTMLHRElement(),
|
||||
_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!;
|
||||
var response = await isolate.invokeRpc('getCpuSamples', {'_code': true});
|
||||
final cpuProfile = new SampleProfile();
|
||||
await cpuProfile.load(isolate, response as S.ServiceMap);
|
||||
_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;
|
||||
var profile = code.profile;
|
||||
if (profile == null) {
|
||||
return '';
|
||||
}
|
||||
var tick = 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, profile.profile.sampleCount);
|
||||
return '$pcent (${tick.inclusiveTicks})';
|
||||
}
|
||||
|
||||
String _formattedExclusive(S.CodeInstruction instruction) {
|
||||
S.Code code = _code as S.Code;
|
||||
var profile = code.profile;
|
||||
if (profile == null) {
|
||||
return '';
|
||||
}
|
||||
var tick = profile.addressTicks[instruction.address];
|
||||
if (tick == null) {
|
||||
return '';
|
||||
}
|
||||
var pcent =
|
||||
Utils.formatPercent(tick.exclusiveTicks, profile.profile.sampleCount);
|
||||
return '$pcent (${tick.exclusiveTicks})';
|
||||
}
|
||||
|
||||
void _updateDisassemblyTable() {
|
||||
S.Code code = _code as S.Code;
|
||||
disassemblyTable.clearRows();
|
||||
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() {
|
||||
// Add new space.
|
||||
_disassemblyTableBody!.insertRow()
|
||||
..insertCell(-1).className = 'monospace'
|
||||
..insertCell(-1).className = 'monospace'
|
||||
..insertCell(-1).className = 'monospace'
|
||||
..insertCell(-1).className = 'monospace'
|
||||
..insertCell(-1).className = 'monospace';
|
||||
}
|
||||
|
||||
void _fillDisassemblyDOMRow(HTMLTableRowElement tr, int rowIndex) {
|
||||
final row = disassemblyTable.rows[rowIndex];
|
||||
final n = row.values.length;
|
||||
for (var i = 0; i < n; i++) {
|
||||
HTMLTableCellElement cell = tr.cells.item(i) as HTMLTableCellElement;
|
||||
final content = row.values[i];
|
||||
if (content is S.HeapObject) {
|
||||
cell.innerHTML = 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.colSpan = 2;
|
||||
cell.className = 'code-comment';
|
||||
text = text.substring(6);
|
||||
} else {
|
||||
cell.colSpan = 1;
|
||||
cell.className = '';
|
||||
}
|
||||
}
|
||||
cell.textContent = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _updateDisassemblyDOMTable() {
|
||||
var tableBody = _disassemblyTableBody!;
|
||||
// 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.deleteRow(tableBody.children.length);
|
||||
}
|
||||
} 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.
|
||||
for (int i = 0; i < tableBody.rows.length; i++) {
|
||||
final tr = tableBody.rows.item(i);
|
||||
final rowIndex = disassemblyTable.sortedRows[i];
|
||||
_fillDisassemblyDOMRow(tr as HTMLTableRowElement, rowIndex);
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
_inlineRangeTableBody!.insertRow()
|
||||
..insertCell(-1)
|
||||
..className = 'monospace'
|
||||
..insertCell(-1)
|
||||
..className = 'monospace'
|
||||
..insertCell(-1)
|
||||
..className = 'monospace'
|
||||
..insertCell(-1)
|
||||
..className = 'monospace';
|
||||
}
|
||||
|
||||
void _fillInlineDOMRow(HTMLTableRowElement tr, int rowIndex) {
|
||||
final row = inlineTable.rows[rowIndex];
|
||||
final columns = row.values.length;
|
||||
final addressRangeColumn = 0;
|
||||
final functionsColumn = columns - 1;
|
||||
|
||||
{
|
||||
final addressRangeCell = tr.cells.item(addressRangeColumn)!;
|
||||
final interval = row.values[addressRangeColumn];
|
||||
final addressRangeString = _formattedAddressRange(interval);
|
||||
final addressRangeElement = new HTMLSpanElement();
|
||||
addressRangeElement.className = 'monospace';
|
||||
addressRangeElement.textContent = addressRangeString;
|
||||
addressRangeCell.removeChildren();
|
||||
addressRangeCell.appendChild(addressRangeElement);
|
||||
}
|
||||
|
||||
for (var i = addressRangeColumn + 1; i < columns - 1; i++) {
|
||||
final cell = tr.cells.item(i) as HTMLTableCellElement;
|
||||
cell.textContent = row.values[i].toString();
|
||||
}
|
||||
final functions = row.values[functionsColumn];
|
||||
final functionsCell = tr.cells.item(functionsColumn)!;
|
||||
functionsCell.removeChildren();
|
||||
for (var func in functions) {
|
||||
functionsCell
|
||||
..appendChild(
|
||||
new FunctionRefElement(_isolate, func, queue: _r.queue).element)
|
||||
..appendChild(HTMLSpanElement()
|
||||
..style.minWidth = '1em'
|
||||
..textContent = ' ');
|
||||
}
|
||||
}
|
||||
|
||||
void _updateInlineDOMTable() {
|
||||
final 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
|
||||
.removeChild(tableBody.childNodes.item(tableBody.children.length)!);
|
||||
}
|
||||
} 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++) {
|
||||
final rowIndex = inlineTable.sortedRows[i];
|
||||
final tr = tableBody.rows.item(i);
|
||||
_fillInlineDOMRow(tr as HTMLTableRowElement, 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';
|
||||
default:
|
||||
throw new Exception('Unknown CodeKind ($kind)');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,208 +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:math' as math;
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.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 {
|
||||
late RenderingScheduler<SearchBarElement> _r;
|
||||
|
||||
StreamController<SearchResultSelected> _onSearchResultSelected =
|
||||
new StreamController<SearchResultSelected>.broadcast();
|
||||
|
||||
Stream<RenderedEvent<SearchBarElement>> get onRendered => _r.onRendered;
|
||||
Stream<SearchResultSelected> get onSearchResultSelected =>
|
||||
_onSearchResultSelected.stream;
|
||||
|
||||
late StreamSubscription _onKeyDownSubscription;
|
||||
|
||||
HTMLElement? _workspace;
|
||||
late SearchBarSearchCallback _search;
|
||||
late 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, HTMLElement? workspace, RenderingQueue? queue}) {
|
||||
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) {
|
||||
_onKeyDownSubscription = 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();
|
||||
}
|
||||
|
||||
HTMLInputElement? _input;
|
||||
HTMLSpanElement? _resultsArea;
|
||||
|
||||
void render() {
|
||||
if (_input == null) {
|
||||
_input = new HTMLInputElement()
|
||||
..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 HTMLSpanElement();
|
||||
children = <HTMLElement>[
|
||||
_input!,
|
||||
_resultsArea!,
|
||||
new HTMLButtonElement()
|
||||
..textContent = '❌'
|
||||
..onClick.listen((_) {
|
||||
isOpen = false;
|
||||
})
|
||||
];
|
||||
}
|
||||
_resultsArea!.appendChildren(<HTMLElement>[
|
||||
new HTMLButtonElement()
|
||||
..textContent = '▲'
|
||||
..disabled = _results.isEmpty
|
||||
..onClick.listen((_) => _prev()),
|
||||
new HTMLSpanElement()
|
||||
..textContent =
|
||||
'${math.min(_current + 1, _results.length)} / ${_results.length}',
|
||||
new HTMLButtonElement()
|
||||
..textContent = '▼'
|
||||
..disabled = _results.isEmpty
|
||||
..onClick.listen((_) => _next())
|
||||
]);
|
||||
style.visibility = isOpen ? '' : '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,267 +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:math' as math;
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/src/elements/containers/search_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.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 {
|
||||
late RenderingScheduler<VirtualCollectionElement> _r;
|
||||
|
||||
Stream<RenderedEvent<VirtualCollectionElement>> get onRendered =>
|
||||
_r.onRendered;
|
||||
|
||||
late VirtualCollectionCreateCallback _create;
|
||||
VirtualCollectionHeaderCallback? _createHeader;
|
||||
late VirtualCollectionUpdateCallback _update;
|
||||
VirtualCollectionSearchCallback? _search;
|
||||
double? _itemHeight;
|
||||
int? _top;
|
||||
double? _height;
|
||||
List? _items;
|
||||
late StreamSubscription _onScrollSubscription;
|
||||
late 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}) {
|
||||
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 = _viewport.onResize.listen(_onResize);
|
||||
}
|
||||
|
||||
@override
|
||||
detached() {
|
||||
super.detached();
|
||||
_r.disable(notify: true);
|
||||
children = const [];
|
||||
_onScrollSubscription.cancel();
|
||||
_onResizeSubscription.cancel();
|
||||
}
|
||||
|
||||
HTMLDivElement? _header;
|
||||
SearchBarElement? _searcher;
|
||||
final HTMLDivElement _viewport = new HTMLDivElement()
|
||||
..className = 'viewport container';
|
||||
final HTMLDivElement _spacer = new HTMLDivElement()..className = 'spacer';
|
||||
final HTMLDivElement _buffer = new HTMLDivElement()..className = '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) {
|
||||
int el_index = _buffer.children.length;
|
||||
while (el_index >= 0 && _buffer.children.item(el_index) != element) {
|
||||
el_index--;
|
||||
}
|
||||
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) {
|
||||
final newChildren = <HTMLElement>[
|
||||
_viewport
|
||||
..appendChild(
|
||||
_spacer..appendChild(_buffer..appendChild(_create())),
|
||||
)
|
||||
];
|
||||
if (_search != null) {
|
||||
_searcher =
|
||||
_searcher ?? new SearchBarElement(_doSearch, queue: _r.queue)
|
||||
..onSearchResultSelected.listen((e) {
|
||||
takeIntoView(e.item);
|
||||
});
|
||||
newChildren.insert(0, _searcher!.element);
|
||||
}
|
||||
if (_createHeader != null) {
|
||||
_header = new HTMLDivElement()
|
||||
..className = 'header container'
|
||||
..appendChildren(_createHeader!());
|
||||
newChildren.insert(0, _header!);
|
||||
final rect = _header!.getBoundingClientRect();
|
||||
_header!.className += 'attached';
|
||||
_viewport.style.top = '${rect.height}px';
|
||||
|
||||
double width = 0.0;
|
||||
for (int i = 0; i < _header!.children.length; i++) {
|
||||
width = math.max(
|
||||
width, _header!.children.item(i)!.getBoundingClientRect().width);
|
||||
}
|
||||
_buffer.style.minWidth = '${width}px';
|
||||
}
|
||||
children = newChildren;
|
||||
|
||||
_itemHeight = (_buffer.firstElementChild! as HTMLElement)
|
||||
.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.appendChild(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 (var j = 0; j < _buffer.children.length; j++) {
|
||||
final e = _buffer.children.item(j) as HTMLElement;
|
||||
if (0 <= i && i < _items!.length) {
|
||||
e.style.display = '';
|
||||
_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 (var j = 0; j < _buffer.children.length; j++) {
|
||||
final e = _buffer.children.item(j) as HTMLElement;
|
||||
if (0 <= i && i < _items!.length) {
|
||||
if (_items![i] == current) {
|
||||
e.className += ' marked';
|
||||
} else {
|
||||
e.className.replaceAll(' marked', '');
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
_updateHeader();
|
||||
}
|
||||
|
||||
void _updateHeader() {
|
||||
if (_header != null) {
|
||||
_header!.style.left = '${-_viewport.scrollLeft}px';
|
||||
final width = _buffer.getBoundingClientRect().width;
|
||||
(_header!.lastChild! as HTMLElement).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,181 +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:collection';
|
||||
import 'dart:math' as Math;
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/src/elements/containers/virtual_collection.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.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(HTMLSpanElement element, int n) {
|
||||
n = Math.max(0, n);
|
||||
while (element.children.length > n) {
|
||||
element.removeChild(element.lastChild!);
|
||||
}
|
||||
while (element.children.length < n) {
|
||||
element.appendChild(HTMLSpanElement());
|
||||
}
|
||||
}
|
||||
|
||||
class VirtualTreeElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<VirtualTreeElement> _r;
|
||||
|
||||
Stream<RenderedEvent<VirtualTreeElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late VirtualTreeGetChildrenCallback _children;
|
||||
late List _items;
|
||||
late 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}) {
|
||||
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 = <HTMLElement>[_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,141 +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 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/curly_block.dart';
|
||||
import 'package:observatory/src/elements/helpers/any_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
|
||||
class ContextRefElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<ContextRefElement> _r;
|
||||
|
||||
Stream<RenderedEvent<ContextRefElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.IsolateRef _isolate;
|
||||
late M.ContextRef _context;
|
||||
late M.ObjectRepository _objects;
|
||||
M.Context? _loadedContext;
|
||||
late 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}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
Future _refresh() async {
|
||||
_loadedContext = await _objects.get(_isolate, _context.id!) as M.Context;
|
||||
_r.dirty();
|
||||
}
|
||||
|
||||
void render() {
|
||||
final children = <HTMLElement>[
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.inspect(_isolate, object: _context)
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()
|
||||
..className = 'emphasize'
|
||||
..textContent = 'Context',
|
||||
new HTMLSpanElement()..textContent = ' (${_context.length})',
|
||||
]),
|
||||
];
|
||||
if (_expandable) {
|
||||
children.addAll([
|
||||
new HTMLSpanElement()..textContent = ' ',
|
||||
(new CurlyBlockElement(expanded: _expanded, queue: _r.queue)
|
||||
..content = <HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'indent'
|
||||
..appendChildren(_createValue())
|
||||
]
|
||||
..onToggle.listen((e) async {
|
||||
_expanded = e.control.expanded;
|
||||
if (_expanded) {
|
||||
e.control.disabled = true;
|
||||
await _refresh();
|
||||
e.control.disabled = false;
|
||||
}
|
||||
}))
|
||||
.element
|
||||
]);
|
||||
}
|
||||
setChildren(children);
|
||||
}
|
||||
|
||||
List<HTMLElement> _createValue() {
|
||||
if (_loadedContext == null) {
|
||||
return [new HTMLSpanElement()..textContent = 'Loading...'];
|
||||
}
|
||||
var members = <HTMLElement>[];
|
||||
if (_loadedContext!.parentContext != null) {
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'parent context',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChild(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 HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = '[ $index ]',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChild(
|
||||
anyRef(_isolate, variable.value, _objects, queue: _r.queue))
|
||||
]));
|
||||
}
|
||||
}
|
||||
return [
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(members)
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,175 +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 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/context_ref.dart';
|
||||
import 'package:observatory/src/elements/curly_block.dart';
|
||||
import 'package:observatory/src/elements/helpers/any_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_menu.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/nav/class_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/isolate_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/refresh.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/vm_menu.dart';
|
||||
import 'package:observatory/src/elements/object_common.dart';
|
||||
|
||||
class ContextViewElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<ContextViewElement> _r;
|
||||
|
||||
Stream<RenderedEvent<ContextViewElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.VM _vm;
|
||||
late M.IsolateRef _isolate;
|
||||
late M.EventRepository _events;
|
||||
late M.NotificationRepository _notifications;
|
||||
late M.Context _context;
|
||||
late M.ContextRepository _contexts;
|
||||
late M.RetainedSizeRepository _retainedSizes;
|
||||
late M.ReachableSizeRepository _reachableSizes;
|
||||
late M.InboundReferencesRepository _references;
|
||||
late M.RetainingPathRepository _retainingPaths;
|
||||
late 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}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
void render() {
|
||||
var content = <HTMLElement>[
|
||||
navBar(<HTMLElement>[
|
||||
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 HTMLDivElement()
|
||||
..className = 'content-centered-big'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLHeadingElement.h2()..textContent = 'Context',
|
||||
new HTMLHRElement(),
|
||||
new ObjectCommonElement(_isolate, _context, _retainedSizes,
|
||||
_reachableSizes, _references, _retainingPaths, _objects,
|
||||
queue: _r.queue)
|
||||
.element
|
||||
])
|
||||
];
|
||||
if (_context.parentContext != null) {
|
||||
content.addAll([
|
||||
new HTMLBRElement(),
|
||||
new HTMLDivElement()
|
||||
..className = 'content-centered-big'
|
||||
..appendChild(new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChild(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'parent context',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChild(new ContextRefElement(
|
||||
_isolate, _context.parentContext!, _objects,
|
||||
queue: _r.queue)
|
||||
.element)
|
||||
])))
|
||||
]);
|
||||
}
|
||||
content.add(new HTMLHRElement());
|
||||
if (_context.variables!.isNotEmpty) {
|
||||
int index = 0;
|
||||
content.addAll([
|
||||
new HTMLDivElement()
|
||||
..className = 'content-centered-big'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'Variables ',
|
||||
(new CurlyBlockElement(expanded: true, queue: _r.queue)
|
||||
..content = <HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..setChildren(_context.variables!
|
||||
.map<HTMLElement>((variable) => new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = '[ ${++index} ]',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChild(anyRef(
|
||||
_isolate, variable.value, _objects,
|
||||
queue: _r.queue))
|
||||
])))
|
||||
])
|
||||
.element
|
||||
])
|
||||
]);
|
||||
}
|
||||
setChildren(content);
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
library cpu_profile_element;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/cpu_profile/virtual_tree.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_menu.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
import 'package:observatory/src/elements/nav/isolate_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/refresh.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/vm_menu.dart';
|
||||
import 'package:observatory/src/elements/sample_buffer_control.dart';
|
||||
import 'package:observatory/src/elements/stack_trace_tree_config.dart';
|
||||
|
||||
class CpuProfileElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<CpuProfileElement> _r;
|
||||
|
||||
Stream<RenderedEvent<CpuProfileElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.VM _vm;
|
||||
late M.IsolateRef _isolate;
|
||||
late M.EventRepository _events;
|
||||
late M.NotificationRepository _notifications;
|
||||
late M.IsolateSampleProfileRepository _profiles;
|
||||
late Stream<M.SampleProfileLoadingProgressEvent> _progressStream;
|
||||
M.SampleProfileLoadingProgress? _progress;
|
||||
late M.SampleProfileTag _tag = M.SampleProfileTag.none;
|
||||
late ProfileTreeMode _mode = ProfileTreeMode.function;
|
||||
late M.ProfileTreeDirection _direction = M.ProfileTreeDirection.exclusive;
|
||||
late 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}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
void render() {
|
||||
var content = <HTMLElement>[
|
||||
navBar(<HTMLElement>[
|
||||
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) {
|
||||
setChildren(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) {
|
||||
late CpuProfileVirtualTreeElement tree;
|
||||
content.addAll([
|
||||
new HTMLBRElement(),
|
||||
(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 HTMLBRElement(),
|
||||
(tree = new CpuProfileVirtualTreeElement(_isolate, _progress!.profile,
|
||||
queue: _r.queue))
|
||||
.element
|
||||
]);
|
||||
}
|
||||
setChildren(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,338 +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:math' as Math;
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/code_ref.dart';
|
||||
import 'package:observatory/src/elements/containers/virtual_tree.dart';
|
||||
import 'package:observatory/src/elements/function_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/utils.dart';
|
||||
|
||||
import 'package:observatory/src/elements/stack_trace_tree_config.dart'
|
||||
show ProfileTreeMode;
|
||||
|
||||
export 'package:observatory/src/elements/stack_trace_tree_config.dart'
|
||||
show ProfileTreeMode;
|
||||
|
||||
class CpuProfileVirtualTreeElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<CpuProfileVirtualTreeElement> _r;
|
||||
|
||||
Stream<RenderedEvent<CpuProfileVirtualTreeElement>> get onRendered =>
|
||||
_r.onRendered;
|
||||
|
||||
late M.ProfileTreeDirection _direction;
|
||||
late ProfileTreeMode _mode;
|
||||
late M.SampleProfileType _type;
|
||||
M.IsolateRef? _isolate;
|
||||
late 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(
|
||||
M.IsolateRef? isolate, M.SampleProfile profile,
|
||||
{ProfileTreeMode mode = ProfileTreeMode.function,
|
||||
M.SampleProfileType type = M.SampleProfileType.cpu,
|
||||
M.ProfileTreeDirection direction = M.ProfileTreeDirection.exclusive,
|
||||
RenderingQueue? queue}) {
|
||||
CpuProfileVirtualTreeElement e = new CpuProfileVirtualTreeElement.created();
|
||||
e._r =
|
||||
new RenderingScheduler<CpuProfileVirtualTreeElement>(e, queue: queue);
|
||||
e._isolate = isolate;
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
if (filters != null) {
|
||||
tree = filters!.fold(tree, (dynamic tree, filter) {
|
||||
return tree?.filtered(filter);
|
||||
});
|
||||
}
|
||||
if (tree == null) {
|
||||
children = <HTMLElement>[
|
||||
new HTMLHeadingElement.h1()..textContent = '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 = <HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'tree-item'
|
||||
..appendChild(HTMLHeadingElement.h1()..textContent = 'No Samples')
|
||||
];
|
||||
return;
|
||||
} else if (tree.root.children.length == 1) {
|
||||
_tree!.expand(tree.root.children.first, autoExpandSingleChildNodes: true);
|
||||
}
|
||||
children = <HTMLElement>[_tree!.element];
|
||||
}
|
||||
|
||||
static HTMLElement _createCpuRow(toggle) {
|
||||
return new HTMLDivElement()
|
||||
..className = 'tree-item'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()
|
||||
..className = 'inclusive'
|
||||
..title = 'global % on stack',
|
||||
new HTMLSpanElement()
|
||||
..className = 'exclusive'
|
||||
..title = 'global % executing',
|
||||
new HTMLSpanElement()..className = 'lines',
|
||||
new HTMLButtonElement()
|
||||
..className = 'expander'
|
||||
..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
|
||||
new HTMLSpanElement()
|
||||
..className = 'percentage'
|
||||
..title = 'tree node %',
|
||||
new HTMLSpanElement()..className = 'name'
|
||||
]);
|
||||
}
|
||||
|
||||
static HTMLElement _createMemoryRow(toggle) {
|
||||
return new HTMLDivElement()
|
||||
..className = 'tree-item'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()
|
||||
..className = 'inclusive'
|
||||
..title = 'memory allocated from resulting calls: ',
|
||||
new HTMLSpanElement()
|
||||
..className = 'exclusive'
|
||||
..title = 'memory allocated during execution: ',
|
||||
new HTMLSpanElement()..className = 'lines',
|
||||
new HTMLButtonElement()
|
||||
..className = 'expander'
|
||||
..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
|
||||
new HTMLSpanElement()
|
||||
..className = 'percentage'
|
||||
..title = 'tree node %',
|
||||
new HTMLSpanElement()..className = '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.item(0) as HTMLElement).textContent =
|
||||
Utils.formatPercentNormalized(
|
||||
item.profileFunction.normalizedInclusiveTicks);
|
||||
(element.children.item(1) as HTMLElement).textContent =
|
||||
Utils.formatPercentNormalized(
|
||||
item.profileFunction.normalizedExclusiveTicks);
|
||||
_updateLines(element.children.item(2) as HTMLElement, depth);
|
||||
if (item.children.isNotEmpty) {
|
||||
(element.children.item(3) as HTMLElement).textContent =
|
||||
_tree!.isExpanded(item) ? _expandedIcon : _collapsedIcon;
|
||||
} else {
|
||||
(element.children.item(3) as HTMLElement).textContent = '';
|
||||
}
|
||||
(element.children.item(4) as HTMLElement).textContent =
|
||||
Utils.formatPercentNormalized(item.percentage);
|
||||
final old = element.children.item(5)!;
|
||||
element.insertBefore(
|
||||
old,
|
||||
element.appendChild((new FunctionRefElement(
|
||||
_isolate, item.profileFunction.function!,
|
||||
queue: _r.queue)
|
||||
..className = 'name')
|
||||
.element));
|
||||
element.removeChild(old);
|
||||
}
|
||||
|
||||
void _updateMemoryFunctionRow(HTMLElement element, itemDynamic, int depth) {
|
||||
M.FunctionCallTreeNode item = itemDynamic;
|
||||
(element.children.item(0) as HTMLElement).textContent =
|
||||
Utils.formatSize(item.inclusiveNativeAllocations);
|
||||
(element.children.item(0) as HTMLElement).title =
|
||||
'memory allocated from resulting calls: '
|
||||
'${item.inclusiveNativeAllocations}B';
|
||||
(element.children.item(1) as HTMLElement).textContent =
|
||||
Utils.formatSize(item.exclusiveNativeAllocations);
|
||||
(element.children.item(1) as HTMLElement).title =
|
||||
'memory allocated during execution: '
|
||||
'${item.exclusiveNativeAllocations}B';
|
||||
_updateLines(element.children.item(2)!, depth);
|
||||
if (item.children.isNotEmpty) {
|
||||
(element.children.item(3) as HTMLElement).textContent =
|
||||
_tree!.isExpanded(item) ? _expandedIcon : _collapsedIcon;
|
||||
} else {
|
||||
(element.children.item(3) as HTMLElement).textContent = '';
|
||||
}
|
||||
(element.children.item(4) as HTMLElement).textContent =
|
||||
Utils.formatPercentNormalized(item.percentage);
|
||||
final old = element.children.item(5)!;
|
||||
element.insertBefore(
|
||||
old,
|
||||
(new FunctionRefElement(null, item.profileFunction.function!,
|
||||
queue: _r.queue)
|
||||
..className = 'name')
|
||||
.element);
|
||||
element.removeChild(old);
|
||||
}
|
||||
|
||||
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.item(0) as HTMLElement).textContent =
|
||||
Utils.formatPercentNormalized(
|
||||
item.profileCode.normalizedInclusiveTicks);
|
||||
(element.children.item(1) as HTMLElement).textContent =
|
||||
Utils.formatPercentNormalized(
|
||||
item.profileCode.normalizedExclusiveTicks);
|
||||
_updateLines(element.children.item(2)!, depth);
|
||||
if (item.children.isNotEmpty) {
|
||||
(element.children.item(3) as HTMLElement).textContent =
|
||||
_tree!.isExpanded(item) ? _expandedIcon : _collapsedIcon;
|
||||
} else {
|
||||
(element.children.item(3) as HTMLElement).textContent = '';
|
||||
}
|
||||
(element.children.item(4) as HTMLElement).textContent =
|
||||
Utils.formatPercentNormalized(item.percentage);
|
||||
final old = element.children.item(5)!;
|
||||
element.insertBefore(
|
||||
old,
|
||||
element.appendChild((new CodeRefElement(
|
||||
_isolate, item.profileCode.code!,
|
||||
queue: _r.queue)
|
||||
..className = 'name')
|
||||
.element));
|
||||
element.removeChild(old);
|
||||
}
|
||||
|
||||
void _updateMemoryCodeRow(HTMLElement element, itemDynamic, int depth) {
|
||||
M.CodeCallTreeNode item = itemDynamic;
|
||||
(element.children.item(0) as HTMLElement).textContent =
|
||||
Utils.formatSize(item.inclusiveNativeAllocations);
|
||||
(element.children.item(0) as HTMLElement).title =
|
||||
'memory allocated from resulting calls: '
|
||||
'${item.inclusiveNativeAllocations}B';
|
||||
(element.children.item(1) as HTMLElement).textContent =
|
||||
Utils.formatSize(item.exclusiveNativeAllocations);
|
||||
(element.children.item(1) as HTMLElement).title =
|
||||
'memory allocated during execution: '
|
||||
'${item.exclusiveNativeAllocations}B';
|
||||
_updateLines(element.children.item(2)!, depth);
|
||||
if (item.children.isNotEmpty) {
|
||||
(element.children.item(3) as HTMLElement).textContent =
|
||||
_tree!.isExpanded(item) ? _expandedIcon : _collapsedIcon;
|
||||
} else {
|
||||
(element.children.item(3) as HTMLElement).textContent = '';
|
||||
}
|
||||
(element.children.item(4) as HTMLElement).textContent =
|
||||
Utils.formatPercentNormalized(item.percentage);
|
||||
final old = element.children.item(5)!;
|
||||
element.insertBefore(
|
||||
old,
|
||||
element.appendChild(
|
||||
(new CodeRefElement(null, item.profileCode.code!, queue: _r.queue)
|
||||
..className = 'name')
|
||||
.element));
|
||||
element.removeChild(old);
|
||||
}
|
||||
|
||||
bool _searchCode(Pattern pattern, itemDynamic) {
|
||||
M.CodeCallTreeNode item = itemDynamic;
|
||||
return item.profileCode.code!.name!.contains(pattern);
|
||||
}
|
||||
|
||||
static _updateLines(Element element, int n) {
|
||||
n = Math.max(0, n);
|
||||
while (element.children.length > n) {
|
||||
element.removeChild(element.lastChild!);
|
||||
}
|
||||
while (element.children.length < n) {
|
||||
element.appendChild(HTMLSpanElement());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,467 +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 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/containers/virtual_collection.dart';
|
||||
import 'package:observatory/src/elements/cpu_profile/virtual_tree.dart';
|
||||
import 'package:observatory/src/elements/function_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_menu.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/nav/isolate_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/refresh.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/vm_menu.dart';
|
||||
import 'package:observatory/src/elements/sample_buffer_control.dart';
|
||||
import 'package:observatory/src/elements/stack_trace_tree_config.dart';
|
||||
import 'package:observatory/utils.dart';
|
||||
|
||||
enum _Table { functions, caller, callee }
|
||||
|
||||
enum _SortingField { exclusive, inclusive, caller, callee, method }
|
||||
|
||||
enum _SortingDirection { ascending, descending }
|
||||
|
||||
class CpuProfileTableElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<CpuProfileTableElement> _r;
|
||||
|
||||
Stream<RenderedEvent<CpuProfileTableElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.VM _vm;
|
||||
late M.IsolateRef _isolate;
|
||||
late M.EventRepository _events;
|
||||
late M.NotificationRepository _notifications;
|
||||
late M.IsolateSampleProfileRepository _profiles;
|
||||
late 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}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
void render() {
|
||||
var content = <HTMLElement>[
|
||||
navBar(<HTMLElement>[
|
||||
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) {
|
||||
setChildren(content);
|
||||
return;
|
||||
}
|
||||
content.add(new SampleBufferControlElement(_vm, _progress!, _progressStream,
|
||||
showTag: false, queue: _r.queue)
|
||||
.element);
|
||||
if (_progress!.status == M.SampleProfileLoadingStatus.loaded) {
|
||||
content.add(new HTMLBRElement());
|
||||
content.addAll(_createTables());
|
||||
content.add(new HTMLBRElement());
|
||||
content.addAll(_createTree());
|
||||
}
|
||||
setChildren(content);
|
||||
}
|
||||
|
||||
M.ProfileFunction? _selected;
|
||||
VirtualCollectionElement? _functions;
|
||||
VirtualCollectionElement? _callers;
|
||||
VirtualCollectionElement? _callees;
|
||||
|
||||
List<HTMLElement> _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 <HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'profile-trees'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'profile-trees-all'
|
||||
..appendChildren(<HTMLElement>[_functions!.element]),
|
||||
new HTMLDivElement()
|
||||
..className = 'profile-trees-current'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'profile-trees-caller'
|
||||
..appendChildren(<HTMLElement>[_callers!.element]),
|
||||
new HTMLDivElement()
|
||||
..className = 'profile-trees-selected'
|
||||
..setChildren(_selected == null
|
||||
? [
|
||||
new HTMLSpanElement()
|
||||
..textContent = 'No element selected'
|
||||
]
|
||||
: [
|
||||
new FunctionRefElement(_isolate, _selected!.function!,
|
||||
queue: _r.queue)
|
||||
.element
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'profile-trees-callee'
|
||||
..appendChildren(<HTMLElement>[_callees!.element])
|
||||
])
|
||||
])
|
||||
];
|
||||
}
|
||||
|
||||
HTMLElement _createFunction() {
|
||||
final element = new HTMLDivElement()
|
||||
..className = 'function-item'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()
|
||||
..className = 'exclusive'
|
||||
..textContent = '0%',
|
||||
new HTMLSpanElement()
|
||||
..className = 'inclusive'
|
||||
..textContent = '0%',
|
||||
new HTMLSpanElement()..className = 'name'
|
||||
]);
|
||||
element.onClick.listen((e) {
|
||||
if (e.target is HTMLAnchorElement) {
|
||||
return;
|
||||
}
|
||||
_selected = _functions!.getItemFromElement(element);
|
||||
_r.dirty();
|
||||
});
|
||||
return element;
|
||||
}
|
||||
|
||||
void _updateFunction(HTMLElement e, itemDynamic, int index) {
|
||||
M.ProfileFunction item = itemDynamic;
|
||||
if (item == _selected) {
|
||||
e.className = 'function-item selected';
|
||||
} else {
|
||||
e.className = 'function-item';
|
||||
}
|
||||
(e.children.item(0) as HTMLElement).textContent =
|
||||
Utils.formatPercentNormalized(_getExclusiveT(item));
|
||||
(e.children.item(1) as HTMLElement).textContent =
|
||||
Utils.formatPercentNormalized(_getInclusiveT(item));
|
||||
(e.children.item(2) as HTMLElement).textContent =
|
||||
M.getFunctionFullName(item.function!);
|
||||
}
|
||||
|
||||
List<HTMLElement> _createFunctionHeader() => [
|
||||
new HTMLDivElement()
|
||||
..className = 'function-item'
|
||||
..appendChildren(<HTMLElement>[
|
||||
_createHeaderButton('exclusive', 'Execution(%)', _Table.functions,
|
||||
_SortingField.exclusive, _SortingDirection.descending),
|
||||
_createHeaderButton('inclusive', 'Stack(%)', _Table.functions,
|
||||
_SortingField.inclusive, _SortingDirection.descending),
|
||||
_createHeaderButton('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 HTMLDivElement()
|
||||
..className = 'function-item'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()
|
||||
..className = 'inclusive'
|
||||
..textContent = '0%',
|
||||
new HTMLSpanElement()..className = 'name'
|
||||
]);
|
||||
element.onClick.listen((e) {
|
||||
if (e.target is HTMLAnchorElement) {
|
||||
return;
|
||||
}
|
||||
_selected = _callees!.getItemFromElement(element);
|
||||
_r.dirty();
|
||||
});
|
||||
return element;
|
||||
}
|
||||
|
||||
void _updateCallee(HTMLElement e, item, int index) {
|
||||
(e.children.item(0) as HTMLElement).textContent =
|
||||
Utils.formatPercentNormalized(_getCalleeT(item));
|
||||
(e.children.item(1) as HTMLElement).textContent =
|
||||
M.getFunctionFullName(item.function);
|
||||
}
|
||||
|
||||
List<HTMLElement> _createCalleeHeader() => [
|
||||
new HTMLDivElement()
|
||||
..className = 'function-item'
|
||||
..appendChildren(<HTMLElement>[
|
||||
_createHeaderButton('inclusive', 'Callees(%)', _Table.callee,
|
||||
_SortingField.callee, _SortingDirection.descending),
|
||||
_createHeaderButton('name', 'Method', _Table.callee,
|
||||
_SortingField.method, _SortingDirection.ascending),
|
||||
])
|
||||
];
|
||||
|
||||
HTMLElement _createCaller() {
|
||||
final element = new HTMLDivElement()
|
||||
..className = 'function-item'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()
|
||||
..className = 'inclusive'
|
||||
..textContent = '0%',
|
||||
new HTMLSpanElement()..className = 'name'
|
||||
]);
|
||||
element.onClick.listen((e) {
|
||||
if (e.target is HTMLAnchorElement) {
|
||||
return;
|
||||
}
|
||||
_selected = _callers!.getItemFromElement(element);
|
||||
_r.dirty();
|
||||
});
|
||||
return element;
|
||||
}
|
||||
|
||||
void _updateCaller(HTMLElement e, item, int index) {
|
||||
(e.children.item(0) as HTMLElement).textContent =
|
||||
Utils.formatPercentNormalized(_getCallerT(item));
|
||||
(e.children.item(1) as HTMLElement).textContent =
|
||||
M.getFunctionFullName(item.function);
|
||||
}
|
||||
|
||||
List<HTMLElement> _createCallerHeader() => [
|
||||
new HTMLDivElement()
|
||||
..className = 'function-item'
|
||||
..appendChildren(<HTMLElement>[
|
||||
_createHeaderButton('inclusive', 'Callers(%)', _Table.caller,
|
||||
_SortingField.caller, _SortingDirection.descending),
|
||||
_createHeaderButton('name', 'Method', _Table.caller,
|
||||
_SortingField.method, _SortingDirection.ascending),
|
||||
])
|
||||
];
|
||||
|
||||
HTMLButtonElement _createHeaderButton(String className, String text,
|
||||
_Table table, _SortingField field, _SortingDirection direction) =>
|
||||
new HTMLButtonElement()
|
||||
..className = className
|
||||
..textContent = _sortingField[table] != field
|
||||
? text
|
||||
: _sortingDirection[table] == _SortingDirection.ascending
|
||||
? '$text▼'
|
||||
: '$text▲'
|
||||
..onClick.listen((_) => _setSorting(table, field, direction));
|
||||
|
||||
List<HTMLElement> _createTree() {
|
||||
late 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 HTMLBRElement(),
|
||||
(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 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
|
||||
class CurlyBlockToggleEvent {
|
||||
final CurlyBlockElement control;
|
||||
|
||||
CurlyBlockToggleEvent(this.control);
|
||||
}
|
||||
|
||||
class CurlyBlockElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<CurlyBlockElement> _r;
|
||||
|
||||
final StreamController<CurlyBlockToggleEvent> _onToggle =
|
||||
new StreamController<CurlyBlockToggleEvent>.broadcast();
|
||||
Stream<CurlyBlockToggleEvent> get onToggle => _onToggle.stream;
|
||||
Stream<RenderedEvent<CurlyBlockElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late bool _expanded;
|
||||
late bool _disabled;
|
||||
Iterable<HTMLElement> _content = const [];
|
||||
|
||||
bool get expanded => _expanded;
|
||||
bool get disabled => _disabled;
|
||||
Iterable<HTMLElement> 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<HTMLElement> value) {
|
||||
_content = value.toList();
|
||||
_r.dirty();
|
||||
}
|
||||
|
||||
factory CurlyBlockElement(
|
||||
{bool expanded = false, bool disabled = false, RenderingQueue? queue}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
void toggle() {
|
||||
if (disabled) {
|
||||
_r.scheduleNotification();
|
||||
return;
|
||||
}
|
||||
expanded = !expanded;
|
||||
}
|
||||
|
||||
void render() {
|
||||
List<HTMLElement> content = <HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = '{'
|
||||
];
|
||||
HTMLSpanElement label = new HTMLSpanElement()
|
||||
..className = disabled ? 'curly-block disabled' : 'curly-block'
|
||||
..textContent = expanded ? '\xa0\xa0⊟\xa0\xa0' : '\xa0\xa0⊞\xa0\xa0';
|
||||
if (disabled) {
|
||||
content.add(label);
|
||||
} else {
|
||||
content.add(new HTMLAnchorElement()
|
||||
..onClick.listen((_) {
|
||||
toggle();
|
||||
})
|
||||
..appendChild(label));
|
||||
}
|
||||
if (expanded) {
|
||||
content.add(new HTMLBRElement());
|
||||
content.addAll(_content);
|
||||
}
|
||||
content.add(new HTMLSpanElement()..textContent = '}');
|
||||
setChildren(content);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,51 +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:async';
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' show ErrorRef;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
|
||||
class ErrorRefElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<ErrorRefElement> _r;
|
||||
|
||||
Stream<RenderedEvent<ErrorRefElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late ErrorRef _error;
|
||||
|
||||
ErrorRef get error => _error;
|
||||
|
||||
factory ErrorRefElement(ErrorRef error, {RenderingQueue? queue}) {
|
||||
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();
|
||||
removeChildren();
|
||||
_r.disable(notify: true);
|
||||
}
|
||||
|
||||
void render() {
|
||||
setChildren(<HTMLElement>[
|
||||
new HTMLPreElement.pre()..textContent = error.message ?? ''
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,88 +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:async';
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
|
||||
class ErrorViewElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<ErrorViewElement> _r;
|
||||
|
||||
Stream<RenderedEvent<ErrorViewElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.Error _error;
|
||||
late M.NotificationRepository _notifications;
|
||||
|
||||
M.Error get error => _error;
|
||||
|
||||
factory ErrorViewElement(
|
||||
M.NotificationRepository notifications, M.Error error,
|
||||
{RenderingQueue? queue}) {
|
||||
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();
|
||||
removeChildren();
|
||||
_r.disable(notify: true);
|
||||
}
|
||||
|
||||
void render() {
|
||||
setChildren(<HTMLElement>[
|
||||
navBar(<HTMLElement>[
|
||||
new NavTopMenuElement(queue: _r.queue).element,
|
||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'content-centered'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLHeadingElement.h1()
|
||||
..textContent = 'Error: ${_kindToString(_error.kind)}',
|
||||
new HTMLBRElement(),
|
||||
new HTMLDivElement()
|
||||
..className = 'well'
|
||||
..appendChild(
|
||||
new HTMLPreElement.pre()..textContent = 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';
|
||||
default:
|
||||
throw new Exception('Unknown M.ErrorKind ($kind)');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,205 +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:async';
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/helpers/any_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
|
||||
class EvalBoxElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<EvalBoxElement> _r;
|
||||
|
||||
Stream<RenderedEvent<EvalBoxElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.IsolateRef _isolate;
|
||||
late M.ObjectRef _context;
|
||||
late M.ObjectRepository _objects;
|
||||
late M.EvalRepository _eval;
|
||||
final _results = <_ExpressionDescription>[];
|
||||
String? _expression = '';
|
||||
late bool _multiline;
|
||||
late 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}) {
|
||||
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);
|
||||
removeChildren();
|
||||
_results.clear();
|
||||
}
|
||||
|
||||
void render() {
|
||||
setChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'quicks'
|
||||
..appendChildren(
|
||||
_quickExpressions.map<HTMLElement>((q) => new HTMLButtonElement()
|
||||
..textContent = q
|
||||
..onClick.listen((_) {
|
||||
_expression = q;
|
||||
_run();
|
||||
}))),
|
||||
new HTMLDivElement()
|
||||
..className = 'heading'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLFormElement()
|
||||
..autocomplete = 'on'
|
||||
..appendChildren(<HTMLElement>[
|
||||
_multiline ? _createEvalTextArea() : _createEvalTextBox(),
|
||||
new HTMLSpanElement()
|
||||
..className = 'buttons'
|
||||
..appendChildren(<HTMLElement>[
|
||||
_createEvalButton(),
|
||||
_createMultilineCheckbox(),
|
||||
new HTMLSpanElement()..textContent = 'Multi-line'
|
||||
])
|
||||
])
|
||||
]),
|
||||
new HTMLTableElement()
|
||||
..appendChildren(_results.reversed
|
||||
.map<HTMLElement>((result) => new HTMLTableRowElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLTableCellElement.td()
|
||||
..className = 'historyExpr'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLButtonElement()
|
||||
..textContent = result.expression
|
||||
..onClick.listen((_) {
|
||||
_expression = result.expression;
|
||||
_r.dirty();
|
||||
})
|
||||
]),
|
||||
new HTMLTableCellElement.td()
|
||||
..className = 'historyValue'
|
||||
..appendChildren(<HTMLElement>[
|
||||
result.isPending
|
||||
? (new HTMLSpanElement()..textContent = 'Pending...')
|
||||
: anyRef(_isolate, result.value, _objects,
|
||||
queue: _r.queue)
|
||||
]),
|
||||
new HTMLTableCellElement.td()
|
||||
..className = 'historyDelete'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLButtonElement()
|
||||
..textContent = '✖ Remove'
|
||||
..onClick.listen((_) {
|
||||
_results.remove(result);
|
||||
_r.dirty();
|
||||
})
|
||||
])
|
||||
]))
|
||||
.toList())
|
||||
]);
|
||||
}
|
||||
|
||||
HTMLTextAreaElement _createEvalTextArea() {
|
||||
var area = new HTMLTextAreaElement()
|
||||
..className = '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;
|
||||
}
|
||||
|
||||
HTMLInputElement _createEvalTextBox() {
|
||||
final expression = (_expression ?? '').split('\n')[0];
|
||||
_expression = expression;
|
||||
var textbox = new HTMLInputElement()
|
||||
..className = '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;
|
||||
}
|
||||
|
||||
HTMLButtonElement _createEvalButton() {
|
||||
final button = new HTMLButtonElement()
|
||||
..textContent = 'Evaluate'
|
||||
..onClick.listen((e) {
|
||||
e.preventDefault();
|
||||
_run();
|
||||
});
|
||||
return button;
|
||||
}
|
||||
|
||||
HTMLInputElement _createMultilineCheckbox() {
|
||||
final checkbox = new HTMLInputElement()
|
||||
..type = 'checkbox'
|
||||
..checked = _multiline;
|
||||
checkbox.onClick.listen((e) {
|
||||
e.preventDefault();
|
||||
_multiline = checkbox.checked;
|
||||
_r.dirty();
|
||||
});
|
||||
return checkbox;
|
||||
}
|
||||
|
||||
Future _run() async {
|
||||
final expression = _expression;
|
||||
if (expression == null || expression.isEmpty) return;
|
||||
_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,91 +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 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
import 'package:observatory/src/elements/instance_ref.dart';
|
||||
|
||||
class FieldRefElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<FieldRefElement> _r;
|
||||
|
||||
Stream<RenderedEvent<FieldRefElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.IsolateRef _isolate;
|
||||
late M.FieldRef _field;
|
||||
late M.ObjectRepository _objects;
|
||||
late 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}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
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') {
|
||||
setChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = header,
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.inspect(_isolate, object: _field)
|
||||
..text = _field.name ?? ''
|
||||
]);
|
||||
} else {
|
||||
setChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = header,
|
||||
new InstanceRefElement(_isolate, _field.declaredType!, _objects,
|
||||
queue: _r.queue, expandable: _expandable)
|
||||
.element,
|
||||
new HTMLSpanElement()..textContent = ' ',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.inspect(_isolate, object: _field)
|
||||
..text = _field.name ?? ''
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,279 +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 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/class_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/any_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_menu.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/nav/class_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/isolate_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/library_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/refresh.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/vm_menu.dart';
|
||||
import 'package:observatory/src/elements/object_common.dart';
|
||||
import 'package:observatory/src/elements/script_inset.dart';
|
||||
import 'package:observatory/src/elements/source_link.dart';
|
||||
|
||||
class FieldViewElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<FieldViewElement> _r;
|
||||
|
||||
Stream<RenderedEvent<FieldViewElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.VM _vm;
|
||||
late M.IsolateRef _isolate;
|
||||
late M.EventRepository _events;
|
||||
late M.NotificationRepository _notifications;
|
||||
late M.Field _field;
|
||||
M.LibraryRef? _library;
|
||||
late M.FieldRepository _fields;
|
||||
late M.ClassRepository _classes;
|
||||
late M.RetainedSizeRepository _retainedSizes;
|
||||
late M.ReachableSizeRepository _reachableSizes;
|
||||
late M.InboundReferencesRepository _references;
|
||||
late M.RetainingPathRepository _retainingPaths;
|
||||
late M.ScriptRepository _scripts;
|
||||
late 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}) {
|
||||
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 as M.LibraryRef;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
FieldViewElement.created() : super.created('field-view');
|
||||
|
||||
@override
|
||||
attached() {
|
||||
super.attached();
|
||||
_r.enable();
|
||||
_refresh();
|
||||
}
|
||||
|
||||
@override
|
||||
detached() {
|
||||
super.detached();
|
||||
_r.disable(notify: true);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
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!;
|
||||
}
|
||||
setChildren(<HTMLElement>[
|
||||
navBar(_createMenu()),
|
||||
new HTMLDivElement()
|
||||
..className = 'content-centered-big'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLHeadingElement.h2()..textContent = '$header ${field.name}',
|
||||
new HTMLHRElement(),
|
||||
new ObjectCommonElement(_isolate, _field, _retainedSizes,
|
||||
_reachableSizes, _references, _retainingPaths, _objects,
|
||||
queue: _r.queue)
|
||||
.element,
|
||||
new HTMLBRElement(),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(_createMembers()),
|
||||
new HTMLHRElement(),
|
||||
new HTMLDivElement()
|
||||
..appendChildren(_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<HTMLElement> _createMenu() {
|
||||
final menu = <HTMLElement>[
|
||||
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 as M.ClassRef,
|
||||
queue: _r.queue)
|
||||
.element);
|
||||
}
|
||||
menu.addAll(<HTMLElement>[
|
||||
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<HTMLElement> _createMembers() {
|
||||
final members = <HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'owner',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(<HTMLElement>[
|
||||
_field.dartOwner == null
|
||||
? (new HTMLSpanElement()..textContent = '...')
|
||||
: anyRef(_isolate, _field.dartOwner, _objects,
|
||||
queue: _r.queue)
|
||||
])
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'script',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new SourceLinkElement(_isolate, field.location!, _scripts,
|
||||
queue: _r.queue)
|
||||
.element
|
||||
])
|
||||
])
|
||||
];
|
||||
if (!_field.isStatic!) {
|
||||
members.add(new HTMLDivElement()
|
||||
..className = '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.'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'observed types',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(_createGuard())
|
||||
]));
|
||||
}
|
||||
if (_field.staticValue != null) {
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'static value',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(<HTMLElement>[
|
||||
anyRef(_isolate, _field.staticValue, _objects, queue: _r.queue)
|
||||
])
|
||||
]));
|
||||
}
|
||||
return members;
|
||||
}
|
||||
|
||||
List<HTMLElement> _createGuard() {
|
||||
final guard = <HTMLElement>[];
|
||||
switch (_field.guardClassKind!) {
|
||||
case M.GuardClassKind.unknown:
|
||||
guard.add(new HTMLSpanElement()..textContent = 'none');
|
||||
break;
|
||||
case M.GuardClassKind.dynamic:
|
||||
guard.add(new HTMLSpanElement()..textContent = 'various');
|
||||
break;
|
||||
case M.GuardClassKind.single:
|
||||
guard.add(
|
||||
new ClassRefElement(_isolate, _field.guardClass!, queue: _r.queue)
|
||||
.element);
|
||||
break;
|
||||
}
|
||||
guard.add(new HTMLSpanElement()
|
||||
..textContent =
|
||||
_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 as M.LibraryRef;
|
||||
} else if (_field.dartOwner is M.ClassRef) {
|
||||
var cls = _field.dartOwner as M.ClassRef;
|
||||
_library = (await _classes.get(_isolate, cls.id!)).library!;
|
||||
}
|
||||
_r.dirty();
|
||||
}
|
||||
}
|
||||
@@ -1,144 +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:async';
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_menu.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/refresh.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/vm_menu.dart';
|
||||
|
||||
class FlagListElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<FlagListElement> _r;
|
||||
|
||||
Stream<RenderedEvent<FlagListElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.VMRef _vm;
|
||||
late M.EventRepository _events;
|
||||
late M.FlagsRepository _repository;
|
||||
late 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}) {
|
||||
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();
|
||||
removeChildren();
|
||||
_r.disable(notify: true);
|
||||
}
|
||||
|
||||
void render() {
|
||||
final content = <HTMLElement>[];
|
||||
if (_flags == null) {
|
||||
content
|
||||
.add(new HTMLHeadingElement.h1()..textContent = 'Loading Flags...');
|
||||
} else {
|
||||
final modified = _flags!.where(_isModified);
|
||||
final unmodified = _flags!.where(_isUnmodified);
|
||||
|
||||
if (modified.isNotEmpty) {
|
||||
content
|
||||
.add(new HTMLHeadingElement.h1()..textContent = 'Modified Flags');
|
||||
content.add(new HTMLBRElement());
|
||||
content.addAll(modified.expand(_renderFlag));
|
||||
content.add(new HTMLHRElement());
|
||||
}
|
||||
|
||||
content
|
||||
.add(new HTMLHeadingElement.h1()..textContent = 'Unmodified Flags');
|
||||
content.add(new HTMLBRElement());
|
||||
|
||||
if (unmodified.isEmpty) {
|
||||
content.add(new HTMLHeadingElement.h2()..textContent = 'None');
|
||||
} else {
|
||||
content.addAll(unmodified.expand(_renderFlag));
|
||||
}
|
||||
}
|
||||
|
||||
setChildren(<HTMLElement>[
|
||||
navBar(<HTMLElement>[
|
||||
new NavTopMenuElement(queue: _r.queue).element,
|
||||
new NavVMMenuElement(_vm as M.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 HTMLDivElement()
|
||||
..className = 'content-centered'
|
||||
..appendChildren(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<HTMLElement> _renderFlag(M.Flag flag) {
|
||||
return [
|
||||
new HTMLSpanElement()
|
||||
..className = 'comment'
|
||||
..textContent = '// ${flag.comment}',
|
||||
new HTMLDivElement()
|
||||
..className = flag.modified ? 'flag modified' : 'flag unmodified'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()
|
||||
..className = 'name'
|
||||
..textContent = flag.name,
|
||||
new HTMLSpanElement()..textContent = '=',
|
||||
new HTMLSpanElement()
|
||||
..className = 'value'
|
||||
..textContent = flag.valueAsString ?? ''
|
||||
]),
|
||||
new HTMLBRElement(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -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:async';
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/src/elements/class_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M
|
||||
show
|
||||
IsolateRef,
|
||||
FunctionRef,
|
||||
isSyntheticFunction,
|
||||
ClassRef,
|
||||
ObjectRef,
|
||||
getFunctionFullName;
|
||||
|
||||
class FunctionRefElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<FunctionRefElement> _r;
|
||||
|
||||
Stream<RenderedEvent<FunctionRefElement>> get onRendered => _r.onRendered;
|
||||
|
||||
M.IsolateRef? _isolate;
|
||||
late M.FunctionRef _function;
|
||||
late 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}) {
|
||||
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();
|
||||
removeChildren();
|
||||
title = '';
|
||||
_r.disable(notify: true);
|
||||
}
|
||||
|
||||
void render() {
|
||||
var content = <HTMLElement>[
|
||||
new HTMLAnchorElement()
|
||||
..href = (M.isSyntheticFunction(_function.kind) || (_isolate == null))
|
||||
? ''
|
||||
: Uris.inspect(_isolate!, object: _function)
|
||||
..text = _function.name ?? ''
|
||||
];
|
||||
if (qualified) {
|
||||
M.ObjectRef? owner = _function.dartOwner;
|
||||
while (owner is M.FunctionRef) {
|
||||
content.addAll([
|
||||
new HTMLSpanElement()..textContent = '.',
|
||||
new HTMLAnchorElement()
|
||||
..href = (M.isSyntheticFunction(owner.kind) || (_isolate == null))
|
||||
? ''
|
||||
: Uris.inspect(_isolate!, object: owner)
|
||||
..text = owner.name!
|
||||
]);
|
||||
owner = owner.dartOwner;
|
||||
}
|
||||
if (owner is M.ClassRef) {
|
||||
content.addAll([
|
||||
new HTMLSpanElement()..textContent = '.',
|
||||
new ClassRefElement(_isolate!, owner, queue: _r.queue).element
|
||||
]);
|
||||
}
|
||||
}
|
||||
children = content.reversed.toList(growable: false);
|
||||
title = M.getFunctionFullName(_function);
|
||||
}
|
||||
}
|
||||
@@ -1,422 +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 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/code_ref.dart';
|
||||
import 'package:observatory/src/elements/field_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/any_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_menu.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/instance_ref.dart';
|
||||
import 'package:observatory/src/elements/nav/class_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/isolate_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/library_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/refresh.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/vm_menu.dart';
|
||||
import 'package:observatory/src/elements/object_common.dart';
|
||||
import 'package:observatory/src/elements/source_inset.dart';
|
||||
import 'package:observatory/src/elements/source_link.dart';
|
||||
|
||||
class FunctionViewElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<FunctionViewElement> _r;
|
||||
|
||||
Stream<RenderedEvent<FunctionViewElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.VM _vm;
|
||||
late M.IsolateRef _isolate;
|
||||
late M.EventRepository _events;
|
||||
late M.NotificationRepository _notifications;
|
||||
late M.ServiceFunction _function;
|
||||
M.LibraryRef? _library;
|
||||
late M.FunctionRepository _functions;
|
||||
late M.ClassRepository _classes;
|
||||
late M.RetainedSizeRepository _retainedSizes;
|
||||
late M.ReachableSizeRepository _reachableSizes;
|
||||
late M.InboundReferencesRepository _references;
|
||||
late M.RetainingPathRepository _retainingPaths;
|
||||
late M.ScriptRepository _scripts;
|
||||
late 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}) {
|
||||
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 as M.LibraryRef;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
FunctionViewElement.created() : super.created('function-view');
|
||||
|
||||
@override
|
||||
attached() {
|
||||
super.attached();
|
||||
_r.enable();
|
||||
_refresh();
|
||||
}
|
||||
|
||||
@override
|
||||
detached() {
|
||||
super.detached();
|
||||
_r.disable(notify: true);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
void render() {
|
||||
setChildren(<HTMLElement>[
|
||||
navBar(_createMenu()),
|
||||
new HTMLDivElement()
|
||||
..className = 'content-centered-big'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLHeadingElement.h2()
|
||||
..textContent = 'Function ${_function.name}',
|
||||
new HTMLHRElement(),
|
||||
new ObjectCommonElement(_isolate, _function, _retainedSizes,
|
||||
_reachableSizes, _references, _retainingPaths, _objects,
|
||||
queue: _r.queue)
|
||||
.element,
|
||||
new HTMLBRElement(),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(_createMembers()),
|
||||
new HTMLHRElement(),
|
||||
new HTMLDivElement()
|
||||
..appendChildren(_function.location == null
|
||||
? const []
|
||||
: [
|
||||
new SourceInsetElement(_isolate, _function.location!,
|
||||
_scripts, _objects, _events,
|
||||
queue: _r.queue)
|
||||
.element
|
||||
]),
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
List<HTMLElement> _createMenu() {
|
||||
final menu = <HTMLElement>[
|
||||
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 as M.ClassRef,
|
||||
queue: _r.queue)
|
||||
.element);
|
||||
}
|
||||
menu.addAll(<HTMLElement>[
|
||||
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<HTMLElement> _createMembers() {
|
||||
final members = <HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'kind',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()
|
||||
..textContent = '${_function.isStatic! ? "static " : ""}'
|
||||
'${_function.isConst! ? "const " : ""}'
|
||||
'${_functionKindToString(_function.kind)}'
|
||||
])
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'owner',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(<HTMLElement>[
|
||||
_function.dartOwner == null
|
||||
? (new HTMLSpanElement()..textContent = '...')
|
||||
: anyRef(_isolate, _function.dartOwner, _objects,
|
||||
queue: _r.queue)
|
||||
])
|
||||
])
|
||||
];
|
||||
if (_function.field != null) {
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'script',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new FieldRefElement(_isolate, _function.field!, _objects,
|
||||
queue: _r.queue)
|
||||
.element
|
||||
])
|
||||
]));
|
||||
}
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'script',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new SourceLinkElement(_isolate, _function.location!, _scripts,
|
||||
queue: _r.queue)
|
||||
.element
|
||||
])
|
||||
]));
|
||||
if (_function.code != null) {
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'current code',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new CodeRefElement(_isolate, _function.code!, queue: _r.queue)
|
||||
.element
|
||||
])
|
||||
]));
|
||||
}
|
||||
if (_function.unoptimizedCode != null) {
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'unoptimized code',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new CodeRefElement(_isolate, _function.unoptimizedCode!,
|
||||
queue: _r.queue)
|
||||
.element,
|
||||
new HTMLSpanElement()
|
||||
..title = 'This count is used to determine when a function '
|
||||
'will be optimized. It is a combination of call '
|
||||
'counts and other factors.'
|
||||
..textContent = ' (usage count: ${function.usageCounter})'
|
||||
])
|
||||
]));
|
||||
}
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..textContent = ' ');
|
||||
|
||||
if (_function.icDataArray != null) {
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'ic data array',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new InstanceRefElement(_isolate, _function.icDataArray!, _objects,
|
||||
queue: _r.queue)
|
||||
.element
|
||||
])
|
||||
]));
|
||||
}
|
||||
|
||||
members.addAll([
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'deoptimizations',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = '${_function.deoptimizations}'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'optimizable',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = _function.isOptimizable! ? 'yes' : 'no'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'inlinable',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = _function.isInlinable! ? 'yes' : 'no'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'intrinsic',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = _function.hasIntrinsic! ? 'yes' : 'no'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'recognized',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = _function.isRecognized! ? 'yes' : 'no'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'native',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = _function.isNative! ? 'yes' : 'no'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'vm name',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = _function.vmName ?? ''
|
||||
])
|
||||
]);
|
||||
return members;
|
||||
}
|
||||
|
||||
Future _refresh() async {
|
||||
_function = await _functions.get(_isolate, _function.id!);
|
||||
if (_function.dartOwner is M.LibraryRef) {
|
||||
_library = _function.dartOwner as M.LibraryRef;
|
||||
} else if (_function.dartOwner is M.ClassRef) {
|
||||
var cls = _function.dartOwner as M.ClassRef;
|
||||
_library = (await _classes.get(_isolate, cls.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';
|
||||
case M.FunctionKind.recordFieldGetter:
|
||||
return 'record field getter';
|
||||
default:
|
||||
throw new Exception('Unknown Functionkind ($kind)');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,72 +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:async';
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
|
||||
class GeneralErrorElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<GeneralErrorElement> _r;
|
||||
|
||||
Stream<RenderedEvent<GeneralErrorElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.NotificationRepository _notifications;
|
||||
late String _message;
|
||||
|
||||
String get message => _message;
|
||||
|
||||
set message(String value) => _message = _r.checkAndReact(_message, value);
|
||||
|
||||
factory GeneralErrorElement(M.NotificationRepository notifications,
|
||||
{String message = '', RenderingQueue? queue}) {
|
||||
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();
|
||||
removeChildren();
|
||||
_r.disable(notify: true);
|
||||
}
|
||||
|
||||
void render() {
|
||||
setChildren(<HTMLElement>[
|
||||
navBar(<HTMLElement>[
|
||||
new NavTopMenuElement(queue: _r.queue).element,
|
||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'content-centered'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLHeadingElement.h1()..textContent = 'Error',
|
||||
new HTMLBRElement(),
|
||||
new HTMLDivElement()
|
||||
..className = 'well'
|
||||
..textContent = message
|
||||
])
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,319 +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:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/service.dart' as S;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_menu.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/nav/isolate_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/refresh.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/vm_menu.dart';
|
||||
|
||||
class HeapMapElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<HeapMapElement> _r;
|
||||
|
||||
Stream<RenderedEvent<HeapMapElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.VM _vm;
|
||||
late M.IsolateRef _isolate;
|
||||
late M.EventRepository _events;
|
||||
late 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}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
HTMLCanvasElement? _canvas;
|
||||
ImageData? _fragmentationData;
|
||||
int? _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 = 'Loading';
|
||||
S.ServiceMap? _fragmentation;
|
||||
|
||||
void render() {
|
||||
if (_canvas == null) {
|
||||
_canvas = new HTMLCanvasElement()
|
||||
..width = 1
|
||||
..height = 1
|
||||
..onMouseMove.listen(_handleMouseMove);
|
||||
}
|
||||
|
||||
// Set hover text to describe the object under the cursor.
|
||||
_canvas!.title = _status;
|
||||
|
||||
setChildren(<HTMLElement>[
|
||||
navBar(<HTMLElement>[
|
||||
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 HTMLDivElement()
|
||||
..className = 'content-centered-big'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLHeadingElement.h2()..textContent = _status,
|
||||
new HTMLHRElement(),
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'flex-row'
|
||||
..appendChild(_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(Point(event.offsetX, event.offsetY));
|
||||
if (info == null) {
|
||||
_status = '';
|
||||
_r.dirty();
|
||||
return;
|
||||
}
|
||||
var addressString = '${info.size}B @ 0x${info.address.toRadixString(16)}';
|
||||
var className = _classNameAt(Point(event.offsetX, event.offsetY));
|
||||
_status = (className == '') ? '-' : '$className $addressString';
|
||||
_r.dirty();
|
||||
}
|
||||
|
||||
void _updateFragmentationData() {
|
||||
if (_fragmentation == null || _canvas == null) {
|
||||
return;
|
||||
}
|
||||
_updateClassList(
|
||||
_fragmentation!['classList'], _fragmentation!['freeClassId']);
|
||||
final pages = _fragmentation!['pages'];
|
||||
final int width = max(_canvas!.parentElement!.clientWidth, 1);
|
||||
_pageHeight = _PAGE_SEPARATION_HEIGHT +
|
||||
(_fragmentation!['pageSizeBytes'] as int) ~/
|
||||
(_fragmentation!['unitSizeBytes'] as int) ~/
|
||||
width;
|
||||
final height = min(_pageHeight! * pages.length, _MAX_CANVAS_HEIGHT) as int;
|
||||
_fragmentationData =
|
||||
_canvas!.context2D.createImageData(width as dynamic, 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 as S.ServiceMap;
|
||||
assert(response['type'] == 'HeapMap');
|
||||
_fragmentation = response;
|
||||
_updateFragmentationData();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// A reference to a particular pixel of ImageData.
|
||||
class PixelReference {
|
||||
final ImageData _data;
|
||||
final int _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 as int;
|
||||
|
||||
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 as Uint8ClampedList)
|
||||
.setRange(_dataIndex, _dataIndex + NUM_COLOR_COMPONENTS, color);
|
||||
}
|
||||
|
||||
Iterable<int> get color => (_data.data as Uint8ClampedList)
|
||||
.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,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.
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/class_ref.dart';
|
||||
import 'package:observatory/src/elements/code_ref.dart';
|
||||
import 'package:observatory/src/elements/context_ref.dart';
|
||||
import 'package:observatory/src/elements/error_ref.dart';
|
||||
import 'package:observatory/src/elements/field_ref.dart';
|
||||
import 'package:observatory/src/elements/function_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
import 'package:observatory/src/elements/icdata_ref.dart';
|
||||
import 'package:observatory/src/elements/instance_ref.dart';
|
||||
import 'package:observatory/src/elements/library_ref.dart';
|
||||
import 'package:observatory/src/elements/local_var_descriptors_ref.dart';
|
||||
import 'package:observatory/src/elements/megamorphiccache_ref.dart';
|
||||
import 'package:observatory/src/elements/objectpool_ref.dart';
|
||||
import 'package:observatory/src/elements/pc_descriptors_ref.dart';
|
||||
import 'package:observatory/src/elements/script_ref.dart';
|
||||
import 'package:observatory/src/elements/sentinel_value.dart';
|
||||
import 'package:observatory/src/elements/singletargetcache_ref.dart';
|
||||
import 'package:observatory/src/elements/subtypetestcache_ref.dart';
|
||||
import 'package:observatory/src/elements/type_arguments_ref.dart';
|
||||
import 'package:observatory/src/elements/unknown_ref.dart';
|
||||
import 'package:observatory/src/elements/unlinkedcall_ref.dart';
|
||||
|
||||
HTMLElement anyRef(M.IsolateRef isolate, ref, M.ObjectRepository objects,
|
||||
{RenderingQueue? queue, bool expandable = true}) {
|
||||
if (ref == null) {
|
||||
return new HTMLSpanElement()..textContent = "???";
|
||||
}
|
||||
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 HTMLAnchorElement()
|
||||
..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 HTMLSpanElement()..textContent = ref.toString();
|
||||
}
|
||||
throw new Exception('Unknown ref type (${ref.runtimeType})');
|
||||
}
|
||||
@@ -1,126 +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:web/web.dart';
|
||||
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
|
||||
HTMLElement element(CustomElement e) => e.element;
|
||||
|
||||
class CustomElement {
|
||||
static Expando reverseElements = new Expando();
|
||||
static CustomElement reverse(HTMLElement element) =>
|
||||
reverseElements[element] as CustomElement;
|
||||
|
||||
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") as HTMLElement {
|
||||
reverseElements[element] = this;
|
||||
element.className = elementClass;
|
||||
|
||||
if (toBeAttached.isEmpty) {
|
||||
scheduleMicrotask(() => drainAttached());
|
||||
}
|
||||
toBeAttached.add(this);
|
||||
}
|
||||
|
||||
void attached() {}
|
||||
void detached() {}
|
||||
|
||||
HTMLElement? get parent => element.parentElement as HTMLElement?;
|
||||
|
||||
List<Node> get children {
|
||||
final list = <Node>[];
|
||||
for (var i = 0; i < element.children.length; i++) {
|
||||
final child = element.children.item(i);
|
||||
if (child != null) {
|
||||
list.add(child);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
set children(List<Node> nodes) {
|
||||
element.removeChildren();
|
||||
for (var node in nodes) {
|
||||
element.appendChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
CustomElement appendChild(Node node) {
|
||||
element.appendChild(node);
|
||||
return this;
|
||||
}
|
||||
|
||||
CustomElement removeChildren() {
|
||||
element.removeChildren();
|
||||
return this;
|
||||
}
|
||||
|
||||
CustomElement appendChildren(Iterable<Node> nodes) {
|
||||
element.appendChildren(nodes);
|
||||
return this;
|
||||
}
|
||||
|
||||
CustomElement setChildren(Iterable<Node> nodes) {
|
||||
element.setChildren(nodes);
|
||||
return this;
|
||||
}
|
||||
|
||||
String get className => element.className;
|
||||
set className(String c) => element.className = c;
|
||||
|
||||
String get title => element.title;
|
||||
set title(String t) => element.title = t;
|
||||
|
||||
String get innerText => element.innerText;
|
||||
set innerText(String t) => element.innerText = t;
|
||||
|
||||
CSSStyleDeclaration get style => element.style;
|
||||
|
||||
ElementStream<MouseEvent> get onClick => element.onClick;
|
||||
|
||||
DOMRect getBoundingClientRect() => element.getBoundingClientRect();
|
||||
|
||||
HTMLCollection getElementsByClassName(String c) =>
|
||||
element.getElementsByClassName(c);
|
||||
|
||||
void scrollIntoView() => element.scrollIntoView();
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright (c) 2025, 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:web/web.dart';
|
||||
|
||||
extension ElementAppendChildren on Element {
|
||||
Element appendChildren(Iterable<Node> nodes) {
|
||||
for (final node in nodes) {
|
||||
this.appendChild(node);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
removeChildren() {
|
||||
while (true) {
|
||||
var child = childNodes.item(0);
|
||||
if (child == null) {
|
||||
break;
|
||||
}
|
||||
removeChild(child);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Element setChildren(Iterable<Node> nodes) {
|
||||
removeChildren();
|
||||
appendChildren(nodes);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
HTMLElement toggleClass(HTMLElement element, String classMember) {
|
||||
if (element.className.contains(classMember)) {
|
||||
element.className = element.className.replaceAll(classMember, '');
|
||||
} else {
|
||||
element.className += classMember;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
@@ -1,15 +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:web/web.dart';
|
||||
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
|
||||
HTMLElement navBar(List<HTMLElement> content) {
|
||||
return (document.createElement('nav')
|
||||
..className = 'nav-bar'
|
||||
..appendChild(
|
||||
new HTMLUListElement()..appendChildren(content),
|
||||
)) as HTMLElement;
|
||||
}
|
||||
@@ -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 'package:web/web.dart';
|
||||
|
||||
navMenu(String label, {String? link, List<HTMLElement> content = const []}) {
|
||||
final ulist = new HTMLUListElement();
|
||||
for (final element in content) {
|
||||
ulist.appendChild(element);
|
||||
}
|
||||
return new HTMLLIElement()
|
||||
..className = 'nav-menu'
|
||||
..appendChild(new HTMLSpanElement()
|
||||
..className = 'nav-menu_label'
|
||||
..appendChild(new HTMLAnchorElement()
|
||||
..href = link ?? ''
|
||||
..text = label)
|
||||
..appendChild(ulist));
|
||||
}
|
||||
@@ -1,90 +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:collection';
|
||||
import 'dart:js_interop';
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
/// 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 {
|
||||
final c = Completer<num>();
|
||||
window.requestAnimationFrame(
|
||||
((num n) => c.complete(DateTime.now().microsecondsSinceEpoch)).toJS);
|
||||
return c.future;
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 number of ms elapsed.
|
||||
void triggerRenderingBarrier({num step = 20}) {
|
||||
_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);
|
||||
|
||||
/// 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}) {
|
||||
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,139 +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/src/elements/helpers/rendering_queue.dart';
|
||||
|
||||
export 'package:observatory/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);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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;
|
||||
|
||||
/// HTMLElement 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}) {
|
||||
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}) {
|
||||
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,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/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,62 +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 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M show IsolateRef, ICDataRef;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
|
||||
class ICDataRefElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<ICDataRefElement> _r;
|
||||
|
||||
Stream<RenderedEvent<ICDataRefElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.IsolateRef _isolate;
|
||||
late M.ICDataRef _icdata;
|
||||
|
||||
M.IsolateRef get isolate => _isolate;
|
||||
M.ICDataRef get icdata => _icdata;
|
||||
|
||||
factory ICDataRefElement(M.IsolateRef isolate, M.ICDataRef icdata,
|
||||
{RenderingQueue? queue}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
void render() {
|
||||
setChildren(<HTMLElement>[
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.inspect(_isolate, object: _icdata)
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()
|
||||
..className = 'emphasize'
|
||||
..textContent = 'ICData',
|
||||
new HTMLSpanElement()..textContent = ' (${_icdata.selector})'
|
||||
])
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,178 +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 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/helpers/any_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_menu.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/nav/isolate_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/refresh.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/vm_menu.dart';
|
||||
import 'package:observatory/src/elements/object_common.dart';
|
||||
|
||||
class ICDataViewElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<ICDataViewElement> _r;
|
||||
|
||||
Stream<RenderedEvent<ICDataViewElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.VM _vm;
|
||||
late M.IsolateRef _isolate;
|
||||
late M.EventRepository _events;
|
||||
late M.NotificationRepository _notifications;
|
||||
late M.ICData _icdata;
|
||||
late M.ICDataRepository _icdatas;
|
||||
late M.RetainedSizeRepository _retainedSizes;
|
||||
late M.ReachableSizeRepository _reachableSizes;
|
||||
late M.InboundReferencesRepository _references;
|
||||
late M.RetainingPathRepository _retainingPaths;
|
||||
late 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}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
void render() {
|
||||
setChildren(<HTMLElement>[
|
||||
navBar(<HTMLElement>[
|
||||
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 HTMLDivElement()
|
||||
..className = 'content-centered-big'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLHeadingElement.h2()..textContent = 'ICData',
|
||||
new HTMLHRElement(),
|
||||
new ObjectCommonElement(_isolate, _icdata, _retainedSizes,
|
||||
_reachableSizes, _references, _retainingPaths, _objects,
|
||||
queue: _r.queue)
|
||||
.element,
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'selector',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = _icdata.selector ?? ''
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'owner',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(<HTMLElement>[
|
||||
_icdata.dartOwner == null
|
||||
? (new HTMLSpanElement()..textContent = '<none>')
|
||||
: anyRef(_isolate, _icdata.dartOwner, _objects,
|
||||
queue: _r.queue)
|
||||
])
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'argumentsDescriptor',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(<HTMLElement>[
|
||||
_icdata.argumentsDescriptor == null
|
||||
? (new HTMLSpanElement()..textContent = '<none>')
|
||||
: anyRef(
|
||||
_isolate, _icdata.argumentsDescriptor, _objects,
|
||||
queue: _r.queue)
|
||||
])
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'entries',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(<HTMLElement>[
|
||||
_icdata.entries == null
|
||||
? (new HTMLSpanElement()..textContent = '<none>')
|
||||
: anyRef(_isolate, _icdata.entries, _objects,
|
||||
queue: _r.queue)
|
||||
])
|
||||
])
|
||||
]),
|
||||
new HTMLHRElement(),
|
||||
])
|
||||
]);
|
||||
}
|
||||
}
|
||||
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,121 +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:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/curly_block.dart';
|
||||
import 'package:observatory/src/elements/helpers/any_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
|
||||
class InboundReferencesElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<InboundReferencesElement> _r;
|
||||
|
||||
Stream<RenderedEvent<InboundReferencesElement>> get onRendered =>
|
||||
_r.onRendered;
|
||||
|
||||
late M.IsolateRef _isolate;
|
||||
late M.ObjectRef _object;
|
||||
late M.InboundReferencesRepository _references;
|
||||
late 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}) {
|
||||
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();
|
||||
removeChildren();
|
||||
_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;
|
||||
}
|
||||
});
|
||||
setChildren(<HTMLElement>[curlyBlock.element]);
|
||||
_r.waitFor([curlyBlock.onRendered.first]);
|
||||
}
|
||||
|
||||
Future _refresh() async {
|
||||
_inbounds = await _references.get(_isolate, _object.id!);
|
||||
_r.dirty();
|
||||
}
|
||||
|
||||
List<HTMLElement> _createContent() {
|
||||
if (_inbounds == null) {
|
||||
return const [];
|
||||
}
|
||||
return _inbounds!.elements.map<HTMLElement>(_createItem).toList();
|
||||
}
|
||||
|
||||
HTMLElement _createItem(M.InboundReference reference) {
|
||||
final content = <HTMLElement>[];
|
||||
|
||||
if (reference.parentField is M.Object) {
|
||||
content.addAll([
|
||||
new HTMLSpanElement()..textContent = 'referenced by ',
|
||||
anyRef(_isolate, reference.parentField, _objects, queue: _r.queue),
|
||||
new HTMLSpanElement()..textContent = ' of '
|
||||
]);
|
||||
} else if (reference.parentField is String ||
|
||||
reference.parentField is int) {
|
||||
content.add(new HTMLSpanElement()
|
||||
..textContent = 'referenced by [ ${reference.parentField} ] of ');
|
||||
} else if (reference.parentListIndex != null) {
|
||||
content.add(new HTMLSpanElement()
|
||||
..textContent = 'referenced by [ ${reference.parentListIndex} ] of ');
|
||||
} else if (reference.parentWordOffset != null) {
|
||||
content.add(new HTMLSpanElement()
|
||||
..textContent =
|
||||
'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 HTMLDivElement()
|
||||
..className = 'indent'
|
||||
..appendChildren(content);
|
||||
}
|
||||
}
|
||||
@@ -1,433 +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 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/curly_block.dart';
|
||||
import 'package:observatory/src/elements/field_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/any_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
import 'package:observatory/utils.dart';
|
||||
|
||||
class InstanceRefElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<InstanceRefElement> _r;
|
||||
|
||||
Stream<RenderedEvent<InstanceRefElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.IsolateRef _isolate;
|
||||
late M.InstanceRef _instance;
|
||||
late M.ObjectRepository _objects;
|
||||
M.Instance? _loadedInstance;
|
||||
late bool _expandable;
|
||||
late 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}) {
|
||||
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();
|
||||
removeChildren();
|
||||
_r.disable(notify: true);
|
||||
}
|
||||
|
||||
void render() {
|
||||
final content = _createLink();
|
||||
|
||||
if (_expandable && _hasValue()) {
|
||||
content.addAll([
|
||||
new HTMLSpanElement()..textContent = ' ',
|
||||
(new CurlyBlockElement(expanded: _expanded, queue: _r.queue)
|
||||
..content = <HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'indent'
|
||||
..appendChildren(_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!) as M.Instance;
|
||||
_r.dirty();
|
||||
}
|
||||
|
||||
List<HTMLElement> _createShowMoreButton() {
|
||||
if (_loadedInstance!.count == null) {
|
||||
return [];
|
||||
}
|
||||
final count = _loadedInstance!.count;
|
||||
final button = new HTMLButtonElement()..textContent = 'show next ${count}';
|
||||
button.onClick.listen((_) async {
|
||||
button.disabled = true;
|
||||
_loadedInstance =
|
||||
await _objects.get(_isolate, _instance.id!) as M.Instance;
|
||||
_r.dirty();
|
||||
});
|
||||
return [button];
|
||||
}
|
||||
|
||||
List<HTMLElement> _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 HTMLAnchorElement()
|
||||
..href = Uris.inspect(_isolate, object: _instance)
|
||||
..text = _instance.valueAsString ?? ''
|
||||
];
|
||||
case M.InstanceKind.string:
|
||||
return [
|
||||
new HTMLAnchorElement()
|
||||
..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 HTMLAnchorElement()
|
||||
..href = Uris.inspect(_isolate, object: _instance)
|
||||
..text = _instance.name ?? ''
|
||||
];
|
||||
case M.InstanceKind.closure:
|
||||
return [
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.inspect(_isolate, object: _instance)
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()
|
||||
..className = 'emphasize'
|
||||
..textContent = 'Closure',
|
||||
new HTMLSpanElement()
|
||||
..textContent = ' (${_instance.closureFunction!.name})'
|
||||
])
|
||||
];
|
||||
case M.InstanceKind.regExp:
|
||||
return [
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.inspect(_isolate, object: _instance)
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()
|
||||
..className = 'emphasize'
|
||||
..textContent = _instance.clazz!.name ?? '',
|
||||
new HTMLSpanElement()
|
||||
..textContent = ' (${_instance.pattern!.valueAsString})'
|
||||
])
|
||||
];
|
||||
case M.InstanceKind.userTag:
|
||||
return [
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.inspect(_isolate, object: _instance)
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()
|
||||
..className = 'emphasize'
|
||||
..textContent = _instance.clazz!.name ?? '',
|
||||
new HTMLSpanElement()..textContent = ' (${_instance.name})'
|
||||
])
|
||||
];
|
||||
case M.InstanceKind.stackTrace:
|
||||
return [
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.inspect(_isolate, object: _instance)
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()
|
||||
..className = 'emphasize'
|
||||
..textContent = _instance.clazz!.name ?? '',
|
||||
])
|
||||
];
|
||||
case M.InstanceKind.plainInstance:
|
||||
case M.InstanceKind.receivePort:
|
||||
return [
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.inspect(_isolate, object: _instance)
|
||||
..className = '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 HTMLAnchorElement()
|
||||
..href = Uris.inspect(_isolate, object: _instance)
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()
|
||||
..className = 'emphasize'
|
||||
..textContent = _instance.clazz!.name ?? '',
|
||||
new HTMLSpanElement()..textContent = ' (${_instance.length})'
|
||||
])
|
||||
];
|
||||
case M.InstanceKind.mirrorReference:
|
||||
case M.InstanceKind.weakProperty:
|
||||
case M.InstanceKind.finalizer:
|
||||
case M.InstanceKind.nativeFinalizer:
|
||||
case M.InstanceKind.finalizerEntry:
|
||||
case M.InstanceKind.weakReference:
|
||||
case M.InstanceKind.record:
|
||||
return [
|
||||
HTMLAnchorElement()
|
||||
..href = Uris.inspect(_isolate, object: _instance)
|
||||
..className = 'emphasize'
|
||||
..textContent = _instance.clazz!.name ?? ''
|
||||
];
|
||||
default:
|
||||
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:
|
||||
case M.InstanceKind.finalizer:
|
||||
case M.InstanceKind.nativeFinalizer:
|
||||
case M.InstanceKind.finalizerEntry:
|
||||
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<HTMLElement> _createValue() {
|
||||
if (_loadedInstance == null) {
|
||||
return [new HTMLSpanElement()..textContent = 'Loading...'];
|
||||
}
|
||||
switch (_instance.kind) {
|
||||
case M.InstanceKind.closure:
|
||||
{
|
||||
var members = <HTMLElement>[];
|
||||
if (_loadedInstance!.closureFunction != null) {
|
||||
members.add(new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'function = ',
|
||||
anyRef(_isolate, _loadedInstance!.closureFunction, _objects,
|
||||
queue: _r.queue)
|
||||
]));
|
||||
}
|
||||
if (_loadedInstance!.closureContext != null) {
|
||||
members.add(new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'context = ',
|
||||
anyRef(_isolate, _loadedInstance!.closureContext, _objects,
|
||||
queue: _r.queue)
|
||||
]));
|
||||
}
|
||||
if (_loadedInstance!.closureReceiver != null) {
|
||||
members.add(new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'receiver = ',
|
||||
anyRef(_isolate, _loadedInstance!.closureReceiver, _objects,
|
||||
queue: _r.queue)
|
||||
]));
|
||||
}
|
||||
return members;
|
||||
}
|
||||
case M.InstanceKind.plainInstance:
|
||||
return _loadedInstance!.fields!
|
||||
.map<HTMLElement>((f) => new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new FieldRefElement(_isolate, f.decl!, _objects,
|
||||
queue: _r.queue)
|
||||
.element,
|
||||
new HTMLSpanElement()..textContent = ' = ',
|
||||
anyRef(_isolate, f.value, _objects, queue: _r.queue)
|
||||
]))
|
||||
.toList();
|
||||
case M.InstanceKind.list:
|
||||
var index = 0;
|
||||
return _loadedInstance!.elements!
|
||||
.map<HTMLElement>((element) => new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = '[ ${index++} ] : ',
|
||||
anyRef(_isolate, element, _objects, queue: _r.queue)
|
||||
]))
|
||||
.toList()
|
||||
..addAll(_createShowMoreButton());
|
||||
case M.InstanceKind.map:
|
||||
return _loadedInstance!.associations!
|
||||
.map<HTMLElement>((association) => new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = '[ ',
|
||||
anyRef(_isolate, association.key, _objects, queue: _r.queue),
|
||||
new HTMLSpanElement()..textContent = ' ] : ',
|
||||
anyRef(_isolate, association.value, _objects, queue: _r.queue)
|
||||
]))
|
||||
.toList()
|
||||
..addAll(_createShowMoreButton());
|
||||
case M.InstanceKind.set:
|
||||
return _loadedInstance!.elements!
|
||||
.map<HTMLElement>((element) => new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
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<HTMLElement>((e) =>
|
||||
new HTMLDivElement()..textContent = '[ ${index++} ] : $e')
|
||||
.toList()
|
||||
..addAll(_createShowMoreButton());
|
||||
case M.InstanceKind.mirrorReference:
|
||||
return [
|
||||
new HTMLSpanElement()..textContent = '<referent> : ',
|
||||
anyRef(_isolate, _loadedInstance!.referent, _objects, queue: _r.queue)
|
||||
];
|
||||
case M.InstanceKind.stackTrace:
|
||||
return [
|
||||
new HTMLDivElement()
|
||||
..className = 'stackTraceBox'
|
||||
..textContent = _instance.valueAsString ?? ''
|
||||
];
|
||||
case M.InstanceKind.weakReference:
|
||||
return [
|
||||
new HTMLSpanElement()..textContent = '<target> : ',
|
||||
anyRef(_isolate, _loadedInstance!.target, _objects, queue: _r.queue)
|
||||
];
|
||||
case M.InstanceKind.weakProperty:
|
||||
return [
|
||||
new HTMLSpanElement()..textContent = '<key> : ',
|
||||
anyRef(_isolate, _loadedInstance!.key!, _objects, queue: _r.queue),
|
||||
new HTMLBRElement(),
|
||||
new HTMLSpanElement()..textContent = '<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 HTMLSpanElement()..textContent = '${fields[i].name} = ',
|
||||
new InstanceRefElement(
|
||||
_isolate, fields[i].value!.asValue!, _objects,
|
||||
queue: _r.queue)
|
||||
.element,
|
||||
if (i + 1 != fields.length) new HTMLBRElement(),
|
||||
]
|
||||
];
|
||||
case M.InstanceKind.finalizer:
|
||||
return [
|
||||
new HTMLSpanElement()..textContent = 'callback = ',
|
||||
anyRef(_isolate, _loadedInstance!.callback!, _objects,
|
||||
queue: _r.queue),
|
||||
new HTMLBRElement(),
|
||||
new HTMLSpanElement()..textContent = 'allEntries = ',
|
||||
anyRef(_isolate, _loadedInstance!.allEntries!, _objects,
|
||||
queue: _r.queue),
|
||||
];
|
||||
case M.InstanceKind.nativeFinalizer:
|
||||
return [
|
||||
new HTMLSpanElement()..textContent = 'allEntries = ',
|
||||
anyRef(_isolate, _loadedInstance!.allEntries!, _objects,
|
||||
queue: _r.queue),
|
||||
];
|
||||
case M.InstanceKind.finalizerEntry:
|
||||
return [
|
||||
new HTMLSpanElement()..textContent = 'value = ',
|
||||
anyRef(_isolate, _loadedInstance!.value!, _objects, queue: _r.queue),
|
||||
new HTMLBRElement(),
|
||||
new HTMLSpanElement()..textContent = 'detach = ',
|
||||
anyRef(_isolate, _loadedInstance!.detach!, _objects, queue: _r.queue),
|
||||
new HTMLBRElement(),
|
||||
new HTMLSpanElement()..textContent = 'token = ',
|
||||
anyRef(_isolate, _loadedInstance!.token!, _objects, queue: _r.queue),
|
||||
];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,517 +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 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/curly_block.dart';
|
||||
import 'package:observatory/src/elements/eval_box.dart';
|
||||
import 'package:observatory/src/elements/helpers/any_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_menu.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/instance_ref.dart';
|
||||
import 'package:observatory/src/elements/nav/class_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/isolate_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/library_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/refresh.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/vm_menu.dart';
|
||||
import 'package:observatory/src/elements/object_common.dart';
|
||||
import 'package:observatory/src/elements/source_inset.dart';
|
||||
import 'package:observatory/utils.dart';
|
||||
|
||||
class InstanceViewElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<InstanceViewElement> _r;
|
||||
|
||||
Stream<RenderedEvent<InstanceViewElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.VM _vm;
|
||||
late M.IsolateRef _isolate;
|
||||
late M.EventRepository _events;
|
||||
late M.NotificationRepository _notifications;
|
||||
late M.Instance _instance;
|
||||
M.LibraryRef? _library;
|
||||
late M.ObjectRepository _objects;
|
||||
late M.ClassRepository _classes;
|
||||
late M.RetainedSizeRepository _retainedSizes;
|
||||
late M.ReachableSizeRepository _reachableSizes;
|
||||
late M.InboundReferencesRepository _references;
|
||||
late M.RetainingPathRepository _retainingPaths;
|
||||
late M.ScriptRepository _scripts;
|
||||
late M.EvalRepository _eval;
|
||||
M.TypeArguments? _typeArguments;
|
||||
late M.TypeArgumentsRepository _arguments;
|
||||
late M.BreakpointRepository _breakpoints;
|
||||
late 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}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
void render() {
|
||||
final content = <HTMLElement>[
|
||||
new HTMLHeadingElement.h2()
|
||||
..textContent = M.isAbstractType(_instance.kind)
|
||||
? 'type ${_instance.name}'
|
||||
: 'instance of ${_instance.clazz!.name}',
|
||||
new HTMLHRElement(),
|
||||
new ObjectCommonElement(_isolate, _instance, _retainedSizes,
|
||||
_reachableSizes, _references, _retainingPaths, _objects,
|
||||
queue: _r.queue)
|
||||
.element,
|
||||
new HTMLBRElement(),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(_createMembers()),
|
||||
new HTMLHRElement(),
|
||||
new EvalBoxElement(_isolate, _instance, _objects, _eval,
|
||||
quickExpressions: const ['toString()', 'runtimeType'],
|
||||
queue: _r.queue)
|
||||
.element
|
||||
];
|
||||
if (_location != null) {
|
||||
content.addAll([
|
||||
new HTMLHRElement(),
|
||||
new SourceInsetElement(
|
||||
_isolate, _location!, _scripts, _objects, _events,
|
||||
queue: _r.queue)
|
||||
.element
|
||||
]);
|
||||
}
|
||||
children = <HTMLElement>[
|
||||
navBar(_createMenu()),
|
||||
new HTMLDivElement()
|
||||
..className = 'content-centered-big'
|
||||
..appendChildren(content)
|
||||
];
|
||||
}
|
||||
|
||||
List<HTMLElement> _createMenu() {
|
||||
final menu = <HTMLElement>[
|
||||
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(<HTMLElement>[
|
||||
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;
|
||||
}
|
||||
|
||||
HTMLElement memberHalf(String cssClass, dynamic half) {
|
||||
var result = new HTMLDivElement()..className = cssClass;
|
||||
if (half is String) {
|
||||
result.textContent = half;
|
||||
} else {
|
||||
result.setChildren(
|
||||
<HTMLElement>[anyRef(_isolate, half, _objects, queue: _r.queue)]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
HTMLElement member(dynamic name, dynamic value) {
|
||||
return new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
memberHalf('memberName', name),
|
||||
memberHalf('memberValue', value),
|
||||
]);
|
||||
}
|
||||
|
||||
List<HTMLElement> _createMembers() {
|
||||
final members = <HTMLElement>[];
|
||||
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 HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'type arguments',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(([new HTMLSpanElement()..textContent = '< ']
|
||||
..addAll(_typeArguments!.types!.expand((type) => [
|
||||
new InstanceRefElement(_isolate, type, _objects,
|
||||
queue: _r.queue)
|
||||
.element,
|
||||
new HTMLSpanElement()..textContent = ', '
|
||||
]))
|
||||
..removeLast()
|
||||
..add(new HTMLSpanElement()..textContent = ' >')))
|
||||
]));
|
||||
}
|
||||
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.closureReceiver != null) {
|
||||
members.add(member('closure receiver', _instance.closureReceiver));
|
||||
}
|
||||
if (_instance.kind == M.InstanceKind.closure) {
|
||||
late HTMLButtonElement btn;
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'closure breakpoint',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(<HTMLElement>[
|
||||
btn = new HTMLButtonElement()
|
||||
..textContent = _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 HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'native fields (${_instance.nativeFields!.length})',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(<HTMLElement>[
|
||||
(new CurlyBlockElement(
|
||||
expanded: _instance.nativeFields!.length <= 100,
|
||||
queue: _r.queue)
|
||||
..content = <HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(_instance.nativeFields!
|
||||
.map<HTMLElement>(
|
||||
(f) => member('[ ${i++} ]', '[ ${f.value} ]')))
|
||||
])
|
||||
.element
|
||||
])
|
||||
]));
|
||||
}
|
||||
|
||||
if (_instance.fields != null && _instance.fields!.isNotEmpty) {
|
||||
final fields = _instance.fields!.toList();
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'fields (${fields.length})',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(<HTMLElement>[
|
||||
(new CurlyBlockElement(
|
||||
expanded: fields.length <= 100, queue: _r.queue)
|
||||
..content = <HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(fields.map<HTMLElement>((f) {
|
||||
final name = _instance.kind == M.InstanceKind.record
|
||||
? f.name
|
||||
: f.decl;
|
||||
return member(name, f.value);
|
||||
}))
|
||||
])
|
||||
.element
|
||||
])
|
||||
]));
|
||||
}
|
||||
|
||||
if (_instance.elements != null && _instance.elements!.isNotEmpty) {
|
||||
final elements = _instance.elements!.toList();
|
||||
int i = 0;
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'elements (${_instance.length})',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(<HTMLElement>[
|
||||
(new CurlyBlockElement(
|
||||
expanded: elements.length <= 100, queue: _r.queue)
|
||||
..content = <HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(elements.map<HTMLElement>(
|
||||
(element) => member('[ ${i++} ]', element)))
|
||||
])
|
||||
.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 HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'associations (${_instance.length})',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(<HTMLElement>[
|
||||
(new CurlyBlockElement(
|
||||
expanded: associations.length <= 100, queue: _r.queue)
|
||||
..content = <HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(associations
|
||||
.map<HTMLElement>((a) => new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = '[ ',
|
||||
anyRef(_isolate, a.key, _objects,
|
||||
queue: _r.queue),
|
||||
new HTMLSpanElement()..textContent = ' ]',
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(<HTMLElement>[
|
||||
anyRef(_isolate, a.value, _objects,
|
||||
queue: _r.queue)
|
||||
])
|
||||
])))
|
||||
])
|
||||
.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 HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'elements (${_instance.length})',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(<HTMLElement>[
|
||||
(new CurlyBlockElement(
|
||||
expanded: typedElements.length <= 100, queue: _r.queue)
|
||||
..content = <HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(typedElements.map<HTMLElement>(
|
||||
(e) => member('[ ${i++} ]', '$e')))
|
||||
])
|
||||
.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.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));
|
||||
}
|
||||
|
||||
if (_instance.kind == M.InstanceKind.finalizer) {
|
||||
members.add(member('callback', _instance.callback));
|
||||
members.add(member('allEntries', _instance.allEntries));
|
||||
}
|
||||
|
||||
if (_instance.kind == M.InstanceKind.nativeFinalizer) {
|
||||
members.add(member('callback', _instance.allEntries));
|
||||
}
|
||||
|
||||
if (_instance.kind == M.InstanceKind.finalizerEntry) {
|
||||
members.add(member('value', _instance.value));
|
||||
members.add(member('detach', _instance.detach));
|
||||
members.add(member('token', _instance.token));
|
||||
}
|
||||
|
||||
return members;
|
||||
}
|
||||
|
||||
Future _refresh() async {
|
||||
_instance = await _objects.get(_isolate, _instance.id!) as M.Instance;
|
||||
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,67 +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:web/web.dart';
|
||||
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
|
||||
class IsolateCounterChartElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<IsolateCounterChartElement> _r;
|
||||
|
||||
Stream<RenderedEvent<IsolateCounterChartElement>> get onRendered =>
|
||||
_r.onRendered;
|
||||
|
||||
late Map _counters;
|
||||
late StreamSubscription _subscription;
|
||||
|
||||
factory IsolateCounterChartElement(Map counters, {RenderingQueue? queue}) {
|
||||
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 = element(this).onResize.listen((_) => _r.dirty());
|
||||
}
|
||||
|
||||
@override
|
||||
void detached() {
|
||||
super.detached();
|
||||
removeChildren();
|
||||
_r.disable(notify: true);
|
||||
_subscription.cancel();
|
||||
}
|
||||
|
||||
void render() {
|
||||
var members = <HTMLElement>[];
|
||||
_counters.forEach((key, value) {
|
||||
members.add(new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = key,
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = value,
|
||||
]));
|
||||
});
|
||||
|
||||
children = <HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(members)
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,129 +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:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/function_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/source_link.dart';
|
||||
|
||||
class IsolateLocationElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<IsolateLocationElement> _r;
|
||||
|
||||
Stream<RenderedEvent<IsolateLocationElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.Isolate _isolate;
|
||||
late M.EventRepository _events;
|
||||
late M.ScriptRepository _scripts;
|
||||
late StreamSubscription _debugSubscription;
|
||||
late StreamSubscription _isolateSubscription;
|
||||
|
||||
factory IsolateLocationElement(
|
||||
M.Isolate isolate, M.EventRepository events, M.ScriptRepository scripts,
|
||||
{RenderingQueue? queue}) {
|
||||
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();
|
||||
removeChildren();
|
||||
_r.disable(notify: true);
|
||||
_debugSubscription.cancel();
|
||||
_isolateSubscription.cancel();
|
||||
}
|
||||
|
||||
void render() {
|
||||
switch (_isolate.status) {
|
||||
case M.IsolateStatus.loading:
|
||||
children = <HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'not yet runnable'
|
||||
];
|
||||
break;
|
||||
case M.IsolateStatus.running:
|
||||
children = <HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'at ',
|
||||
new FunctionRefElement(
|
||||
_isolate, M.topFrame(_isolate.pauseEvent)!.function!,
|
||||
queue: _r.queue)
|
||||
.element,
|
||||
new HTMLSpanElement()..textContent = ' (',
|
||||
new SourceLinkElement(_isolate,
|
||||
M.topFrame(_isolate.pauseEvent)!.location!, _scripts,
|
||||
queue: _r.queue)
|
||||
.element,
|
||||
new HTMLSpanElement()..textContent = ') '
|
||||
];
|
||||
break;
|
||||
case M.IsolateStatus.paused:
|
||||
if (_isolate.pauseEvent is M.PauseStartEvent) {
|
||||
children = <HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'at isolate start'
|
||||
];
|
||||
} else if (_isolate.pauseEvent is M.PauseExitEvent) {
|
||||
children = <HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'at isolate exit'
|
||||
];
|
||||
} else if (_isolate.pauseEvent is M.NoneEvent) {
|
||||
children = <HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'not yet runnable'
|
||||
];
|
||||
} else {
|
||||
final content = <HTMLElement>[];
|
||||
if (_isolate.pauseEvent is M.PauseBreakpointEvent) {
|
||||
content.add(new HTMLSpanElement()..textContent = 'by breakpoint');
|
||||
} else if (_isolate.pauseEvent is M.PauseExceptionEvent) {
|
||||
content.add(new HTMLSpanElement()..textContent = 'by exception');
|
||||
}
|
||||
if (M.topFrame(_isolate.pauseEvent) != null) {
|
||||
content.addAll([
|
||||
new HTMLSpanElement()..textContent = ' at ',
|
||||
new FunctionRefElement(
|
||||
_isolate, M.topFrame(_isolate.pauseEvent)!.function!,
|
||||
queue: _r.queue)
|
||||
.element,
|
||||
new HTMLSpanElement()..textContent = ' (',
|
||||
new SourceLinkElement(_isolate,
|
||||
M.topFrame(_isolate.pauseEvent)!.location!, _scripts,
|
||||
queue: _r.queue)
|
||||
.element,
|
||||
new HTMLSpanElement()..textContent = ') '
|
||||
]);
|
||||
}
|
||||
children = content;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
children = const [];
|
||||
}
|
||||
}
|
||||
|
||||
void _eventListener(e) {
|
||||
if (e.isolate.id == _isolate.id) {
|
||||
// This view doesn't display registered service extensions.
|
||||
if (e is! M.ServiceRegisteredEvent && e is! M.ServiceUnregisteredEvent) {
|
||||
_r.dirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,85 +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:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
|
||||
class IsolateRunStateElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<IsolateRunStateElement> _r;
|
||||
|
||||
Stream<RenderedEvent<IsolateRunStateElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.Isolate _isolate;
|
||||
late M.EventRepository _events;
|
||||
late StreamSubscription _debugSubscription;
|
||||
late StreamSubscription _isolateSubscription;
|
||||
|
||||
factory IsolateRunStateElement(M.Isolate isolate, M.EventRepository events,
|
||||
{RenderingQueue? queue}) {
|
||||
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();
|
||||
removeChildren();
|
||||
_r.disable(notify: true);
|
||||
_debugSubscription.cancel();
|
||||
_isolateSubscription.cancel();
|
||||
}
|
||||
|
||||
void render() {
|
||||
switch (_isolate.status!) {
|
||||
case M.IsolateStatus.loading:
|
||||
children = <HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'loading... '
|
||||
];
|
||||
break;
|
||||
case M.IsolateStatus.running:
|
||||
children = <HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'running '
|
||||
];
|
||||
break;
|
||||
case M.IsolateStatus.idle:
|
||||
children = <HTMLElement>[new HTMLSpanElement()..textContent = 'idle '];
|
||||
break;
|
||||
case M.IsolateStatus.paused:
|
||||
children = <HTMLElement>[
|
||||
new HTMLSpanElement()
|
||||
..title = '${_isolate.pauseEvent!.timestamp}'
|
||||
..textContent = 'paused '
|
||||
];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _eventListener(e) {
|
||||
if (e.isolate.id == _isolate.id) {
|
||||
// This view doesn't display registered service extensions.
|
||||
if (e is! M.ServiceRegisteredEvent && e is! M.ServiceUnregisteredEvent) {
|
||||
_isolate = e.isolate;
|
||||
_r.dirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,197 +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:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
import 'package:observatory/src/elements/isolate/counter_chart.dart';
|
||||
import 'package:observatory/utils.dart';
|
||||
|
||||
class IsolateSharedSummaryElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<IsolateSharedSummaryElement> _r;
|
||||
|
||||
Stream<RenderedEvent<IsolateSharedSummaryElement>> get onRendered =>
|
||||
_r.onRendered;
|
||||
|
||||
late M.Isolate _isolate;
|
||||
late M.EventRepository _events;
|
||||
late StreamSubscription _isolateSubscription;
|
||||
|
||||
factory IsolateSharedSummaryElement(
|
||||
M.Isolate isolate, M.EventRepository events,
|
||||
{RenderingQueue? queue}) {
|
||||
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();
|
||||
removeChildren();
|
||||
_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 = <HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'menu'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'new heap',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = '$newHeapUsed of $newHeapCapacity',
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'old heap',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = '$oldHeapUsed of $oldHeapCapacity',
|
||||
])
|
||||
]),
|
||||
new HTMLBRElement(),
|
||||
new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'see ',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.debugger(_isolate)
|
||||
..textContent = 'debugger'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'see ',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.classTree(_isolate)
|
||||
..textContent = 'class hierarchy'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'see ',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.cpuProfiler(_isolate)
|
||||
..textContent = 'cpu profile'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'see ',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.cpuProfilerTable(_isolate)
|
||||
..textContent = 'cpu profile (table)'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'see ',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.allocationProfiler(_isolate)
|
||||
..textContent = 'allocation profile'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'see ',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.heapSnapshot(_isolate)
|
||||
..textContent = 'heap snapshot'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'see ',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.heapMap(_isolate)
|
||||
..textContent = 'heap map'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'see ',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.metrics(_isolate)
|
||||
..textContent = 'metrics'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'see ',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.persistentHandles(_isolate)
|
||||
..textContent = 'persistent handles'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'see ',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.ports(_isolate)
|
||||
..textContent = 'ports'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'see ',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.logging(_isolate)
|
||||
..textContent = 'logging'
|
||||
])
|
||||
]),
|
||||
new IsolateCounterChartElement(_isolate.counters!, queue: _r.queue)
|
||||
.element
|
||||
];
|
||||
if (_isolate.error != null) {
|
||||
appendChildren(<HTMLElement>[
|
||||
new HTMLPreElement.pre()
|
||||
..className = 'errorBox'
|
||||
..textContent = _isolate.error!.message ?? '',
|
||||
new HTMLDivElement()
|
||||
..className = 'summary'
|
||||
..appendChildren(content)
|
||||
]);
|
||||
} else {
|
||||
appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'summary'
|
||||
..appendChildren(content)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
void _eventListener(e) {
|
||||
if (e.isolate.id == _isolate.id) {
|
||||
// This view doesn't display registered service extensions.
|
||||
if (e is! M.ServiceRegisteredEvent && e is! M.ServiceUnregisteredEvent) {
|
||||
_isolate = e.isolate;
|
||||
_r.dirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,192 +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:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
import 'package:observatory/src/elements/isolate/location.dart';
|
||||
import 'package:observatory/src/elements/isolate/run_state.dart';
|
||||
import 'package:observatory/src/elements/isolate_ref.dart';
|
||||
import 'package:observatory/utils.dart';
|
||||
|
||||
class IsolateSummaryElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<IsolateSummaryElement> _r;
|
||||
|
||||
Stream<RenderedEvent<IsolateSummaryElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.IsolateRef _isolate;
|
||||
late M.EventRepository _events;
|
||||
late M.IsolateRepository _isolates;
|
||||
late M.ScriptRepository _scripts;
|
||||
M.Isolate? _loadedIsolate;
|
||||
|
||||
factory IsolateSummaryElement(
|
||||
M.IsolateRef isolate,
|
||||
M.IsolateRepository isolates,
|
||||
M.EventRepository events,
|
||||
M.ScriptRepository scripts,
|
||||
{RenderingQueue? queue}) {
|
||||
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();
|
||||
removeChildren();
|
||||
_r.disable(notify: true);
|
||||
}
|
||||
|
||||
void render() {
|
||||
if (_loadedIsolate == null) {
|
||||
children = <HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'loading ',
|
||||
new IsolateRefElement(_isolate, _events, queue: _r.queue).element
|
||||
];
|
||||
} else {
|
||||
children = <HTMLElement>[
|
||||
linkAndStatusRow(),
|
||||
new HTMLBRElement(),
|
||||
memoryRow(),
|
||||
new HTMLBRElement(),
|
||||
toolsRow(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
HTMLElement linkAndStatusRow() {
|
||||
return new HTMLDivElement()
|
||||
..className = 'flex-row-wrap'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'isolate-ref-container'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new IsolateRefElement(_isolate, _events, queue: _r.queue).element
|
||||
]),
|
||||
new HTMLDivElement()..style.flex = '1',
|
||||
new HTMLDivElement()
|
||||
..className = 'flex-row isolate-state-container'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new IsolateRunStateElement(_isolate as M.Isolate, _events,
|
||||
queue: _r.queue)
|
||||
.element,
|
||||
new IsolateLocationElement(_isolate as M.Isolate, _events, _scripts,
|
||||
queue: _r.queue)
|
||||
.element,
|
||||
new HTMLSpanElement()..textContent = ' [',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.debugger(_isolate)
|
||||
..textContent = 'debug',
|
||||
new HTMLSpanElement()..textContent = ']'
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
HTMLElement 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 HTMLDivElement()
|
||||
..className = 'flex-row-wrap-right'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..style.padding = '5px'
|
||||
..textContent = 'new-space $newHeapUsed of $newHeapCapacity',
|
||||
new HTMLDivElement()
|
||||
..style.padding = '5px'
|
||||
..textContent = '/',
|
||||
new HTMLDivElement()
|
||||
..style.padding = '5px'
|
||||
..textContent = 'old-space $oldHeapUsed of $oldHeapCapacity',
|
||||
new HTMLDivElement()
|
||||
..style.padding = '5px'
|
||||
..textContent = '/',
|
||||
new HTMLDivElement()
|
||||
..style.padding = '5px'
|
||||
..textContent = 'heap $heapUsed of $heapCapacity',
|
||||
]);
|
||||
}
|
||||
|
||||
HTMLElement toolsRow() {
|
||||
return new HTMLDivElement()
|
||||
..className = 'flex-row-spaced'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.debugger(_isolate)
|
||||
..className = 'flex-item-even'
|
||||
..text = 'debugger',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.classTree(_isolate)
|
||||
..className = 'flex-item-even'
|
||||
..text = 'class hierarchy',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.cpuProfiler(_isolate)
|
||||
..className = 'flex-item-even'
|
||||
..text = 'cpu profile',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.cpuProfilerTable(_isolate)
|
||||
..className = 'flex-item-even'
|
||||
..text = 'cpu profile (table)',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.allocationProfiler(_isolate)
|
||||
..className = 'flex-item-even'
|
||||
..text = 'allocation profile',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.heapSnapshot(_isolate)
|
||||
..className = 'flex-item-even'
|
||||
..text = 'heap snapshot',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.heapMap(_isolate)
|
||||
..className = 'flex-item-even'
|
||||
..text = 'heap map',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.metrics(_isolate)
|
||||
..className = 'flex-item-even'
|
||||
..text = 'metrics',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.persistentHandles(_isolate)
|
||||
..className = 'flex-item-even'
|
||||
..text = 'persistent handles',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.ports(_isolate)
|
||||
..className = 'flex-item-even'
|
||||
..text = 'ports',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.logging(_isolate)
|
||||
..className = 'flex-item-even'
|
||||
..text = 'logging',
|
||||
]);
|
||||
}
|
||||
|
||||
Future _load() async {
|
||||
_loadedIsolate = await _isolates.get(_isolate);
|
||||
_r.dirty();
|
||||
}
|
||||
}
|
||||
@@ -1,110 +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:async';
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
|
||||
class IsolateReconnectElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<IsolateReconnectElement> _r;
|
||||
|
||||
Stream<RenderedEvent<IsolateReconnectElement>> get onRendered =>
|
||||
_r.onRendered;
|
||||
|
||||
late M.VM _vm;
|
||||
late String _missing;
|
||||
late Uri _uri;
|
||||
late M.EventRepository _events;
|
||||
late StreamSubscription _subscription;
|
||||
|
||||
M.VM get vm => _vm;
|
||||
String get missing => _missing;
|
||||
Uri get uri => _uri;
|
||||
|
||||
late M.NotificationRepository _notifications;
|
||||
factory IsolateReconnectElement(M.VM vm, M.EventRepository events,
|
||||
M.NotificationRepository notifications, String missing, Uri uri,
|
||||
{RenderingQueue? queue}) {
|
||||
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 as M.VM;
|
||||
_r.dirty();
|
||||
});
|
||||
_r.enable();
|
||||
}
|
||||
|
||||
@override
|
||||
void detached() {
|
||||
super.detached();
|
||||
removeChildren();
|
||||
_r.disable(notify: true);
|
||||
_subscription.cancel();
|
||||
}
|
||||
|
||||
void render() {
|
||||
children = <HTMLElement>[
|
||||
navBar(<HTMLElement>[
|
||||
new NavTopMenuElement(queue: _r.queue).element,
|
||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'content-centered'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLHeadingElement.h1()
|
||||
..textContent = 'Isolate $_missing no longer exists',
|
||||
new HTMLHRElement(),
|
||||
new HTMLBRElement(),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren((_vm.isolates.map<HTMLElement>((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 HTMLDivElement()
|
||||
..className = 'memberItem doubleSpaced'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'Continue in ',
|
||||
new HTMLAnchorElement()
|
||||
..href = '#$href'
|
||||
..className = 'isolate-link'
|
||||
..text = '${isolate.id} (${isolate.name})'
|
||||
]);
|
||||
}).toList()
|
||||
..add(new HTMLDivElement()
|
||||
..className = 'memberItem doubleSpaced'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'Go to ',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.vm()
|
||||
..text = 'isolates summary',
|
||||
]))))
|
||||
]),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,67 +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:async';
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M show IsolateRef, EventRepository;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
|
||||
class IsolateRefElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<IsolateRefElement> _r;
|
||||
|
||||
Stream<RenderedEvent<IsolateRefElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.IsolateRef _isolate;
|
||||
late M.EventRepository _events;
|
||||
late StreamSubscription _updatesSubscription;
|
||||
|
||||
M.IsolateRef get isolate => _isolate;
|
||||
|
||||
factory IsolateRefElement(M.IsolateRef isolate, M.EventRepository events,
|
||||
{RenderingQueue? queue}) {
|
||||
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();
|
||||
removeChildren();
|
||||
_r.disable(notify: true);
|
||||
_updatesSubscription.cancel();
|
||||
}
|
||||
|
||||
void render() {
|
||||
final isolateType = isolate.isSystemIsolate! ? 'System Isolate' : 'Isolate';
|
||||
children = <HTMLElement>[
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.inspect(isolate)
|
||||
..text = '$isolateType ${isolate.number} (${isolate.name})'
|
||||
..className = 'isolate-ref'
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,318 +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 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/curly_block.dart';
|
||||
import 'package:observatory/src/elements/eval_box.dart';
|
||||
import 'package:observatory/src/elements/function_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
import 'package:observatory/src/elements/isolate/location.dart';
|
||||
import 'package:observatory/src/elements/isolate/run_state.dart';
|
||||
import 'package:observatory/src/elements/isolate/shared_summary.dart';
|
||||
import 'package:observatory/src/elements/library_ref.dart';
|
||||
import 'package:observatory/src/elements/nav/isolate_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/refresh.dart';
|
||||
import 'package:observatory/src/elements/nav/reload.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/vm_menu.dart';
|
||||
import 'package:observatory/src/elements/script_inset.dart';
|
||||
import 'package:observatory/src/elements/source_inset.dart';
|
||||
|
||||
class IsolateViewElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<IsolateViewElement> _r;
|
||||
|
||||
Stream<RenderedEvent<IsolateViewElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.VM _vm;
|
||||
late M.Isolate _isolate;
|
||||
late M.EventRepository _events;
|
||||
late M.NotificationRepository _notifications;
|
||||
late M.IsolateRepository _isolates;
|
||||
late M.ScriptRepository _scripts;
|
||||
late M.FunctionRepository _functions;
|
||||
late M.LibraryRepository _libraries;
|
||||
late M.ObjectRepository _objects;
|
||||
late M.EvalRepository _eval;
|
||||
M.ServiceFunction? _function;
|
||||
M.ScriptRef? _rootScript;
|
||||
late 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}) {
|
||||
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);
|
||||
removeChildren();
|
||||
_subscription.cancel();
|
||||
}
|
||||
|
||||
void render() {
|
||||
final uptime = new DateTime.now().difference(_isolate.startTime!);
|
||||
final libraries = _isolate.libraries!.toList();
|
||||
children = <HTMLElement>[
|
||||
navBar(<HTMLElement>[
|
||||
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 HTMLDivElement()
|
||||
..className = 'content-centered-big'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLHeadingElement.h2()..textContent = 'Isolate ${_isolate.name}',
|
||||
new HTMLBRElement(),
|
||||
new HTMLDivElement()
|
||||
..className = 'flex-row'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()..style.flex = '1',
|
||||
new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new IsolateRunStateElement(_isolate, _events, queue: _r.queue)
|
||||
.element,
|
||||
new IsolateLocationElement(_isolate, _events, _scripts,
|
||||
queue: _r.queue)
|
||||
.element,
|
||||
new HTMLSpanElement()..textContent = ' [',
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.debugger(_isolate)
|
||||
..textContent = 'debug',
|
||||
new HTMLSpanElement()..textContent = ']'
|
||||
])
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..appendChildren(_function != null
|
||||
? [
|
||||
new HTMLBRElement(),
|
||||
(new SourceInsetElement(_isolate, _function!.location!,
|
||||
_scripts, _objects, _events,
|
||||
currentPos: M
|
||||
.topFrame(isolate.pauseEvent)!
|
||||
.location!
|
||||
.tokenPos,
|
||||
queue: _r.queue)
|
||||
..className = 'header_inset')
|
||||
.element
|
||||
]
|
||||
: const []),
|
||||
new HTMLHRElement(),
|
||||
new IsolateSharedSummaryElement(_isolate, _events, queue: _r.queue)
|
||||
.element,
|
||||
new HTMLHRElement(),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'started at',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = '${_isolate.startTime}'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'uptime',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = '$uptime'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'root library',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(<HTMLElement>[
|
||||
_isolate.rootLibrary == null
|
||||
? (new HTMLSpanElement()..textContent = 'loading...')
|
||||
: new LibraryRefElement(
|
||||
_isolate, _isolate.rootLibrary!,
|
||||
queue: _r.queue)
|
||||
.element
|
||||
])
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(_isolate.entry != null
|
||||
? [
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'entry',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new FunctionRefElement(_isolate, _isolate.entry!,
|
||||
queue: _r.queue)
|
||||
.element
|
||||
])
|
||||
]
|
||||
: const []),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'isolate id',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = '${_isolate.number}'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'service protocol extensions',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = '${_isolate.extensionRPCs}'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'object store',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(<HTMLElement>[
|
||||
HTMLAnchorElement()
|
||||
..href = Uris.objectStore(_isolate)
|
||||
..textContent = 'object store'
|
||||
])
|
||||
]),
|
||||
new HTMLBRElement(),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'libraries (${libraries.length})',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(<HTMLElement>[
|
||||
(new CurlyBlockElement(queue: _r.queue)
|
||||
..content = libraries
|
||||
.map<HTMLElement>((l) => new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new LibraryRefElement(_isolate, l,
|
||||
queue: _r.queue)
|
||||
.element
|
||||
]))
|
||||
.toList())
|
||||
.element
|
||||
])
|
||||
]),
|
||||
]),
|
||||
new HTMLHRElement(),
|
||||
new EvalBoxElement(_isolate, _isolate.rootLibrary!, _objects, _eval,
|
||||
queue: _r.queue)
|
||||
.element,
|
||||
new HTMLDivElement()
|
||||
..appendChildren(_rootScript != null
|
||||
? [
|
||||
new HTMLHRElement(),
|
||||
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,146 +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 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
|
||||
class JSONViewElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<JSONViewElement> _r;
|
||||
|
||||
Stream<RenderedEvent<JSONViewElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.NotificationRepository _notifications;
|
||||
late Map _map;
|
||||
|
||||
M.NotificationRepository get notifications => _notifications;
|
||||
Map get map => _map;
|
||||
|
||||
factory JSONViewElement(Map map, M.NotificationRepository notifications,
|
||||
{RenderingQueue? queue}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
void render() {
|
||||
children = <HTMLElement>[
|
||||
navBar(<HTMLElement>[
|
||||
new NavTopMenuElement(queue: _r.queue).element,
|
||||
new NavNotifyElement(_notifications, queue: _r.queue).element
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'content-centered-big'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLHeadingElement.h2()..textContent = 'Object',
|
||||
new HTMLHRElement(),
|
||||
new HTMLPreElement.pre()..textContent = 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,59 +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:async';
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M show IsolateRef, LibraryRef;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
|
||||
class LibraryRefElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<LibraryRefElement> _r;
|
||||
|
||||
Stream<RenderedEvent<LibraryRefElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.IsolateRef _isolate;
|
||||
late M.LibraryRef _library;
|
||||
|
||||
M.IsolateRef get isolate => _isolate;
|
||||
M.LibraryRef get library => _library;
|
||||
|
||||
factory LibraryRefElement(M.IsolateRef isolate, M.LibraryRef library,
|
||||
{RenderingQueue? queue}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
void render() {
|
||||
final name = _library.name;
|
||||
children = <HTMLElement>[
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.inspect(_isolate, object: _library)
|
||||
..text = (name == null || name.isEmpty) ? 'unnamed' : name
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,321 +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 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/class_ref.dart';
|
||||
import 'package:observatory/src/elements/curly_block.dart';
|
||||
import 'package:observatory/src/elements/eval_box.dart';
|
||||
import 'package:observatory/src/elements/field_ref.dart';
|
||||
import 'package:observatory/src/elements/function_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/any_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/library_ref.dart';
|
||||
import 'package:observatory/src/elements/nav/isolate_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/library_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/refresh.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/vm_menu.dart';
|
||||
import 'package:observatory/src/elements/object_common.dart';
|
||||
import 'package:observatory/src/elements/script_inset.dart';
|
||||
import 'package:observatory/src/elements/script_ref.dart';
|
||||
|
||||
class LibraryViewElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<LibraryViewElement> _r;
|
||||
|
||||
Stream<RenderedEvent<LibraryViewElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.VM _vm;
|
||||
late M.IsolateRef _isolate;
|
||||
late M.EventRepository _events;
|
||||
late M.NotificationRepository _notifications;
|
||||
late M.Library _library;
|
||||
late M.LibraryRepository _libraries;
|
||||
late M.FieldRepository _fields;
|
||||
late M.RetainedSizeRepository _retainedSizes;
|
||||
late M.ReachableSizeRepository _reachableSizes;
|
||||
late M.InboundReferencesRepository _references;
|
||||
late M.RetainingPathRepository _retainingPaths;
|
||||
late M.ScriptRepository _scripts;
|
||||
late M.ObjectRepository _objects;
|
||||
late 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}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
void render() {
|
||||
final rootScript = library.rootScript;
|
||||
|
||||
children = <HTMLElement>[
|
||||
navBar(<HTMLElement>[
|
||||
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 HTMLDivElement()
|
||||
..className = 'content-centered-big'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLHeadingElement.h2()..textContent = 'Library',
|
||||
new HTMLHRElement(),
|
||||
new ObjectCommonElement(_isolate, _library, _retainedSizes,
|
||||
_reachableSizes, _references, _retainingPaths, _objects,
|
||||
queue: _r.queue)
|
||||
.element,
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'uri',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = _library.uri ?? ''
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'vm name',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = _library.vmName ?? ''
|
||||
])
|
||||
]),
|
||||
new HTMLHRElement(),
|
||||
new EvalBoxElement(_isolate, _library, _objects, _eval,
|
||||
queue: _r.queue)
|
||||
.element,
|
||||
new HTMLHRElement(),
|
||||
_createDependencies(),
|
||||
new HTMLBRElement(),
|
||||
_createScripts(),
|
||||
new HTMLBRElement(),
|
||||
_createClasses(),
|
||||
new HTMLBRElement(),
|
||||
_createVariables(),
|
||||
new HTMLBRElement(),
|
||||
_createFunctions(),
|
||||
if (rootScript != null) ...[
|
||||
new HTMLHRElement(),
|
||||
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();
|
||||
}
|
||||
|
||||
HTMLElement _createDependencies() {
|
||||
if (_library.dependencies!.isEmpty) {
|
||||
return new HTMLSpanElement();
|
||||
}
|
||||
final dependencies = _library.dependencies!.toList();
|
||||
return new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()
|
||||
..textContent = 'dependencies (${dependencies.length}) ',
|
||||
(new CurlyBlockElement(queue: _r.queue)
|
||||
..content = dependencies
|
||||
.map<HTMLElement>((d) => new HTMLDivElement()
|
||||
..className = 'indent'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()
|
||||
..textContent = d.isImport ? 'import ' : 'export ',
|
||||
new LibraryRefElement(_isolate, d.target, queue: _r.queue)
|
||||
.element,
|
||||
new HTMLSpanElement()
|
||||
..textContent =
|
||||
d.prefix == null ? '' : ' as ${d.prefix}',
|
||||
new HTMLSpanElement()
|
||||
..textContent = d.isDeferred ? ' deferred' : '',
|
||||
]))
|
||||
.toList())
|
||||
.element
|
||||
]);
|
||||
}
|
||||
|
||||
HTMLElement _createScripts() {
|
||||
if (_library.scripts!.isEmpty) {
|
||||
return new HTMLSpanElement();
|
||||
}
|
||||
final scripts = _library.scripts!.toList();
|
||||
return new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'scripts (${scripts.length}) ',
|
||||
(new CurlyBlockElement(queue: _r.queue)
|
||||
..content = scripts
|
||||
.map<HTMLElement>((s) => new HTMLDivElement()
|
||||
..className = 'indent'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new ScriptRefElement(_isolate, s, queue: _r.queue).element
|
||||
]))
|
||||
.toList())
|
||||
.element
|
||||
]);
|
||||
}
|
||||
|
||||
HTMLElement _createClasses() {
|
||||
if (_library.classes!.isEmpty) {
|
||||
return new HTMLSpanElement();
|
||||
}
|
||||
final classes = _library.classes!.toList();
|
||||
return new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'classes (${classes.length}) ',
|
||||
(new CurlyBlockElement(queue: _r.queue)
|
||||
..content = classes
|
||||
.map<HTMLElement>((c) => new HTMLDivElement()
|
||||
..className = 'indent'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new ClassRefElement(_isolate, c, queue: _r.queue).element
|
||||
]))
|
||||
.toList())
|
||||
.element
|
||||
]);
|
||||
}
|
||||
|
||||
HTMLElement _createVariables() {
|
||||
if (_library.variables!.isEmpty) {
|
||||
return new HTMLSpanElement();
|
||||
}
|
||||
final variables = _library.variables!.toList();
|
||||
return new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'variables (${variables.length}) ',
|
||||
(new CurlyBlockElement(queue: _r.queue)
|
||||
..content = <HTMLElement>[
|
||||
_variables == null
|
||||
? (new HTMLSpanElement()..textContent = 'loading...')
|
||||
: (new HTMLDivElement()
|
||||
..className = 'indent memberList'
|
||||
..appendChildren(_variables!
|
||||
.map<HTMLElement>((f) => new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new FieldRefElement(_isolate, f, _objects,
|
||||
queue: _r.queue)
|
||||
.element
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = ' = ',
|
||||
anyRef(_isolate, f.staticValue, _objects,
|
||||
queue: _r.queue)
|
||||
])
|
||||
]))))
|
||||
])
|
||||
.element
|
||||
]);
|
||||
}
|
||||
|
||||
HTMLElement _createFunctions() {
|
||||
if (_library.functions!.isEmpty) {
|
||||
return new HTMLSpanElement();
|
||||
}
|
||||
final functions = _library.functions!.toList();
|
||||
return new HTMLDivElement()
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()..textContent = 'functions (${functions.length}) ',
|
||||
(new CurlyBlockElement(queue: _r.queue)
|
||||
..content = functions
|
||||
.map<HTMLElement>((f) => new HTMLDivElement()
|
||||
..className = 'indent'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new FunctionRefElement(_isolate, f, queue: _r.queue)
|
||||
.element
|
||||
]))
|
||||
.toList())
|
||||
.element
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M
|
||||
show IsolateRef, LocalVarDescriptorsRef;
|
||||
|
||||
class LocalVarDescriptorsRefElement extends CustomElement
|
||||
implements Renderable {
|
||||
late RenderingScheduler<LocalVarDescriptorsRefElement> _r;
|
||||
|
||||
Stream<RenderedEvent<LocalVarDescriptorsRefElement>> get onRendered =>
|
||||
_r.onRendered;
|
||||
|
||||
late M.IsolateRef _isolate;
|
||||
late M.LocalVarDescriptorsRef _localVar;
|
||||
|
||||
M.IsolateRef get isolate => _isolate;
|
||||
M.LocalVarDescriptorsRef get localVar => _localVar;
|
||||
|
||||
factory LocalVarDescriptorsRefElement(
|
||||
M.IsolateRef isolate, M.LocalVarDescriptorsRef localVar,
|
||||
{RenderingQueue? queue}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
void render() {
|
||||
final text = (_localVar.name == null || _localVar.name == '')
|
||||
? 'LocalVarDescriptors'
|
||||
: _localVar.name;
|
||||
children = <HTMLElement>[
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.inspect(_isolate, object: _localVar)
|
||||
..text = text ?? ''
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,112 +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 'package:logging/logging.dart';
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_menu.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/logging_list.dart';
|
||||
import 'package:observatory/src/elements/nav/isolate_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/refresh.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/vm_menu.dart';
|
||||
|
||||
class LoggingPageElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<LoggingPageElement> _r;
|
||||
|
||||
Stream<RenderedEvent<LoggingPageElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.VM _vm;
|
||||
late M.IsolateRef _isolate;
|
||||
late M.EventRepository _events;
|
||||
late M.NotificationRepository _notifications;
|
||||
late 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}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
LoggingListElement? _logs;
|
||||
|
||||
void render() {
|
||||
_logs = _logs ?? new LoggingListElement(_isolate, _events);
|
||||
_logs!.level = _level;
|
||||
children = <HTMLElement>[
|
||||
navBar(<HTMLElement>[
|
||||
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 HTMLDivElement()
|
||||
..className = 'content-centered-big'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLHeadingElement.h2()..textContent = 'Logging',
|
||||
new HTMLSpanElement()..textContent = 'Show messages with severity ',
|
||||
_createLevelSelector(),
|
||||
new HTMLHRElement(),
|
||||
_logs!.element
|
||||
])
|
||||
];
|
||||
}
|
||||
|
||||
HTMLElement _createLevelSelector() {
|
||||
final s = HTMLSelectElement()
|
||||
..value = _level.name
|
||||
..appendChildren(Level.LEVELS.map((level) => HTMLOptionElement()
|
||||
..value = level.name
|
||||
..selected = _level == level
|
||||
..text = level.name));
|
||||
s.onChange.listen((_) {
|
||||
_level = Level.LEVELS[s.selectedIndex];
|
||||
_r.dirty();
|
||||
});
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -1,85 +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 'package:logging/logging.dart';
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/utils.dart';
|
||||
|
||||
class LoggingListElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<LoggingListElement> _r;
|
||||
|
||||
Stream<RenderedEvent<LoggingListElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.IsolateRef _isolate;
|
||||
late M.EventRepository _events;
|
||||
late 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}) {
|
||||
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);
|
||||
removeChildren();
|
||||
_subscription.cancel();
|
||||
}
|
||||
|
||||
void render() {
|
||||
children = _logs
|
||||
.where(_shouldBeVisible)
|
||||
.map<HTMLElement>((logRecord) => new HTMLDivElement()
|
||||
..className = 'logItem ${logRecord["level"].name}'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()
|
||||
..className = 'level'
|
||||
..textContent = logRecord['level'].name,
|
||||
new HTMLSpanElement()
|
||||
..className = 'time'
|
||||
..textContent = Utils.formatDateTime(logRecord['time']),
|
||||
new HTMLSpanElement()
|
||||
..className = 'message'
|
||||
..textContent = logRecord["message"].valueAsString
|
||||
]))
|
||||
.toList();
|
||||
}
|
||||
|
||||
bool _shouldBeVisible(Map record) => _level.compareTo(record['level']) <= 0;
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M
|
||||
show IsolateRef, MegamorphicCacheRef;
|
||||
|
||||
class MegamorphicCacheRefElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<MegamorphicCacheRefElement> _r;
|
||||
|
||||
Stream<RenderedEvent<MegamorphicCacheRefElement>> get onRendered =>
|
||||
_r.onRendered;
|
||||
|
||||
late M.IsolateRef _isolate;
|
||||
late M.MegamorphicCacheRef _cache;
|
||||
|
||||
M.IsolateRef get isolate => _isolate;
|
||||
M.MegamorphicCacheRef get cache => _cache;
|
||||
|
||||
factory MegamorphicCacheRefElement(
|
||||
M.IsolateRef isolate, M.MegamorphicCacheRef cache,
|
||||
{RenderingQueue? queue}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
void render() {
|
||||
children = <HTMLElement>[
|
||||
new HTMLAnchorElement()
|
||||
..href = Uris.inspect(_isolate, object: _cache)
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLSpanElement()
|
||||
..className = 'emphasize'
|
||||
..textContent = 'MegamorphicCache',
|
||||
new HTMLSpanElement()..textContent = ' (${_cache.selector})'
|
||||
])
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,172 +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 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/helpers/any_ref.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_menu.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/nav/isolate_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/refresh.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/vm_menu.dart';
|
||||
import 'package:observatory/src/elements/object_common.dart';
|
||||
|
||||
class MegamorphicCacheViewElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<MegamorphicCacheViewElement> _r;
|
||||
|
||||
Stream<RenderedEvent<MegamorphicCacheViewElement>> get onRendered =>
|
||||
_r.onRendered;
|
||||
|
||||
late M.VM _vm;
|
||||
late M.IsolateRef _isolate;
|
||||
late M.EventRepository _events;
|
||||
late M.NotificationRepository _notifications;
|
||||
late M.MegamorphicCache _cache;
|
||||
late M.MegamorphicCacheRepository _caches;
|
||||
late M.RetainedSizeRepository _retainedSizes;
|
||||
late M.ReachableSizeRepository _reachableSizes;
|
||||
late M.InboundReferencesRepository _references;
|
||||
late M.RetainingPathRepository _retainingPaths;
|
||||
late 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}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
void render() {
|
||||
children = <HTMLElement>[
|
||||
navBar(<HTMLElement>[
|
||||
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 HTMLDivElement()
|
||||
..className = 'content-centered-big'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLHeadingElement.h2()..textContent = 'Megamorphic Cache',
|
||||
new HTMLHRElement(),
|
||||
new ObjectCommonElement(_isolate, _cache, _retainedSizes,
|
||||
_reachableSizes, _references, _retainingPaths, _objects,
|
||||
queue: _r.queue)
|
||||
.element,
|
||||
new HTMLBRElement(),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'selector',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = '${_cache.selector}'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'mask',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = '${_cache.mask}'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'buckets',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(<HTMLElement>[
|
||||
anyRef(_isolate, _cache.buckets, _objects,
|
||||
queue: _r.queue)
|
||||
])
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'argumentsDescriptor',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..appendChildren(<HTMLElement>[
|
||||
anyRef(_isolate, _cache.argumentsDescriptor, _objects,
|
||||
queue: _r.queue)
|
||||
])
|
||||
])
|
||||
]),
|
||||
])
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,166 +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:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
|
||||
class MetricDetailsElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<MetricDetailsElement> _r;
|
||||
|
||||
Stream<RenderedEvent<MetricDetailsElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.IsolateRef _isolate;
|
||||
late M.Metric _metric;
|
||||
late 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}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
void render() {
|
||||
children = <HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'name',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = _metric.name ?? '',
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'description',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = _metric.description ?? '',
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'refresh rate',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(_createRefreshRateSelect()),
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'buffer size',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(_createBufferSizeSelect()),
|
||||
])
|
||||
])
|
||||
];
|
||||
}
|
||||
|
||||
List<HTMLElement> _createRefreshRateSelect() {
|
||||
final current = _metrics.getSamplingRate(_isolate, _metric);
|
||||
final s = new HTMLSelectElement()
|
||||
..value = _rateToString(current)
|
||||
..appendChildren(
|
||||
M.MetricSamplingRate.values.map((rate) => HTMLOptionElement()
|
||||
..value = _rateToString(current)
|
||||
..selected = current == rate
|
||||
..textContent = _rateToString(rate)));
|
||||
return [
|
||||
s
|
||||
..onChange.listen((_) {
|
||||
_metrics.setSamplingRate(
|
||||
_isolate, _metric, M.MetricSamplingRate.values[s.selectedIndex]);
|
||||
_r.dirty();
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
List<HTMLElement> _createBufferSizeSelect() {
|
||||
final current = _metrics.getBufferSize(_isolate, _metric);
|
||||
final s = HTMLSelectElement()
|
||||
..value = _sizeToString(current)
|
||||
..appendChildren(
|
||||
M.MetricBufferSize.values.map((rate) => HTMLOptionElement()
|
||||
..value = _sizeToString(current)
|
||||
..selected = current == rate
|
||||
..textContent = _sizeToString(rate)));
|
||||
return [
|
||||
s
|
||||
..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';
|
||||
}
|
||||
}
|
||||
|
||||
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';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,108 +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:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
|
||||
class MetricGraphElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<MetricGraphElement> _r;
|
||||
|
||||
Stream<RenderedEvent<MetricGraphElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.IsolateRef _isolate;
|
||||
late M.Metric _metric;
|
||||
late M.MetricRepository _metrics;
|
||||
late 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}) {
|
||||
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);
|
||||
removeChildren();
|
||||
_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';
|
||||
message = 'min: $min, $message';
|
||||
message = message + ', max: $max';
|
||||
|
||||
children = <HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren([
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'min',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = '$min'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'current',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = '$current'
|
||||
]),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren([
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'max',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..textContent = '$max'
|
||||
])
|
||||
]),
|
||||
];
|
||||
if (rows.length <= 1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,157 +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 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_menu.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/metric/details.dart';
|
||||
import 'package:observatory/src/elements/metric/graph.dart';
|
||||
import 'package:observatory/src/elements/nav/isolate_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/refresh.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/vm_menu.dart';
|
||||
|
||||
class MetricsPageElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<MetricsPageElement> _r;
|
||||
|
||||
Stream<RenderedEvent<MetricsPageElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.VM _vm;
|
||||
late M.IsolateRef _isolate;
|
||||
late M.EventRepository _events;
|
||||
late M.NotificationRepository _notifications;
|
||||
late 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}) {
|
||||
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);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
void render() {
|
||||
children = <HTMLElement>[
|
||||
navBar(<HTMLElement>[
|
||||
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 HTMLDivElement()
|
||||
..className = 'content-centered-big'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLHeadingElement.h2()..textContent = 'Metrics',
|
||||
new HTMLHRElement(),
|
||||
new HTMLDivElement()
|
||||
..className = 'memberList'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberItem'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLDivElement()
|
||||
..className = 'memberName'
|
||||
..textContent = 'Metric',
|
||||
new HTMLDivElement()
|
||||
..className = 'memberValue'
|
||||
..appendChildren(_available == null
|
||||
? [new HTMLSpanElement()..textContent = 'Loading..']
|
||||
: _createMetricSelect())
|
||||
])
|
||||
]),
|
||||
new HTMLHRElement(),
|
||||
new HTMLDivElement()
|
||||
..appendChildren(_selected == null
|
||||
? const []
|
||||
: [
|
||||
new MetricDetailsElement(_isolate, _selected!, _metrics,
|
||||
queue: _r.queue)
|
||||
.element
|
||||
]),
|
||||
new HTMLHRElement(),
|
||||
new HTMLDivElement()
|
||||
..className = 'graph'
|
||||
..appendChildren(_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<HTMLElement> _createMetricSelect() {
|
||||
final s = new HTMLSelectElement()
|
||||
..value = _selected!.name!
|
||||
..appendChildren(_available!.map((metric) => HTMLOptionElement()
|
||||
..value = metric.name!
|
||||
..selected = _selected == metric
|
||||
..textContent = metric.name!));
|
||||
return [
|
||||
s
|
||||
..onChange.listen((_) {
|
||||
_selected = _available![s.selectedIndex];
|
||||
_r.dirty();
|
||||
})
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,156 +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 native_memory_profile;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M;
|
||||
import 'package:observatory/src/elements/cpu_profile/virtual_tree.dart';
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_bar.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_menu.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
import 'package:observatory/src/elements/nav/notify.dart';
|
||||
import 'package:observatory/src/elements/nav/refresh.dart';
|
||||
import 'package:observatory/src/elements/nav/top_menu.dart';
|
||||
import 'package:observatory/src/elements/nav/vm_menu.dart';
|
||||
import 'package:observatory/src/elements/sample_buffer_control.dart';
|
||||
import 'package:observatory/src/elements/stack_trace_tree_config.dart';
|
||||
|
||||
class NativeMemoryProfileElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<NativeMemoryProfileElement> _r;
|
||||
|
||||
Stream<RenderedEvent<NativeMemoryProfileElement>> get onRendered =>
|
||||
_r.onRendered;
|
||||
|
||||
late M.VM _vm;
|
||||
late M.EventRepository _events;
|
||||
late M.NotificationRepository _notifications;
|
||||
late M.NativeMemorySampleProfileRepository _profiles;
|
||||
Stream<M.SampleProfileLoadingProgressEvent>? _progressStream;
|
||||
M.SampleProfileLoadingProgress? _progress;
|
||||
late M.SampleProfileTag _tag = M.SampleProfileTag.none;
|
||||
late ProfileTreeMode _mode = ProfileTreeMode.function;
|
||||
late M.ProfileTreeDirection _direction = M.ProfileTreeDirection.exclusive;
|
||||
late String _filter = '';
|
||||
|
||||
M.NotificationRepository get notifications => _notifications;
|
||||
M.NativeMemorySampleProfileRepository get profiles => _profiles;
|
||||
|
||||
factory NativeMemoryProfileElement(
|
||||
M.VM vm,
|
||||
M.EventRepository events,
|
||||
M.NotificationRepository notifications,
|
||||
M.NativeMemorySampleProfileRepository profiles,
|
||||
{RenderingQueue? queue}) {
|
||||
NativeMemoryProfileElement e = new NativeMemoryProfileElement.created();
|
||||
e._r = new RenderingScheduler<NativeMemoryProfileElement>(e, queue: queue);
|
||||
e._vm = vm;
|
||||
e._events = events;
|
||||
e._notifications = notifications;
|
||||
e._profiles = profiles;
|
||||
return e;
|
||||
}
|
||||
|
||||
NativeMemoryProfileElement.created() : super.created('native-memory-profile');
|
||||
|
||||
@override
|
||||
attached() {
|
||||
super.attached();
|
||||
_r.enable();
|
||||
_request();
|
||||
}
|
||||
|
||||
@override
|
||||
detached() {
|
||||
super.detached();
|
||||
_r.disable(notify: true);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
void render() {
|
||||
var content = <HTMLElement>[
|
||||
navBar(<HTMLElement>[
|
||||
new NavTopMenuElement(queue: _r.queue).element,
|
||||
new NavVMMenuElement(_vm, _events, queue: _r.queue).element,
|
||||
navMenu('native memory profile', link: Uris.nativeMemory()),
|
||||
(new NavRefreshElement(queue: _r.queue)..onRefresh.listen(_refresh))
|
||||
.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) {
|
||||
late CpuProfileVirtualTreeElement tree;
|
||||
content.addAll([
|
||||
new HTMLBRElement(),
|
||||
(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 HTMLBRElement(),
|
||||
(tree = new CpuProfileVirtualTreeElement(null, _progress!.profile,
|
||||
queue: _r.queue, type: M.SampleProfileType.memory))
|
||||
.element,
|
||||
]);
|
||||
}
|
||||
children = content;
|
||||
}
|
||||
|
||||
Future _request({bool forceFetch = false}) async {
|
||||
if (forceFetch) {
|
||||
for (M.IsolateRef isolate in _vm.isolates) {
|
||||
await isolate.collectAllGarbage();
|
||||
}
|
||||
}
|
||||
_progress = null;
|
||||
_progressStream = _profiles.get(_vm, _tag, forceFetch: forceFetch);
|
||||
_r.dirty();
|
||||
_progress = (await _progressStream!.first).progress;
|
||||
_r.dirty();
|
||||
if (M.isSampleProcessRunning(_progress!.status)) {
|
||||
_progress = (await _progressStream!.last).progress;
|
||||
_r.dirty();
|
||||
}
|
||||
}
|
||||
|
||||
Future _refresh(e) async {
|
||||
e.element.disabled = true;
|
||||
await _request(forceFetch: true);
|
||||
e.element.disabled = false;
|
||||
}
|
||||
}
|
||||
@@ -1,63 +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:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M show IsolateRef, ClassRef;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_menu.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
|
||||
class NavClassMenuElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<NavClassMenuElement> _r;
|
||||
|
||||
Stream<RenderedEvent<NavClassMenuElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.IsolateRef _isolate;
|
||||
late M.ClassRef _cls;
|
||||
List<HTMLElement> _content = const [];
|
||||
|
||||
M.IsolateRef get isolate => _isolate;
|
||||
M.ClassRef get cls => _cls;
|
||||
List<HTMLElement> get content => _content;
|
||||
|
||||
set content(Iterable<HTMLElement> value) {
|
||||
_content = value.toList();
|
||||
_r.dirty();
|
||||
}
|
||||
|
||||
factory NavClassMenuElement(M.IsolateRef isolate, M.ClassRef cls,
|
||||
{RenderingQueue? queue}) {
|
||||
NavClassMenuElement e = new NavClassMenuElement.created();
|
||||
e._r = new RenderingScheduler<NavClassMenuElement>(e, queue: queue);
|
||||
e._isolate = isolate;
|
||||
e._cls = cls;
|
||||
return e;
|
||||
}
|
||||
|
||||
NavClassMenuElement.created() : super.created('nav-class-menu');
|
||||
|
||||
@override
|
||||
void attached() {
|
||||
super.attached();
|
||||
_r.enable();
|
||||
}
|
||||
|
||||
@override
|
||||
void detached() {
|
||||
super.detached();
|
||||
removeChildren();
|
||||
_r.disable(notify: true);
|
||||
}
|
||||
|
||||
void render() {
|
||||
children = <HTMLElement>[
|
||||
navMenu(cls.name!,
|
||||
content: _content, link: Uris.inspect(isolate, object: cls))
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,107 +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:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M show IsolateRef, EventRepository;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_menu.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
import 'package:observatory/src/elements/nav/menu_item.dart';
|
||||
|
||||
class NavIsolateMenuElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<NavIsolateMenuElement> _r;
|
||||
|
||||
Stream<RenderedEvent<NavIsolateMenuElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.IsolateRef _isolate;
|
||||
late M.EventRepository _events;
|
||||
StreamSubscription? _updatesSubscription;
|
||||
late Iterable<HTMLElement> _content = const [];
|
||||
|
||||
M.IsolateRef get isolate => _isolate;
|
||||
Iterable<HTMLElement> get content => _content;
|
||||
|
||||
set content(Iterable<HTMLElement> value) {
|
||||
_content = value.toList();
|
||||
_r.dirty();
|
||||
}
|
||||
|
||||
factory NavIsolateMenuElement(M.IsolateRef isolate, M.EventRepository events,
|
||||
{RenderingQueue? queue}) {
|
||||
NavIsolateMenuElement e = new NavIsolateMenuElement.created();
|
||||
e._r = new RenderingScheduler<NavIsolateMenuElement>(e, queue: queue);
|
||||
e._isolate = isolate;
|
||||
e._events = events;
|
||||
return e;
|
||||
}
|
||||
|
||||
NavIsolateMenuElement.created() : super.created('nav-isolate-menu');
|
||||
|
||||
@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();
|
||||
removeChildren();
|
||||
_r.disable(notify: true);
|
||||
assert(_updatesSubscription != null);
|
||||
_updatesSubscription!.cancel();
|
||||
_updatesSubscription = null;
|
||||
}
|
||||
|
||||
void render() {
|
||||
final content = <HTMLElement>[
|
||||
new NavMenuItemElement('debugger',
|
||||
queue: _r.queue, link: Uris.debugger(isolate))
|
||||
.element,
|
||||
new NavMenuItemElement('class hierarchy',
|
||||
queue: _r.queue, link: Uris.classTree(isolate))
|
||||
.element,
|
||||
new NavMenuItemElement('cpu profile',
|
||||
queue: _r.queue, link: Uris.cpuProfiler(isolate))
|
||||
.element,
|
||||
new NavMenuItemElement('cpu profile (table)',
|
||||
queue: _r.queue, link: Uris.cpuProfilerTable(isolate))
|
||||
.element,
|
||||
new NavMenuItemElement('allocation profile',
|
||||
queue: _r.queue, link: Uris.allocationProfiler(isolate))
|
||||
.element,
|
||||
new NavMenuItemElement('heap snapshot',
|
||||
queue: _r.queue, link: Uris.heapSnapshot(isolate))
|
||||
.element,
|
||||
new NavMenuItemElement('heap map',
|
||||
queue: _r.queue, link: Uris.heapMap(isolate))
|
||||
.element,
|
||||
new NavMenuItemElement('metrics',
|
||||
queue: _r.queue, link: Uris.metrics(isolate))
|
||||
.element,
|
||||
new NavMenuItemElement('persistent handles',
|
||||
queue: _r.queue, link: Uris.persistentHandles(isolate))
|
||||
.element,
|
||||
new NavMenuItemElement('ports',
|
||||
queue: _r.queue, link: Uris.ports(isolate))
|
||||
.element,
|
||||
new NavMenuItemElement('logging',
|
||||
queue: _r.queue, link: Uris.logging(isolate))
|
||||
.element,
|
||||
]..addAll(_content);
|
||||
children = <HTMLElement>[
|
||||
navMenu(isolate.name!, content: content, link: Uris.inspect(isolate))
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,64 +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:web/web.dart';
|
||||
|
||||
import 'package:observatory/models.dart' as M show IsolateRef, LibraryRef;
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/nav_menu.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
import 'package:observatory/src/elements/helpers/uris.dart';
|
||||
|
||||
class NavLibraryMenuElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<NavLibraryMenuElement> _r;
|
||||
|
||||
Stream<RenderedEvent<NavLibraryMenuElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late M.IsolateRef _isolate;
|
||||
late M.LibraryRef _library;
|
||||
List<HTMLElement> _content = const [];
|
||||
|
||||
M.IsolateRef get isolate => _isolate;
|
||||
M.LibraryRef get library => _library;
|
||||
List<HTMLElement> get content => _content;
|
||||
|
||||
set content(Iterable<HTMLElement> value) {
|
||||
_content = value.toList();
|
||||
_r.dirty();
|
||||
}
|
||||
|
||||
factory NavLibraryMenuElement(M.IsolateRef isolate, M.LibraryRef library,
|
||||
{RenderingQueue? queue}) {
|
||||
NavLibraryMenuElement e = new NavLibraryMenuElement.created();
|
||||
e._r = new RenderingScheduler<NavLibraryMenuElement>(e, queue: queue);
|
||||
e._isolate = isolate;
|
||||
e._library = library;
|
||||
return e;
|
||||
}
|
||||
|
||||
NavLibraryMenuElement.created() : super.created('nav-library-menu');
|
||||
|
||||
@override
|
||||
void attached() {
|
||||
super.attached();
|
||||
_r.enable();
|
||||
}
|
||||
|
||||
@override
|
||||
void detached() {
|
||||
super.detached();
|
||||
removeChildren();
|
||||
_r.disable(notify: true);
|
||||
}
|
||||
|
||||
void render() {
|
||||
children = <HTMLElement>[
|
||||
navMenu(library.name!,
|
||||
content: _content,
|
||||
link: Uris.inspect(isolate, object: library).toString())
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,69 +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:web/web.dart';
|
||||
|
||||
import 'package:observatory/src/elements/helpers/custom_element.dart';
|
||||
import 'package:observatory/src/elements/helpers/element_utils.dart';
|
||||
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
||||
|
||||
class NavMenuItemElement extends CustomElement implements Renderable {
|
||||
late RenderingScheduler<NavMenuItemElement> _r;
|
||||
|
||||
Stream<RenderedEvent<NavMenuItemElement>> get onRendered => _r.onRendered;
|
||||
|
||||
late String _label;
|
||||
late String _link;
|
||||
List<HTMLElement> _content = const <HTMLElement>[];
|
||||
|
||||
String get label => _label;
|
||||
String get link => _link;
|
||||
List<HTMLElement> get content => _content;
|
||||
|
||||
set label(String value) => _label = _r.checkAndReact(_label, value);
|
||||
set link(String value) => _link = _r.checkAndReact(_link, value);
|
||||
set content(Iterable<HTMLElement> value) {
|
||||
_content = value.toList();
|
||||
_r.dirty();
|
||||
}
|
||||
|
||||
factory NavMenuItemElement(String label,
|
||||
{String link = '', RenderingQueue? queue}) {
|
||||
NavMenuItemElement e = new NavMenuItemElement.created();
|
||||
e._r = new RenderingScheduler<NavMenuItemElement>(e, queue: queue);
|
||||
e._label = label;
|
||||
e._link = link;
|
||||
return e;
|
||||
}
|
||||
|
||||
NavMenuItemElement.created() : super.created('nav-menu-item');
|
||||
|
||||
@override
|
||||
void attached() {
|
||||
super.attached();
|
||||
_r.enable();
|
||||
}
|
||||
|
||||
@override
|
||||
void detached() {
|
||||
super.detached();
|
||||
_r.disable(notify: true);
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
void render() {
|
||||
children = <HTMLElement>[
|
||||
new HTMLLIElement()
|
||||
..className = 'nav-menu-item'
|
||||
..appendChildren(<HTMLElement>[
|
||||
new HTMLAnchorElement()
|
||||
..href = link
|
||||
..text = label,
|
||||
new HTMLUListElement()..appendChildren(_content)
|
||||
])
|
||||
];
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user