mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Dump backtrace when cannot attach to observatory (#98550)
This commit is contained in:
parent
58ad6e1bef
commit
cef6823637
@ -437,12 +437,12 @@ class IOSDevice extends Device {
|
||||
final Uri? localUri = await observatoryDiscovery?.uri;
|
||||
timer.cancel();
|
||||
if (localUri == null) {
|
||||
iosDeployDebugger?.detach();
|
||||
await iosDeployDebugger?.stopAndDumpBacktrace();
|
||||
return LaunchResult.failed();
|
||||
}
|
||||
return LaunchResult.succeeded(observatoryUri: localUri);
|
||||
} on ProcessException catch (e) {
|
||||
iosDeployDebugger?.detach();
|
||||
await iosDeployDebugger?.stopAndDumpBacktrace();
|
||||
_logger.printError(e.message);
|
||||
return LaunchResult.failed();
|
||||
} finally {
|
||||
|
||||
@ -293,6 +293,15 @@ class IOSDeployDebugger {
|
||||
// (lldb) Process 6152 stopped
|
||||
static final RegExp _lldbProcessStopped = RegExp(r'Process \d* stopped');
|
||||
|
||||
// (lldb) Process 6152 detached
|
||||
static final RegExp _lldbProcessDetached = RegExp(r'Process \d* detached');
|
||||
|
||||
// Send signal to stop (pause) the app. Used before a backtrace dump.
|
||||
static const String _signalStop = 'process signal SIGSTOP';
|
||||
|
||||
// Print backtrace for all threads while app is stopped.
|
||||
static const String _backTraceAll = 'thread backtrace all';
|
||||
|
||||
/// Launch the app on the device, and attach the debugger.
|
||||
///
|
||||
/// Returns whether or not the debugger successfully attached.
|
||||
@ -330,16 +339,41 @@ class IOSDeployDebugger {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (line.contains('PROCESS_STOPPED') ||
|
||||
line.contains('PROCESS_EXITED') ||
|
||||
_lldbProcessExit.hasMatch(line) ||
|
||||
_lldbProcessStopped.hasMatch(line)) {
|
||||
if (line == _signalStop) {
|
||||
// The app is about to be stopped. Only show in verbose mode.
|
||||
_logger.printTrace(line);
|
||||
return;
|
||||
}
|
||||
if (line == _backTraceAll) {
|
||||
// The app is stopped and the backtrace for all threads will be printed.
|
||||
_logger.printTrace(line);
|
||||
// Even though we're not "detached", just stopped, mark as detached so the backtrace
|
||||
// is only show in verbose.
|
||||
_debuggerState = _IOSDeployDebuggerState.detached;
|
||||
return;
|
||||
}
|
||||
|
||||
if (line.contains('PROCESS_STOPPED') || _lldbProcessStopped.hasMatch(line)) {
|
||||
// The app has been stopped. Dump the backtrace, and detach.
|
||||
_logger.printTrace(line);
|
||||
_iosDeployProcess?.stdin.writeln(_backTraceAll);
|
||||
detach();
|
||||
return;
|
||||
}
|
||||
if (line.contains('PROCESS_EXITED') || _lldbProcessExit.hasMatch(line)) {
|
||||
// The app exited or crashed, so exit. Continue passing debugging
|
||||
// messages to the log reader until it exits to capture crash dumps.
|
||||
_logger.printTrace(line);
|
||||
exit();
|
||||
return;
|
||||
}
|
||||
if (_lldbProcessDetached.hasMatch(line)) {
|
||||
// The debugger has detached from the app, and there will be no more debugging messages.
|
||||
// Kill the ios-deploy process.
|
||||
exit();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_debuggerState != _IOSDeployDebuggerState.attached) {
|
||||
_logger.printTrace(line);
|
||||
return;
|
||||
@ -395,6 +429,22 @@ class IOSDeployDebugger {
|
||||
return success;
|
||||
}
|
||||
|
||||
Future<void> stopAndDumpBacktrace() async {
|
||||
if (!debuggerAttached) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Stop the app, which will prompt the backtrace to be printed for all threads in the stdoutSubscription handler.
|
||||
_iosDeployProcess?.stdin.writeln(_signalStop);
|
||||
} on SocketException catch (error) {
|
||||
// Best effort, try to detach, but maybe the app already exited or already detached.
|
||||
_logger.printTrace('Could not stop app from debugger: $error');
|
||||
}
|
||||
// Wait for logging to finish on process exit.
|
||||
return logLines.drain();
|
||||
}
|
||||
|
||||
void detach() {
|
||||
if (!debuggerAttached) {
|
||||
return;
|
||||
@ -403,7 +453,6 @@ class IOSDeployDebugger {
|
||||
try {
|
||||
// Detach lldb from the app process.
|
||||
_iosDeployProcess?.stdin.writeln('process detach');
|
||||
_debuggerState = _IOSDeployDebuggerState.detached;
|
||||
} on SocketException catch (error) {
|
||||
// Best effort, try to detach, but maybe the app already exited or already detached.
|
||||
_logger.printTrace('Could not detach from debugger: $error');
|
||||
|
||||
@ -90,11 +90,14 @@ void main () {
|
||||
logger = BufferLogger.test();
|
||||
});
|
||||
|
||||
testWithoutContext('debugger attached', () async {
|
||||
testWithoutContext('debugger attached and stopped', () async {
|
||||
final StreamController<List<int>> stdin = StreamController<List<int>>();
|
||||
final Stream<String> stdinStream = stdin.stream.transform<String>(const Utf8Decoder());
|
||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||
const FakeCommand(
|
||||
command: <String>['ios-deploy'],
|
||||
stdout: '(lldb) run\r\nsuccess\r\nsuccess\r\nLog on attach1\r\n\r\nLog on attach2\r\n\r\n\r\n\r\nPROCESS_STOPPED\r\nLog after process exit',
|
||||
FakeCommand(
|
||||
command: const <String>['ios-deploy'],
|
||||
stdout: "(lldb) run\r\nsuccess\r\nsuccess\r\nLog on attach1\r\n\r\nLog on attach2\r\n\r\n\r\n\r\nPROCESS_STOPPED\r\nLog after process stop\r\nthread backtrace all\r\n* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP",
|
||||
stdin: IOSink(stdin.sink),
|
||||
),
|
||||
]);
|
||||
final IOSDeployDebugger iosDeployDebugger = IOSDeployDebugger.test(
|
||||
@ -113,7 +116,15 @@ void main () {
|
||||
'Log on attach2',
|
||||
'',
|
||||
'',
|
||||
'Log after process exit',
|
||||
'Log after process stop'
|
||||
]);
|
||||
expect(logger.traceText, contains('PROCESS_STOPPED'));
|
||||
expect(logger.traceText, contains('thread backtrace all'));
|
||||
expect(logger.traceText, contains('* thread #1'));
|
||||
expect(await stdinStream.take(3).toList(), <String>[
|
||||
'thread backtrace all',
|
||||
'\n',
|
||||
'process detach',
|
||||
]);
|
||||
});
|
||||
|
||||
@ -141,11 +152,14 @@ void main () {
|
||||
});
|
||||
|
||||
testWithoutContext('app crash', () async {
|
||||
final StreamController<List<int>> stdin = StreamController<List<int>>();
|
||||
final Stream<String> stdinStream = stdin.stream.transform<String>(const Utf8Decoder());
|
||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||
const FakeCommand(
|
||||
command: <String>['ios-deploy'],
|
||||
FakeCommand(
|
||||
command: const <String>['ios-deploy'],
|
||||
stdout:
|
||||
'(lldb) run\r\nsuccess\r\nLog on attach\r\n(lldb) Process 6156 stopped\r\n* thread #1, stop reason = Assertion failed:',
|
||||
'(lldb) run\r\nsuccess\r\nLog on attach\r\n(lldb) Process 6156 stopped\r\n* thread #1, stop reason = Assertion failed:\r\nthread backtrace all\r\n* thread #1, stop reason = Assertion failed:',
|
||||
stdin: IOSink(stdin.sink),
|
||||
),
|
||||
]);
|
||||
final IOSDeployDebugger iosDeployDebugger = IOSDeployDebugger.test(
|
||||
@ -162,6 +176,14 @@ void main () {
|
||||
'Log on attach',
|
||||
'* thread #1, stop reason = Assertion failed:',
|
||||
]);
|
||||
expect(logger.traceText, contains('Process 6156 stopped'));
|
||||
expect(logger.traceText, contains('thread backtrace all'));
|
||||
expect(logger.traceText, contains('* thread #1'));
|
||||
expect(await stdinStream.take(3).toList(), <String>[
|
||||
'thread backtrace all',
|
||||
'\n',
|
||||
'process detach',
|
||||
]);
|
||||
});
|
||||
|
||||
testWithoutContext('attach failed', () async {
|
||||
@ -268,6 +290,31 @@ void main () {
|
||||
iosDeployDebugger.detach();
|
||||
expect(await stdinStream.first, 'process detach');
|
||||
});
|
||||
|
||||
testWithoutContext('stop with backtrace', () async {
|
||||
final StreamController<List<int>> stdin = StreamController<List<int>>();
|
||||
final Stream<String> stdinStream = stdin.stream.transform<String>(const Utf8Decoder());
|
||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||
FakeCommand(
|
||||
command: const <String>[
|
||||
'ios-deploy',
|
||||
],
|
||||
stdout:
|
||||
'(lldb) run\nsuccess\nLog on attach\n(lldb) Process 6156 stopped\n* thread #1, stop reason = Assertion failed:\n(lldb) Process 6156 detached',
|
||||
stdin: IOSink(stdin.sink),
|
||||
),
|
||||
]);
|
||||
final IOSDeployDebugger iosDeployDebugger = IOSDeployDebugger.test(
|
||||
processManager: processManager,
|
||||
);
|
||||
await iosDeployDebugger.launchAndAttach();
|
||||
await iosDeployDebugger.stopAndDumpBacktrace();
|
||||
expect(await stdinStream.take(3).toList(), <String>[
|
||||
'thread backtrace all',
|
||||
'\n',
|
||||
'process detach',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
group('IOSDeploy.uninstallApp', () {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user