From d60221ed47d410f345dd6a65f9a891725d9fd860 Mon Sep 17 00:00:00 2001 From: Parker Lougheed Date: Tue, 6 Aug 2024 15:37:44 +0000 Subject: [PATCH] [vm_service] Get tester example functional and clean up Makes adjustments so `example/vm_service_tester.dart` runs successfully, and complete some clean up to make the code and comments in the file more consistent. Closes https://github.com/dart-lang/sdk/issues/55753 TEST=N/A Change-Id: Ib91985792a25a27008c2ef96786a40bb7a26ad57 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/374242 Reviewed-by: Derek Xu Reviewed-by: Ben Konyi Commit-Queue: Ben Konyi --- pkg/vm_service/example/vm_service_tester.dart | 151 +++++++++++------- pkg/vm_service/lib/src/vm_service.dart | 18 ++- runtime/vm/service/service.md | 8 +- 3 files changed, 113 insertions(+), 64 deletions(-) diff --git a/pkg/vm_service/example/vm_service_tester.dart b/pkg/vm_service/example/vm_service_tester.dart index 54edcee7661..3311b7fa0d1 100644 --- a/pkg/vm_service/example/vm_service_tester.dart +++ b/pkg/vm_service/example/vm_service_tester.dart @@ -2,7 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library service_tester; +/// An example of using the the libraries provided by `package:vm_service`. +library; import 'dart:async'; import 'dart:collection'; @@ -14,8 +15,8 @@ import 'package:test/test.dart'; import 'package:vm_service/vm_service.dart'; import 'package:vm_service/vm_service_io.dart'; -final String host = 'localhost'; -final int port = 7575; +const String host = 'localhost'; +const int port = 7575; late VmService serviceClient; @@ -27,35 +28,40 @@ void main() { }); test('integration', () async { - String sdk = path.dirname(path.dirname(Platform.resolvedExecutable)); - - print('Using sdk at $sdk.'); + final sdkPath = path.dirname(path.dirname(Platform.resolvedExecutable)); + print('Using sdk at $sdkPath.'); // pause_isolates_on_start, pause_isolates_on_exit - process = await Process.start('$sdk/bin/dart', [ - '--pause_isolates_on_start', - '--enable-vm-service=$port', - '--disable-service-auth-codes', - 'example/sample_main.dart' - ]); + final sampleProcess = process = await Process.start( + Platform.resolvedExecutable, + [ + '--pause-isolates-on-start', + '--enable-vm-service=$port', + '--disable-service-auth-codes', + 'example/sample_main.dart', + ], + ); - print('dart process started'); + print('Dart process started.'); - unawaited(process!.exitCode.then((code) => print('vm exited: $code'))); - process!.stdout.transform(utf8.decoder).listen(print); - process!.stderr.transform(utf8.decoder).listen(print); + unawaited(sampleProcess.exitCode.then((code) => print('vm exited: $code'))); + sampleProcess.stdout.transform(utf8.decoder).listen(print); + sampleProcess.stderr.transform(utf8.decoder).listen(print); - await Future.delayed(Duration(milliseconds: 500)); + await Future.delayed(const Duration(milliseconds: 500)); - final wsUri = 'ws://$host$port/ws'; - serviceClient = await vmServiceConnectUri(wsUri, log: StdoutLog()); + final wsUri = Uri(scheme: 'ws', host: host, port: port, path: 'ws'); + serviceClient = await vmServiceConnectUri( + wsUri.toString(), + log: StdoutLog(), + ); - print('socket connected'); + print('VM service web socket connected.'); serviceClient.onSend.listen((str) => print('--> $str')); - // The next listener will bail out if you toggle this to false, which we need - // to do for some things like the custom service registration tests. + // The next listener will bail out if you toggle this to false, which is + // needed for some things like the custom service registration tests. var checkResponseJsonCompatibility = true; serviceClient.onReceive.listen((str) { print('<-- $str'); @@ -65,25 +71,41 @@ void main() { // For each received event, check that we can deserialize it and // reserialize it back to the same exact representation (minus private // fields). - var json = jsonDecode(str); + final json = jsonDecode(str); var originalJson = json['result'] as Map?; if (originalJson == null && json['method'] == 'streamNotify') { originalJson = json['params']['event']; } expect(originalJson, isNotNull, reason: 'Unrecognized event type! $json'); - var instance = - createServiceObject(originalJson, const ['Event', 'Success']); + final instance = + createServiceObject(originalJson!, const ['Event', 'Success']); expect(instance, isNotNull, - reason: 'failed to deserialize object $originalJson!'); + reason: 'Failed to deserialize object $originalJson!'); - var reserializedJson = (instance as dynamic).toJson(); + final reserializedJson = (instance as dynamic).toJson(); - forEachNestedMap(originalJson!, (obj) { - // Private fields that we don't reproduce + forEachNestedMap(originalJson, (obj) { + // Remove private fields that we don't reproduce. obj.removeWhere((k, v) => k.startsWith('_')); - // Extra fields that aren't specified and we don't reproduce + + // Remove extra fields that aren't specified and we don't reproduce. obj.remove('isExport'); + obj.remove('isolate_group'); + obj.remove('parameterizedClass'); + + // Convert `Null` instances in the original JSON to + // just `null` as `createServiceObject` will use `null` + // to represent the reference. + obj.updateAll((key, value) { + if (value is Map && + value['type'] == '@Instance' && + value['kind'] == 'Null') { + return null; + } else { + return value; + } + }); }); forEachNestedMap(reserializedJson, (obj) { @@ -104,14 +126,14 @@ void main() { unawaited(serviceClient.streamListen(EventStreams.kDebug)); unawaited(serviceClient.streamListen(EventStreams.kStdout)); - VM vm = await serviceClient.getVM(); + final vm = await serviceClient.getVM(); print('hostCPU=${vm.hostCPU}'); print(await serviceClient.getVersion()); - List isolates = vm.isolates!; + final isolates = vm.isolates!; print(isolates); - // Disable the json reserialization checks since custom services are not - // supported. + // Disable the json reserialization checks since custom services are + // not supported. checkResponseJsonCompatibility = false; await testServiceRegistration(); checkResponseJsonCompatibility = true; @@ -119,23 +141,23 @@ void main() { await testScriptParse(vm.isolates!.first); await testSourceReport(vm.isolates!.first); - IsolateRef isolateRef = isolates.first; + final isolateRef = isolates.first; print(await serviceClient.resume(isolateRef.id!)); - print('waiting for client to shut down...'); + print('Waiting for service client to shut down...'); await serviceClient.dispose(); await serviceClient.onDone; - print('service client shut down'); + print('Service client shut down.'); }); } -// Deeply traverses a map and calls [cb] with each nested map and the -// parent map. -void forEachNestedMap(Map input, Function(Map) cb) { - var queue = Queue.from([input]); +/// Deeply traverses the [input] map and calls [cb] with +/// each nested map and the parent map. +void forEachNestedMap(Map input, void Function(Map) cb) { + final queue = Queue.from([input]); while (queue.isNotEmpty) { - var next = queue.removeFirst(); + final next = queue.removeFirst(); if (next is Map) { cb(next); queue.addAll(next.values); @@ -145,7 +167,7 @@ void forEachNestedMap(Map input, Function(Map) cb) { } } -Future testServiceRegistration() async { +Future testServiceRegistration() async { const String serviceName = 'serviceName'; const String serviceAlias = 'serviceAlias'; const String movedValue = 'movedValue'; @@ -157,15 +179,18 @@ Future testServiceRegistration() async { }; }); await serviceClient.registerService(serviceName, serviceAlias); - final wsUri = 'ws://$host$port/ws'; - VmService otherClient = await vmServiceConnectUri(wsUri, log: StdoutLog()); - Completer completer = Completer(); + final wsUri = Uri(scheme: 'ws', host: host, port: port, path: 'ws'); + final otherClient = await vmServiceConnectUri( + wsUri.toString(), + log: StdoutLog(), + ); + final completer = Completer(); otherClient.onEvent('Service').listen((e) async { if (e.service == serviceName && e.kind == EventKind.kServiceRegistered) { assert(e.alias == serviceAlias); - Response? response = await serviceClient.callMethod( + final response = await serviceClient.callMethod( e.method!, - args: {'input': movedValue}, + args: {'input': movedValue}, ); assert(response.json!['output'] == movedValue); completer.complete(); @@ -176,14 +201,14 @@ Future testServiceRegistration() async { await otherClient.dispose(); } -Future testScriptParse(IsolateRef isolateRef) async { +Future testScriptParse(IsolateRef isolateRef) async { final isolateId = isolateRef.id!; - final Isolate isolate = await serviceClient.getIsolate(isolateId); - final Library rootLibrary = + final isolate = await serviceClient.getIsolate(isolateId); + final rootLibrary = await serviceClient.getObject(isolateId, isolate.rootLib!.id!) as Library; - final ScriptRef scriptRef = rootLibrary.scripts!.first; + final scriptRef = rootLibrary.scripts!.first; - final Script script = + final script = await serviceClient.getObject(isolateId, scriptRef.id!) as Script; print(script); print(script.uri); @@ -192,21 +217,23 @@ Future testScriptParse(IsolateRef isolateRef) async { print(script.tokenPosTable!.length); } -Future testSourceReport(IsolateRef isolateRef) async { +Future testSourceReport(IsolateRef isolateRef) async { final isolateId = isolateRef.id!; - final Isolate isolate = await serviceClient.getIsolate(isolateId); - final Library rootLibrary = + final isolate = await serviceClient.getIsolate(isolateId); + final rootLibrary = await serviceClient.getObject(isolateId, isolate.rootLib!.id!) as Library; - final ScriptRef scriptRef = rootLibrary.scripts!.first; + final scriptRef = rootLibrary.scripts!.first; - // make sure some code has run + // Make sure that some code has run. await serviceClient.resume(isolateId); await Future.delayed(const Duration(milliseconds: 25)); - final SourceReport sourceReport = await serviceClient.getSourceReport( - isolateId, [SourceReportKind.kCoverage], - scriptId: scriptRef.id); - for (SourceReportRange range in sourceReport.ranges!) { + final sourceReport = await serviceClient.getSourceReport( + isolateId, + [SourceReportKind.kCoverage], + scriptId: scriptRef.id, + ); + for (final range in sourceReport.ranges!) { print(' $range'); if (range.coverage != null) { print(' ${range.coverage}'); diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart index 0e4391bf929..6eb853f2224 100644 --- a/pkg/vm_service/lib/src/vm_service.dart +++ b/pkg/vm_service/lib/src/vm_service.dart @@ -2512,7 +2512,6 @@ class AllocationProfile extends Response { /// /// If the field is uninitialized, the `value` will be the `NotInitialized` /// [Sentinel]. -/// class BoundField { static BoundField? parse(Map? json) => json == null ? null : BoundField._fromJson(json); @@ -3073,10 +3072,15 @@ class CodeRef extends ObjRef { /// What kind of code object is this? /*CodeKind*/ String? kind; + /// This code object's corresponding function. + @optional + FuncRef? function; + CodeRef({ this.name, this.kind, required String id, + this.function, }) : super( id: id, ); @@ -3084,6 +3088,8 @@ class CodeRef extends ObjRef { CodeRef._fromJson(Map json) : super._fromJson(json) { name = json['name'] ?? ''; kind = json['kind'] ?? ''; + function = + createServiceObject(json['function'], const ['FuncRef']) as FuncRef?; } @override @@ -3097,6 +3103,7 @@ class CodeRef extends ObjRef { 'name': name ?? '', 'kind': kind ?? '', }); + _setIfNotNull(json, 'function', function?.toJson()); return json; } @@ -3123,10 +3130,16 @@ class Code extends Obj implements CodeRef { @override /*CodeKind*/ String? kind; + /// This code object's corresponding function. + @optional + @override + FuncRef? function; + Code({ this.name, this.kind, required String id, + this.function, }) : super( id: id, ); @@ -3134,6 +3147,8 @@ class Code extends Obj implements CodeRef { Code._fromJson(Map json) : super._fromJson(json) { name = json['name'] ?? ''; kind = json['kind'] ?? ''; + function = + createServiceObject(json['function'], const ['FuncRef']) as FuncRef?; } @override @@ -3147,6 +3162,7 @@ class Code extends Obj implements CodeRef { 'name': name ?? '', 'kind': kind ?? '', }); + _setIfNotNull(json, 'function', function?.toJson()); return json; } diff --git a/runtime/vm/service/service.md b/runtime/vm/service/service.md index 8c9e858d4fa..5c714ecf7f3 100644 --- a/runtime/vm/service/service.md +++ b/runtime/vm/service/service.md @@ -2085,6 +2085,9 @@ class @Code extends @Object { // What kind of code object is this? CodeKind kind; + + // This code object's corresponding function. + @Function function [optional]; } ``` @@ -2097,6 +2100,9 @@ class Code extends Object { // What kind of code object is this? CodeKind kind; + + // This code object's corresponding function. + @Function function [optional]; } ``` @@ -3188,7 +3194,7 @@ class Instance extends Object { // RegExp @Instance pattern [optional]; -// The function associated with a Closure instance. + // The function associated with a Closure instance. // // Provided for instance kinds: // Closure