diff --git a/pkg/dartdev/lib/src/dds_runner.dart b/pkg/dartdev/lib/src/dds_runner.dart index 749f0625997..912edd48c2b 100644 --- a/pkg/dartdev/lib/src/dds_runner.dart +++ b/pkg/dartdev/lib/src/dds_runner.dart @@ -46,12 +46,17 @@ class DDSRunner { ], mode: ProcessStartMode.detachedWithStdio, ); - final completer = Completer(); + + // NOTE: update pkg/dartdev/lib/src/commands/run.dart if this message + // is changed to ensure consistency. const devToolsMessagePrefix = 'The Dart DevTools debugger and profiler is available at:'; if (debugDds) { late final StreamSubscription stdoutSub; - stdoutSub = process.stdout.transform(utf8.decoder).listen((event) { + stdoutSub = process.stdout + .transform(utf8.decoder) + .transform(const LineSplitter()) + .listen((event) { if (event.startsWith(devToolsMessagePrefix)) { final ddsDebuggingUri = event.split(' ').last; print( @@ -61,21 +66,27 @@ class DDSRunner { } }); } - late final StreamSubscription stderrSub; - stderrSub = process.stderr.transform(utf8.decoder).listen((event) { - final result = json.decode(event) as Map; - final state = result['state']; - if (state == 'started') { - if (result.containsKey('devToolsUri')) { - final devToolsUri = result['devToolsUri']; + + // DDS will close stderr once it's finished launching. + final launchResult = await process.stderr.transform(utf8.decoder).join(); + + void printError(String details) => stderr.writeln( + 'Could not start the VM service:\n$details', + ); + + try { + final result = json.decode(launchResult); + if (result + case { + 'state': 'started', + 'ddsUri': final String ddsUriStr, + }) { + ddsUri = Uri.parse(ddsUriStr); + if (result case {'devToolsUri': String devToolsUri}) { print('$devToolsMessagePrefix $devToolsUri'); } - ddsUri = Uri.parse(result['ddsUri']); - stderrSub.cancel(); - completer.complete(); } else { - stderrSub.cancel(); - final error = result['error'] ?? event; + final error = result['error'] ?? result; final stacktrace = result['stacktrace'] ?? ''; String message = 'Could not start the VM service: '; if (error.contains('Failed to create server socket')) { @@ -83,15 +94,15 @@ class DDSRunner { } else { message += '$error\n$stacktrace\n'; } - completer.completeError(message); + printError(message); + return false; } - }); - try { - await completer.future; - return true; - } catch (e) { - stderr.write(e); + } catch (_) { + // Malformed JSON was likely encountered, so output the entirety of + // stderr in the error message. + printError(launchResult); return false; } + return true; } } diff --git a/pkg/dds/bin/dds.dart b/pkg/dds/bin/dds.dart index 1d38387881e..2741855d316 100644 --- a/pkg/dds/bin/dds.dart +++ b/pkg/dds/bin/dds.dart @@ -131,6 +131,10 @@ ${argParser.usage} stderr.close(); } catch (e, st) { writeErrorResponse(e, st); + } finally { + // Always close stderr to notify tooling that DDS has finished writing + // launch details. + await stderr.close(); } } diff --git a/sdk/lib/_internal/vm/bin/vmservice_io.dart b/sdk/lib/_internal/vm/bin/vmservice_io.dart index 4ac1f205e23..e21d6ca22b4 100644 --- a/sdk/lib/_internal/vm/bin/vmservice_io.dart +++ b/sdk/lib/_internal/vm/bin/vmservice_io.dart @@ -114,12 +114,20 @@ class _DebuggingSession { ], mode: ProcessStartMode.detachedWithStdio, ); - final completer = Completer(); - late StreamSubscription stderrSub; - stderrSub = _process!.stderr.transform(utf8.decoder).listen((event) { - final result = json.decode(event) as Map; - final state = result['state']; - if (state == 'started') { + + // DDS will close stderr once it's finished launching. + final launchResult = await _process!.stderr.transform(utf8.decoder).join(); + + void printError(String details) => stderr.writeln( + 'Could not start the VM service:\n$details', + ); + + try { + final result = json.decode(launchResult); + if (result + case { + 'state': 'started', + }) { if (result case {'devToolsUri': String devToolsUri}) { // NOTE: update pkg/dartdev/lib/src/commands/run.dart if this message // is changed to ensure consistency. @@ -135,21 +143,17 @@ class _DebuggingSession { } when _printDtd) { print('The Dart Tooling Daemon (DTD) is available at: $dtdUri'); } - stderrSub.cancel(); - completer.complete(); } else { - final error = result['error'] ?? event; - stderrSub.cancel(); - completer.completeError('Could not start the VM service:\n$error\n'); + printError(result['error'] ?? result); + return false; } - }); - try { - await completer.future; - return true; - } catch (e) { - stderr.write(e); + } catch (_) { + // Malformed JSON was likely encountered, so output the entirety of + // stderr in the error message. + printError(launchResult); return false; } + return true; } void shutdown() => _process!.kill();