File: Subprocess%20debugging.md

package info (click to toggle)
debugpy 1.8.19%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,452 kB
  • sloc: python: 14,840; sh: 185; makefile: 33
file content (316 lines) | stat: -rw-r--r-- 8,627 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
# Subprocess debugging

## Terminology

_Debuggee process_ - the process that is being debugged.

_IDE_ - VSCode or other DAP client.

_Debug server_ - pydevd with debugpy wrapper; hosted inside the debuggee process,
one for each.

_Debug adapter_ - debugpy adapter that mediates between IDE and server.

_IDE listener port_ - port opened by the adapter, on which it listens for incoming
connections from the IDE.

_Server listener port_ - port opened by the adapter, on which it listens for incoming
connections from the servers.

_Adapter listener port_ - port opened by the server, on which it listens for incoming
connection from the adapter.



## "launch" scenario

1. User starts debugging (F5) with "launch" debug config.
1. User code spawns child process.
1. User stops debugging.

```mermaid
sequenceDiagram
# Install "GitHub + Mermaid" from the Chrome Web Store to render the diagram

participant IDE
participant Adapter
participant Debuggee_1
participant Debuggee_2


Note left of IDE: user starts<br/>debugging

IDE ->> Adapter: spawn and connect over stdio

IDE ->>+ Adapter: request "launch"

Adapter ->>+ Debuggee_1: spawn and pass server listener port (cmdline)

Debuggee_1 -->>- Adapter: connect to server listener port

Adapter ->>+ Debuggee_1: request "initialize", "launch"
activate Debuggee_1
note right of Debuggee_1: debug session begins

Debuggee_1 -->>- Adapter: respond to "initialize", "launch"

Adapter -->>- IDE: respond to "launch"

loop every message between IDE and Debuggee_1
Note over IDE,Debuggee_1: propagate message
end


Note right of Debuggee_1: user code spawns<br/>child process

Debuggee_1 ->>+ Debuggee_2: spawn and pass server listener port (cmdline)

Debuggee_2 ->>- Adapter: connect to server listener port

Adapter ->>+ Debuggee_2: request "pydevd_systemInfo"

Debuggee_2 -->>- Adapter: respond to "pydevd_systemInfo"

Adapter ->>+ IDE: "ptvsd_subprocess" event:

IDE ->>- Adapter: connect to IDE listener port

IDE ->>+ Adapter: request "attach" to Debuggee_2

Adapter ->>+ Debuggee_2: request "initialize", "attach"
activate Debuggee_2
note right of Debuggee_2: debug session begins

Debuggee_2 -->>- Adapter: respond to "initialize", "attach"

Adapter -->>- IDE: respond to "attach"

loop every message between IDE and Debuggee_2
Note over IDE,Debuggee_2: propagate message
end


Note left of IDE: user stops debugging

IDE -X+ Adapter: request "disconnect" from Debuggee_1

Note over Adapter: implies "terminate"

Adapter -X+ Debuggee_2: request "terminate"

Debuggee_2 -->>- Adapter: confirm "terminate"
deactivate Debuggee_2

Adapter ->> IDE: "exited" event for Debuggee_2

Adapter -X+ Debuggee_1: request "terminate"

Debuggee_1 -->>- Adapter: confirm "terminate"
deactivate Debuggee_1

Adapter ->> IDE: "exited" event for Debuggee_1

Adapter -->>- IDE: confirm "disconnect" from Debuggee_1
```



## "attach" scenario

1. User starts debuggee process with debug server in it (debugpy command line or `debugpy.enable_attach()`).
1. User starts debugging (F5) with "attach" debug config.
1. User code spawns child process.
1. User disconnects from debuggee.
1. User reconnects to debuggee.

