Commit 3a012b32 authored by John McCutchan's avatar John McCutchan Committed by GitHub

vmservice redux (#5437)

parent 53dd5dbd
......@@ -143,7 +143,7 @@ class RunCommand extends RunCommandBase {
return 1;
}
} else {
if (argResults['control-pipe']) {
if (argResults['control-pipe'] != null) {
printError('--control-pipe requires --hot');
return 1;
}
......
......@@ -95,40 +95,39 @@ class TraceCommand extends FlutterCommand {
}
class Tracing {
Tracing(this.observatory);
Tracing(this.vmService);
static Future<Tracing> connect(int port) {
return VMService.connect(port).then((VMService observatory) => new Tracing(observatory));
}
final VMService observatory;
final VMService vmService;
Future<Null> startTracing() async {
await observatory.setVMTimelineFlags(<String>['Compiler', 'Dart', 'Embedder', 'GC']);
await observatory.clearVMTimeline();
await vmService.vm.setVMTimelineFlags(<String>['Compiler', 'Dart', 'Embedder', 'GC']);
await vmService.vm.clearVMTimeline();
}
/// Stops tracing; optionally wait for first frame.
Future<Map<String, dynamic>> stopTracingAndDownloadTimeline({
bool waitForFirstFrame: false
}) async {
Response timeline;
Map<String, dynamic> timeline;
if (!waitForFirstFrame) {
// Stop tracing immediately and get the timeline
await observatory.setVMTimelineFlags(<String>[]);
timeline = await observatory.getVMTimeline();
await vmService.vm.setVMTimelineFlags(<String>[]);
timeline = await vmService.vm.getVMTimeline();
} else {
Completer<Null> whenFirstFrameRendered = new Completer<Null>();
observatory.onTimelineEvent.listen((Event timelineEvent) {
List<Map<String, dynamic>> events = timelineEvent['timelineEvents'];
vmService.onTimelineEvent.listen((ServiceEvent timelineEvent) {
List<Map<String, dynamic>> events = timelineEvent.timelineEvents;
for (Map<String, dynamic> event in events) {
if (event['name'] == kFirstUsefulFrameEventName)
whenFirstFrameRendered.complete();
}
});
await observatory.streamListen('Timeline');
await whenFirstFrameRendered.future.timeout(
const Duration(seconds: 10),
......@@ -142,12 +141,12 @@ class Tracing {
}
);
timeline = await observatory.getVMTimeline();
timeline = await vmService.vm.getVMTimeline();
await observatory.setVMTimelineFlags(<String>[]);
await vmService.vm.setVMTimelineFlags(<String>[]);
}
return timeline.response;
return timeline;
}
}
......
......@@ -106,22 +106,22 @@ abstract class DevFSOperations {
}
/// An implementation of [DevFSOperations] that speaks to the
/// service protocol.
/// vm service.
class ServiceProtocolDevFSOperations implements DevFSOperations {
final VMService serviceProtocol;
final VMService vmService;
ServiceProtocolDevFSOperations(this.serviceProtocol);
ServiceProtocolDevFSOperations(this.vmService);
@override
Future<Uri> create(String fsName) async {
Response response = await serviceProtocol.createDevFS(fsName);
Map<String, dynamic> response = await vmService.vm.createDevFS(fsName);
return Uri.parse(response['uri']);
}
@override
Future<dynamic> destroy(String fsName) async {
await serviceProtocol.sendRequest('_deleteDevFS',
<String, dynamic> { 'fsName': fsName });
await vmService.vm.invokeRpcRaw('_deleteDevFS',
<String, dynamic> { 'fsName': fsName });
}
@override
......@@ -134,12 +134,12 @@ class ServiceProtocolDevFSOperations implements DevFSOperations {
}
String fileContents = BASE64.encode(bytes);
try {
return await serviceProtocol.sendRequest('_writeDevFSFile',
<String, dynamic> {
'fsName': fsName,
'path': entry.devicePath,
'fileContents': fileContents
});
return await vmService.vm.invokeRpcRaw('_writeDevFSFile',
<String, dynamic> {
'fsName': fsName,
'path': entry.devicePath,
'fileContents': fileContents
});
} catch (e) {
printTrace('DevFS: Failed to write ${entry.devicePath}: $e');
}
......@@ -155,12 +155,12 @@ class ServiceProtocolDevFSOperations implements DevFSOperations {
String devicePath,
String contents) async {
String fileContents = BASE64.encode(UTF8.encode(contents));
return await serviceProtocol.sendRequest('_writeDevFSFile',
<String, dynamic> {
'fsName': fsName,
'path': devicePath,
'fileContents': fileContents
});
return await vmService.vm.invokeRpcRaw('_writeDevFSFile',
<String, dynamic> {
'fsName': fsName,
'path': devicePath,
'fileContents': fileContents
});
}
}
......@@ -251,7 +251,8 @@ class DevFS {
final Set<DevFSEntry> _deletedEntries = new Set<DevFSEntry>();
final Set<DevFSEntry> dirtyAssetEntries = new Set<DevFSEntry>();
final List<Future<Response>> _pendingOperations = new List<Future<Response>>();
final List<Future<Map<String, dynamic>>> _pendingOperations =
new List<Future<Map<String, dynamic>>>();
int _bytes = 0;
int get bytes => _bytes;
......@@ -358,7 +359,8 @@ class DevFS {
if (_deletedEntries.length > 0) {
status = logger.startProgress('Removing deleted files...');
for (DevFSEntry entry in _deletedEntries) {
Future<Response> operation = _operations.deleteFile(fsName, entry);
Future<Map<String, dynamic>> operation =
_operations.deleteFile(fsName, entry);
if (operation != null)
_pendingOperations.add(operation);
}
......@@ -382,7 +384,8 @@ class DevFS {
} else {
// Make service protocol requests for each.
for (DevFSEntry entry in _dirtyEntries) {
Future<Response> operation = _operations.writeFile(fsName, entry);
Future<Map<String, dynamic>> operation =
_operations.writeFile(fsName, entry);
if (operation != null)
_pendingOperations.add(operation);
}
......
......@@ -23,7 +23,6 @@ import 'devfs.dart';
import 'vmservice.dart';
import 'resident_runner.dart';
import 'toolchain.dart';
import 'view.dart';
String getDevFSLoaderScript() {
return path.absolute(path.join(Cache.flutterRoot,
......@@ -80,18 +79,18 @@ class StartupDependencySetBuilder {
class FirstFrameTimer {
FirstFrameTimer(this.serviceProtocol);
FirstFrameTimer(this.vmService);
void start() {
stopwatch.reset();
stopwatch.start();
_subscription = serviceProtocol.onExtensionEvent.listen(_onExtensionEvent);
_subscription = vmService.onExtensionEvent.listen(_onExtensionEvent);
}
/// Returns a Future which completes after the first frame event is received.
Future<Null> firstFrame() => _completer.future;
void _onExtensionEvent(Event event) {
void _onExtensionEvent(ServiceEvent event) {
if (event.extensionKind == 'Flutter.FirstFrame')
_stop();
}
......@@ -108,10 +107,10 @@ class FirstFrameTimer {
return stopwatch.elapsed;
}
final VMService serviceProtocol;
final VMService vmService;
final Stopwatch stopwatch = new Stopwatch();
final Completer<Null> _completer = new Completer<Null>();
StreamSubscription<Event> _subscription;
StreamSubscription<ServiceEvent> _subscription;
}
class HotRunner extends ResidentRunner {
......@@ -294,8 +293,8 @@ class HotRunner extends ResidentRunner {
return 3;
}
await viewManager.refresh();
printStatus('Connected to view \'${viewManager.mainView}\'.');
await vmService.vm.refreshViews();
printStatus('Connected to view \'${vmService.vm.mainView}\'.');
printStatus('Running ${getDisplayPath(_mainPath)} on ${device.name}...');
_loaderShowMessage('Launching...');
......@@ -332,13 +331,13 @@ class HotRunner extends ResidentRunner {
}
void _loaderShowMessage(String message, { int progress, int max }) {
serviceProtocol.flutterLoaderShowMessage(serviceProtocol.firstIsolateId, message);
currentView.uiIsolate.flutterLoaderShowMessage(message);
if (progress != null) {
serviceProtocol.flutterLoaderSetProgress(serviceProtocol.firstIsolateId, progress.toDouble());
serviceProtocol.flutterLoaderSetProgressMax(serviceProtocol.firstIsolateId, max?.toDouble() ?? 0.0);
currentView.uiIsolate.flutterLoaderSetProgress(progress.toDouble());
currentView.uiIsolate.flutterLoaderSetProgressMax(max?.toDouble() ?? 0.0);
} else {
serviceProtocol.flutterLoaderSetProgress(serviceProtocol.firstIsolateId, 0.0);
serviceProtocol.flutterLoaderSetProgressMax(serviceProtocol.firstIsolateId, -1.0);
currentView.uiIsolate.flutterLoaderSetProgress(0.0);
currentView.uiIsolate.flutterLoaderSetProgressMax(-1.0);
}
}
......@@ -346,7 +345,7 @@ class HotRunner extends ResidentRunner {
Future<Uri> _initDevFS() {
String fsName = path.basename(_projectRootPath);
_devFS = new DevFS(serviceProtocol,
_devFS = new DevFS(vmService,
fsName,
new Directory(_projectRootPath));
return _devFS.create();
......@@ -377,11 +376,10 @@ class HotRunner extends ResidentRunner {
Future<Null> _evictDirtyAssets() async {
if (_devFS.dirtyAssetEntries.length == 0)
return;
if (serviceProtocol.firstIsolateId == null)
if (currentView.uiIsolate == null)
throw 'Application isolate not found';
for (DevFSEntry entry in _devFS.dirtyAssetEntries) {
await serviceProtocol.flutterEvictAsset(serviceProtocol.firstIsolateId,
entry.assetPath);
await currentView.uiIsolate.flutterEvictAsset(entry.assetPath);
}
}
......@@ -400,7 +398,7 @@ class HotRunner extends ResidentRunner {
Future<Null> _launchInView(String entryPath,
String packagesPath,
String assetsDirectoryPath) async {
FlutterView view = viewManager.mainView;
FlutterView view = vmService.vm.mainView;
return view.runFromSource(entryPath, packagesPath, assetsDirectoryPath);
}
......@@ -419,7 +417,7 @@ class HotRunner extends ResidentRunner {
}
Future<Null> _restartFromSources() async {
FirstFrameTimer firstFrameTimer = new FirstFrameTimer(serviceProtocol);
FirstFrameTimer firstFrameTimer = new FirstFrameTimer(vmService);
firstFrameTimer.start();
await _updateDevFS();
await _launchFromDevFS(_package, _mainPath);
......@@ -459,16 +457,16 @@ class HotRunner extends ResidentRunner {
}
Future<bool> _reloadSources() async {
if (serviceProtocol.firstIsolateId == null)
if (currentView.uiIsolate == null)
throw 'Application isolate not found';
FirstFrameTimer firstFrameTimer = new FirstFrameTimer(serviceProtocol);
FirstFrameTimer firstFrameTimer = new FirstFrameTimer(vmService);
firstFrameTimer.start();
if (_devFS != null)
await _updateDevFS();
Status reloadStatus = logger.startProgress('Performing hot reload...');
try {
Map<String, dynamic> reloadReport =
await serviceProtocol.reloadSources(serviceProtocol.firstIsolateId);
await currentView.uiIsolate.reloadSources();
reloadStatus.stop(showElapsedTime: true);
if (!_printReloadReport(reloadReport)) {
// Reload failed.
......@@ -477,16 +475,16 @@ class HotRunner extends ResidentRunner {
} else {
flutterUsage.sendEvent('hot', 'reload');
}
} catch (errorMessage) {
} catch (errorMessage, st) {
reloadStatus.stop(showElapsedTime: true);
printError('Hot reload failed:\n$errorMessage');
printError('Hot reload failed:\n$errorMessage\n$st');
return false;
}
await _evictDirtyAssets();
Status reassembleStatus =
logger.startProgress('Reassembling application...');
try {
await serviceProtocol.flutterReassemble(serviceProtocol.firstIsolateId);
await currentView.uiIsolate.flutterReassemble();
} catch (_) {
reassembleStatus.stop(showElapsedTime: true);
printError('Reassembling application failed.');
......
......@@ -12,7 +12,6 @@ import 'build_info.dart';
import 'device.dart';
import 'globals.dart';
import 'vmservice.dart';
import 'view.dart';
// Shared code between different resident application runners.
abstract class ResidentRunner {
......@@ -28,8 +27,8 @@ abstract class ResidentRunner {
final bool usesTerminalUI;
final Completer<int> _finished = new Completer<int>();
VMService serviceProtocol;
ViewManager viewManager;
VMService vmService;
FlutterView currentView;
StreamSubscription<String> _loggingSubscription;
/// Start the app and keep the process running during its lifetime.
......@@ -48,11 +47,11 @@ abstract class ResidentRunner {
}
Future<Null> _debugDumpApp() async {
await serviceProtocol.flutterDebugDumpApp(serviceProtocol.firstIsolateId);
await currentView.uiIsolate.flutterDebugDumpApp();
}
Future<Null> _debugDumpRenderTree() async {
await serviceProtocol.flutterDebugDumpRenderTree(serviceProtocol.firstIsolateId);
await currentView.uiIsolate.flutterDebugDumpRenderTree();
}
void registerSignalHandlers() {
......@@ -90,22 +89,23 @@ abstract class ResidentRunner {
if (!debuggingOptions.debuggingEnabled) {
return new Future<Null>.error('Error the service protocol is not enabled.');
}
serviceProtocol = await VMService.connect(port);
vmService = await VMService.connect(port);
printTrace('Connected to service protocol on port $port');
serviceProtocol.populateIsolateInfo();
serviceProtocol.onExtensionEvent.listen((Event event) {
await vmService.getVM();
vmService.onExtensionEvent.listen((ServiceEvent event) {
printTrace(event.toString());
});
serviceProtocol.onIsolateEvent.listen((Event event) {
vmService.onIsolateEvent.listen((ServiceEvent event) {
printTrace(event.toString());
});
// Setup view manager and refresh the view list.
viewManager = new ViewManager(serviceProtocol);
await viewManager.refresh();
// Refresh the view list.
await vmService.vm.refreshViews();
currentView = vmService.vm.mainView;
assert(currentView != null);
// Listen for service protocol connection to close.
serviceProtocol.done.whenComplete(() {
vmService.done.whenComplete(() {
appFinished();
});
}
......@@ -175,9 +175,10 @@ abstract class ResidentRunner {
Future<Null> preStop() async { }
Future<Null> stopApp() async {
if (serviceProtocol != null && !serviceProtocol.isClosed) {
if (serviceProtocol.isolates.isNotEmpty) {
serviceProtocol.flutterExit(serviceProtocol.firstIsolateId);
if (vmService != null && !vmService.isClosed) {
if ((currentView != null) && (currentView.uiIsolate != null)) {
// TODO(johnmccutchan): Wait for the exit command to complete.
currentView.uiIsolate.flutterExit();
await new Future<Null>.delayed(new Duration(milliseconds: 100));
}
}
......
......@@ -59,17 +59,17 @@ class RunAndStayResident extends ResidentRunner {
@override
Future<bool> restart({ bool fullRestart: false }) async {
if (serviceProtocol == null) {
if (vmService == null) {
printError('Debugging is not enabled.');
return false;
} else {
Status status = logger.startProgress('Re-starting application...');
Future<Event> extensionAddedEvent;
Future<ServiceEvent> extensionAddedEvent;
if (device.restartSendsFrameworkInitEvent) {
extensionAddedEvent = serviceProtocol.onExtensionEvent
.where((Event event) => event.extensionKind == 'Flutter.FrameworkInitialization')
extensionAddedEvent = vmService.onExtensionEvent
.where((ServiceEvent event) => event.extensionKind == 'Flutter.FrameworkInitialization')
.first;
}
......@@ -77,7 +77,7 @@ class RunAndStayResident extends ResidentRunner {
_package,
_result,
mainPath: _mainPath,
observatory: serviceProtocol
observatory: vmService
);
status.stop(showElapsedTime: true);
......@@ -178,16 +178,20 @@ class RunAndStayResident extends ResidentRunner {
if (debuggingOptions.debuggingEnabled) {
await connectToServiceProtocol(_result.observatoryPort);
if (benchmark)
await serviceProtocol.waitFirstIsolate;
if (benchmark) {
await vmService.getVM();
}
}
printStatus('Application running.');
if (serviceProtocol != null && traceStartup) {
await vmService.vm.refreshViews();
printStatus('Connected to view \'${vmService.vm.mainView}\'.');
if (vmService != null && traceStartup) {
printStatus('Downloading startup trace info...');
try {
await downloadStartupTrace(serviceProtocol);
await downloadStartupTrace(vmService);
} catch(error) {
printError(error);
return 2;
......
// Copyright 2016 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 'globals.dart';
import 'vmservice.dart';
/// Peered to a Android/iOS FlutterView widget on a device.
class FlutterView {
FlutterView(this.viewId, this.viewManager);
final String viewId;
final ViewManager viewManager;
String _uiIsolateId;
String get uiIsolateId => _uiIsolateId;
Future<Null> runFromSource(String entryPath,
String packagesPath,
String assetsDirectoryPath) async {
return viewManager._runFromSource(this,
entryPath,
packagesPath,
assetsDirectoryPath);
}
@override
String toString() => viewId;
@override
bool operator ==(FlutterView other) {
return other.viewId == viewId;
}
@override
int get hashCode => viewId.hashCode;
}
/// Manager of FlutterViews.
class ViewManager {
ViewManager(this.serviceProtocol);
final VMService serviceProtocol;
Future<Null> refresh() async {
List<Map<String, String>> viewList = await serviceProtocol.getViewList();
for (Map<String, String> viewDescription in viewList) {
FlutterView view = new FlutterView(viewDescription['id'], this);
if (!views.contains(view)) {
// Canonicalize views against the view set.
views.add(view);
}
}
}
// TODO(johnmccutchan): Report errors when running failed.
Future<Null> _runFromSource(FlutterView view,
String entryPath,
String packagesPath,
String assetsDirectoryPath) async {
final String viewId = await serviceProtocol.getFirstViewId();
// When this completer completes the isolate is running.
final Completer<Null> completer = new Completer<Null>();
final StreamSubscription<Event> subscription =
serviceProtocol.onIsolateEvent.listen((Event event) {
// TODO(johnmccutchan): Listen to the debug stream and catch initial
// launch errors.
if (event.kind == 'IsolateRunnable') {
printTrace('Isolate is runnable.');
completer.complete(null);
}
});
await serviceProtocol.runInView(viewId,
entryPath,
packagesPath,
assetsDirectoryPath);
await completer.future;
await subscription.cancel();
}
// TODO(johnmccutchan): Remove this accessor and make the runner multi-view
// aware.
FlutterView get mainView {
return views.first;
}
final Set<FlutterView> views = new Set<FlutterView>();
}
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