Clean up orphaned Instruments processes (#11189)
In some cases, we've seen interactions between Instruments and the iOS
simulator that cause hung instruments and DTServiceHub processes. If
enough instances pile up, the host machine eventually becomes
unresponsive.
Until the underlying issue is resolved, manually kill any orphaned
instances (where the parent process has died and PPID is 1) before
launching another instruments run.
diff --git a/packages/flutter_tools/test/ios/mac_test.dart b/packages/flutter_tools/test/ios/mac_test.dart
index 01d891e..229504b 100644
--- a/packages/flutter_tools/test/ios/mac_test.dart
+++ b/packages/flutter_tools/test/ios/mac_test.dart
@@ -21,10 +21,10 @@
class MockFile extends Mock implements File {}
void main() {
- final FakePlatform osx = new FakePlatform.fromPlatform(const LocalPlatform());
- osx.operatingSystem = 'macos';
-
group('IMobileDevice', () {
+ final FakePlatform osx = new FakePlatform.fromPlatform(const LocalPlatform());
+ osx.operatingSystem = 'macos';
+
group('screenshot', () {
final String outputPath = fs.path.join('some', 'test', 'path', 'image.png');
MockProcessManager mockProcessManager;
@@ -68,6 +68,7 @@
group('Xcode', () {
MockProcessManager mockProcessManager;
+ final FakePlatform fakePlatform = new FakePlatform(environment: <String, String>{'USER': 'rwaters'});
Xcode xcode;
setUp(() {
@@ -213,6 +214,8 @@
});
testUsingContext('getAvailableDevices throws ToolExit when instruments is not installed', () async {
+ when(mockProcessManager.run(<String>['ps', '-e', '-o', 'user,ppid,pid,comm']))
+ .thenReturn(new ProcessResult(1, 0, '', ''));
when(mockProcessManager.run(<String>['/usr/bin/instruments', '-s', 'devices']))
.thenThrow(const ProcessException('/usr/bin/instruments', const <String>['-s', 'devices']));
expect(() async => await xcode.getAvailableDevices(), throwsToolExit());
@@ -221,6 +224,8 @@
});
testUsingContext('getAvailableDevices throws ToolExit when instruments returns non-zero', () async {
+ when(mockProcessManager.run(<String>['ps', '-e', '-o', 'user,ppid,pid,comm']))
+ .thenReturn(new ProcessResult(1, 0, '', ''));
when(mockProcessManager.run(<String>['/usr/bin/instruments', '-s', 'devices']))
.thenReturn(new ProcessResult(1, 1, '', 'Sad today'));
expect(() async => await xcode.getAvailableDevices(), throwsToolExit());
@@ -229,12 +234,47 @@
});
testUsingContext('getAvailableDevices returns instruments output when installed', () async {
+ when(mockProcessManager.run(<String>['ps', '-e', '-o', 'user,ppid,pid,comm']))
+ .thenReturn(new ProcessResult(1, 0, '', ''));
when(mockProcessManager.run(<String>['/usr/bin/instruments', '-s', 'devices']))
.thenReturn(new ProcessResult(1, 0, 'Known Devices:\niPhone 6s (10.3.3) [foo]', ''));
expect(await xcode.getAvailableDevices(), 'Known Devices:\niPhone 6s (10.3.3) [foo]');
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
+
+ testUsingContext('getAvailableDevices works even if orphan listing fails', () async {
+ when(mockProcessManager.run(<String>['ps', '-e', '-o', 'user,ppid,pid,comm']))
+ .thenReturn(new ProcessResult(1, 1, '', ''));
+ when(mockProcessManager.run(<String>['/usr/bin/instruments', '-s', 'devices']))
+ .thenReturn(new ProcessResult(1, 0, 'Known Devices:\niPhone 6s (10.3.3) [foo]', ''));
+ expect(await xcode.getAvailableDevices(), 'Known Devices:\niPhone 6s (10.3.3) [foo]');
+ }, overrides: <Type, Generator>{
+ ProcessManager: () => mockProcessManager,
+ });
+
+ testUsingContext('getAvailableDevices cleans up orphaned intstruments processes', () async {
+ when(mockProcessManager.run(<String>['ps', '-e', '-o', 'user,ppid,pid,comm']))
+ .thenReturn(new ProcessResult(1, 0, '''
+USER PPID PID COMM
+rwaters 1 36580 /Applications/Xcode.app/Contents/Developer/usr/bin/make
+rwaters 36579 36581 /Applications/Xcode.app/Contents/Developer/usr/bin/instruments
+rwaters 1 36582 /Applications/Xcode.app/Contents/Developer/usr/bin/instruments
+rwaters 1 36583 /Applications/Xcode.app/Contents/SharedFrameworks/DVTInstrumentsFoundation.framework/Resources/DTServiceHub
+rwaters 36581 36584 /Applications/Xcode.app/Contents/SharedFrameworks/DVTInstrumentsFoundation.framework/Resources/DTServiceHub
+''', ''));
+ when(mockProcessManager.run(<String>['/usr/bin/instruments', '-s', 'devices']))
+ .thenReturn(new ProcessResult(1, 0, 'Known Devices:\niPhone 6s (10.3.3) [foo]', ''));
+ await xcode.getAvailableDevices();
+ verify(mockProcessManager.killPid(36582));
+ verify(mockProcessManager.killPid(36583));
+ verifyNever(mockProcessManager.killPid(36580));
+ verifyNever(mockProcessManager.killPid(36581));
+ verifyNever(mockProcessManager.killPid(36584));
+ }, overrides: <Type, Generator>{
+ ProcessManager: () => mockProcessManager,
+ Platform: () => fakePlatform,
+ });
});
group('Diagnose Xcode build failure', () {