Ask questionsMessages sequence for attach different from that of PTVSD

Environment data

  • debugpy version: 1.0.0b9
  • OS and version: gentoo (5.4.60-gentoo-x86_64)
  • Python version (& distribution if applicable, e.g. Anaconda): 3.8.6 from conda
  • Using VS Code or Visual Studio: No

Actual behavior

When trying to attach a process to the debugpy, the expected messages sequence is the following:

client -> debugpy: initialize request
client <- debugpy: intialize response
client <- debugpy: initialize event
client -> debugpy: attach request
client -> debugpy: setBreakpoints request
client <- debugpy: setBreakpoints response
client -> debugpy: configurationDone request
client <- debugpy: configurationDone response
client <- debugpy: attach response

This is different from the messages sequence required to attach a process to PTVSD:

client -> PTVSD: initialize request
client <- PTVSD: initialize response
client <- PTVSD: initialize event
client -> PTVSD: attach request
client <- PTVSD: attach response
client -> PTVSD: setBreakpoints request
client <- PTVSD: setBreakpoints response
client -> PTVSD: configurationDone request
client <- PTVSD: configurationDone response

Expected behavior

The sequence should be the same as that of PTVSD, that is, attach response should be sent once the process is attached instead of waiting for configurationDone request. Besides, this seems to break the "reply - response" pattern from the Debug Adapter protocol, and is not consistent with the initialization sequence described on this page

Steps to reproduce:

Build xeus-python from this branch: jupyter-xeus/xeus-python#329 Try to run the bugger


Answer questions int19h

@JohanMabille It's a legal sequence, but it's up to the debug adapter to signal the "initialized" event when the conditions are met. In case of debugpy, the conditions aren't met until after you issue the "launch" or "attach" request.

The reason why it needs it is because the adapter doesn't handle breakpoints - the debug server does. But, there's no debug server until one is either spawned (if "launch") or attached to. If "setBreakpoints" were an event, it could be deferred and propagated when the server is there - but it's a request, requiring a response, so you'd just end up with a deadlock where the client is waiting for a response, and the adapter is waiting for "launch" or "attach".

This is different from ptvsd 4, because in that one, the debug adapter proper was not a part of ptvsd at all, but rather a bunch of TypeScript code in the VSCode extension - ptvsd itself was strictly a debug server, and didn't meaningfully handle "launch" or "attach" at all, since, by definition, it's already in the process it's trying to debug. The old adapter was aware of ptvsd's non-coformance idiosyncracies, and massaged them into something that VSCode could handle; in particular, it would not send "initialized" event until it received "launch", and spawned the ptvsd server (and thus ready to receive "setBreakpoints").

The new adapter that's a part of debugpy itself does the same thing. Previously, if you were using ptvsd directly as a client - e.g. by attaching to the socket - you were skipping the old adapter, and thus getting ptvsd's broken implementation of DAP. But now you're always going via the adapter, hence why you are seeing different behavior.

DAP spec needs to be updated to reflect the diagram linked above. There was an issue about that on their issue tracker somewhere. But in any case, the client is supposed to follow the adapter lead here - it's up to the adapter to decide which sequence is more appropriate to its implementation (whether it's because that's the only way to do it, or because it's just easier). A conforming client should wait until the "initialized" event is received before sending "setBreakpoints", and should be able to handle either sequence in the diagram - meaning that it cannot wait for "launch" or "attach" response before sending "setBreakpoints" and/or "configurationDone". This is not something that's clear from the DAP spec by itself, but it doesn't contradict it, either.

(@weinand, please correct me if anything is wrong in the above.)

From our perspective, which sequence debugpy implements is an implementation detail that can change at any time in the future, and should not be relied upon. The only guarantee that we make here is conformance to the spec as clarified by the diagram.

Github User Rank List