microsoft / debug-adapter-protocol

Defines a common protocol for debug adapters.
https://microsoft.github.io/debug-adapter-protocol/
Other
1.44k stars 131 forks source link

Proposal: Allow multiple debug sessions through a single adapter #79

Closed EricCornelson closed 2 years ago

EricCornelson commented 5 years ago

Currently, both Visual Studio and Visual Studio Code both support multiple concurrently running debug sessions, and Visual Studio Code supports hierarchical debug sessions which can represent debugging child processes or multiple "global objects" (example).

In this vein, there are some scenarios where it would be useful to be able to have multiple debug sessions running through a single debug adapter, which would allow for some more advanced orchestration between debug targets.

A particularly interesting example of this is when debugging a web page which has associated web workers and service workers. The Chrome DevTools protocol allows for the concept of a "flat session" debug mode, which allows a debugger to simultaneously debug all related pages and workers at the same time through a single connection using a "targetId" to route messages to the correct debug target. Additionally, it looks like this will soon become the default in Chrome DevTools, and the "non-flat" session mode will be deprecated (see crbug.com/991325).

In the above example, debugging a web page through a single "flat session" confers many benefits (e.g. being able to start each child target in a suspended state to allow the debugger to configure breakpoints and other set-up before the child starts running). However, running in "flat session" mode requires debugging these multiple targets through a single connection, something which, today, is hard to do using built-in functionality in the debug adapter protocol (it can be achieved by running in-process in VS Code and leveraging the DebugAdapterDescriptorFactory to share state between sessions, but that solution is clunky, and specific to the VS Code API, and therefor does not translate well to Visual Studio, for example).

Some other examples of scenarios where a "flat session" mode might be useful are:

I've created a PR which, as a first step, adds an optional "sessionId" parameter to each protocol message. If this sounds reasonable, I think it would also be nice to give adapters a way to notify the host that a new child session has been attached, as right now an adapter has to manually call VSCode or Visual Studio APIs to launch a new debug session and then trap it on entry to achieve this.

Let me know what you think!

roblourens commented 2 years ago

That's the "debug type", which raises the question whether we want to support the creation of debug sessions of other types than the caller's type. I think this is an open question that we need to discuss.

I don't think type should be part of this. It's introducing a new concept. It's only needed if debugger A needs to launch an instance of debugger B, and I can't think of a scenario where that's needed.

icholy commented 2 years ago

@connor4312 will js-debug continue multiplexing the sessions over a single connection?

weinand commented 2 years ago

@roblourens you said:

But if the user configures variables in their launch.json, and this trickles down to a child debug session, then I suppose that's ok right?

Most likely "yes", but I wonder how a variable can "trickle down"? In my proposal the DA passes a launch configuration to the startDebugging request. If this configuration is based on the configuration received in the launch or attach request, then it should not contain any variables, because they were already resolved before the DA has received them. I think what I wanted to say is that the DA should not use variables in the configuration passed to the startDebugging request because it cannot be sure that the client supports variables at all.


I don't think type should be part of this. It's introducing a new concept. It's only needed if debugger A needs to launch an instance of debugger B, and I can't think of a scenario where that's needed.

I had a scenario in mind where a Python debugger starts a C debugger for some native code that is used in the implementation of a Python function. But the problem here is how can the Python DA know the "configuration" for the C debugger.

I think using the "type" would only make sense if the debuggers A and B are "good friends" and know each other in detail.
@connor4312 would js-debug use the "type"?

puremourning commented 2 years ago

even if debuggers A and B are "good friends" , neither of them can know anything about how the client identifies debug configurations, or debug adapters. so the type field cannot work in any way I can imagine.

connor4312 commented 2 years ago

will js-debug continue multiplexing the sessions over a single connection?

When we introduce multiplexing, I want to make this happen. But once this proposal is in, I would just allow the debug server to manage multiple incoming connections for its child sessions so as to adhere to the standard.

I think using the "type" would only make sense if the debuggers A and B are "good friends" and know each other in detail. Would js-debug use the "type"?

I would use it; there's a few cases where the child session might be of a different type than its parents, such as when debugging webviews inside a VS Code extension, or using the "server" option of browser-type configs.

