DAP documentation
pretentious7 opened this issue ยท 34 comments
Hello!
I've been trying to get this running in neovim, but I've been having a lot of trouble, are there any docs for how just the DAP is supposed to work, separate from vscode?
Thanks.
Insofar as DAP is concerned, we follow the specification.
Though I don't think we have consumers using debugServer.js
-- VS (proper, not Code) uses the flatSessionLauncher
. The primary difference in this versus the older debugger is that it leverages multiple debug sessions. The messaging for dealing with multiple sessions is not defined in DAP, so the flatSessionLauncher tacks on a session ID property that can be used to disambiguate messages -- and likewise it expects messages coming in to have a session ID so it knows where to send them. Along with that there's this custom message which is used to indicate when a new target is available and should be initialized.
From the other thread:
But it then rejected all the breakpoints that I tried to set.
This is actually expected if they were sent to the root session -- the root is a logical wrapper session and doesn't implement any debugging methods.
The messaging for dealing with multiple sessions is not defined in DAP, so the flatSessionLauncher tacks on a session ID property that can be used to disambiguate messages -- and likewise it expects messages coming in to have a session ID so it knows where to send them.
Which launcher should be used if the client doesn't support the customized sessionID and custom message?
The comments in the flatSessionLauncher
mention stdin/out communication. How do you launch it to use stdio communication instead of TCP?
Is there an issue in the debug-adapter-protocol repository that suggests a sessionID
protocol extension or something like that - or is the intention that this multi-session handling will only ever work with Visual Studio and Visual studio Code?
Which launcher should be used if the client doesn't support the customized sessionID and custom message?
You can try out the vsDebugServer
, which spins up a server on a separate port instead of using session IDs for messages. Here's the config/message that comes down when a child session is ready for attachment:
vscode-js-debug/src/vsDebugServer.ts
Lines 111 to 123 in d8fcf56
The comments in the flatSessionLauncher mention stdin/out communication. How do you launch it to use stdio communication instead of TCP?
by not passing a port when calling node flatSessionLauncher [port]
Is there an issue in the debug-adapter-protocol repository that suggests a sessionID protocol extension or something like that - or is the intention that this multi-session handling will only ever work with Visual Studio and Visual studio Code?
Not right now. I'll talk to Andre and open an issue there after the holiday (monday)
@connor4312 Would we also be able to use debugServer.js
to avoid the whole sessionId thingy? (#902 (comment))
How does this package relate to https://github.com/microsoft/vscode-node-debug2?
node-debug2 is deprecated and has been replaced by this package.
Ultimately you'll need to deal with multiple sessions and sessionIds in some way.
Okay, that's helpful to know, I really love that microsoft is investing in LSP and DAP to make it more extensible and make it work across editors but it would really help if the custom extensions were as documented, at least for the required ones. As it's not super clear from this thread.
Is the following flow correct:
Editor starts debug adapter
adapter returns that message with the sessionId/port in
Editor initializes with seq to 0 and with the sessionId / to that port
if the editor wants a new session, it can just re-init with a new sessionId and again with sequence number 0?
Ultimately you'll need to deal with multiple sessions and sessionIds in some way.
I don't see such sessions part of the DAP specification. Am I missing something?
I tried implementing this for nvim-dap but it feels very complex. So first you start up a vsDebugServer.js, that one only outputs the port of the root session. With that port, you talk DAP to initialize and configure, breakpoints get rejected and when you send a launch/attach command, you get the specific message (attachedChildSession).
Then you need to connect to that port and send the init DAP messages again (including breakpoints?), I guess? Do you still need to send the launch command as well?
( I guess instead of connecting to that port, you could use the other server and then you would have to use the same connection, just with a connectionId?)
I tried implementing this for nvim-dap but it feels very complex. So first you start up a vsDebugServer.js, that one only outputs the port of the root session.
This can be simplified by passing directly a port to the command, so you don't have to read the output to know the port.
you get the specific message (attachedChildSession).
That's the main issue I see. This command seems necessary yet it's not part of the spec. So it seems to me that vscode-js-debug is not actually DAP compliant as it requires non-standard messages to be usable?
What I also find strange is that if there is a launch/attach request for pwa-node
, the attachedChildSession
has type pwa-chrome
. Do we just need to send this whole config again? What's the difference between pwa-node and pwa-chrome? Is this a bug or expected? A bit more documentation would really help to understand what's supposed to happen (e.g. a sequence diagram or something)
I don't see such sessions part of the DAP specification. Am I missing something?
Currently DAP itself has no knowledge of the relation between different sessions, therefore we use out-of-bounds signalling today. However, that might change with microsoft/vscode#116730.
Is the following flow correct
That looks correct, yea. Though we don't really care about seq
.
if the editor wants a new session, it can just re-init with a new sessionId
If by re-init you mean launch a new vsDebugServer, yes. I believe the vsDebugServer is currently designed such that it is unique for each session 'tree'. Though in practice I'm less familiar with it as it's only consumed by our friends at VS proper.
Then you need to connect to that port and send the init DAP messages again (including breakpoints?), I guess? Do you still need to send the launch command as well?
.... Do we just need to send this whole config again?
That's all correct. VS Code sends all its breakpoints to each session, so that's what js-debug expects. When launching the child you should use the configuration given in the config
argument of attachedChildSession
.
What I also find strange is that if there is a launch/attach request for pwa-node, the attachedChildSession has type pwa-chrome
That's surprising, the type should be the same except when developing a VS Code extension, which you are not doing.
vscode-js-debug/src/sessionManager.ts
Lines 215 to 218 in 0564a51
OK, so it's impossible to use this debug adapter with plain DAP at the moment. Would it be possible to have some flag or initialization option such as --no-session
that would just start one session and stick to DAP standard messages (ie no need to care about attachedChildSession
) ?
If by re-init you mean launch a new vsDebugServer, yes. I believe the vsDebugServer is currently designed such that it is unique for each session 'tree'. Though in practice I'm less familiar with it as it's only consumed by our friends at VS proper.
So every time the editor wants to start a session, it needs to start a new root session? What is the tree you are talking about? In what scenario can you get more than a root and child session?
How would the flow look with flatSessionManager, would you also need to restart a new flatSessionManager? Or how do you start a new session / tree there?
That's surprising, the type should be the same except when developing a VS Code extension, which you are not doing.
I'll report back if I work on this further, but that's what I got in my initial tests of the protocol etc. I was very confused by that
In what scenario can you get more than a root and child session?
There are several of them: multiple tabs debugging a browser, webworkers, service workers, iframes, child processes, and worker_threads. These form the "tree" I was referring to.
How would the flow look with flatSessionManager, would you also need to restart a new flatSessionManager?
Yea, the difference is that the sessions are inline in the same server/connection instead of having a separate websocket server for each child.
But so every time the editor starts up a session, a new root session is needed? The root session cannot be re-used by the client?
Right
@EricCornelson What do you think about proposal in #902 (comment) to have a flag instructing debug adapter to assume there is just 1 session and not send unexpected messages?
Would it be possible to have some flag or initialization option such as --no-session that would just start one session and stick to DAP standard messages
I am not eager to support such a feature. It adds complexity to this already pretty complex package and loses features.
A far simpler way would perhaps be some bit of translator code that flattens the top session and ignores all child sessions if the editor can't support them.
I am not eager to support such a feature. It adds complexity to this already pretty complex package and loses features.
It loses features only for those clients that don't support it, but on the other hand, it will actually provide features as right now it just doesn't work in other editors. And I get that there is complexity but right now the complexity will actually have to live in every editor, which is fine if it were the official protocol but it's just an extension for 1 language.
A far simpler way would perhaps be some bit of translator code that flattens the top session and ignores all child sessions if the editor can't support them.
That sounds okay to me. Would that top session then just be the first child session (so no root session, you immediately get the "child" session and there cannot be any other children?
A far simpler way would perhaps be some bit of translator code that flattens the top session and ignores all child sessions if the editor can't support them.
Sure, that sounds like a good way to do it as well; and I trust you if you think it's simpler.
The idea is to allow editors that support plain and regular DAP, and are working well with node-debug2 or other debug adapters that stick to DAP, to be able to work with this debug adapter without dealing with the vscode-js-debug specific "session" model that is not part of the protocol (yet). Would the suggested approach allow that?
Or maybe is it possible to implement the Debug Adapter in a way that if only 1 session is existing and attached, all requests to the root debug adapter get forwarded to the single session? Most clients that are doing just-DAP could work with that as they'll likely never start multiple sessions (DAP doesn't allow that).
I'd agree with Connor here. It's already sort of cumbersome in VS to deal with the "root session", as that whole concept (if I recall correctly) was created particularly because VS Code can use it to show a nice hierarchical tree of debug targets (VS doesn't have the concept of "child" and "parent" sessions at all, so we end up with a dummy process in the UI that represents the "root" session).
I'd be in favor of flattening the root session to be whatever the first debug session happens to be (e.g. the web page or the first node process).
For what it's worth, we did propose adding this to the debug adapter protocol, but it hasn't reached a critical mass yet to be adopted.
I was able to get a child session working with vsDebugServer.js but my breakpoints are still being rejected. Is there a certain moment you need to send them? Or maybe it needs more info? (I'm using the vsDebugServer.js)
(Is there maybe a way to get a log of all messages sent from VSCode to this DAP so I can checkout what they are doing? Because the exact same flow is working in VSCode but not in neovim)
(In case it's important, it's in typescript and I'm trying to debug a jest test)
Unless you pass --runInBand
, Jest will run tests in parallel in child processes. Without supporting nested debug sessions you will be unable to debug these.
You can have js-debug log traffic to a text file by setting trace: { logFile: "log.txt" }
in the launch config.
Unless you pass --runInBand, Jest will run tests in parallel in child processes. Without supporting nested debug sessions you will be unable to debug these.
OK, but what about the case of a plain node.js hello world ? Couldn't the Debug Adapter support a similar --runInBand
flag that would remove the session grain to make it work with regular/standard DAP clients.
Sure, that is technically possible. Perhaps with some custom debug launcher script that has some bit of translation code (#902 (comment)). I would accept a PR for this provided it can be done is a reasonably durable self-contained way.
I got it working for neovim now (mfussenegger/nvim-dap#136 (comment)) but we were wondering (mfussenegger/nvim-dap#281), do we actually need to send a response to the attachedChildSession
request? Or should this have been an event instead of a request? Because it seems to work without a response...
Also, when I use jest and launch, my breakpoints keep getting rejected (even on the actual session) while they are working (the debugger stops). When I try to use an attach configuration, they keep getting rejected and they are not working. I'm in the right session though because when I set a breakpoint in vs code, it does show in my ui, I get all the details from the debugger
From what I can see in the trace logs, I don't see a difference in the command, is there anything special you need to do for breakpoints in typescript to work?
I'm back on this topic. Do I get it right that at the moment:
vscode-js-debug adapter is not compliant with DAP specification, because it cannot work without client adding support for custom attachedChildSession
?
If I'm wrong, perfect; can someone enlighten me on how to work without my client understanding those sessions?
If I'm right, is there any chance this "session" pattern can be properly described in the specification? I saw they are mentioned, but concrete integration data like message type and expected reaction from client still seems missing.
@connor4312 While trying to use vscode-js-debug
for Node debugging as a DAP server for a DAP Client, I did manage to get it working with debugServerMain.js <port>
using DAP over TCP and it looks like everything works under that single connection, but I'm experiencing a weird issue in which everything looks to work OK (The debug console, output, etc), breakpoints are bounded correctly, but once a breakpoint is hit the session is stuck and the process being debugged is stuck.
Did something like that happened somehow in the past? I'm using node v18.0.0
, and vscode-js-debug v1.69.0
.
@gfszr I managed to get it working with vsDebugServer
, and I did in fact run into this issue. What was happening was that a child session was launched, and I needed to initialize it in order for the program to continue. After the command was sent, however, that child session was terminated, and I needed to then send my continue requests to its parent session.
I'm not sure exactly how it works with debugServerMain
, but if it does run multiple sessions, then maybe see if you are sending your continue command to the right session. Hope that helps!
For anyone else stumbling upon this thread, check out this post where I discuss potential solutions to the aforementioned breakpoints issue.
@mxsdev Thanks for your quick reply! I'm not sure exactly how debugServerMain
splits child sessions, for now it looks like all sessions operate on the single connection (Which is good - I don't want to perform changes to the DAP Client but rather write a simple "translation" layer that proxies DAP) - whilst vsDebugServer
spawns different DAP connections for each. What changes have you done in the DAP client side to handle multiple connections?
Hey all! Came here after a short jaunt through google searches for a DAP-compliant javascript debugger. Sad to see that the choice between conforming to a specification and having features is as difficult as it seems here ๐
Is it the general consensus that if I want to debug javascript using a tool that implements DAP as specified, I should use the deprecated node-debug2
package?
Fyi this will soon be compliant with the adoption of microsoft/debug-adapter-protocol#79 this month, though DAP clients may take longer to update.