Skip to content

Conversation

@devoncarew
Copy link
Contributor

We're currently recording frame info to the VM timeline; this PR also fires service protocol extension events for flutter frames. The event includes the frameNumber, the startTime (in micros, from app start), and elapsed time, in micros. Debugger clients can opt-into this event via streamListen('Extension').

This info will allow clients to show a bit more awareness of real-time frame times.

@Hixie

Duration _currentFrameTimeStamp;

int _debugFrameNumber = 0;
Stopwatch _frameStopwatch;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is debug-only, please name it _debugFrameStopwatch

@Hixie
Copy link
Contributor

Hixie commented Jun 26, 2017

Seems a bit dubious to be recording this information in debug mode, since we don't make particular efforts to get good performance in debug builds. Ideally this would be profile-mode-only.

@devoncarew
Copy link
Contributor Author

devoncarew commented Jun 26, 2017

Ideally this would be profile-mode-only.

Ah, yeah, we would want the events when in profile mode. Is there a way to recognize that in the framework? (I realize that the asserts will all be stripped out at that point)

@Hixie
Copy link
Contributor

Hixie commented Jun 26, 2017

I think that's a Dart-level problem. Ideally I'd like a way to have profile-mode-only code stripped out of the binary in release builds.

@devoncarew
Copy link
Contributor Author

We have a way today to recognize debug mode (via assert()s). I'm guessing we don't have a way to differentiate between profile and release modes? Ideally I think I'd want the frame events available in debug and profile modes (profile for accurate, realtime frame info, and debug mode because the info is still interesting, and it would allow tools to give a more live sense of the running app.

@Hixie
Copy link
Contributor

Hixie commented Jun 29, 2017

I suggest we create a function in the foundation layer called profile that takes a VoidCallback and that only calls its argument if we're in profile mode. Then, we should file a bug on the VM asking for a way to guarantee (with a test) that this function is only included in profile mode, and that it is entirely omitted (including even the jump into the function) in release builds.

@devoncarew
Copy link
Contributor Author

sgtm; I'm in the process of re-working this given the const bool.fromEnvironment("dart.vm.product") test. An initial version of profile() could be based on that.

@devoncarew
Copy link
Contributor Author

@Hixie, re-worked, but I could use some guidance about where profile() should live.

@devoncarew
Copy link
Contributor Author

(I added profile() to foundation/profile.dart)

/// When running in profile mode (or debug mode), invoke the given function.
///
/// In release mode, the function is not invoked. In the future, we'd want the
/// given closure - and the call to [profile] - to be tree-shaken out.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "In the future" sentence should probably just be a TODO() rather than part of the documentation.

@Hixie
Copy link
Contributor

Hixie commented Jul 19, 2017

This looks fine but needs a test for the Flutter.Frame logic (verifying that the data is being sent in the expected format, for example, so that nobody accidentally breaks the protocol during a global search-and-replace of the string "startTime" or some such).

@devoncarew
Copy link
Contributor Author

This looks fine but needs a test for the Flutter.Frame logic

👍, will add a test

@devoncarew
Copy link
Contributor Author

Just for context, here is the real-time display I'm playing with in IntelliJ:

screen shot 2017-07-18 at 6 57 41 pm

@sethladd
Copy link
Contributor

cc @DanTup for ideas for VS Code :)

@devoncarew this looks great, thanks!

@devoncarew
Copy link
Contributor Author

@Hixie, I did refactor this a bit for better testability; we now have an event stream for the frame information, and that gets copied into service protocol events. It's not exactly the test you mentioned above, but does test that we're creating frame info events w/ the expected info.

Stream<FrameInfo> get onFrameInfo;

return true;
}

/// Performance information for a Flutter frame.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need much more information here, such as, how to get an instance of such a class, what information it stores, what modes it would be available in, the protocol by which it's sent over to IDEs or pointers to documentation on that protocol, etc.


/// The frame number.
final int number;
/// The start time in microseconds.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank lines between members