I think the type is a little misleading, since if it refers to a different DA the client may not have any knowledge about how that different DA should be started. Maybe we just say that the new debug session must go to the same DA, so if the child session has a different type then the DA must know how to handle it.

puremourning commented 2 years ago

but "type" isn't a field in the debug adapter protocol today... if it's a launch argument, then it can already be passed in the (proposed) unspecified key/values. it need not be specified in DAP.

connor4312 commented 2 years ago

Good point 👍

weinand commented 2 years ago

Revised edition of previous proposal.


Reverse Request startDebugging

This "reverse" request is sent from the debug adapter to the client to start a new debug session of the same type as the caller. This request should only be called if the corresponding capability supportsStartDebugging is true.

A client implementation of startDebugging should start a new debug session (of the same type as the caller) in the same way as a debug session is started by the user.

If the client (UI) supports hierarchical debug sessions, the newly created session can be treated as a child of the caller's session.

interface StartDebuggingRequest extends Request {
  command: 'startDebugging';

  arguments: StartDebuggingRequestArguments;
}

Arguments for startDebugging request.

interface StartDebuggingRequestArguments {
  /**
   * Arguments passed to the new debug session.
   * The arguments must only contain properties understood by the `launch` or `attach` requests
   * of the debug adapter and they must not contain any client-specific properties (e.g. `type`) or
   * client-specific features (e.g. "variables").
   */
  configuration: {
    request: 'launch' | 'attach';
    [key: string]: unknown;   // any number of properties
  }
}

Response to startDebugging request.

interface StartDebuggingResponse extends Response {
  body: {
    // no return values so far...
  };
}
zobo commented 2 years ago

How did I miss this?! I just skimmed over latest Release Notes in VSC and noticed the section about this DAP proposal. Here link to my old issue on this topic: https://github.com/microsoft/vscode/issues/116730

I'll review all the backlog and leave some thoughts as soon as I can.

Thanks so much for working on this!

zobo commented 2 years ago

A bit late to the game, but here are my thoughts.

Looking from my own perspective (php-debug) the most important thing is how the DA is spawned from the client (VSCode). Since in my case the first DA instance will listen for incoming TCP connections from PHP/Xdebug and then create a new DA instance for each of them. This means either all of the DAs need to run in the same process (be it VSCode itself or one DA host process) or do something very creative with fork() or some TCP proxying - feels too complex.

Also, I feel that for my use case the user experience is better if the "child" debug sessions are actually NOT hierarchically under the primary "listener" debug session. This way the user can "stop listening" but continue to debug the open sessions.

I understand that this proposal tries to unify how different clients offer creating a new session from a DA instance, but with all the "implementation detail" out of scope we'll see how it will work in VSCode and then across other clients.

Great first steps, happy this is moving along.

connor4312 commented 2 years ago

Since in my case the first DA instance will listen for incoming TCP connections from PHP/Xdebug and then create a new DA instance for each of them. This means either all of the DAs need to run in the same process (be it VSCode itself or one DA host process) or do something very creative with fork() or some TCP proxying - feels too complex.

I'm not totally clear on what would change for you in this mode. Looking at php-debug, you use the program attribute in the package.json, so VS Code will run that Node program for each new client session. If you use the new startDebugging request, that will continue to happen. Am I misunderstanding your scenario?

connor4312 commented 2 years ago

Also, I opened the proposal for multiplexing here: https://github.com/microsoft/debug-adapter-protocol/issues/329

zobo commented 2 years ago

I should have been more concrete. What I am talking about is not in the main branch, but a separate feature branch, not yet merged.

Here I use the InlineDebugAdapterFactory so that all DA instances run in the same process and can share data in static properties...

What I'm trying to say is that startDebugging will not be enough for some extensions without being able to control how DAs are spawned. I'm looking at your other proposal and this could probably be a good start.

Maybe a flag in package.json could be used to set a spawning mode like:

Or a filed in the startDebugging reverse request...

Keeping this consistent across different clients could be a problem...

jonahgraham commented 2 years ago

Can https://github.com/microsoft/debug-adapter-protocol/issues/333 be looked at before 1.59 is finalized?

roblourens commented 2 years ago

Due to https://github.com/microsoft/debug-adapter-protocol/issues/333, I'll revert this change in main, and we will finalize it in October. I'd like to implement it in vscode during October.