connectDebugger method

Future<void> connectDebugger(
  1. Uri uri
)

Connects to the VM Service at uri and initializes debugging.

This method will be called by sub-classes when they are ready to start a debug session and may provide a URI given by the user (in the case of attach) or from something like a vm-service-info file or Flutter app.debugPort message.

The URI protocol will be changed to ws/wss but otherwise not normalized. The caller should handle any other normalisation (such as adding /ws to the end if required).

Implementation

Future<void> connectDebugger(Uri uri) async {
  // Start up a DDS instance for this VM.
  if (enableDds) {
    logger?.call('Starting a DDS instance for $uri');
    try {
      final dds = await startDds(uri, uriConverter());
      _dds = dds;
      uri = dds.wsUri!;
    } on DartDevelopmentServiceException catch (e) {
      // If there's already a DDS instance, then just continue. This is common
      // when attaching, as the program may have already been run with a DDS
      // instance.
      if (e.errorCode ==
          DartDevelopmentServiceException.existingDdsInstanceError) {
        uri = vmServiceUriToWebSocket(uri);
      } else {
        rethrow;
      }
    }
  } else {
    uri = vmServiceUriToWebSocket(uri);
  }

  logger?.call('Connecting to debugger at $uri');
  sendConsoleOutput('Connecting to VM Service at $uri');
  final vmService = await _vmServiceConnectUri(uri.toString());
  logger?.call('Connected to debugger at $uri!');

  // Fetch DDS capabilities.
  final supportedProtocols = await vmService.getSupportedProtocols();
  final ddsProtocol = supportedProtocols.protocols
      ?.firstWhereOrNull((protocol) => protocol.protocolName == 'DDS');
  if (ddsProtocol != null) {
    _ddsCapabilities = _DdsCapabilities(
      major: ddsProtocol.major ?? 0,
      minor: ddsProtocol.minor ?? 0,
    );
  }
  final supportsCustomStreams = _ddsCapabilities.supportsCustomStreams;

  // Send debugger URI to the client.
  sendDebuggerUris(uri);

  this.vmService = vmService;

  unawaited(vmService.onDone.then((_) => _handleVmServiceClosed()));

  // Handlers must be wrapped to handle Service Disappeared errors if async
  // code tries to call the VM Service after termination begins.
  final wrap = _wrapHandlerWithErrorHandling;
  _subscriptions.addAll([
    vmService.onIsolateEvent.listen(wrap(handleIsolateEvent)),
    vmService.onDebugEvent.listen(wrap(handleDebugEvent)),
    vmService.onLoggingEvent.listen(wrap(handleLoggingEvent)),
    vmService.onExtensionEvent.listen(wrap(handleExtensionEvent)),
    vmService.onServiceEvent.listen(wrap(handleServiceEvent)),
    if (supportsCustomStreams)
      vmService.onEvent(toolEventStreamId).listen(wrap(handleToolEvent)),
    if (_subscribeToOutputStreams) ...[
      vmService.onStdoutEvent.listen(wrap(_handleStdoutEvent)),
      vmService.onStderrEvent.listen(wrap(_handleStderrEvent)),
    ],
  ]);
  await Future.wait([
    vmService.streamListen(vm.EventStreams.kIsolate),
    vmService.streamListen(vm.EventStreams.kDebug),
    vmService.streamListen(vm.EventStreams.kLogging),
    vmService.streamListen(vm.EventStreams.kExtension),
    vmService.streamListen(vm.EventStreams.kService),
    if (supportsCustomStreams) vmService.streamListen(toolEventStreamId),
    if (_subscribeToOutputStreams) ...[
      vmService.streamListen(vm.EventStreams.kStdout),
      vmService.streamListen(vm.EventStreams.kStderr),
    ],
  ]);

  final vmInfo = await vmService.getVM();
  logger?.call('Connected to ${vmInfo.name} on ${vmInfo.operatingSystem}');

  // Let the subclass do any existing setup once we have a connection.
  await debuggerConnected(vmInfo);

  await _withErrorHandling(
    () => _configureExistingIsolates(vmService, vmInfo),
  );

  _debuggerInitializedCompleter.complete();
}