class FrameInfo {
FrameInfo(this.number, this.startTime, this.elapsed);

/// The frame number.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this documentation doesn't comply to our style guide.


/// The frame number.
final int number;
/// The start time in microseconds.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"start time in microseconds" only raises more questions! relative to what? is it monotonic? is it affected by timeDilation? etc etc etc

final int elapsed;

@override
String toString() => 'frame $number, elapsed time: $elapsed micros';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rather than "micros" let's just say "µs".

int _debugFrameNumber = 0;
int _profileFrameNumber = 0;
Stopwatch _profileFrameStopwatch = new Stopwatch();
StreamController<FrameInfo> _frameInfoController = new StreamController<FrameInfo>.broadcast();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's not initialise this in release mode...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also presumably it should be final?

/// These events are created after a frame is rendered and contain performance related
/// information. Events are only fired when the app is running in debug or profile mode;
/// no frame information events are fired in release mode.
Stream<FrameInfo> get onFrameInfo => _frameInfoController.stream;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this member doesn't comply with our style guide's naming convention.

@Hixie
Copy link
Contributor

Hixie commented Jul 20, 2017

i'm not entirely convinced that exposing this stream to the app makes much sense... what's the use case other than tests?

@devoncarew
Copy link
Contributor Author

i'm not entirely convinced that exposing this stream to the app makes much sense... what's the use case other than tests?

I don't have any other use case in mind than for tests. It looks like we do expose some other timing related information in SchedulerBinding. I'm also happy to try and reduce the visibility of the API to get at the stream of event info.

@Hixie
Copy link
Contributor

Hixie commented Jul 20, 2017

I'm just dubious about instantiating a whole Stream object and so forth in applications, just for testing purposes. That's a very high cost for something that happens very rarely.

@Hixie
Copy link
Contributor

Hixie commented Jul 20, 2017

Maybe you could collect the data then pass it to a protected method that calls developer.whatever, and in the test just subclass the binding and override that method?

The real thing that needs testing though is the VM service protocol and none of these tests are going to do that...

@devoncarew
Copy link
Contributor Author

Maybe you could collect the data then pass it to a protected method that calls developer.whatever, and in the test just subclass the binding and override that method?

I'll do some noodling, and either rework for visibility, or move to another type of test (a devicelab test) which tests at a different level.

The real thing that needs testing though is the VM service protocol and none of these tests are going to do that...

I agree it would be good to test at that level - not sure that we currently are for our other service extension work.

@Hixie
Copy link
Contributor

Hixie commented Jul 20, 2017

not sure that we currently are for our other service extension work.

Someone has to be the first. :-)

For example, until yesterday, we didn't have any test of flutter run that actually tested that pressing keys did anything. So when testing my new "show performance overlay" feature, rather than testing it the way we test the other features, I created a new kind of test that runs flutter run, and writes to its stdin stream, then runs flutter drive tests to see if it's having any effect. Bam, new kind of test. :-)

@DanTup
Copy link
Contributor

DanTup commented Jul 20, 2017

@sethladd I don't believe Code has any way for us to render arbitrary content like this :(

Also; we don't currently support profiling mode (I don't really know much about this) which I figure is where this makes sense. I've raised Dart-Code/Dart-Code#373 to have this in the list though :-)

@devoncarew
Copy link
Contributor Author

@Hixie - circling back to this one - I updated it to add a devicelab test, and removed the public API around listening for frame events (we're back to only firing them on the service protocol).

expect(event.data['startTime'] is int);
expect(event.data['startTime'] >= 0);
expect(event.data['elapsed'] is int);
expect(event.data['elapsed'] >= 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is fantastic. thank you!

@Hixie
Copy link
Contributor

Hixie commented Jul 27, 2017

This is wonderful.
LGTM

@devoncarew
Copy link
Contributor Author

bombs away

@devoncarew devoncarew merged commit 4b4cabb into flutter:master Jul 27, 2017
@devoncarew
Copy link
Contributor Author

devoncarew added a commit to devoncarew/flutter that referenced this pull request Jul 27, 2017
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 13, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants