[dds] Support logging VM Service traffic to the client from DAP

Change-Id: Id9f6b2158dcf1603468b989ed2f900b390b9b259
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/209546
Reviewed-by: Ben Konyi <bkonyi@google.com>
Commit-Queue: Ben Konyi <bkonyi@google.com>
This commit is contained in:
Danny Tuppeny 2021-08-18 20:55:53 +00:00 committed by commit-bot@chromium.org
parent 52e94d1bbb
commit 7a4fbdf88f
6 changed files with 52 additions and 19 deletions

View file

@ -176,6 +176,8 @@ abstract class DartDebugAdapter<T extends DartLaunchRequestArguments>
/// 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<T extends DartLaunchRequestArguments>
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<T extends DartLaunchRequestArguments>
}
}
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<void> _prepareForLaunchOrAttach() async {
@ -1352,17 +1360,15 @@ abstract class DartDebugAdapter<T extends DartLaunchRequestArguments>
/// A wrapper around the same name function from package:vm_service that
/// allows logging all traffic over the VM Service.
Future<vm.VmService> _vmServiceConnectUri(
String wsUri, {
Logger? logger,
}) async {
Future<vm.VmService> _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<T extends DartLaunchRequestArguments>
controller.stream,
(String message) {
logger?.call('==> [VM] $message');
_logTraffic('==> [VM] $message');
socket.add(message);
},
log: logger != null ? VmServiceLogger(logger) : null,

View file

@ -167,10 +167,10 @@ abstract class BaseDebugAdapter<TLaunchArgs extends LaunchRequestArguments> {
/// 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);

View file

@ -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<String, Object?>?;
}
/// 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

View file

@ -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);

View file

@ -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<DapTestSession> setUp({List<String>? 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);
}

View file

@ -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<List<int>> _inputStream;
final StreamSink<List<int>> _outputSink;