Skip to content

Commit 1725a26

Browse files
authored
Switch the way we retrieve the vm_service_port from /hub to iquery, on device. (#114834)
1 parent a84e369 commit 1725a26

File tree

2 files changed

+174
-100
lines changed

2 files changed

+174
-100
lines changed

packages/fuchsia_remote_debug_protocol/lib/src/fuchsia_remote_connection.dart

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// found in the LICENSE file.
44

55
import 'dart:async';
6+
import 'dart:convert';
67
import 'dart:io';
78

89
import 'package:process/process.dart';
@@ -506,6 +507,39 @@ class FuchsiaRemoteConnection {
506507
_pollDartVms = true;
507508
}
508509

510+
/// Helper for getDeviceServicePorts() to extract the vm_service_port from
511+
/// json response.
512+
List<int> getVmServicePortFromInspectSnapshot(dynamic inspectSnapshot) {
513+
final List<Map<String, dynamic>> snapshot =
514+
List<Map<String, dynamic>>.from(inspectSnapshot as List<dynamic>);
515+
final List<int> ports = <int>[];
516+
517+
for (final Map<String, dynamic> item in snapshot) {
518+
if (!item.containsKey('payload') || item['payload'] == null) {
519+
continue;
520+
}
521+
final Map<String, dynamic> payload =
522+
Map<String, dynamic>.from(item['payload'] as Map<String, dynamic>);
523+
524+
if (!payload.containsKey('root') || payload['root'] == null) {
525+
continue;
526+
}
527+
final Map<String, dynamic> root =
528+
Map<String, dynamic>.from(payload['root'] as Map<String, dynamic>);
529+
530+
if (!root.containsKey('vm_service_port') ||
531+
root['vm_service_port'] == null) {
532+
continue;
533+
}
534+
535+
final int? port = int.tryParse(root['vm_service_port'] as String);
536+
if (port != null) {
537+
ports.add(port);
538+
}
539+
}
540+
return ports;
541+
}
542+
509543
/// Gets the open Dart VM service ports on a remote Fuchsia device.
510544
///
511545
/// The method attempts to get service ports through an SSH connection. Upon
@@ -514,24 +548,14 @@ class FuchsiaRemoteConnection {
514548
/// found. An exception is thrown in the event of an actual error when
515549
/// attempting to acquire the ports.
516550
Future<List<int>> getDeviceServicePorts() async {
517-
final List<String> portPaths = await _sshCommandRunner
518-
.run('/bin/find /hub -name vmservice-port');
519-
final List<int> ports = <int>[];
520-
for (final String path in portPaths) {
521-
if (path == '') {
522-
continue;
523-
}
524-
final List<String> lsOutput =
525-
await _sshCommandRunner.run('/bin/ls $path');
526-
for (final String line in lsOutput) {
527-
if (line == '') {
528-
continue;
529-
}
530-
final int? port = int.tryParse(line);
531-
if (port != null) {
532-
ports.add(port);
533-
}
534-
}
551+
final List<String> inspectResult = await _sshCommandRunner
552+
.run("iquery --format json show '**:root:vm_service_port'");
553+
final dynamic inspectOutputJson = jsonDecode(inspectResult.join('\n'));
554+
final List<int> ports =
555+
getVmServicePortFromInspectSnapshot(inspectOutputJson);
556+
557+
if (ports.length > 1) {
558+
throw StateError('More than one Flutter observatory port found');
535559
}
536560
return ports;
537561
}

packages/fuchsia_remote_debug_protocol/test/fuchsia_remote_connection_test.dart

Lines changed: 132 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ void main() {
8282
restoreVmServiceConnectionFunction();
8383
});
8484

85-
test('end-to-end with three vm connections and flutter view query', () async {
85+
test('end-to-end with one vm connection and flutter view query', () async {
8686
int port = 0;
8787
Future<PortForwarder> fakePortForwardingFunction(
8888
String address,
@@ -102,54 +102,72 @@ void main() {
102102
fuchsiaPortForwardingFunction = fakePortForwardingFunction;
103103
final FakeSshCommandRunner fakeRunner = FakeSshCommandRunner();
104104
// Adds some extra junk to make sure the strings will be cleaned up.
105-
fakeRunner.findResponse = <String>['/hub/blah/blah/blah/vmservice-port\n'];
106-
fakeRunner.lsResponse = <String>['123\n\n\n', '456 ', '789'];
105+
fakeRunner.iqueryResponse = <String>[
106+
'[',
107+
' {',
108+
' "data_source": "Inspect",',
109+
' "metadata": {',
110+
' "filename": "fuchsia.inspect.Tree",',
111+
' "component_url": "fuchsia-pkg://fuchsia.com/flutter_runner#meta/flutter_runner.cm",',
112+
' "timestamp": 12345678901234',
113+
' },',
114+
' "moniker": "core/session-manager/session/flutter_runner",',
115+
' "payload": {',
116+
' "root": {',
117+
' "vm_service_port": "12345",',
118+
' "16859221": {',
119+
' "empty_tree": "this semantic tree is empty"',
120+
' },',
121+
' "build_info": {',
122+
' "dart_sdk_git_revision": "77e83fcc14fa94049f363d554579f48fbd6bb7a1",',
123+
' "dart_sdk_semantic_version": "2.19.0-317.0.dev",',
124+
' "flutter_engine_git_revision": "563b8e830c697a543bf0a8a9f4ae3edfad86ea86",',
125+
' "fuchsia_sdk_version": "10.20221018.0.1"',
126+
' },',
127+
' "vm": {',
128+
' "dst_status": 1,',
129+
' "get_profile_status": 0,',
130+
' "num_get_profile_calls": 1,',
131+
' "num_intl_provider_errors": 0,',
132+
' "num_on_change_calls": 0,',
133+
' "timezone_content_status": 0,',
134+
' "tz_data_close_status": -1,',
135+
' "tz_data_status": -1',
136+
' }',
137+
' }',
138+
' },',
139+
' "version": 1',
140+
' }',
141+
' ]'
142+
];
107143
fakeRunner.address = 'fe80::8eae:4cff:fef4:9247';
108144
fakeRunner.interface = 'eno1';
109145

110146
final FuchsiaRemoteConnection connection =
111147
await FuchsiaRemoteConnection.connectWithSshCommandRunner(fakeRunner);
112148

113-
// [fakePortForwardingFunction] will have returned three different
114-
// forwarded ports, incrementing the port each time by one. (Just a sanity
115-
// check that the forwarding port was called).
116-
expect(forwardedPorts.length, 3);
117-
expect(forwardedPorts[0].remotePort, 123);
118-
expect(forwardedPorts[1].remotePort, 456);
119-
expect(forwardedPorts[2].remotePort, 789);
120-
expect(forwardedPorts[0].port, 0);
121-
expect(forwardedPorts[1].port, 1);
122-
expect(forwardedPorts[2].port, 2);
149+
expect(forwardedPorts.length, 1);
150+
expect(forwardedPorts[0].remotePort, 12345);
123151

124152
// VMs should be accessed via localhost ports given by
125153
// [fakePortForwardingFunction].
126154
expect(uriConnections[0],
127-
Uri(scheme:'ws', host:'[::1]', port:0, path:'/ws'));
128-
expect(uriConnections[1],
129-
Uri(scheme:'ws', host:'[::1]', port:1, path:'/ws'));
130-
expect(uriConnections[2],
131-
Uri(scheme:'ws', host:'[::1]', port:2, path:'/ws'));
155+
Uri(scheme: 'ws', host: '[::1]', port: 0, path: '/ws'));
132156

133157
final List<FlutterView> views = await connection.getFlutterViews();
134158
expect(views, isNot(null));
135-
expect(views.length, 3);
159+
expect(views.length, 1);
136160
// Since name can be null, check for the ID on all of them.
137161
expect(views[0].id, 'flutterView0');
138-
expect(views[1].id, 'flutterView1');
139-
expect(views[2].id, 'flutterView2');
140162

141163
expect(views[0].name, equals(null));
142-
expect(views[1].name, 'file://flutterBinary1');
143-
expect(views[2].name, 'file://flutterBinary2');
144164

145165
// Ensure the ports are all closed after stop was called.
146166
await connection.stop();
147167
expect(forwardedPorts[0].stopped, true);
148-
expect(forwardedPorts[1].stopped, true);
149-
expect(forwardedPorts[2].stopped, true);
150168
});
151169

152-
test('end-to-end with three vms and remote open port', () async {
170+
test('end-to-end with one vm and remote open port', () async {
153171
int port = 0;
154172
Future<PortForwarder> fakePortForwardingFunction(
155173
String address,
@@ -170,53 +188,71 @@ void main() {
170188
fuchsiaPortForwardingFunction = fakePortForwardingFunction;
171189
final FakeSshCommandRunner fakeRunner = FakeSshCommandRunner();
172190
// Adds some extra junk to make sure the strings will be cleaned up.
173-
fakeRunner.findResponse = <String>['/hub/blah/blah/blah/vmservice-port\n'];
174-
fakeRunner.lsResponse = <String>['123\n\n\n', '456 ', '789'];
191+
fakeRunner.iqueryResponse = <String>[
192+
'[',
193+
' {',
194+
' "data_source": "Inspect",',
195+
' "metadata": {',
196+
' "filename": "fuchsia.inspect.Tree",',
197+
' "component_url": "fuchsia-pkg://fuchsia.com/flutter_runner#meta/flutter_runner.cm",',
198+
' "timestamp": 12345678901234',
199+
' },',
200+
' "moniker": "core/session-manager/session/flutter_runner",',
201+
' "payload": {',
202+
' "root": {',
203+
' "vm_service_port": "12345",',
204+
' "16859221": {',
205+
' "empty_tree": "this semantic tree is empty"',
206+
' },',
207+
' "build_info": {',
208+
' "dart_sdk_git_revision": "77e83fcc14fa94049f363d554579f48fbd6bb7a1",',
209+
' "dart_sdk_semantic_version": "2.19.0-317.0.dev",',
210+
' "flutter_engine_git_revision": "563b8e830c697a543bf0a8a9f4ae3edfad86ea86",',
211+
' "fuchsia_sdk_version": "10.20221018.0.1"',
212+
' },',
213+
' "vm": {',
214+
' "dst_status": 1,',
215+
' "get_profile_status": 0,',
216+
' "num_get_profile_calls": 1,',
217+
' "num_intl_provider_errors": 0,',
218+
' "num_on_change_calls": 0,',
219+
' "timezone_content_status": 0,',
220+
' "tz_data_close_status": -1,',
221+
' "tz_data_status": -1',
222+
' }',
223+
' }',
224+
' },',
225+
' "version": 1',
226+
' }',
227+
' ]'
228+
];
175229
fakeRunner.address = 'fe80::8eae:4cff:fef4:9247';
176230
fakeRunner.interface = 'eno1';
177231
final FuchsiaRemoteConnection connection =
178232
await FuchsiaRemoteConnection.connectWithSshCommandRunner(fakeRunner);
179233

180-
// [fakePortForwardingFunction] will have returned three different
181-
// forwarded ports, incrementing the port each time by one. (Just a sanity
182-
// check that the forwarding port was called).
183-
expect(forwardedPorts.length, 3);
184-
expect(forwardedPorts[0].remotePort, 123);
185-
expect(forwardedPorts[1].remotePort, 456);
186-
expect(forwardedPorts[2].remotePort, 789);
187-
expect(forwardedPorts[0].port, 0);
188-
expect(forwardedPorts[1].port, 1);
189-
expect(forwardedPorts[2].port, 2);
234+
expect(forwardedPorts.length, 1);
235+
expect(forwardedPorts[0].remotePort, 12345);
190236

191237
// VMs should be accessed via the alternate address given by
192238
// [fakePortForwardingFunction].
193239
expect(uriConnections[0],
194-
Uri(scheme:'ws', host:'[fe80::1:2%25eno2]', port:0, path:'/ws'));
195-
expect(uriConnections[1],
196-
Uri(scheme:'ws', host:'[fe80::1:2%25eno2]', port:1, path:'/ws'));
197-
expect(uriConnections[2],
198-
Uri(scheme:'ws', host:'[fe80::1:2%25eno2]', port:2, path:'/ws'));
240+
Uri(scheme: 'ws', host: '[fe80::1:2%25eno2]', port: 0, path: '/ws'));
199241

200242
final List<FlutterView> views = await connection.getFlutterViews();
201243
expect(views, isNot(null));
202-
expect(views.length, 3);
244+
expect(views.length, 1);
203245
// Since name can be null, check for the ID on all of them.
204246
expect(views[0].id, 'flutterView0');
205-
expect(views[1].id, 'flutterView1');
206-
expect(views[2].id, 'flutterView2');
207247

208248
expect(views[0].name, equals(null));
209-
expect(views[1].name, 'file://flutterBinary1');
210-
expect(views[2].name, 'file://flutterBinary2');
211249

212250
// Ensure the ports are all closed after stop was called.
213251
await connection.stop();
214252
expect(forwardedPorts[0].stopped, true);
215-
expect(forwardedPorts[1].stopped, true);
216-
expect(forwardedPorts[2].stopped, true);
217253
});
218254

219-
test('end-to-end with three vms and ipv4', () async {
255+
test('end-to-end with one vm and ipv4', () async {
220256
int port = 0;
221257
Future<PortForwarder> fakePortForwardingFunction(
222258
String address,
@@ -236,49 +272,67 @@ void main() {
236272
fuchsiaPortForwardingFunction = fakePortForwardingFunction;
237273
final FakeSshCommandRunner fakeRunner = FakeSshCommandRunner();
238274
// Adds some extra junk to make sure the strings will be cleaned up.
239-
fakeRunner.findResponse = <String>['/hub/blah/blah/blah/vmservice-port\n'];
240-
fakeRunner.lsResponse = <String>['123\n\n\n', '456 ', '789'];
275+
fakeRunner.iqueryResponse = <String>[
276+
'[',
277+
' {',
278+
' "data_source": "Inspect",',
279+
' "metadata": {',
280+
' "filename": "fuchsia.inspect.Tree",',
281+
' "component_url": "fuchsia-pkg://fuchsia.com/flutter_runner#meta/flutter_runner.cm",',
282+
' "timestamp": 12345678901234',
283+
' },',
284+
' "moniker": "core/session-manager/session/flutter_runner",',
285+
' "payload": {',
286+
' "root": {',
287+
' "vm_service_port": "12345",',
288+
' "16859221": {',
289+
' "empty_tree": "this semantic tree is empty"',
290+
' },',
291+
' "build_info": {',
292+
' "dart_sdk_git_revision": "77e83fcc14fa94049f363d554579f48fbd6bb7a1",',
293+
' "dart_sdk_semantic_version": "2.19.0-317.0.dev",',
294+
' "flutter_engine_git_revision": "563b8e830c697a543bf0a8a9f4ae3edfad86ea86",',
295+
' "fuchsia_sdk_version": "10.20221018.0.1"',
296+
' },',
297+
' "vm": {',
298+
' "dst_status": 1,',
299+
' "get_profile_status": 0,',
300+
' "num_get_profile_calls": 1,',
301+
' "num_intl_provider_errors": 0,',
302+
' "num_on_change_calls": 0,',
303+
' "timezone_content_status": 0,',
304+
' "tz_data_close_status": -1,',
305+
' "tz_data_status": -1',
306+
' }',
307+
' }',
308+
' },',
309+
' "version": 1',
310+
' }',
311+
' ]'
312+
];
241313
fakeRunner.address = '196.168.1.4';
242314

243315
final FuchsiaRemoteConnection connection =
244316
await FuchsiaRemoteConnection.connectWithSshCommandRunner(fakeRunner);
245317

246-
// [fakePortForwardingFunction] will have returned three different
247-
// forwarded ports, incrementing the port each time by one. (Just a sanity
248-
// check that the forwarding port was called).
249-
expect(forwardedPorts.length, 3);
250-
expect(forwardedPorts[0].remotePort, 123);
251-
expect(forwardedPorts[1].remotePort, 456);
252-
expect(forwardedPorts[2].remotePort, 789);
253-
expect(forwardedPorts[0].port, 0);
254-
expect(forwardedPorts[1].port, 1);
255-
expect(forwardedPorts[2].port, 2);
318+
expect(forwardedPorts.length, 1);
319+
expect(forwardedPorts[0].remotePort, 12345);
256320

257321
// VMs should be accessed via the ipv4 loopback.
258322
expect(uriConnections[0],
259-
Uri(scheme:'ws', host:'127.0.0.1', port:0, path:'/ws'));
260-
expect(uriConnections[1],
261-
Uri(scheme:'ws', host:'127.0.0.1', port:1, path:'/ws'));
262-
expect(uriConnections[2],
263-
Uri(scheme:'ws', host:'127.0.0.1', port:2, path:'/ws'));
323+
Uri(scheme: 'ws', host: '127.0.0.1', port: 0, path: '/ws'));
264324

265325
final List<FlutterView> views = await connection.getFlutterViews();
266326
expect(views, isNot(null));
267-
expect(views.length, 3);
327+
expect(views.length, 1);
268328
// Since name can be null, check for the ID on all of them.
269329
expect(views[0].id, 'flutterView0');
270-
expect(views[1].id, 'flutterView1');
271-
expect(views[2].id, 'flutterView2');
272330

273331
expect(views[0].name, equals(null));
274-
expect(views[1].name, 'file://flutterBinary1');
275-
expect(views[2].name, 'file://flutterBinary2');
276332

277333
// Ensure the ports are all closed after stop was called.
278334
await connection.stop();
279335
expect(forwardedPorts[0].stopped, true);
280-
expect(forwardedPorts[1].stopped, true);
281-
expect(forwardedPorts[2].stopped, true);
282336
});
283337

284338
test('env variable test without remote addr', () async {
@@ -294,15 +348,11 @@ void main() {
294348
}
295349

296350
class FakeSshCommandRunner extends Fake implements SshCommandRunner {
297-
List<String>? findResponse;
298-
List<String>? lsResponse;
351+
List<String>? iqueryResponse;
299352
@override
300353
Future<List<String>> run(String command) async {
301-
if (command.startsWith('/bin/find')) {
302-
return findResponse!;
303-
}
304-
if (command.startsWith('/bin/ls')) {
305-
return lsResponse!;
354+
if (command.startsWith('iquery --format json show')) {
355+
return iqueryResponse!;
306356
}
307357
throw UnimplementedError(command);
308358
}

0 commit comments

Comments
 (0)