Chromium Code Reviews| Index: runtime/observatory/lib/src/elements/sample_buffer_control.dart |
| diff --git a/runtime/observatory/lib/src/elements/sample_buffer_control.dart b/runtime/observatory/lib/src/elements/sample_buffer_control.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..59c46a27c4ee4d9623a0713e7ffa6f98822cd813 |
| --- /dev/null |
| +++ b/runtime/observatory/lib/src/elements/sample_buffer_control.dart |
| @@ -0,0 +1,383 @@ |
| +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +import 'dart:async'; |
| +import 'dart:html'; |
| +import 'package:observatory/models.dart' as M; |
| +import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; |
| +import 'package:observatory/src/elements/helpers/tag.dart'; |
| +import 'package:observatory/src/elements/helpers/uris.dart'; |
| +import 'package:observatory/utils.dart'; |
| + |
| +class SampleBufferControlChangedElement { |
| + final SampleBufferControlElement element; |
| + SampleBufferControlChangedElement(this.element); |
| +} |
| + |
| +class SampleBufferControlElement extends HtmlElement implements Renderable { |
| + static const tag = |
| + const Tag<SampleBufferControlElement>('sample-buffer-control'); |
| + |
| + RenderingScheduler<SampleBufferControlElement> _r; |
| + |
| + Stream<RenderedEvent<SampleBufferControlElement>> get onRendered => |
| + _r.onRendered; |
| + |
| + StreamController<SampleBufferControlChangedElement> _onTagChange = |
| + new StreamController<SampleBufferControlChangedElement>.broadcast(); |
| + Stream<SampleBufferControlChangedElement> get onTagChange => |
| + _onTagChange.stream; |
| + |
| + M.SampleProfileLoadingProgress _progress; |
| + M.SampleProfileTag _tag; |
| + bool _showTag = false; |
| + bool _profileVM = false; |
| + StreamSubscription _subscription; |
| + |
| + M.SampleProfileLoadingProgress get progress => _progress; |
| + M.SampleProfileTag get selectedTag => _tag; |
| + bool get showTag => _showTag; |
| + bool get profileVM => _profileVM; |
| + |
| + set selectedTag(M.SampleProfileTag value) => |
| + _tag = _r.checkAndReact(_tag, value); |
| + set showTag(bool value) => _showTag = _r.checkAndReact(_showTag, value); |
| + set profileVM(bool value) => _profileVM = _r.checkAndReact(_profileVM, value); |
| + |
| + |
| + factory SampleBufferControlElement(M.SampleProfileLoadingProgress progress, |
| + {M.SampleProfileTag selectedTag: M.SampleProfileTag.None, |
| + bool showTag: true, RenderingQueue queue}) { |
| + assert(progress != null); |
| + assert(selectedTag != null); |
| + assert(showTag != null); |
| + SampleBufferControlElement e = document.createElement(tag.name); |
| + e._r = new RenderingScheduler(e, queue: queue); |
| + e._progress = progress; |
| + e._tag = selectedTag; |
| + e._showTag = showTag; |
| + return e; |
| + } |
| + |
| + SampleBufferControlElement.created() : super.created(); |
| + |
| + @override |
| + void attached() { |
| + super.attached(); |
| + _r.enable(); |
| + _subscription = _progress.onProgress.listen((_) => _r.dirty()); |
| + } |
| + |
| + @override |
| + void detached() { |
| + super.detached(); _r.disable(notify: true); |
| + children = const []; |
| + _subscription.cancel(); |
| + } |
| + |
| + void render() { |
| + var content = <Element>[ |
| + new HeadingElement.h2()..text = 'Sample buffer', |
| + new HRElement() |
| + ]; |
| + switch (_progress.status) { |
| + case M.SampleProfileLoadingStatus.Fetching : |
| + content.addAll(_createStatusMessage('Fetching profile from VM...')); |
| + break; |
| + case M.SampleProfileLoadingStatus.Loading : |
| + content.addAll(_createStatusMessage('Loading profile...', |
| + progress: _progress.progress)); |
| + break; |
| + case M.SampleProfileLoadingStatus.Disabled : |
| + content.addAll(_createDisabledMessage()); |
| + break; |
| + case M.SampleProfileLoadingStatus.Loaded: |
| + content.addAll(_createStatusReport()); |
| + break; |
| + } |
| + children = [ |
| + new DivElement()..classes = ['content-centered-big'] |
| + ..children = content |
| + ]; |
| + } |
| + |
| + static List<Element> _createStatusMessage(String message, |
| + {double progress: 0.0}) { |
| + return [new DivElement()..classes = ['statusBox', 'shadow', 'center'] |
| + ..children = [ |
| + new DivElement()..classes = ['statusMessage'] |
| + ..text = message, |
| + new DivElement()..style.background = '#0489c3' |
| + ..style.width = '$progress%' |
| + ..style.height = '15px' |
| + ..style.borderRadius = '4px' |
| + ] |
| + ]; |
| + } |
| + |
| + static List<Element> _createDisabledMessage() { |
| + return [new DivElement()..classes = ['statusBox' 'shadow' 'center'] |
| + ..children = [ |
| + new DivElement() |
| + ..children = [ |
| + new HeadingElement.h1() |
| + ..text = 'Profiling is disabled', |
| + new BRElement(), |
| + new DivElement() |
| + ..innerHtml = 'Perhaps the <b>profile</b> ' |
| + 'flag has been disabled for this VM.', |
| + new BRElement(), |
| + new SpanElement()..text = 'See all', |
| + new AnchorElement(href: Uris.flags())..text = 'vm flags' |
| + ] |
| + ] |
| + ]; |
| + } |
| + |
| + List<Element> _createStatusReport() { |
| + final fetchT = Utils.formatDurationInSeconds(_progress.fetchingTime); |
| + final loadT = Utils.formatDurationInSeconds(_progress.loadingTime); |
| + final sampleCount = _progress.profile.sampleCount; |
| + final refreshT = new DateTime.now(); |
| + final stackDepth = _progress.profile.stackDepth; |
| + final sampleRate = _progress.profile.sampleRate.toStringAsFixed(0); |
| + final timeSpan = _progress.profile.sampleCount == 0 ? '0s' |
| + : Utils.formatTimePrecise(_progress.profile.timeSpan); |
| + |
| + var content = <Element>[ |
| + new DivElement()..classes = ['memberItem'] |
| + ..children = [ |
| + new DivElement()..classes = ['memberName'] |
| + ..text = 'Refreshed at', |
| + new DivElement()..classes = ['memberValue'] |
| + ..text = '$refreshT (fetched in ${fetchT}s) (loaded in ${loadT}s)' |
| + ], |
| + new DivElement()..classes = ['memberItem'] |
| + ..children = [ |
| + new DivElement()..classes = ['memberName'] |
| + ..text = 'Profile contains ', |
| + new DivElement()..classes = ['memberValue'] |
| + ..text = '$sampleCount samples (spanning $timeSpan)' |
| + ], |
| + new DivElement()..classes = ['memberItem'] |
| + ..children = [ |
| + new DivElement()..classes = ['memberName'] |
| + ..text = 'Sampling', |
| + new DivElement()..classes = ['memberValue'] |
| + ..text = '$stackDepth stack frames @ ${sampleRate}Hz' |
| + ], |
| + ]; |
| + if (_showTag) { |
| + content.add( |
| + new DivElement()..classes = ['memberItem'] |
| + ..children = [ |
| + new DivElement()..classes = ['memberName'] |
| + ..text = 'Tag Order', |
| + new DivElement()..classes = ['memberValue'] |
| + ..children = _createTagSelect() |
| + ] |
| + ); |
| + } |
| + return [ |
| + new DivElement()..classes = ['memberList'] |
| + ..children = content |
| + ]; |
| + } |
| + |
| + List<Element> _createTagSelect() { |
| + var values = M.SampleProfileTag.values; |
| + if (!_profileVM) { |
| + values = const [M.SampleProfileTag.UserOnly, M.SampleProfileTag.None]; |
| + } |
| + var s; |
| + return [ |
| + s = new SelectElement()..value = tagToString(_tag) |
| + ..children = values.map((tag) { |
| + return new OptionElement(value : tagToString(tag), |
| + selected: _tag == tag) |
| + ..text = tagToString(tag); |
| + }).toList(growable: false) |
| + ..onChange.listen((_) { |
| + _tag = values[s.selectedIndex]; |
| + }) |
| + ..onChange.map(_toEvent).listen(_triggerModeChange), |
| + ]; |
| + } |
| + |
| + static String tagToString(M.SampleProfileTag tag) { |
| + switch (tag) { |
| + case M.SampleProfileTag.UserVM: return 'User > VM'; |
| + case M.SampleProfileTag.UserOnly: return 'User'; |
| + case M.SampleProfileTag.VMUser: return 'VM > User'; |
| + case M.SampleProfileTag.VMOnly: return 'VM'; |
| + case M.SampleProfileTag.None: return 'None'; |
| + } |
| + throw new Exception('Unknown tagToString'); |
| + } |
| + |
| + SampleBufferControlChangedElement _toEvent(_) { |
| + return new SampleBufferControlChangedElement(this); |
| + } |
| + |
| + void _triggerModeChange(e) => _onTagChange.add(e); |
| + |
| + /*Future _changeState(SampleBufferState newState, [exception, stackTrace]) { |
|
Cutch
2016/08/02 15:52:20
delete commented out code
cbernaschina
2016/08/02 17:40:24
Done.
|
| + if ((newState == Sampl |
| + } else if (newState == SampleBufferState.Loading) { |
| + fetchTime = formatTimeMilliseconds(_stopWatch.elapsedMilliseconds); |
| + loadTime = ''; |
| + } else if (newState == SampleBufferState.Loaded) { |
| + loadTime = formatTimeMilliseconds(_stopWatch.elapsedMilliseconds); |
| + } |
| + state = newState; |
| + this.exception = exception; |
| + this.stackTrace = stackTrace; |
| + _stopWatch.reset(); |
| + return window.animationFrame; |
| + } |
| + |
| + _update(CpuProfile sampleBuffer) { |
| + sampleCount = profile.sampleCount.toString(); |
| + refreshTime = new DateTime.now().toString(); |
| + stackDepth = profile.stackDepth.toString(); |
| + sampleRate = profile.sampleRate.toStringAsFixed(0); |
| + if (profile.sampleCount == 0) { |
| + timeSpan = '0s'; |
| + } else { |
| + timeSpan = formatTime(profile.timeSpan); |
| + } |
| + } |
| + |
| + void tagSelectorChanged(oldValue) { |
| + reload(profile.isolate); |
| + } |
| + |
| + Function onSampleBufferUpdate; |
| + bool showTagSelector = true; |
| + bool profileVM = false; |
| + String sampleCount = ''; |
| + String refreshTime = ''; |
| + String sampleRate = ''; |
| + String stackDepth = ''; |
| + String timeSpan = ''; |
| + String fetchTime = ''; |
| + String loadTime = ''; |
| + String tagSelector; |
| + SampleBufferState state = SampleBufferState.Fetching; |
| + var exception; |
| + var stackTrace; |
| + |
| + Class allocationProfileClass; |
| + |
| + final Stopwatch _stopWatch = new Stopwatch();*/ |
| +} |
| + |
| +/*<polymer-element name="sample-buffer-control"> |
| + <template> |
| + <link rel="stylesheet" href="css/shared.css"> |
| + <style> |
| + .statusMessage { |
| + font-size: 150%; |
| + font-weight: bold; |
| + } |
| + |
| + .statusBox { |
| + height: 100%; |
| + padding: 1em; |
| + } |
| + |
| + .center { |
| + align-items: center; |
| + justify-content: center; |
| + } |
| + |
| + .notice { |
| + background-color: #fcf8e3; |
| + } |
| + |
| + .red { |
| + background-color: #f2dede; |
| + } |
| + </style> |
| + <div class="content-centered-big"> |
| + <template if="{{ state != 'kNotLoaded' }}"> |
| + <h2>Sample buffer</h2> |
| + <hr> |
| + </template> |
| + <template if="{{ state == 'kFetching' }}"> |
| + <div class="statusBox shadow center"> |
| + <div class="statusMessage">Fetching profile from VM...</div> |
| + </div> |
| + </template> |
| + <template if="{{ state == 'kLoading' }}"> |
| + <div class="statusBox shadow center"> |
| + <div class="statusMessage">Loading profile...</div> |
| + </div> |
| + </template> |
| + <template if="{{ state == 'kDisabled' }}"> |
| + <div class="statusBox shadow center"> |
| + <div> |
| + <h1>Profiling is disabled</h1> |
| + <br> |
| + Perhaps the <b>profile</b> flag has been disabled for this VM. |
| + <br><br> |
| + See all |
| + <a on-click="{{ goto }}" _href="{{ gotoLink('/flags') }}">vm flags</a> |
| + </div> |
| + </div> |
| + </template> |
| + <template if="{{ state == 'kException' }}"> |
| + <div class="statusBox shadow center"> |
| + <div class="statusMessage"> |
| + <h1>Profiling is disabled due to unexpected exception:</h1> |
| + <br> |
| + <pre>{{ exception.toString() }}</pre> |
| + <br> |
| + <h1>Stack trace:</h1> |
| + <br> |
| + <pre>{{ stackTrace.toString() }}</pre> |
| + </div> |
| + </div> |
| + </template> |
| + <template if="{{ state == 'kLoaded' }}"> |
| + <div class="memberList"> |
| + <div class="memberItem"> |
| + <div class="memberName">Refreshed at </div> |
| + <div class="memberValue">{{ refreshTime }} (fetched in {{ fetchTime }}) (loaded in {{ loadTime }})</div> |
| + </div> |
| + <div class="memberItem"> |
| + <div class="memberName">Profile contains</div> |
| + <div class="memberValue">{{ sampleCount }} samples (spanning {{ timeSpan }})</div> |
| + </div> |
| + <div class="memberItem"> |
| + <div class="memberName">Sampling</div> |
| + <div class="memberValue">{{ stackDepth }} stack frames @ {{ sampleRate }} Hz</div> |
| + </div> |
| + <template if="{{ showTagSelector }}"> |
| + <div class="memberItem"> |
| + <div class="memberName">Tag Order</div> |
| + <div class="memberValue"> |
| + <template if="{{ profileVM }}"> |
| + <select value="{{tagSelector}}"> |
| + <option value="UserVM">User > VM</option> |
| + <option value="UserOnly">User</option> |
| + <option value="VMUser">VM > User</option> |
| + <option value="VMOnly">VM</option> |
| + <option value="None">None</option> |
| + </select> |
| + </template> |
| + <template if="{{ !profileVM }}"> |
| + <select value="{{tagSelector}}"> |
| + <option value="UserOnly">User</option> |
| + <option value="None">None</option> |
| + </select> |
| + </template> |
| + </div> |
| + </div> |
| + </template> |
| + </div> |
| + </template> |
| + </div> |
| + </template> |
| +</polymer-element>*/ |