Skip to content

always record timeline frames when on the timeline page #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/device/device.dart
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class DeviceScreen extends Screen {
_createBoolToggle('ext.flutter.repaintRainbow');
_createBoolToggle('ext.flutter.showPerformanceOverlay');
_createBoolToggle('ext.flutter.debugAllowBanner');
_createBoolToggle('ext.flutter.debugProfileBuilds');
}

void _createBoolToggle(String rpc) {
Expand Down
12 changes: 10 additions & 2 deletions lib/framework/framework.dart
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,12 @@ class Framework {

void load(Screen screen) {
if (current != null) {
current.exiting();
final Screen oldScreen = current;
current = null;
oldScreen.exiting();

pageStatus.removeAll();
_contents[current] = mainElement.element.children.toList();
_contents[oldScreen] = mainElement.element.children.toList();
mainElement.element.children.clear();
} else {
mainElement.element.children.clear();
Expand All @@ -122,6 +125,7 @@ class Framework {
if (_contents.containsKey(current)) {
mainElement.element.children.addAll(_contents[current]);
} else {
current.framework = this;
current.createContent(this, mainElement);
}

Expand Down Expand Up @@ -231,6 +235,8 @@ abstract class Screen {
final String id;
final String iconClass;

Framework framework;

final Property<bool> _visible = new Property<bool>(true);

final List<StatusItem> statusItems = <StatusItem>[];
Expand All @@ -255,6 +261,8 @@ abstract class Screen {

void entering() {}

bool get isCurrentScreen => framework != null && framework.current == this;

void exiting() {}

// TODO(devoncarew): generalize this - global and page status items
Expand Down
3 changes: 0 additions & 3 deletions lib/logging/logging.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import '../utils.dart';
const int kMaxLogItemsLength = 40;

class LoggingScreen extends Screen {
Framework framework;
Table<LogData> loggingTable;
StatusItem logCountStatus;
SetStateMixin loggingStateMixin = new SetStateMixin();
Expand All @@ -44,8 +43,6 @@ class LoggingScreen extends Screen {

@override
void createContent(Framework framework, CoreElement mainDiv) {
this.framework = framework;

mainDiv.add(<CoreElement>[
_createTableView()..clazz('section'),
]);
Expand Down
3 changes: 0 additions & 3 deletions lib/memory/memory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ class MemoryScreen extends Screen {

PButton loadSnapshotButton;
Table<ClassHeapStats> memoryTable;
Framework framework;

MemoryChart memoryChart;
SetStateMixin memoryChartStateMixin = new SetStateMixin();
Expand All @@ -44,8 +43,6 @@ class MemoryScreen extends Screen {

@override
void createContent(Framework framework, CoreElement mainDiv) {
this.framework = framework;

mainDiv.add(<CoreElement>[
createLiveChartArea(),
div(c: 'section'),
Expand Down
3 changes: 0 additions & 3 deletions lib/performance/performance.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ class PerformanceScreen extends Screen {
PButton resetButton;
CoreElement progressElement;
Table<PerfData> perfTable;
Framework framework;

CpuChart cpuChart;
SetStateMixin cpuChartStateMixin = new SetStateMixin();
Expand All @@ -43,8 +42,6 @@ class PerformanceScreen extends Screen {

@override
void createContent(Framework framework, CoreElement mainDiv) {
this.framework = framework;

mainDiv.add(<CoreElement>[
createLiveChartArea(),
div(c: 'section'),
Expand Down
169 changes: 106 additions & 63 deletions lib/timeline/timeline.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,61 +13,68 @@ import '../ui/elements.dart';
import '../ui/primer.dart';
import 'fps.dart';

// TODO(devoncarew): expose perf debugging toggles (ext.flutter.repaintRainbow,
// ext.flutter.showPerformanceOverlay, others)

// TODO(devoncarew): show the Skia picture (gpu drawing commands) for a frame

// TODO(devoncarew): show the list of widgets re-draw during a frame
// TODO(devoncarew): show the list of widgets re-drawn during a frame

// TODO(devoncarew): display whether running in debug or profile

// TODO(devoncarew): use colors for the category

class TimelineScreen extends Screen {
TimelineScreen()
: super(name: 'Timeline', id: 'timeline', iconClass: 'octicon-pulse');

FramesChart framesChart;
SetStateMixin framesChartStateMixin = new SetStateMixin();
FramesTracker framesTracker;
TimelineFramesBuilder timelineFramesBuilder = new TimelineFramesBuilder();

TimelineFramesUI timelineFramesUI;

TimelineScreen()
: super(name: 'Timeline', id: 'timeline', iconClass: 'octicon-pulse');
bool paused = false;

PButton pauseButton;
PButton resumeButton;

@override
void createContent(Framework framework, CoreElement mainDiv) {
FrameDetailsUI frameDetailsUI;

// TODO(devoncarew): pause and resume
pauseButton = new PButton('Pause recording')
..small()
..primary()
..click(_pauseRecording);

final PButton debugDrawButton = new PButton('Debug draw')..small();
resumeButton = new PButton('Resume recording')
..small()
..clazz('margin-left')
..disabled = true
..click(_resumeRecording);

final PButton trackWidgetBuildsButton = new PButton('Track widget builds')
..small();
final PButton perfOverlayButton = new PButton('Performance overlay')
..small();
final PButton debugBannerButton = new PButton('Debug banner')
..small()
..clazz('selected');
final PButton repaintRainbowButton = new PButton('Repaint rainbow')
..small();
final PButton debugDrawButton = new PButton('Debug draw')..small();

mainDiv.add(<CoreElement>[
createLiveChartArea(),
div(c: 'section'),
div(c: 'section')
..layoutHorizontal()
..add(<CoreElement>[
new PButton('Start timeline recording')
..small()
..primary()
..click(_startTimeline),
new PButton('Stop recording')
..small()
..clazz('margin-left')
..click(_stopTimeline),
pauseButton,
resumeButton,
div()..flex(),
div(c: 'btn-group')
..add(<CoreElement>[
debugDrawButton,
trackWidgetBuildsButton,
perfOverlayButton,
debugBannerButton,
repaintRainbowButton,
debugDrawButton,
]),
]),
div(c: 'section')
Expand All @@ -80,33 +87,23 @@ class TimelineScreen extends Screen {
..add(frameDetailsUI = new FrameDetailsUI()..attribute('hidden')),
]);

debugDrawButton.click(() {
final bool wasSelected =
debugDrawButton.element.classes.contains('selected');
debugDrawButton.toggleClass('selected');
serviceInfo.service.callServiceExtension('ext.flutter.debugPaint',
void handleToggleButton(PButton button, String serviceCallName) {
button.click(() {
final bool wasSelected = button.element.classes.contains('selected');
button.toggleClass('selected');
serviceInfo.service.callServiceExtension(
serviceCallName,
isolateId: serviceInfo.isolateManager.selectedIsolate.id,
args: <String, bool>{'enabled': !wasSelected});
});

perfOverlayButton.click(() {
final bool wasSelected =
perfOverlayButton.element.classes.contains('selected');
perfOverlayButton.toggleClass('selected');
serviceInfo.service.callServiceExtension(
'ext.flutter.showPerformanceOverlay',
isolateId: serviceInfo.isolateManager.selectedIsolate.id,
args: <String, bool>{'enabled': !wasSelected});
});
args: <String, bool>{'enabled': !wasSelected},
);
});
}

debugBannerButton.click(() {
final bool wasSelected =
debugBannerButton.element.classes.contains('selected');
debugBannerButton.toggleClass('selected');
serviceInfo.service.callServiceExtension('ext.flutter.debugAllowBanner',
isolateId: serviceInfo.isolateManager.selectedIsolate.id,
args: <String, bool>{'enabled': !wasSelected});
});
handleToggleButton(
trackWidgetBuildsButton, 'ext.flutter.debugProfileBuilds');
handleToggleButton(perfOverlayButton, 'ext.flutter.showPerformanceOverlay');
handleToggleButton(repaintRainbowButton, 'ext.flutter.repaintRainbow');
handleToggleButton(debugDrawButton, 'ext.flutter.debugPaint');

serviceInfo.onConnectionAvailable.listen(_handleConnectionStart);
if (serviceInfo.hasConnection) {
Expand All @@ -117,8 +114,7 @@ class TimelineScreen extends Screen {
timelineFramesUI.onSelectedFrame.listen((TimelineFrame frame) {
frameDetailsUI.attribute('hidden', frame == null);

// TODO(devoncarew): allow frame selection while recording data
if (frame != null && timelineFramesUI.timelineData != null) {
if (frame != null && timelineFramesUI.hasStarted()) {
final TimelineFrameData data =
timelineFramesUI.timelineData.getFrameData(frame);
frameDetailsUI.updateData(data);
Expand All @@ -136,12 +132,12 @@ class TimelineScreen extends Screen {

@override
void entering() {
//print('entering $name');
_updateListeningState();
}

@override
void exiting() {
//print('exiting $name');
_updateListeningState();
}

void _handleConnectionStart(VmService service) {
Expand Down Expand Up @@ -176,10 +172,46 @@ class TimelineScreen extends Screen {
framesTracker?.stop();
}

void _startTimeline() async {
timelineFramesBuilder.clear();
timelineFramesUI.timelineData = null;
void _pauseRecording() {
pauseButton.disabled = true;
resumeButton.disabled = false;

paused = true;

_updateListeningState();
}

void _resumeRecording() {
pauseButton.disabled = false;
resumeButton.disabled = true;

paused = false;

_updateListeningState();
}

void _updateListeningState() async {
final bool shouldBeRunning = !paused && isCurrentScreen;
final bool isRunning = !timelineFramesBuilder.isPaused;

if (shouldBeRunning && isRunning && !timelineFramesUI.hasStarted()) {
_startTimeline();
}

if (shouldBeRunning && !isRunning) {
timelineFramesBuilder.resume();

await serviceInfo.service
.setVMTimelineFlags(<String>['GC', 'Dart', 'Embedder']);
} else if (!shouldBeRunning && isRunning) {
// TODO: turn off the events
await serviceInfo.service.setVMTimelineFlags(<String>[]);

timelineFramesBuilder.pause();
}
}

void _startTimeline() async {
await serviceInfo.service
.setVMTimelineFlags(<String>['GC', 'Dart', 'Embedder']);
await serviceInfo.service.clearVMTimeline();
Expand Down Expand Up @@ -207,16 +239,6 @@ class TimelineScreen extends Screen {
timelineFramesUI.timelineData = new TimelineData(threads);
}

void _stopTimeline() async {
// http://127.0.0.1:8100/_getCpuProfileTimeline?tags=None&
// isolateId=isolates/140204549&timeOriginMicros=225679584415&
// timeExtentMicros=35716620

//timelineData.printData();

await serviceInfo.service.setVMTimelineFlags(<String>[]);
}

@override
HelpInfo get helpInfo =>
new HelpInfo(title: 'timeline docs', url: 'http://www.cheese.com');
Expand Down Expand Up @@ -538,6 +560,8 @@ class TimelineFramesUI extends CoreElement {
});
}

bool hasStarted() => timelineData != null;

Stream<TimelineFrame> get onSelectedFrame => _selectedFrameController.stream;

void setSelected(TimelineFrameUI frameUI) {
Expand Down Expand Up @@ -607,8 +631,12 @@ class TimelineFrameUI extends CoreElement {
}

class TimelineFramesBuilder {
static const int maxFrames = 120;

List<TimelineFrame> frames = <TimelineFrame>[];

bool isPaused = false;

final StreamController<TimelineFrame> _frameAddedController =
new StreamController<TimelineFrame>.broadcast();

Expand All @@ -619,6 +647,18 @@ class TimelineFramesBuilder {

Stream<Null> get onCleared => _clearedController.stream;

void pause() {
isPaused = true;

if (frames.isNotEmpty && !frames.last.isComplete) {
frames.removeLast();
}
}

void resume() {
isPaused = false;
}

void processTimelineEvent(TimelineEvent event) {
if (event.category != 'Embedder') {
return;
Expand Down Expand Up @@ -650,6 +690,9 @@ class TimelineFramesBuilder {
if (frame == null) {
frame = new TimelineFrame();
frames.add(frame);
if (frames.length > maxFrames) {
frames.removeAt(0);
}
}
frame.setRastereizeStart(event.timestampMicros);
} else if (event.phase == 'E') {
Expand Down