Commit 9cb914df authored by Todd Volkert's avatar Todd Volkert Committed by GitHub

Allow for application-specific log readers. (#6898)

* Allow for application-specific log readers.

When running with prebuilt application binaries, those applications
aren't guaranteed to be named "Runner" (as it is when we build
the app locally in Flutter tools)
parent dbacf775
......@@ -294,6 +294,7 @@ class AndroidDevice extends Device {
ProtocolDiscovery observatoryDiscovery;
ProtocolDiscovery diagnosticDiscovery;
DeviceLogReader logReader = getLogReader();
if (options.debuggingEnabled) {
observatoryDiscovery = new ProtocolDiscovery(logReader, ProtocolDiscovery.kObservatoryService);
diagnosticDiscovery = new ProtocolDiscovery(logReader, ProtocolDiscovery.kDiagnosticService);
......@@ -439,9 +440,9 @@ class AndroidDevice extends Device {
}
@override
DeviceLogReader get logReader {
if (_logReader == null)
_logReader = new _AdbLogReader(this);
DeviceLogReader getLogReader({ApplicationPackage app}) {
// The Android log reader isn't app-specific.
_logReader ??= new _AdbLogReader(this);
return _logReader;
}
......@@ -487,7 +488,7 @@ class AndroidDevice extends Device {
Future<List<DiscoveredApp>> discoverApps() {
RegExp discoverExp = new RegExp(r'DISCOVER: (.*)');
List<DiscoveredApp> result = <DiscoveredApp>[];
StreamSubscription<String> logs = logReader.logLines.listen((String line) {
StreamSubscription<String> logs = getLogReader().logLines.listen((String line) {
Match match = discoverExp.firstMatch(line);
if (match != null) {
Map<String, dynamic> app = JSON.decode(match.group(1));
......
......@@ -301,7 +301,11 @@ Future<int> startApp(DriveCommand command) async {
printTrace('Starting application.');
// Forward device log messages to the terminal window running the "drive" command.
command._deviceLogSubscription = command.device.logReader.logLines.listen(printStatus);
command._deviceLogSubscription = command
.device
.getLogReader(app: package)
.logLines
.listen(printStatus);
LaunchResult result = await command.device.startApp(
package,
......
......@@ -41,7 +41,7 @@ class LogsCommand extends FlutterCommand {
if (argResults['clear'])
device.clearLogs();
DeviceLogReader logReader = device.logReader;
DeviceLogReader logReader = device.getLogReader();
Cache.releaseLockEarly();
......
......@@ -164,8 +164,10 @@ abstract class Device {
String get sdkNameAndVersion;
/// Get the log reader for this device.
DeviceLogReader get logReader;
/// Get a log reader for this device.
/// If [app] is specified, this will return a log reader specific to that
/// application. Otherwise, a global log reader will be returned.
DeviceLogReader getLogReader({ApplicationPackage app});
/// Get the port forwarder for this device.
DevicePortForwarder get portForwarder;
......
......@@ -226,7 +226,7 @@ class HotRunner extends ResidentRunner {
Map<String, dynamic> platformArgs = new Map<String, dynamic>();
await startEchoingDeviceLog();
await startEchoingDeviceLog(_package);
printTrace('Launching loader on ${device.name}...');
......
......@@ -19,6 +19,8 @@ const String _ideviceinstallerInstructions =
'To work with iOS devices, please install ideviceinstaller.\n'
'If you use homebrew, you can install it with "\$ brew install ideviceinstaller".';
const Duration kPortForwardTimeout = const Duration(seconds: 10);
class IOSDevices extends PollingDeviceDiscovery {
IOSDevices() : super('IOSDevices');
......@@ -72,7 +74,7 @@ class IOSDevice extends Device {
@override
final String name;
_IOSDeviceLogReader _logReader;
Map<ApplicationPackage, _IOSDeviceLogReader> _logReaders;
_IOSDevicePortForwarder _portForwarder;
......@@ -251,11 +253,13 @@ class IOSDevice extends Device {
// ports post launch.
printTrace("Debugging is enabled, connecting to observatory and the diagnostic server");
Future<int> forwardObsPort = _acquireAndForwardPort(ProtocolDiscovery.kObservatoryService,
Future<int> forwardObsPort = _acquireAndForwardPort(app,
ProtocolDiscovery.kObservatoryService,
debuggingOptions.observatoryPort);
Future<int> forwardDiagPort;
if (debuggingOptions.buildMode == BuildMode.debug) {
forwardDiagPort = _acquireAndForwardPort(ProtocolDiscovery.kDiagnosticService,
forwardDiagPort = _acquireAndForwardPort(app,
ProtocolDiscovery.kDiagnosticService,
debuggingOptions.diagnosticPort);
} else {
forwardDiagPort = new Future<int>.value(null);
......@@ -272,7 +276,13 @@ class IOSDevice extends Device {
}
printTrace("Application launched on the device. Attempting to forward ports.");
return Future.wait(<Future<int>>[forwardObsPort, forwardDiagPort]);
return await Future.wait(<Future<int>>[forwardObsPort, forwardDiagPort])
.timeout(
kPortForwardTimeout,
onTimeout: () {
throw new TimeoutException('Timeout while waiting to acquire and forward ports.');
},
);
});
printTrace("Local Observatory Port: ${ports[0]}");
......@@ -293,10 +303,13 @@ class IOSDevice extends Device {
return new LaunchResult.succeeded(observatoryPort: localObsPort, diagnosticPort: localDiagPort);
}
Future<int> _acquireAndForwardPort(String serviceName, int localPort) async {
Future<int> _acquireAndForwardPort(
ApplicationPackage app,
String serviceName,
int localPort) async {
Duration stepTimeout = const Duration(seconds: 60);
Future<int> remote = new ProtocolDiscovery(logReader, serviceName).nextPort();
Future<int> remote = new ProtocolDiscovery(getLogReader(app: app), serviceName).nextPort();
int remotePort = await remote.timeout(stepTimeout,
onTimeout: () {
......@@ -365,11 +378,9 @@ class IOSDevice extends Device {
String get _buildVersion => _getDeviceInfo(id, 'BuildVersion');
@override
DeviceLogReader get logReader {
if (_logReader == null)
_logReader = new _IOSDeviceLogReader(this);
return _logReader;
DeviceLogReader getLogReader({ApplicationPackage app}) {
_logReaders ??= <ApplicationPackage, _IOSDeviceLogReader>{};
return _logReaders.putIfAbsent(app, () => new _IOSDeviceLogReader(this, app));
}
@override
......@@ -398,11 +409,20 @@ class IOSDevice extends Device {
}
class _IOSDeviceLogReader extends DeviceLogReader {
_IOSDeviceLogReader(this.device) {
RegExp _lineRegex;
_IOSDeviceLogReader(this.device, ApplicationPackage app) {
_linesController = new StreamController<String>.broadcast(
onListen: _start,
onCancel: _stop
);
onListen: _start,
onCancel: _stop
);
// Match for lines for the runner in syslog.
//
// iOS 9 format: Runner[297] <Notice>:
// iOS 10 format: Runner(libsystem_asl.dylib)[297] <Notice>:
String appName = app == null ? '' : app.name.replaceAll('.app', '');
_lineRegex = new RegExp(appName + r'(\(.*\))?\[[\d]+\] <[A-Za-z]+>: ');
}
final IOSDevice device;
......@@ -429,14 +449,8 @@ class _IOSDeviceLogReader extends DeviceLogReader {
});
}
// Match for lines for the runner in syslog.
//
// iOS 9 format: Runner[297] <Notice>:
// iOS 10 format: Runner(libsystem_asl.dylib)[297] <Notice>:
static final RegExp _runnerRegex = new RegExp(r'Runner(\(.*\))?\[[\d]+\] <[A-Za-z]+>: ');
void _onLine(String line) {
Match match = _runnerRegex.firstMatch(line);
Match match = _lineRegex.firstMatch(line);
if (match != null) {
// Only display the log line after the initial device and executable information.
......
......@@ -315,7 +315,7 @@ class IOSSimulator extends Device {
@override
bool get supportsHotMode => true;
_IOSSimulatorLogReader _logReader;
Map<ApplicationPackage, _IOSSimulatorLogReader> _logReaders;
_IOSSimulatorDevicePortForwarder _portForwarder;
String get xcrunPath => path.join('/usr', 'bin', 'xcrun');
......@@ -431,7 +431,8 @@ class IOSSimulator extends Device {
ProtocolDiscovery observatoryDiscovery;
if (debuggingOptions.debuggingEnabled)
observatoryDiscovery = new ProtocolDiscovery(logReader, ProtocolDiscovery.kObservatoryService);
observatoryDiscovery = new ProtocolDiscovery(getLogReader(app: app),
ProtocolDiscovery.kObservatoryService);
// Prepare launch arguments.
List<String> args = <String>[];
......@@ -554,11 +555,9 @@ class IOSSimulator extends Device {
String get sdkNameAndVersion => category;
@override
DeviceLogReader get logReader {
if (_logReader == null)
_logReader = new _IOSSimulatorLogReader(this);
return _logReader;
DeviceLogReader getLogReader({ApplicationPackage app}) {
_logReaders ??= <ApplicationPackage, _IOSSimulatorLogReader>{};
return _logReaders.putIfAbsent(app, () => new _IOSSimulatorLogReader(this, app));
}
@override
......@@ -629,13 +628,16 @@ class IOSSimulator extends Device {
}
class _IOSSimulatorLogReader extends DeviceLogReader {
_IOSSimulatorLogReader(this.device) {
String _appName;
_IOSSimulatorLogReader(this.device, ApplicationPackage app) {
_linesController = new StreamController<String>.broadcast(
onListen: () {
_start();
},
onCancel: _stop
);
_appName = app == null ? null : app.name.replaceAll('.app', '');
}
final IOSSimulator device;
......@@ -712,8 +714,9 @@ class _IOSSimulatorLogReader extends DeviceLogReader {
&& content.endsWith(']: 0x1'))
return null;
if (category == 'Runner')
if (_appName != null && category == _appName) {
return content;
}
return '$category: $content';
}
match = _lastMessageSingleRegex.matchAsPrefix(string);
......
......@@ -8,6 +8,7 @@ import 'dart:io';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;
import 'application_package.dart';
import 'base/logger.dart';
import 'build_info.dart';
import 'device.dart';
......@@ -96,11 +97,11 @@ abstract class ResidentRunner {
});
}
Future<Null> startEchoingDeviceLog() async {
Future<Null> startEchoingDeviceLog(ApplicationPackage app) async {
if (_loggingSubscription != null) {
return;
}
_loggingSubscription = device.logReader.logLines.listen((String line) {
_loggingSubscription = device.getLogReader(app: app).logLines.listen((String line) {
if (!line.contains('Observatory listening on http') &&
!line.contains('Diagnostic server listening on http'))
printStatus(line);
......
......@@ -119,7 +119,7 @@ class RunAndStayResident extends ResidentRunner {
if (traceStartup != null)
platformArgs = <String, dynamic>{ 'trace-startup': traceStartup };
await startEchoingDeviceLog();
await startEchoingDeviceLog(_package);
if (_mainPath == null) {
assert(prebuiltMode);
printStatus('Running ${_package.displayName} on ${device.name}');
......
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