diff --git a/pkg/dds/lib/src/dap/adapters/dart.dart b/pkg/dds/lib/src/dap/adapters/dart.dart index e61c8c48ae3..27ad1570b59 100644 --- a/pkg/dds/lib/src/dap/adapters/dart.dart +++ b/pkg/dds/lib/src/dap/adapters/dart.dart @@ -176,6 +176,8 @@ abstract class DartDebugAdapter /// VM Service closing). bool _hasSentTerminatedEvent = false; + late final sendLogsToClient = args.sendLogsToClient ?? false; + DartDebugAdapter( ByteStreamServerChannel channel, { this.ipv6 = false, @@ -305,8 +307,7 @@ abstract class DartDebugAdapter logger?.call('Connecting to debugger at $uri'); sendOutput('console', 'Connecting to VM Service at $uri\n'); - final vmService = - await _vmServiceConnectUri(uri.toString(), logger: logger); + final vmService = await _vmServiceConnectUri(uri.toString()); logger?.call('Connected to debugger at $uri!'); // TODO(dantup): VS Code currently depends on a custom dart.debuggerUris @@ -1313,6 +1314,13 @@ abstract class DartDebugAdapter } } + void _logTraffic(String data) { + logger?.call(data); + if (sendLogsToClient) { + sendEvent(RawEventBody(data), eventType: 'dart.log'); + } + } + /// Performs some setup that is common to both [launchRequest] and /// [attachRequest]. Future _prepareForLaunchOrAttach() async { @@ -1352,17 +1360,15 @@ abstract class DartDebugAdapter /// A wrapper around the same name function from package:vm_service that /// allows logging all traffic over the VM Service. - Future _vmServiceConnectUri( - String wsUri, { - Logger? logger, - }) async { + Future _vmServiceConnectUri(String wsUri) async { final socket = await WebSocket.connect(wsUri); final controller = StreamController(); final streamClosedCompleter = Completer(); + final logger = this.logger; socket.listen( (data) { - logger?.call('<== [VM] $data'); + _logTraffic('<== [VM] $data'); controller.add(data); }, onDone: () => streamClosedCompleter.complete(), @@ -1372,6 +1378,7 @@ abstract class DartDebugAdapter controller.stream, (String message) { logger?.call('==> [VM] $message'); + _logTraffic('==> [VM] $message'); socket.add(message); }, log: logger != null ? VmServiceLogger(logger) : null, diff --git a/pkg/dds/lib/src/dap/base_debug_adapter.dart b/pkg/dds/lib/src/dap/base_debug_adapter.dart index 6b15face68e..1071fee406f 100644 --- a/pkg/dds/lib/src/dap/base_debug_adapter.dart +++ b/pkg/dds/lib/src/dap/base_debug_adapter.dart @@ -167,10 +167,10 @@ abstract class BaseDebugAdapter { /// Sends an event, lookup up the event type based on the runtimeType of /// [body]. - void sendEvent(EventBody body) { + void sendEvent(EventBody body, {String? eventType}) { final event = Event( seq: _sequence++, - event: eventTypes[body.runtimeType]!, + event: eventType ?? eventTypes[body.runtimeType]!, body: body, ); _channel.sendEvent(event); diff --git a/pkg/dds/lib/src/dap/protocol_common.dart b/pkg/dds/lib/src/dap/protocol_common.dart index f9c09f40ec4..9a20311c86f 100644 --- a/pkg/dds/lib/src/dap/protocol_common.dart +++ b/pkg/dds/lib/src/dap/protocol_common.dart @@ -2,12 +2,35 @@ // 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. +import 'dart:convert'; + /// A base class for (spec-generated) classes that represent the `body` of a an /// event. abstract class EventBody { static bool canParse(Object? obj) => obj is Map?; } +/// A generic event body class that just supplies an object directly. +/// +/// Used to support custom events sent by the debug adapter such as 'dart.log'. +/// +/// The supplied [body] must be convertable to JSON. +class RawEventBody extends EventBody { + final Object body; + + RawEventBody(this.body) + : assert(() { + try { + jsonEncode(body); + return true; + } catch (e) { + return false; + } + }(), 'body should be JSON encodable'); + + Object toJson() => body; +} + /// A generic arguments class that just supplies the arguments map directly. /// /// Used to support custom requests that may be provided by other implementing diff --git a/pkg/dds/test/dap/integration/test_server.dart b/pkg/dds/test/dap/integration/test_server.dart index ffaabf20e1a..3e4cb72500f 100644 --- a/pkg/dds/test/dap/integration/test_server.dart +++ b/pkg/dds/test/dap/integration/test_server.dart @@ -53,7 +53,6 @@ class InProcessDapTestServer extends DapTestServer { }) async { return InProcessDapTestServer._([ ...?additionalArgs, - if (logger != null) '--verbose', ]); } } @@ -107,12 +106,7 @@ class OutOfProcessDapTestServer extends DapTestServer { final _process = await Process.start( Platform.resolvedExecutable, - [ - dapServerScript, - 'dap', - ...?additionalArgs, - if (logger != null) '--verbose' - ], + [dapServerScript, 'dap', ...?additionalArgs], ); return OutOfProcessDapTestServer._(_process, logger); diff --git a/pkg/dds/test/dap/integration/test_support.dart b/pkg/dds/test/dap/integration/test_support.dart index 4f8fac1f463..9fd5a88aa19 100644 --- a/pkg/dds/test/dap/integration/test_support.dart +++ b/pkg/dds/test/dap/integration/test_support.dart @@ -23,6 +23,13 @@ import 'test_server.dart'; /// simplified in VS Code by using a launch config with custom CodeLens links). final useInProcessDap = Platform.environment['DAP_TEST_INTERNAL'] == 'true'; +/// Whether to print all protocol traffic to stdout while running tests. +/// +/// This is useful for debugging locally or on the bots and will include both +/// DAP traffic (between the test DAP client and the DAP server) and the VM +/// Service traffic (wrapped in a custom 'dart.log' event). +final verboseLogging = Platform.environment['DAP_TEST_VERBOSE'] == 'true'; + /// A [RegExp] that matches the `path` part of a VM Service URI that contains /// an authentication token. final vmServiceAuthCodePathPattern = RegExp(r'^/[\w_\-=]{5,15}/ws$'); @@ -141,8 +148,11 @@ foo() { static Future setUp({List? additionalArgs}) async { final server = await _startServer(additionalArgs: additionalArgs); - final client = - await DapTestClient.connect(server, captureVmServiceTraffic: true); + final client = await DapTestClient.connect( + server, + captureVmServiceTraffic: verboseLogging, + logger: verboseLogging ? print : null, + ); return DapTestSession._(server, client); } diff --git a/pkg/dds/tool/dap/run_server.dart b/pkg/dds/tool/dap/run_server.dart index ba22d720f3e..0178e850d26 100644 --- a/pkg/dds/tool/dap/run_server.dart +++ b/pkg/dds/tool/dap/run_server.dart @@ -26,7 +26,6 @@ class DapCommand extends Command { static const argIpv6 = 'ipv6'; static const argDds = 'dds'; static const argAuthCodes = 'auth-codes'; - static const argVerbose = 'verbose'; final Stream> _inputStream; final StreamSink> _outputSink;