blob: ff4cc6ac425b139c9fc49236ae0de48e77a03c62 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. 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:meta/meta.dart';
import '../framework/framework.dart';
import '../profiler/cpu_profile_flame_chart.dart';
import '../profiler/cpu_profile_tables.dart';
import '../profiler/cpu_profiler.dart';
import '../ui/custom.dart';
import '../ui/elements.dart';
import '../ui/material_icons.dart';
import '../ui/primer.dart';
import '../ui/ui_utils.dart';
import '../ui/vm_flag_elements.dart';
import 'performance_controller.dart';
const performanceScreenId = 'performance';
class PerformanceScreen extends Screen {
PerformanceScreen({bool disabled, String disabledTooltip})
: super(
name: 'Performance',
id: 'performance',
iconClass: 'octicon-dashboard',
disabled: disabled,
disabledTooltip: disabledTooltip,
);
final PerformanceController _performanceController = PerformanceController();
PButton _startRecordingButton;
PButton _stopRecordingButton;
PButton _clearButton;
ProfileGranularitySelector _profileGranularitySelector;
CoreElement _recordingInstructions;
CoreElement _recordingStatus;
CoreElement _recordingStatusMessage;
CpuProfilerTabNav _tabNav;
_CpuProfiler _cpuProfiler;
@override
CoreElement createContent(Framework framework) {
final CoreElement screenDiv = div(c: 'custom-scrollbar')..layoutVertical();
// Initialize screen content.
_initContent();
screenDiv.add([
div(c: 'section')
..layoutHorizontal()
..add([
div(c: 'btn-group')
..add([
_startRecordingButton,
_stopRecordingButton,
]),
_clearButton,
div()..flex(),
_profileGranularitySelector.selector,
]),
div(c: 'section')
..layoutVertical()
..flex()
..add([
_tabNav.element..hidden(true),
div(c: 'profiler-container section-border')
..add([
_cpuProfiler..hidden(true),
_recordingInstructions,
_recordingStatus..hidden(true)
]),
]),
]);
maybeAddDebugMessage(framework, performanceScreenId);
return screenDiv;
}
void _initContent() {
_startRecordingButton = PButton.icon('Record', recordPrimary)
..small()
..primary()
..click(() async => await _startRecording());
_stopRecordingButton = PButton.icon('Stop', stop)
..small()
..clazz('margin-left')
..disabled = true
..click(() async => await _stopRecording());
_clearButton = PButton.icon('Clear', clearIcon)
..small()
..clazz('margin-left')
..setAttribute('title', 'Clear timeline')
..click(_clear);
_profileGranularitySelector = ProfileGranularitySelector(framework);
_recordingInstructions = createRecordingInstructions(
recordingGoal: 'to start recording a CPU profile.');
_recordingStatus = div(c: 'center-in-parent')
..layoutVertical()
..flex()
..add([
_recordingStatusMessage = div(c: 'recording-status-message'),
Spinner.centered(classes: ['recording-spinner']),
]);
_cpuProfiler = _CpuProfiler(
_performanceController,
() => _performanceController.cpuProfileData,
);
_tabNav = CpuProfilerTabNav(
_cpuProfiler,
CpuProfilerTabOrder(
first: CpuProfilerViewType.callTree,
second: CpuProfilerViewType.bottomUp,
third: CpuProfilerViewType.flameChart,
),
);
}
@override
void entering() {
_profileGranularitySelector.setGranularity();
}
Future<void> _startRecording() async {
await _performanceController.startRecording();
_updateCpuProfilerVisibility(hidden: true);
_updateButtonStates();
_recordingInstructions.hidden(true);
_recordingStatusMessage.text = 'Recording profile';
_recordingStatus.hidden(false);
}
Future<void> _stopRecording() async {
_recordingStatusMessage.text = 'Processing profile';
await _performanceController.stopRecording();
_recordingStatus.hidden(true);
_updateCpuProfilerVisibility(hidden: false);
_updateButtonStates();
await _cpuProfiler.update();
}
void _clear() {
_performanceController.reset();
_updateCpuProfilerVisibility(hidden: true);
_recordingInstructions.hidden(false);
}
void _updateButtonStates() {
_startRecordingButton.disabled = _performanceController.recording;
_clearButton.disabled = _performanceController.recording;
_stopRecordingButton.disabled = !_performanceController.recording;
}
void _updateCpuProfilerVisibility({@required bool hidden}) {
_tabNav.element.hidden(hidden);
_cpuProfiler.hidden(hidden);
}
}
class _CpuProfiler extends CpuProfiler {
_CpuProfiler(
this._performanceController,
CpuProfileDataProvider profileDataProvider,
) : super(
CpuFlameChart(profileDataProvider),
CpuCallTree(profileDataProvider),
CpuBottomUp(profileDataProvider),
defaultView: CpuProfilerViewType.callTree,
);
final PerformanceController _performanceController;
@override
Future<void> prepareCpuProfile() async {
_performanceController.cpuProfileTransformer
.processData(_performanceController.cpuProfileData);
}
@override
bool maybeShowMessageOnUpdate() {
if (_performanceController.cpuProfileData == null ||
_performanceController.cpuProfileData.profileMetaData.sampleCount ==
0) {
showMessage(div(text: 'No CPU samples recorded.'));
return true;
}
return false;
}
}