Skip to content

Commit 35174cc

Browse files
authored
Fix issue where DevTools would not be immediately available when using --start-paused (flutter#126698)
Service extensions are unable to handle requests when the isolate they were registered on is paused. The DevTools launcher logic was waiting for some service extension invocations to complete before advertising the already active DevTools instance, but when --start-paused was provided these requests would never complete, preventing users from using DevTools to resume the paused isolate. Fixes flutter#126691
1 parent acf5a25 commit 35174cc

File tree

5 files changed

+90
-10
lines changed

5 files changed

+90
-10
lines changed

packages/flutter_tools/lib/src/resident_devtools_handler.dart

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ abstract class ResidentDevtoolsHandler {
3535
Future<void> serveAndAnnounceDevTools({
3636
Uri? devToolsServerAddress,
3737
required List<FlutterDevice?> flutterDevices,
38+
bool isStartPaused = false,
3839
});
3940

4041
bool launchDevToolsInBrowser({required List<FlutterDevice?> flutterDevices});
@@ -71,6 +72,7 @@ class FlutterResidentDevtoolsHandler implements ResidentDevtoolsHandler {
7172
Future<void> serveAndAnnounceDevTools({
7273
Uri? devToolsServerAddress,
7374
required List<FlutterDevice?> flutterDevices,
75+
bool isStartPaused = false,
7476
}) async {
7577
assert(!_readyToAnnounce);
7678
if (!_residentRunner.supportsServiceProtocol || _devToolsLauncher == null) {
@@ -88,20 +90,14 @@ class FlutterResidentDevtoolsHandler implements ResidentDevtoolsHandler {
8890
assert(!_readyToAnnounce);
8991
return;
9092
}
91-
final List<FlutterDevice?> devicesWithExtension = await _devicesWithExtensions(flutterDevices);
92-
await _maybeCallDevToolsUriServiceExtension(devicesWithExtension);
93-
await _callConnectedVmServiceUriExtension(devicesWithExtension);
94-
9593
if (_shutdown) {
9694
// If we're shutting down, no point reporting the debugger list.
9795
return;
9896
}
99-
_readyToAnnounce = true;
100-
assert(_devToolsLauncher!.activeDevToolsServer != null);
10197

10298
final Uri? devToolsUrl = _devToolsLauncher!.devToolsUrl;
10399
if (devToolsUrl != null) {
104-
for (final FlutterDevice? device in devicesWithExtension) {
100+
for (final FlutterDevice? device in flutterDevices) {
105101
if (device == null) {
106102
continue;
107103
}
@@ -111,11 +107,35 @@ class FlutterResidentDevtoolsHandler implements ResidentDevtoolsHandler {
111107
}
112108
}
113109

110+
Future<void> callServiceExtensions() async {
111+
final List<FlutterDevice?> devicesWithExtension = await _devicesWithExtensions(flutterDevices);
112+
await Future.wait(
113+
<Future<void>>[
114+
_maybeCallDevToolsUriServiceExtension(devicesWithExtension),
115+
_callConnectedVmServiceUriExtension(devicesWithExtension)
116+
]
117+
);
118+
}
119+
120+
// If the application is starting paused, we can't invoke service extensions
121+
// as they're handled on the target app's paused isolate. Since invoking
122+
// service extensions will block in this situation, we should wait to invoke
123+
// them until after we've output the DevTools connection details.
124+
if (!isStartPaused) {
125+
await callServiceExtensions();
126+
}
127+
128+
_readyToAnnounce = true;
129+
assert(_devToolsLauncher!.activeDevToolsServer != null);
114130
if (_residentRunner.reportedDebuggers) {
115131
// Since the DevTools only just became available, we haven't had a chance to
116132
// report their URLs yet. Do so now.
117133
_residentRunner.printDebuggerList(includeVmService: false);
118134
}
135+
136+
if (isStartPaused) {
137+
await callServiceExtensions();
138+
}
119139
}
120140

121141
// This must be guaranteed not to return a Future that fails.
@@ -295,7 +315,11 @@ class NoOpDevtoolsHandler implements ResidentDevtoolsHandler {
295315
}
296316

297317
@override
298-
Future<void> serveAndAnnounceDevTools({Uri? devToolsServerAddress, List<FlutterDevice?>? flutterDevices}) async {
318+
Future<void> serveAndAnnounceDevTools({
319+
Uri? devToolsServerAddress,
320+
List<FlutterDevice?>? flutterDevices,
321+
bool isStartPaused = false,
322+
}) async {
299323
return;
300324
}
301325

packages/flutter_tools/lib/src/run_cold.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ class ColdRunner extends ResidentRunner {
8686
unawaited(residentDevtoolsHandler!.serveAndAnnounceDevTools(
8787
devToolsServerAddress: debuggingOptions.devToolsServerAddress,
8888
flutterDevices: flutterDevices,
89+
isStartPaused: debuggingOptions.startPaused,
8990
));
9091
}
9192
if (debuggingOptions.serveObservatory) {
@@ -173,6 +174,7 @@ class ColdRunner extends ResidentRunner {
173174
unawaited(residentDevtoolsHandler!.serveAndAnnounceDevTools(
174175
devToolsServerAddress: debuggingOptions.devToolsServerAddress,
175176
flutterDevices: flutterDevices,
177+
isStartPaused: debuggingOptions.startPaused,
176178
));
177179
}
178180
if (debuggingOptions.serveObservatory) {

packages/flutter_tools/lib/src/run_hot.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ class HotRunner extends ResidentRunner {
246246
unawaited(residentDevtoolsHandler!.serveAndAnnounceDevTools(
247247
devToolsServerAddress: debuggingOptions.devToolsServerAddress,
248248
flutterDevices: flutterDevices,
249+
isStartPaused: debuggingOptions.startPaused,
249250
));
250251
}
251252

packages/flutter_tools/test/general.shard/resident_devtools_handler_test.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,14 @@ void main() {
156156
},
157157
),
158158
listViews,
159+
listViews,
159160
const FakeVmServiceRequest(
160161
method: 'ext.flutter.activeDevToolsServerAddress',
161162
args: <String, Object>{
162163
'isolateId': '1',
163164
'value': 'http://localhost:8080',
164165
},
165166
),
166-
listViews,
167167
const FakeVmServiceRequest(
168168
method: 'ext.flutter.connectedVmServiceUri',
169169
args: <String, Object>{
@@ -314,14 +314,14 @@ void main() {
314314
},
315315
),
316316
listViews,
317+
listViews,
317318
const FakeVmServiceRequest(
318319
method: 'ext.flutter.activeDevToolsServerAddress',
319320
args: <String, Object>{
320321
'isolateId': '1',
321322
'value': 'http://localhost:8080',
322323
},
323324
),
324-
listViews,
325325
const FakeVmServiceRequest(
326326
method: 'ext.flutter.connectedVmServiceUri',
327327
args: <String, Object>{
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:async';
6+
7+
import 'package:flutter_tools/src/base/file_system.dart';
8+
import 'package:flutter_tools/src/base/io.dart';
9+
import 'package:flutter_tools/src/convert.dart';
10+
11+
import '../src/common.dart';
12+
import 'test_data/basic_project.dart';
13+
import 'test_utils.dart';
14+
15+
void main() {
16+
late Directory tempDir;
17+
final BasicProject project = BasicProject();
18+
19+
setUp(() async {
20+
tempDir = createResolvedTempDirectorySync('run_test.');
21+
await project.setUpIn(tempDir);
22+
});
23+
24+
tearDown(() async {
25+
tryToDelete(tempDir);
26+
});
27+
28+
// Regression test for https://github.com/flutter/flutter/issues/126691
29+
testWithoutContext('flutter run --start-paused prints DevTools URI', () async {
30+
final Completer<void> completer = Completer<void>();
31+
final RegExp matcher = RegExp(r'The Flutter DevTools debugger and profiler on');
32+
33+
final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter');
34+
final Process process = await processManager.start(<String>[
35+
flutterBin,
36+
'run',
37+
'--start-paused',
38+
'-d',
39+
'flutter-tester',
40+
], workingDirectory: tempDir.path);
41+
42+
late StreamSubscription<String> sub;
43+
sub = process.stdout.transform(utf8.decoder).listen((String message) {
44+
if (message.contains(matcher)) {
45+
completer.complete();
46+
sub.cancel();
47+
}
48+
});
49+
await completer.future;
50+
process.kill();
51+
await process.exitCode;
52+
});
53+
}

0 commit comments

Comments
 (0)