blob: 33d4132d9bc65d25c19dc903664ede95f6b92f08 [file] [log] [blame]
// 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/custom_element.dart';
import 'package:observatory/utils.dart';
class SampleBufferControlChangedElement {
final SampleBufferControlElement element;
SampleBufferControlChangedElement(this.element);
}
class SampleBufferControlElement extends CustomElement implements Renderable {
late RenderingScheduler<SampleBufferControlElement> _r;
Stream<RenderedEvent<SampleBufferControlElement>> get onRendered =>
_r.onRendered;
StreamController<SampleBufferControlChangedElement> _onTagChange =
new StreamController<SampleBufferControlChangedElement>.broadcast();
Stream<SampleBufferControlChangedElement> get onTagChange =>
_onTagChange.stream;
late M.VM _vm;
late Stream<M.SampleProfileLoadingProgressEvent> _progressStream;
late M.SampleProfileLoadingProgress _progress;
late M.SampleProfileTag _tag;
bool _showTag = false;
bool _profileVM = false;
late 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.VM vm,
M.SampleProfileLoadingProgress progress,
Stream<M.SampleProfileLoadingProgressEvent> progressStream,
{M.SampleProfileTag selectedTag: M.SampleProfileTag.none,
bool showTag: true,
RenderingQueue? queue}) {
assert(progress != null);
assert(progressStream != null);
assert(selectedTag != null);
assert(showTag != null);
SampleBufferControlElement e = new SampleBufferControlElement.created();
e._r = new RenderingScheduler<SampleBufferControlElement>(e, queue: queue);
e._vm = vm;
e._progress = progress;
e._progressStream = progressStream;
e._tag = selectedTag;
e._showTag = showTag;
return e;
}
SampleBufferControlElement.created() : super.created('sample-buffer-control');
@override
void attached() {
super.attached();
_r.enable();
_subscription = _progressStream.listen((e) {
_progress = e.progress;
_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 = <Element>[
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 = <Element>[
new DivElement()
..classes = ['statusMessage']
..text = message,
new DivElement()
..style.background = '#0489c3'
..style.width = '$progress%'
..style.height = '15px'
..style.borderRadius = '4px'
]
];
}
List<Element> _createDisabledMessage() {
return [
new DivElement()
..classes = ['statusBox' 'shadow' 'center']
..children = <Element>[
new DivElement()
..children = <Element>[
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 ButtonElement()
..text = 'Enable profiler'
..onClick.listen((_) {
_enableProfiler();
})
]
]
];
}
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 maxStackDepth = _progress.profile.maxStackDepth;
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 = <Element>[
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 = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'Profile contains ',
new DivElement()
..classes = ['memberValue']
..text = '$sampleCount samples (spanning $timeSpan)'
],
new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'Sampling',
new DivElement()
..classes = ['memberValue']
..text = '$maxStackDepth stack frames @ ${sampleRate}Hz'
],
];
if (_showTag) {
content.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
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.vmOnly,
M.SampleProfileTag.none
];
}
var s;
return [
s = new SelectElement()
..classes = ['tag-select']
..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 _enableProfiler() {
_vm.enableProfiler().then((_) {
_triggerModeChange(_toEvent(null));
});
}
void _triggerModeChange(e) => _onTagChange.add(e);
}