Commit 5f9acc41 authored by Devon Carew's avatar Devon Carew Committed by GitHub

fire service protocol events for frames (#11565)

parent 5462ddb9
...@@ -13,9 +13,6 @@ import 'package:flutter_devicelab/framework/adb.dart'; ...@@ -13,9 +13,6 @@ import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart'; import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/utils.dart'; import 'package:flutter_devicelab/framework/utils.dart';
// "An Observatory debugger and profiler on iPhone SE is available at: http://127.0.0.1:8100/"
final RegExp observatoryRegExp = new RegExp(r'An Observatory debugger .* is available at: (\S+:(\d+))');
void main() { void main() {
task(() async { task(() async {
int vmServicePort; int vmServicePort;
...@@ -38,10 +35,9 @@ void main() { ...@@ -38,10 +35,9 @@ void main() {
.listen((String line) { .listen((String line) {
print('run:stdout: $line'); print('run:stdout: $line');
stdout.add(line); stdout.add(line);
if (line.contains(observatoryRegExp)) { if (lineContainsServicePort(line)) {
final Match match = observatoryRegExp.firstMatch(line); vmServicePort = parseServicePort(line);
vmServicePort = int.parse(match.group(2)); print('service protocol connection available at port $vmServicePort');
print('service protocol connection available at ${match.group(1)}');
print('run: ready!'); print('run: ready!');
ready.complete(); ready.complete();
ok ??= true; ok ??= true;
......
...@@ -14,6 +14,8 @@ import 'package:flutter_devicelab/framework/utils.dart'; ...@@ -14,6 +14,8 @@ import 'package:flutter_devicelab/framework/utils.dart';
void main() { void main() {
task(() async { task(() async {
int vmServicePort;
final Device device = await devices.workingDevice; final Device device = await devices.workingDevice;
await device.unlock(); await device.unlock();
final Directory appDir = dir(path.join(flutterDirectory.path, 'dev/integration_tests/ui')); final Directory appDir = dir(path.join(flutterDirectory.path, 'dev/integration_tests/ui'));
...@@ -30,18 +32,18 @@ void main() { ...@@ -30,18 +32,18 @@ void main() {
final Completer<Null> ready = new Completer<Null>(); final Completer<Null> ready = new Completer<Null>();
bool ok; bool ok;
print('run: starting...'); print('run: starting...');
// TODO(devoncarew): Instead of passing in a specific port, we should let the app
// bind to a free port and detect which port was chosen; see commands_test.dart.
final Process run = await startProcess( final Process run = await startProcess(
path.join(flutterDirectory.path, 'bin', 'flutter'), path.join(flutterDirectory.path, 'bin', 'flutter'),
<String>['run', '--verbose', '--observatory-port=8888', '-d', device.deviceId, '--route', '/smuggle-it', 'lib/route.dart'], <String>['run', '--verbose', '-d', device.deviceId, '--route', '/smuggle-it', 'lib/route.dart'],
); );
run.stdout run.stdout
.transform(UTF8.decoder) .transform(UTF8.decoder)
.transform(const LineSplitter()) .transform(const LineSplitter())
.listen((String line) { .listen((String line) {
print('run:stdout: $line'); print('run:stdout: $line');
if (line.contains(new RegExp(r'^\[\s+\] For a more detailed help message, press "h"\. To quit, press "q"\.'))) { if (lineContainsServicePort(line)) {
vmServicePort = parseServicePort(line);
print('service protocol connection available at port $vmServicePort');
print('run: ready!'); print('run: ready!');
ready.complete(); ready.complete();
ok ??= true; ok ??= true;
...@@ -60,7 +62,7 @@ void main() { ...@@ -60,7 +62,7 @@ void main() {
print('drive: starting...'); print('drive: starting...');
final Process drive = await startProcess( final Process drive = await startProcess(
path.join(flutterDirectory.path, 'bin', 'flutter'), path.join(flutterDirectory.path, 'bin', 'flutter'),
<String>['drive', '--use-existing-app', 'http://127.0.0.1:8888/', '--no-keep-app-running', 'lib/route.dart'], <String>['drive', '--use-existing-app', 'http://127.0.0.1:$vmServicePort/', '--no-keep-app-running', 'lib/route.dart'],
); );
drive.stdout drive.stdout
.transform(UTF8.decoder) .transform(UTF8.decoder)
......
// Copyright (c) 2017 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 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:vm_service_client/vm_service_client.dart';
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/utils.dart';
void main() {
task(() async {
int vmServicePort;
final Device device = await devices.workingDevice;
await device.unlock();
final Directory appDir = dir(path.join(flutterDirectory.path, 'dev/integration_tests/ui'));
await inDirectory(appDir, () async {
final Completer<Null> ready = new Completer<Null>();
bool ok;
print('run: starting...');
final Process run = await startProcess(
path.join(flutterDirectory.path, 'bin', 'flutter'),
<String>['run', '--verbose', '-d', device.deviceId, 'lib/main.dart'],
);
run.stdout
.transform(UTF8.decoder)
.transform(const LineSplitter())
.listen((String line) {
print('run:stdout: $line');
if (lineContainsServicePort(line)) {
vmServicePort = parseServicePort(line);
print('service protocol connection available at port $vmServicePort');
print('run: ready!');
ready.complete();
ok ??= true;
}
});
run.stderr
.transform(UTF8.decoder)
.transform(const LineSplitter())
.listen((String line) {
stderr.writeln('run:stderr: $line');
});
run.exitCode.then((int exitCode) { ok = false; });
await Future.any<dynamic>(<Future<dynamic>>[ ready.future, run.exitCode ]);
if (!ok)
throw 'Failed to run test app.';
final VMServiceClient client = new VMServiceClient.connect('ws://localhost:$vmServicePort/ws');
final VM vm = await client.getVM();
final VMIsolateRef isolate = vm.isolates.first;
final Stream<VMExtensionEvent> frameEvents = isolate.onExtensionEvent.where(
(VMExtensionEvent e) => e.kind == 'Flutter.Frame');
print('reassembling app...');
final Future<VMExtensionEvent> frameFuture = frameEvents.first;
await isolate.invokeExtension('ext.flutter.reassemble');
// ensure we get an event
final VMExtensionEvent event = await frameFuture;
print('${event.kind}: ${event.data}');
// validate the fields
// {number: 8, startTime: 0, elapsed: 1437}
expect(event.data['number'] is int);
expect(event.data['number'] >= 0);
expect(event.data['startTime'] is int);
expect(event.data['startTime'] >= 0);
expect(event.data['elapsed'] is int);
expect(event.data['elapsed'] >= 0);
run.stdin.write('q');
final int result = await run.exitCode;
if (result != 0)
throw 'Received unexpected exit code $result from run process.';
});
return new TaskResult.success(null);
});
}
void expect(bool value) {
if (!value)
throw 'failed assertion in service extensions test';
}
...@@ -473,3 +473,13 @@ String extractCloudAuthTokenArg(List<String> rawArgs) { ...@@ -473,3 +473,13 @@ String extractCloudAuthTokenArg(List<String> rawArgs) {
} }
return token; return token;
} }
// "An Observatory debugger and profiler on ... is available at: http://127.0.0.1:8100/"
final RegExp _kObservatoryRegExp = new RegExp(r'An Observatory debugger .* is available at: (\S+:(\d+))');
bool lineContainsServicePort(String line) => line.contains(_kObservatoryRegExp);
int parseServicePort(String line) {
final Match match = _kObservatoryRegExp.firstMatch(line);
return match == null ? null : int.parse(match.group(2));
}
...@@ -127,6 +127,13 @@ tasks: ...@@ -127,6 +127,13 @@ tasks:
required_agent_capabilities: ["has-android-device"] required_agent_capabilities: ["has-android-device"]
flaky: true flaky: true
service_extensions_test:
description: >
Validates our service protocol extensions.
stage: devicelab
required_agent_capabilities: ["has-android-device"]
flaky: true
android_sample_catalog_generator: android_sample_catalog_generator:
description: > description: >
Builds sample catalog markdown pages and Android screenshots Builds sample catalog markdown pages and Android screenshots
......
...@@ -557,7 +557,8 @@ abstract class SchedulerBinding extends BindingBase { ...@@ -557,7 +557,8 @@ abstract class SchedulerBinding extends BindingBase {
} }
Duration _currentFrameTimeStamp; Duration _currentFrameTimeStamp;
int _debugFrameNumber = 0; int _profileFrameNumber = 0;
final Stopwatch _profileFrameStopwatch = new Stopwatch();
String _debugBanner; String _debugBanner;
/// Called by the engine to prepare the framework to produce a new frame. /// Called by the engine to prepare the framework to produce a new frame.
...@@ -590,8 +591,13 @@ abstract class SchedulerBinding extends BindingBase { ...@@ -590,8 +591,13 @@ abstract class SchedulerBinding extends BindingBase {
if (rawTimeStamp != null) if (rawTimeStamp != null)
_lastRawTimeStamp = rawTimeStamp; _lastRawTimeStamp = rawTimeStamp;
profile(() {
_profileFrameNumber += 1;
_profileFrameStopwatch.reset();
_profileFrameStopwatch.start();
});
assert(() { assert(() {
_debugFrameNumber += 1;
if (debugPrintBeginFrameBanner || debugPrintEndFrameBanner) { if (debugPrintBeginFrameBanner || debugPrintEndFrameBanner) {
final StringBuffer frameTimeStampDescription = new StringBuffer(); final StringBuffer frameTimeStampDescription = new StringBuffer();
if (rawTimeStamp != null) { if (rawTimeStamp != null) {
...@@ -599,7 +605,7 @@ abstract class SchedulerBinding extends BindingBase { ...@@ -599,7 +605,7 @@ abstract class SchedulerBinding extends BindingBase {
} else { } else {
frameTimeStampDescription.write('(warm-up frame)'); frameTimeStampDescription.write('(warm-up frame)');
} }
_debugBanner = '▄▄▄▄▄▄▄▄ Frame ${_debugFrameNumber.toString().padRight(7)} ${frameTimeStampDescription.toString().padLeft(18)} ▄▄▄▄▄▄▄▄'; _debugBanner = '▄▄▄▄▄▄▄▄ Frame ${_profileFrameNumber.toString().padRight(7)} ${frameTimeStampDescription.toString().padLeft(18)} ▄▄▄▄▄▄▄▄';
if (debugPrintBeginFrameBanner) if (debugPrintBeginFrameBanner)
debugPrint(_debugBanner); debugPrint(_debugBanner);
} }
...@@ -651,14 +657,22 @@ abstract class SchedulerBinding extends BindingBase { ...@@ -651,14 +657,22 @@ abstract class SchedulerBinding extends BindingBase {
_invokeFrameCallback(callback, _currentFrameTimeStamp); _invokeFrameCallback(callback, _currentFrameTimeStamp);
} finally { } finally {
_schedulerPhase = SchedulerPhase.idle; _schedulerPhase = SchedulerPhase.idle;
_currentFrameTimeStamp = null; Timeline.finishSync(); // end the Frame
Timeline.finishSync(); profile(() {
_profileFrameStopwatch.stop();
postEvent('Flutter.Frame', <String, dynamic>{
'number': _profileFrameNumber,
'startTime': _currentFrameTimeStamp.inMicroseconds,
'elapsed': _profileFrameStopwatch.elapsedMicroseconds
});
});
assert(() { assert(() {
if (debugPrintEndFrameBanner) if (debugPrintEndFrameBanner)
debugPrint('▀' * _debugBanner.length); debugPrint('▀' * _debugBanner.length);
_debugBanner = null; _debugBanner = null;
return true; return true;
}); });
_currentFrameTimeStamp = null;
} }
// All frame-related callbacks have been executed. Run lower-priority tasks. // All frame-related callbacks have been executed. Run lower-priority tasks.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment