Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1424)

Side by Side Diff: runtime/observatory/lib/src/elements/memory/snapshot.dart

Issue 3002843002: Introduce heap snapshot into Memory Dashboard (Closed)
Patch Set: Added missing public APIs to EditorRepository Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
6 // for details. All rights reserved. Use of this source code is governed by a
7 // BSD-style license that can be found in the LICENSE file.
8
9 import 'dart:async';
10 import 'dart:html';
11 import 'dart:math' as Math;
12 import 'package:observatory/models.dart' as M;
13 import 'package:observatory/src/elements/class_ref.dart';
14 import 'package:observatory/src/elements/containers/virtual_tree.dart';
15 import 'package:observatory/src/elements/helpers/any_ref.dart';
16 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
17 import 'package:observatory/src/elements/helpers/tag.dart';
18 import 'package:observatory/src/elements/helpers/uris.dart';
19 import 'package:observatory/utils.dart';
20
21 class MemorySnapshotElement extends HtmlElement implements Renderable {
22 static const tag =
23 const Tag<MemorySnapshotElement>('memory-snapshot', dependencies: const [
24 ClassRefElement.tag,
25 VirtualTreeElement.tag,
26 ]);
27
28 RenderingScheduler<MemorySnapshotElement> _r;
29
30 Stream<RenderedEvent<MemorySnapshotElement>> get onRendered => _r.onRendered;
31
32 M.IsolateRef _isolate;
33 M.EditorRepository _editor;
34 M.HeapSnapshotRepository _snapshots;
35 M.ObjectRepository _objects;
36 M.HeapSnapshot _snapshot;
37 Stream<M.HeapSnapshotLoadingProgressEvent> _progressStream;
38 M.HeapSnapshotLoadingProgress _progress;
39
40 M.IsolateRef get isolate => _isolate;
41
42 factory MemorySnapshotElement(M.IsolateRef isolate, M.EditorRepository editor,
43 M.HeapSnapshotRepository snapshots, M.ObjectRepository objects,
44 {RenderingQueue queue}) {
45 assert(isolate != null);
46 assert(editor != null);
47 assert(snapshots != null);
48 assert(objects != null);
49 MemorySnapshotElement e = document.createElement(tag.name);
50 e._r = new RenderingScheduler(e, queue: queue);
51 e._isolate = isolate;
52 e._editor = editor;
53 e._snapshots = snapshots;
54 e._objects = objects;
55 return e;
56 }
57
58 MemorySnapshotElement.created() : super.created();
59
60 @override
61 attached() {
62 super.attached();
63 _r.enable();
64 _refresh();
65 }
66
67 @override
68 detached() {
69 super.detached();
70 _r.disable(notify: true);
71 children = [];
72 }
73
74 void render() {
75 if (_progress == null) {
76 children = const [];
77 return;
78 }
79 List<HtmlElement> content;
80 switch (_progress.status) {
81 case M.HeapSnapshotLoadingStatus.fetching:
82 content = _createStatusMessage('Fetching snapshot from VM...',
83 description: _progress.stepDescription,
84 progress: _progress.progress);
85 break;
86 case M.HeapSnapshotLoadingStatus.loading:
87 content = _createStatusMessage('Loading snapshot...',
88 description: _progress.stepDescription,
89 progress: _progress.progress);
90 break;
91 case M.HeapSnapshotLoadingStatus.loaded:
92 content = _createReport();
93 break;
94 }
95 children = content;
96 }
97
98 Future reload({bool gc: false}) => _refresh(gc: gc);
99
100 Future _refresh({bool gc: false}) async {
101 _progress = null;
102 _progressStream =
103 _snapshots.get(isolate, roots: M.HeapSnapshotRoots.user, gc: gc);
104 _r.dirty();
105 _progressStream.listen((e) {
106 _progress = e.progress;
107 _r.dirty();
108 });
109 _progress = (await _progressStream.first).progress;
110 _r.dirty();
111 if (M.isHeapSnapshotProgressRunning(_progress.status)) {
112 _progress = (await _progressStream.last).progress;
113 _snapshot = _progress.snapshot;
114 _r.dirty();
115 }
116 }
117
118 static List<Element> _createStatusMessage(String message,
119 {String description: '', double progress: 0.0}) {
120 return [
121 new DivElement()
122 ..classes = ['content-centered-big']
123 ..children = [
124 new DivElement()
125 ..classes = ['statusBox', 'shadow', 'center']
126 ..children = [
127 new DivElement()
128 ..classes = ['statusMessage']
129 ..text = message,
130 new DivElement()
131 ..classes = ['statusDescription']
132 ..text = description,
133 new DivElement()
134 ..style.background = '#0489c3'
135 ..style.width = '$progress%'
136 ..style.height = '15px'
137 ..style.borderRadius = '4px'
138 ]
139 ]
140 ];
141 }
142
143 VirtualTreeElement _tree;
144
145 List<Element> _createReport() {
146 final List roots = _getChildrenDominator(_snapshot.dominatorTree);
147 _tree = new VirtualTreeElement(
148 _createDominator, _updateDominator, _getChildrenDominator,
149 items: roots, queue: _r.queue);
150 if (roots.length == 1) {
151 _tree.expand(roots.first, autoExpandSingleChildNodes: true);
152 }
153 final text = 'In a heap dominator tree, an object X is a parent of '
154 'object Y if every path from the root to Y goes through '
155 'X. This allows you to find "choke points" that are '
156 'holding onto a lot of memory. If an object becomes '
157 'garbage, all its children in the dominator tree become '
158 'garbage as well. '
159 'The retained size of an object is the sum of the '
160 'retained sizes of its children in the dominator tree '
161 'plus its own shallow size, and is the amount of memory '
162 'that would be freed if the object became garbage.';
163 return <HtmlElement>[
164 new DivElement()
165 ..classes = ['content-centered-big', 'explanation']
166 ..text = text
167 ..title = text,
168 _tree
169 ];
170 }
171
172 static Element _createDominator(toggle) {
173 return new DivElement()
174 ..classes = ['tree-item']
175 ..children = [
176 new SpanElement()
177 ..classes = ['size']
178 ..title = 'retained size',
179 new SpanElement()..classes = ['lines'],
180 new ButtonElement()
181 ..classes = ['expander']
182 ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
183 new SpanElement()
184 ..classes = ['percentage']
185 ..title = 'percentage of heap being retained',
186 new SpanElement()..classes = ['name']
187 ];
188 }
189
190 static const int kMaxChildren = 100;
191 static const int kMinRetainedSize = 4096;
192
193 static _getChildrenDominator(M.HeapSnapshotDominatorNode node) {
194 final list = node.children.toList();
195 list.sort((a, b) => b.retainedSize - a.retainedSize);
196 return list
197 .where((child) => child.retainedSize >= kMinRetainedSize)
198 .take(kMaxChildren);
199 }
200
201 void _updateDominator(
202 HtmlElement element, M.HeapSnapshotDominatorNode node, int depth) {
203 element.children[0].text = Utils.formatSize(node.retainedSize);
204 _updateLines(element.children[1].children, depth);
205 if (_getChildrenDominator(node).isNotEmpty) {
206 element.children[2].text = _tree.isExpanded(node) ? '▼' : '►';
207 } else {
208 element.children[2].text = '';
209 }
210 element.children[3].text =
211 Utils.formatPercentNormalized(node.retainedSize * 1.0 / _snapshot.size);
212 final wrapper = new SpanElement()
213 ..classes = ['name']
214 ..text = 'Loading...';
215 element.children[4] = wrapper;
216 if (node.isStack) {
217 wrapper
218 ..text = ''
219 ..children = [
220 new AnchorElement(href: Uris.debugger(isolate))..text = 'stack frames'
221 ];
222 } else {
223 node.object.then((object) {
224 wrapper
225 ..text = ''
226 ..children = [
227 anyRef(_isolate, object, _objects,
228 queue: _r.queue, expandable: false)
229 ];
230 });
231 }
232 Element.clickEvent
233 .forTarget(element.children[4], useCapture: true)
234 .listen((e) {
235 if (_editor.isAvailable) {
236 e.preventDefault();
237 _sendNodeToEditor(node);
238 }
239 });
240 }
241
242 Future _sendNodeToEditor(M.HeapSnapshotDominatorNode node) async {
243 final object = await node.object;
244 if (node.isStack) {
245 // TODO(cbernaschina) open debugger?
siva 2017/08/19 00:35:46 Maybe open an issue and refer to this using the is
246 return new Future.value();
247 }
248 _editor.openObject(_isolate, object);
249 }
250
251 static _updateLines(List<Element> lines, int n) {
252 n = Math.max(0, n);
253 while (lines.length > n) {
254 lines.removeLast();
255 }
256 while (lines.length < n) {
257 lines.add(new SpanElement());
258 }
259 }
260
261 static String rootsToString(M.HeapSnapshotRoots roots) {
262 switch (roots) {
263 case M.HeapSnapshotRoots.user:
264 return 'User';
265 case M.HeapSnapshotRoots.vm:
266 return 'VM';
267 }
268 throw new Exception('Unknown HeapSnapshotRoots');
269 }
270 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698