```mermaid
sequenceDiagram
# Install "GitHub + Mermaid" from the Chrome Web Store to render the diagram

participant IDE
participant Adapter
participant Debuggee_1
participant Debuggee_2


Note left of Debuggee_1: user spawns<br/>debuggee

Debuggee_1 ->>+ Adapter: spawn

Adapter ->> Debuggee_1: pass server listener port (stdout)
deactivate Adapter
activate Debuggee_1

Debuggee_1 ->> Adapter: connect to server listener port
deactivate Debuggee_1


Note left of IDE: user starts<br/>debugging

IDE ->> Adapter: connect to IDE listener port

IDE ->>+ Adapter: request "attach"

Adapter ->>+ Debuggee_1: request "initialize", "attach"
activate Debuggee_1
note right of Debuggee_1: debug session begins

Debuggee_1 -->>- Adapter: respond to "initialize", "attach"

Adapter -->>- IDE: respond to "attach"

loop every message between IDE and Debuggee_1
Note over IDE,Debuggee_1: propagate message
end


Note right of Debuggee_1: user code spawns<br/>child process

Debuggee_1 ->>+ Debuggee_2: spawn and pass server listener port (cmdline)

Debuggee_2 ->>- Adapter: connect to server listener port

Adapter ->>+ Debuggee_2: request "pydevd_systemInfo"

Debuggee_2 -->>- Adapter: respond to "pydevd_systemInfo"

Adapter ->>+ IDE: "ptvsd_subprocess" event

IDE ->>- Adapter: connect to IDE listener port

IDE ->>+ Adapter: request "attach" to Debuggee_2

Adapter ->>+ Debuggee_2: request "initialize", "attach"
activate Debuggee_2
note right of Debuggee_2: debug session begins

Debuggee_2 -->>- Adapter: respond to "initialize", "attach"

Adapter -->>- IDE: respond to "attach"

loop every message between IDE and Debuggee_2
Note over IDE,Debuggee_2: propagate message
end


Note left of IDE: user detaches IDE

IDE ->>+ Adapter: request "disconnect" from Debuggee_1

Adapter ->>+ Debuggee_2: request "disconnect"

Debuggee_2 -->>- Adapter: confirm "disconnect"
deactivate Debuggee_2
note right of Debuggee_2: debug session ends

Adapter ->> IDE: "terminated" event for Debuggee_2

Note over Adapter,Debuggee_2: TCP connection is maintained

Adapter ->>+ Debuggee_1: request "disconnect"

Debuggee_1 ->> Adapter: "terminated" event

Adapter ->> IDE: "terminated" event for Debuggee_1

Debuggee_1 -->>- Adapter: confirm "disconnect"
deactivate Debuggee_1
note right of Debuggee_1: debug session ends

Note over Adapter,Debuggee_1: TCP connection is maintained

Adapter -->>- IDE: confirm "disconnect" from Debuggee_1

Note over Adapter: continues running



Note left of IDE: User re-attaches IDE<br/>(same host/port)

IDE ->> Adapter: connect to IDE listener port

IDE ->>+ Adapter: request "attach"

Adapter ->>+ Debuggee_1: request "initialize", "attach"
activate Debuggee_1
note right of Debuggee_1: debug session begins

Debuggee_1 -->>- Adapter: respond to "initialize", "attach"

Adapter ->>+ IDE: "ptvsd_subprocess" event

Adapter -->>- IDE: respond to "attach"

loop every message between IDE and Debuggee_1
Note over IDE,Debuggee_1: propagate message
end

IDE ->>- Adapter: connect to IDE listener port

IDE ->>+ Adapter: request "attach" to Debuggee_2

Adapter ->>+ Debuggee_2: request "initialize", "attach"
activate Debuggee_2
note right of Debuggee_2: debug session begins

Debuggee_2 -->>- Adapter: respond to "initialize", "attach"

Adapter -->>- IDE: respond to "attach"

loop every message between IDE and Debuggee_2
Note over IDE,Debuggee_2: propagate message
end


Note right of Debuggee_2: user code exits

Debuggee_2 -X- Debuggee_2: exits

Adapter ->> IDE: "exited" event for Debuggee_2

Note right of Debuggee_1: user code exits

Debuggee_1 -X- Debuggee_1: exits

Adapter ->> IDE: "exited" event for Debuggee_1

Adapter -X Adapter: exits
```



## Important points

### How does the adapter know that connection from the server is for a subprocess?

By counting connections. The first one is for the root process, all others are for
subprocesses of that process.

### How does the adapter track server connections?

It creates a `Session` instance as soon as the server establishes a socket connection,
and maintains it until the corresponding debuggee process exits. Whenever the IDE
disconnects, the state of the instance is reset.

### How does the IDE know which subprocess to connect to?

It receives a "ptvsd_subprocess" event from the adapter (using the connection for the
root process), which contains host and port on which the adapter is listening for new
connections from the IDE, and PID of the subprocess. It then connects to the specified
host and port, and sends an "attach" request with "processId" from the event.

### How does the adapter know that connection from the IDE is for a specific subprocess?

The first connection is always for the root process. All subsequent connections are
for subprocesses, and must have "processId" specified in the "attach" request. The
adapter keeps track of PID for all processes that it tracks, and uses the PID specified
in the "attach" request to look up the corresponding `Session`.

### How does the server know that IDE has connected or disconnected?

The adapter sends an "initialized" request to the server for every one it receives from
the IDE, and sends a "disconnected" request every time the IDE disconnects (even if it
doesn't send one itself). The server uses those events to keep track of logical debug
sessions, even though the TCP connection is the same throughout the lifetime of the
debuggee. This allows it to enable/disable tracing, continue running if it was stopped
at a breakpoint etc.