OLD | NEW |
---|---|
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library class_view_element; | 5 library class_view_element; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'observatory_element.dart'; | 8 import 'dart:html'; |
9 import 'sample_buffer_control.dart'; | |
10 import 'stack_trace_tree_config.dart'; | |
11 import 'cpu_profile/virtual_tree.dart'; | |
12 import 'package:observatory/heap_snapshot.dart'; | |
13 import 'package:observatory/elements.dart'; | |
14 import 'package:observatory/models.dart' as M; | 9 import 'package:observatory/models.dart' as M; |
15 import 'package:observatory/service.dart'; | 10 import 'package:observatory/src/elements/class_allocation_profile.dart'; |
16 import 'package:observatory/repositories.dart'; | 11 import 'package:observatory/src/elements/class_instances.dart'; |
17 import 'package:polymer/polymer.dart'; | 12 import 'package:observatory/src/elements/class_ref.dart'; |
18 | 13 import 'package:observatory/src/elements/curly_block.dart'; |
19 @CustomTag('class-view') | 14 import 'package:observatory/src/elements/error_ref.dart'; |
20 class ClassViewElement extends ObservatoryElement { | 15 import 'package:observatory/src/elements/eval_box.dart'; |
21 @published Class cls; | 16 import 'package:observatory/src/elements/field_ref.dart'; |
22 @observable ServiceMap instances; | 17 import 'package:observatory/src/elements/function_ref.dart'; |
23 @observable int reachableBytes; | 18 import 'package:observatory/src/elements/helpers/any_ref.dart'; |
24 @observable int retainedBytes; | 19 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; |
25 @observable ObservableList mostRetained; | 20 import 'package:observatory/src/elements/helpers/tag.dart'; |
26 SampleBufferControlElement sampleBufferControlElement; | 21 import 'package:observatory/src/elements/instance_ref.dart'; |
27 StackTraceTreeConfigElement stackTraceTreeConfigElement; | 22 import 'package:observatory/src/elements/library_ref.dart'; |
28 CpuProfileVirtualTreeElement cpuProfileTreeElement; | 23 import 'package:observatory/src/elements/nav/bar.dart'; |
29 ClassSampleProfileRepository repository = new ClassSampleProfileRepository(); | 24 import 'package:observatory/src/elements/nav/class_menu.dart'; |
30 | 25 import 'package:observatory/src/elements/nav/isolate_menu.dart'; |
26 import 'package:observatory/src/elements/nav/menu.dart'; | |
27 import 'package:observatory/src/elements/nav/notify.dart'; | |
28 import 'package:observatory/src/elements/nav/refresh.dart'; | |
29 import 'package:observatory/src/elements/nav/top_menu.dart'; | |
30 import 'package:observatory/src/elements/nav/vm_menu.dart'; | |
31 import 'package:observatory/src/elements/object_common.dart'; | |
32 import 'package:observatory/src/elements/source_inset.dart'; | |
33 import 'package:observatory/src/elements/source_link.dart'; | |
34 import 'package:observatory/src/elements/view_footer.dart'; | |
35 | |
36 class ClassViewElement extends HtmlElement implements Renderable { | |
37 static const tag = const Tag<ClassViewElement>('class-view', | |
38 dependencies: const [ | |
39 ClassInstancesElement.tag, | |
40 ClassRefElement.tag, | |
41 CurlyBlockElement.tag, | |
42 ErrorRefElement.tag, | |
43 EvalBoxElement.tag, | |
44 FieldRefElement.tag, | |
45 FunctionRefElement.tag, | |
46 InstanceRefElement.tag, | |
47 LibraryRefElement.tag, | |
48 NavBarElement.tag, | |
49 NavClassMenuElement.tag, | |
50 NavTopMenuElement.tag, | |
51 NavVMMenuElement.tag, | |
52 NavIsolateMenuElement.tag, | |
53 NavMenuElement.tag, | |
54 NavRefreshElement.tag, | |
55 NavNotifyElement.tag, | |
56 ObjectCommonElement.tag, | |
57 SourceInsetElement.tag, | |
58 SourceLinkElement.tag, | |
59 ViewFooterElement.tag | |
60 ]); | |
61 | |
62 RenderingScheduler<ClassViewElement> _r; | |
63 | |
64 Stream<RenderedEvent<ClassViewElement>> get onRendered => _r.onRendered; | |
65 | |
66 M.VM _vm; | |
67 M.IsolateRef _isolate; | |
68 M.EventRepository _events; | |
69 M.NotificationRepository _notifications; | |
70 M.Class _cls; | |
71 M.ClassRepository _classes; | |
72 M.RetainedSizeRepository _retainedSizes; | |
73 M.ReachableSizeRepository _reachableSizes; | |
74 M.InboundReferencesRepository _references; | |
75 M.RetainingPathRepository _retainingPaths; | |
76 M.StronglyReachableInstancesRepository _stronglyReachableInstances; | |
77 M.TopRetainingInstancesRepository _topRetainedInstances; | |
78 M.FieldRepository _fields; | |
79 M.ScriptRepository _scripts; | |
80 M.InstanceRepository _instances; | |
81 M.EvalRepository _eval; | |
82 M.ClassSampleProfileRepository _profiles; | |
83 Iterable<M.Field> _classFields; | |
84 | |
85 | |
86 M.VMRef get vm => _vm; | |
87 M.IsolateRef get isolate => _isolate; | |
88 M.NotificationRepository get notifications => _notifications; | |
89 M.Class get cls => _cls; | |
90 | |
91 factory ClassViewElement(M.VM vm, M.IsolateRef isolate, M.Class cls, | |
92 M.EventRepository events, | |
93 M.NotificationRepository notifications, | |
94 M.ClassRepository classes, | |
95 M.RetainedSizeRepository retainedSizes, | |
96 M.ReachableSizeRepository reachableSizes, | |
97 M.InboundReferencesRepository references, | |
98 M.RetainingPathRepository retainingPaths, | |
99 M.FieldRepository fields, | |
100 M.ScriptRepository scripts, | |
101 M.InstanceRepository instances, | |
102 M.EvalRepository eval, | |
103 M.StronglyReachableInstancesRepository stronglyReachable, | |
104 M.TopRetainingInstancesRepository topRetained, | |
105 M.ClassSampleProfileRepository profiles, | |
106 {RenderingQueue queue}) { | |
107 assert(vm != null); | |
108 assert(isolate != null); | |
109 assert(events != null); | |
110 assert(notifications != null); | |
111 assert(cls != null); | |
112 assert(classes != null); | |
113 assert(retainedSizes != null); | |
114 assert(reachableSizes != null); | |
115 assert(references != null); | |
116 assert(retainingPaths != null); | |
117 assert(fields != null); | |
118 assert(scripts != null); | |
119 assert(instances != null); | |
120 assert(eval != null); | |
121 assert(stronglyReachable != null); | |
122 assert(topRetained != null); | |
123 assert(profiles != null); | |
124 ClassViewElement e = document.createElement(tag.name); | |
125 e._r = new RenderingScheduler(e, queue: queue); | |
126 e._vm = vm; | |
127 e._isolate = isolate; | |
128 e._events = events; | |
129 e._notifications = notifications; | |
130 e._cls = cls; | |
131 e._classes = classes; | |
132 e._retainedSizes = retainedSizes; | |
133 e._reachableSizes = reachableSizes; | |
134 e._references = references; | |
135 e._retainingPaths = retainingPaths; | |
136 e._fields = fields; | |
137 e._scripts = scripts; | |
138 e._instances = instances; | |
139 e._eval = eval; | |
140 e._stronglyReachableInstances = stronglyReachable; | |
141 e._topRetainedInstances = topRetained; | |
142 e._profiles = profiles; | |
143 return e; | |
144 } | |
31 | 145 |
32 ClassViewElement.created() : super.created(); | 146 ClassViewElement.created() : super.created(); |
33 | 147 |
34 Future<ServiceObject> evaluate(String expression) { | 148 @override |
35 return cls.evaluate(expression); | 149 attached() { |
36 } | |
37 | |
38 Future<ServiceObject> reachable(var limit) { | |
39 return cls.isolate.getInstances(cls, limit).then((ServiceMap obj) { | |
40 instances = obj; | |
41 }); | |
42 } | |
43 | |
44 Future retainedToplist(var limit) async { | |
45 final raw = await cls.isolate.fetchHeapSnapshot(true).last; | |
46 final snapshot = new HeapSnapshot(); | |
47 await snapshot.loadProgress(cls.isolate, raw).last; | |
48 final most = await Future.wait(snapshot.getMostRetained(cls.isolate, | |
49 classId: cls.vmCid , | |
50 limit: 10)); | |
51 mostRetained = new ObservableList.from(most); | |
52 } | |
53 | |
54 // TODO(koda): Add no-arg "calculate-link" instead of reusing "eval-link". | |
55 Future<ServiceObject> reachableSize(var dummy) { | |
56 return cls.isolate.getReachableSize(cls).then((Instance obj) { | |
57 reachableBytes = int.parse(obj.valueAsString); | |
58 }); | |
59 } | |
60 | |
61 Future<ServiceObject> retainedSize(var dummy) { | |
62 return cls.isolate.getRetainedSize(cls).then((Instance obj) { | |
63 retainedBytes = int.parse(obj.valueAsString); | |
64 }); | |
65 } | |
66 | |
67 void attached() { | |
68 super.attached(); | 150 super.attached(); |
69 cls.fields.forEach((field) => field.reload()); | 151 _r.enable(); |
70 } | 152 _loadAdditionalData(); |
71 | 153 } |
72 Future refresh() async { | 154 |
73 instances = null; | 155 @override |
74 retainedBytes = null; | 156 detached() { |
75 mostRetained = null; | 157 super.detached(); |
76 await cls.reload(); | 158 _r.disable(notify: true); |
77 await Future.wait(cls.fields.map((field) => field.reload())); | 159 children = []; |
78 } | 160 } |
79 | 161 |
80 M.SampleProfileTag _tag = M.SampleProfileTag.none; | 162 ObjectCommonElement _common; |
81 | 163 ClassInstancesElement _classInstances; |
82 Future refreshAllocationProfile() async { | 164 bool _loadProfile = false; |
83 shadowRoot.querySelector('#sampleBufferControl').children = const []; | 165 |
84 shadowRoot.querySelector('#stackTraceTreeConfig').children = const []; | 166 void render() { |
85 shadowRoot.querySelector('#cpuProfileTree').children = const []; | 167 _common = _common ?? new ObjectCommonElement(_isolate, _cls, _retainedSizes, |
86 final stream = repository.get(cls, _tag); | 168 _reachableSizes, _references, _retainingPaths, _instances, |
87 var progress = (await stream.first).progress; | 169 queue: _r.queue); |
88 shadowRoot.querySelector('#sampleBufferControl')..children = [ | 170 _classInstances = _classInstances ?? new ClassInstancesElement(_isolate, |
89 new SampleBufferControlElement(progress, stream, queue: app.queue, | 171 _cls, _retainedSizes, _reachableSizes, _stronglyReachableInstances, |
90 selectedTag: _tag) | 172 _topRetainedInstances, _instances, queue: _r.queue); |
91 ..onTagChange.listen((e) { | 173 var header = ''; |
92 _tag = e.element.selectedTag; | 174 if (_cls.isAbstract) { |
93 refreshAllocationProfile(); | 175 header += 'abstract '; |
94 }) | 176 } |
177 if (_cls.isPatch) { | |
178 header += 'patch '; | |
179 } | |
180 if (_cls.mixin != null) { | |
rmacnak
2016/08/31 23:51:39
I'd remove this. If class's mixin isn't null, that
cbernaschina
2016/09/01 00:04:07
Done.
| |
181 header += 'mixin '; | |
182 } | |
183 children = [ | |
184 new NavBarElement(queue: _r.queue) | |
185 ..children = [ | |
186 new NavTopMenuElement(queue: _r.queue), | |
187 new NavVMMenuElement(_vm, _events, queue: _r.queue), | |
188 new NavIsolateMenuElement(_isolate, _events, queue: _r.queue), | |
189 new NavClassMenuElement(_isolate, _cls, queue: _r.queue), | |
190 new NavRefreshElement(label: 'Refresh Allocation Profile', | |
191 queue: _r.queue) | |
192 ..onRefresh.listen((e) { | |
193 e.element.disabled = true; | |
194 _loadProfile = true; | |
195 _r.dirty(); | |
196 }), | |
197 new NavRefreshElement(queue: _r.queue) | |
198 ..onRefresh.listen((e) { | |
199 e.element.disabled = true; | |
200 _common = null; | |
201 _classInstances = null; | |
202 _fieldsExpanded = null; | |
203 _functionsExpanded = null; | |
204 _refresh(); | |
205 }), | |
206 new NavNotifyElement(_notifications, queue: _r.queue) | |
207 ], | |
208 new DivElement()..classes = ['content-centered-big'] | |
209 ..children = [ | |
210 new HeadingElement.h2()..text = '$header class ${_cls.name}', | |
211 new HRElement(), | |
212 _common, | |
213 new BRElement(), | |
214 new DivElement()..classes = ['memberList'] | |
215 ..children = _createMembers(), | |
216 new DivElement() | |
217 ..children = _cls.error == null | |
218 ? const [] | |
219 : [ | |
220 new HRElement(), | |
221 new ErrorRefElement(_cls.error, queue: _r.queue) | |
222 ], | |
223 new HRElement(), | |
224 new EvalBoxElement(_isolate, _cls, _instances, _eval, | |
225 queue: _r.queue), | |
226 new HRElement(), | |
227 new HeadingElement.h2()..text = 'Fields & Functions', | |
228 new DivElement()..classes = ['memberList'] | |
229 ..children = _createElements(), | |
230 new HRElement(), | |
231 new HeadingElement.h2()..text = 'Instances', | |
232 new DivElement() | |
233 ..children = _cls.hasAllocations | |
234 ? [_classInstances] | |
235 : const [], | |
236 new HRElement(), | |
237 new HeadingElement.h2()..text = 'Allocations', | |
238 new DivElement()..classes = ['memberList'] | |
239 ..children = [ | |
240 new DivElement()..classes = ['memberName'] | |
241 ..text = 'Tracing allocations?» ', | |
242 new DivElement()..classes = ['memberValue'] | |
243 ..children = _cls.traceAllocations | |
244 ? [ | |
245 new SpanElement()..text = 'Yes ', | |
246 new ButtonElement()..text = 'disable' | |
247 ..onClick.listen((e) async { | |
248 e.target.disabled = true; | |
249 await _profiles.disable(_isolate, _cls); | |
250 _loadProfile = true; | |
251 _refresh(); | |
252 }) | |
253 ] | |
254 : [ | |
255 new SpanElement()..text = 'No ', | |
256 new ButtonElement()..text = 'enable' | |
257 ..onClick.listen((e) async { | |
258 e.target.disabled = true; | |
259 await _profiles.enable(_isolate, _cls); | |
260 _refresh(); | |
261 }) | |
262 ] | |
263 ], | |
264 new DivElement() | |
265 ..children = _loadProfile | |
266 ? [new ClassAllocationProfileElement(_isolate, _cls, _profiles, | |
267 queue: _r.queue)] | |
268 : const [], | |
269 new DivElement() | |
270 ..children = _cls.location != null | |
271 ? [new HRElement(), | |
272 new SourceInsetElement(_isolate, _cls.location, _scripts, | |
273 _instances, _events, queue: _r.queue)] | |
274 : const [], | |
275 new HRElement(), | |
276 new ViewFooterElement(queue: _r.queue) | |
277 ] | |
95 ]; | 278 ]; |
96 if (M.isSampleProcessRunning(progress.status)) { | 279 } |
97 progress = (await stream.last).progress; | 280 |
98 } | 281 bool _fieldsExpanded; |
99 if (progress.status == M.SampleProfileLoadingStatus.loaded) { | 282 bool _functionsExpanded; |
100 shadowRoot.querySelector('#stackTraceTreeConfig')..children = [ | 283 |
101 new StackTraceTreeConfigElement( | 284 List<Element> _createMembers() { |
102 queue: app.queue) | 285 final members = <Element>[]; |
103 ..showFilter = false | 286 if (_cls.library != null) { |
104 ..onModeChange.listen((e) { | 287 members.add( |
105 cpuProfileTreeElement.mode = e.element.mode; | 288 new DivElement()..classes = ['memberItem'] |
106 }) | 289 ..children = [ |
107 ..onDirectionChange.listen((e) { | 290 new DivElement()..classes = ['memberName'] |
108 cpuProfileTreeElement.direction = e.element.direction; | 291 ..text = 'library', |
109 }) | 292 new DivElement()..classes = ['memberValue'] |
110 ]; | 293 ..children = [ |
111 shadowRoot.querySelector('#cpuProfileTree')..children = [ | 294 new LibraryRefElement(_isolate, _cls.library, queue: _r.queue) |
112 cpuProfileTreeElement = new CpuProfileVirtualTreeElement(cls.isolate, | 295 ] |
113 progress.profile, queue: app.queue) | 296 ] |
114 ]; | 297 ); |
115 } | 298 } |
116 } | 299 if (_cls.location != null) { |
117 | 300 members.add( |
118 Future toggleAllocationTrace() { | 301 new DivElement()..classes = ['memberItem'] |
119 if (cls == null) { | 302 ..children = [ |
120 return new Future(refresh); | 303 new DivElement()..classes = ['memberName'] |
121 } | 304 ..text = 'script', |
122 if (cls.traceAllocations) { | 305 new DivElement()..classes = ['memberValue'] |
123 refreshAllocationProfile(); | 306 ..children = [ |
124 } | 307 new SourceLinkElement(_isolate, _cls.location, _scripts, |
125 return cls.setTraceAllocations(!cls.traceAllocations).whenComplete(refresh); | 308 queue: _r.queue) |
309 ] | |
310 ] | |
311 ); | |
312 } | |
313 if (_cls.superclass != null) { | |
314 members.add( | |
315 new DivElement()..classes = ['memberItem'] | |
316 ..children = [ | |
317 new DivElement()..classes = ['memberName'] | |
318 ..text = 'superclass', | |
319 new DivElement()..classes = ['memberValue'] | |
320 ..children = [ | |
321 new ClassRefElement(_isolate, _cls.superclass, queue: _r.queue) | |
322 ] | |
323 ] | |
324 ); | |
325 } | |
326 if (_cls.superType != null) { | |
327 members.add( | |
328 new DivElement()..classes = ['memberItem'] | |
329 ..children = [ | |
330 new DivElement()..classes = ['memberName'] | |
331 ..text = 'supertype', | |
332 new DivElement()..classes = ['memberValue'] | |
333 ..children = [ | |
334 new InstanceRefElement(_isolate, _cls.superType, _instances, | |
335 queue: _r.queue) | |
336 ] | |
337 ] | |
338 ); | |
339 } | |
340 if (cls.mixin != null) { | |
341 members.add( | |
342 new DivElement()..classes = ['memberItem'] | |
343 ..children = [ | |
344 new DivElement()..classes = ['memberName'] | |
345 ..text = 'mixin', | |
346 new DivElement()..classes = ['memberValue'] | |
347 ..children = [ | |
348 new InstanceRefElement(_isolate, _cls.mixin, _instances, | |
349 queue: _r.queue) | |
350 ] | |
351 ] | |
352 ); | |
353 } | |
354 if (_cls.subclasses.length > 0) { | |
355 members.add( | |
356 new DivElement()..classes = ['memberItem'] | |
357 ..children = [ | |
358 new DivElement()..classes = ['memberName'] | |
359 ..text = 'extended by', | |
360 new DivElement()..classes = ['memberValue'] | |
361 ..children = (_cls.subclasses.expand((subcls) => [ | |
362 new ClassRefElement(_isolate, subcls, queue: _r.queue), | |
363 new SpanElement()..text = ', ' | |
364 ]).toList()..removeLast()) | |
365 ] | |
366 ); | |
367 } | |
368 | |
369 members.add(new BRElement()); | |
370 | |
371 if (_cls.interfaces.length > 0) { | |
372 members.add( | |
rmacnak
2016/08/31 23:51:39
This pattern seems to get repeated a lot in many o
cbernaschina
2016/09/01 00:04:07
Acknowledged.
| |
373 new DivElement()..classes = ['memberItem'] | |
374 ..children = [ | |
375 new DivElement()..classes = ['memberName'] | |
376 ..text = 'implements', | |
377 new DivElement()..classes = ['memberValue'] | |
378 ..children = (_cls.interfaces.expand((interf) => [ | |
379 new InstanceRefElement(_isolate, interf, _instances, | |
380 queue: _r.queue), | |
381 new SpanElement()..text = ', ' | |
382 ]).toList()..removeLast()) | |
383 ] | |
384 ); | |
385 } | |
386 if (_cls.name != _cls.vmName) { | |
387 members.add( | |
388 new DivElement()..classes = ['memberItem'] | |
389 ..children = [ | |
390 new DivElement()..classes = ['memberName'] | |
391 ..text = 'vm name', | |
392 new DivElement()..classes = ['memberValue'] | |
393 ..text = '${_cls.vmName}' | |
394 ] | |
395 ); | |
396 } | |
397 return members; | |
398 } | |
399 | |
400 List<Element> _createElements() { | |
401 final members = <Element>[]; | |
402 if (_classFields != null && _classFields.isNotEmpty) { | |
403 final fields = _classFields.toList(); | |
404 _fieldsExpanded = _fieldsExpanded ?? (fields.length <= 8); | |
405 members.add( | |
406 new DivElement()..classes = ['memberItem'] | |
407 ..children = [ | |
408 new DivElement()..classes = ['memberName'] | |
409 ..text = 'fields ${fields.length}', | |
410 new DivElement()..classes = ['memberValue'] | |
411 ..children =[ | |
412 new CurlyBlockElement(expanded: _fieldsExpanded) | |
413 ..onToggle.listen((e) => _fieldsExpanded = e.control.expanded) | |
414 ..children = [ | |
415 new DivElement()..classes = ['memberList'] | |
416 ..children = (fields.map((f) => | |
417 new DivElement()..classes = ['memberItem'] | |
418 ..children = [ | |
419 new DivElement()..classes = ['memberName'] | |
420 ..children =[ | |
421 new FieldRefElement(_isolate, f, _instances, | |
422 queue: _r.queue) | |
423 ], | |
424 new DivElement()..classes = ['memberValue'] | |
425 ..children = [ | |
426 anyRef(_isolate, f.staticValue, _instances, | |
427 queue: _r.queue) | |
428 ] | |
429 ] | |
430 ).toList()) | |
431 ] | |
432 ] | |
433 ] | |
434 ); | |
435 } | |
436 | |
437 if (_cls.functions.isNotEmpty) { | |
438 final functions = _cls.functions.toList(); | |
439 _functionsExpanded = _functionsExpanded ?? (functions.length <= 8); | |
440 members.add( | |
441 new DivElement()..classes = ['memberItem'] | |
442 ..children = [ | |
443 new DivElement()..classes = ['memberName'] | |
444 ..text = 'functions (${functions.length})', | |
445 new DivElement()..classes = ['memberValue'] | |
446 ..children =[ | |
447 new CurlyBlockElement(expanded: _functionsExpanded) | |
448 ..onToggle.listen((e) => | |
449 _functionsExpanded = e.control.expanded) | |
450 ..children = (functions.map((f) => | |
451 new DivElement()..classes = ['indent'] | |
452 ..children = [ | |
453 new FunctionRefElement(_isolate, f, queue: _r.queue) | |
454 ] | |
455 ).toList()) | |
456 ] | |
457 ] | |
458 ); | |
459 } | |
460 return members; | |
461 } | |
462 | |
463 Future _refresh() async { | |
464 _cls = await _classes.get(_isolate, _cls.id); | |
465 await _loadAdditionalData(); | |
466 _r.dirty(); | |
467 } | |
468 | |
469 Future _loadAdditionalData() async { | |
470 _classFields = await Future.wait(_cls.fields.map((f) | |
471 => _fields.get(_isolate, f.id))); | |
472 _r.dirty(); | |
126 } | 473 } |
127 } | 474 } |
OLD | NEW |