microsoft/ptvsd

Debugging Python in Docker via SSH, breakpoints will not be hit

eohlde opened this issue · 12 comments

Issue Type: Bug

-> vscode-remote ssh into ubuntu remote machine
-> docker-compose build [app]
-> docker-compose up [app]
-> CMD python -m ptvsd --host 0.0.0.0 --port 5678 --wait server.py
-> attach debugger

I then expect the breakpoints I have defined in source (little red dots) to be stopped at. This does not happen.
-> only 'breakpoint()' will be stopped at, then any red dot breakpoints will be honored in the same .py file

VS Code version: Code 1.41.1 (26076a4de974ead31f97692a0d32f90d735645c0, 2019-12-18T15:04:31.999Z)
OS version: Linux x64 5.3.0-29-generic
Remote OS version: Linux x64 4.15.0-72-generic

System Info
Item Value
CPUs Intel(R) Xeon(R) CPU E3-1505M v5 @ 2.80GHz (2 x 2808)
GPU Status 2d_canvas: unavailable_software
flash_3d: unavailable_off
flash_stage3d: unavailable_off
flash_stage3d_baseline: unavailable_off
gpu_compositing: unavailable_off
metal: disabled_off
multiple_raster_threads: disabled_off
oop_rasterization: unavailable_off
protected_video_decode: unavailable_off
rasterization: unavailable_off
skia_renderer: disabled_off
surface_control: disabled_off
surface_synchronization: enabled_on
video_decode: unavailable_off
viz_display_compositor: enabled_on
viz_hit_test_surface_layer: disabled_off
webgl: enabled_readback
webgl2: unavailable_off
Load (avg) 0, 0, 0
Memory (System) 7.78GB (2.78GB free)
Process Argv --no-sandbox --unity-launch
Screen Reader no
VM 0%
Item Value
Remote SSH: 10.0.1.252
OS Linux x64 4.15.0-72-generic
CPUs Intel(R) Xeon(R) Platinum 8269CY CPU @ 2.50GHz (2 x 2500)
Memory (System) 3.71GB (0.32GB free)
VM 50%
Extensions (8)
Extension Author (truncated) Version
vscode-docker ms- 0.10.0
remote-containers ms- 0.94.0
remote-ssh ms- 0.48.0
remote-ssh-edit ms- 0.49.0
remote-wsl ms- 0.41.9
vscode-remote-extensionpack ms- 0.19.0
vscode-docker ms- 0.10.0
python ms- 2020.1.58038

Environment data

  • VS Code version: Code 1.41.1 (26076a4de974ead31f97692a0d32f90d735645c0, 2019-12-18T15:04:31.999Z)
  • Extension version (available under the Extensions sidebar): 2020.1.58038
  • OS and version: Ubuntu 18.04
  • Python version (& distribution if applicable, e.g. Anaconda): 3.7.6 Alpine
  • Type of virtual environment used (N/A | venv | virtualenv | conda | ...): docker from 3.7-alpine
  • Relevant/affected Python packages and their versions: tornado == 6.0.3
  • Relevant/affected Python-related VS Code extensions and their versions:
    vscode-docker|ms-|0.10.0
    remote-containers|ms-|0.94.0
    remote-ssh|ms-|0.48.0
    remote-ssh-edit|ms-|0.49.0
    remote-wsl|ms-|0.41.9
    vscode-remote-extensionpack|ms-|0.19.0
    vscode-docker|ms-|0.10.0
    python|ms-|2020.1.58038
  • Jedi or Language Server? (i.e. what is "python.jediEnabled" set to; more info microsoft/vscode-python#3977):
{
    "python.testing.pytestArgs": [
        "tests",
        "--no-cov"
    ],
    "python.testing.unittestEnabled": false,
    "python.testing.nosetestsEnabled": false,
    "python.testing.pytestEnabled": true,
    "python.jediEnabled": true,
    "python.linting.pylintEnabled": true,
    "python.linting.enabled": true
}
  • Value of the python.languageServer setting: I'm not sure what this means?

Expected behaviour

I then expect the breakpoints I have defined in source (little red dots) to be stopped at.

Actual behaviour

if I do not include a 'breakpoint()' statement:
The program executes as if there was no debugger attached (I do get output to the debug console)
if I do include a 'breakpoint()' statement:
The program stops at the first breakpoint() statement, then will honor other red-dot breakpoints only in the same file

Steps to reproduce:

-> vscode-remote ssh into ubuntu remote machine
-> docker-compose build myapp
-> docker-compose up myapp
-> CMD python -m ptvsd --host 0.0.0.0 --port 5678 --wait server.py
-> attach debugger

Dockerfile:

FROM python:3.7-alpine as base

COPY /src/requirements.txt /tmp/

RUN pip install -r /tmp/requirements.txt

RUN mkdir /opt/myapp
RUN ls -la /opt
RUN ls -la /opt/myapp

####### Start New Image : Debugger ####
FROM base as debug

RUN pip install ptvsd
RUN pip freeze | grep "ptvsd"

WORKDIR /opt/myapp

CMD python -m ptvsd --host 0.0.0.0 --port 5678 --wait --multiprocess server.py

docker-compose.yaml:

version: '3.4'
services:
  myapp:
    container_name: myapp
    image: 'myapp'
    build:
      context: ./
      target: debug
    ports:
      - 80:8090
      #- 443:8090
      - 5678:5678
    volumes:
      - ./src/myapp:/opt/myapp
    environment:
      - IP_NETWORK=10.0.1.0/24
      - SERVER_PORT=8090
      - PORTAL_SERVER_URI=http://10.0.1.209:80/myapi/endpoint
      - MYAPP-DEBUG=False
    restart: always

Logs

Output for Python in the Output panel (ViewOutput, change the drop-down the upper-right of the Output panel to Python)
The Output -> Python window contains no data

Output -> Log(Window)

[renderer5] [error] 'setBreakpoints' request must be issued after 'launch' or 'attach' request.: Error: 'setBreakpoints' request must be issued after 'launch' or 'attach' request.
    at t.RawDebugSession.handleErrorResponse (file:///usr/share/code/resources/app/out/vs/workbench/workbench.desktop.main.js:2821:819)
    at file:///usr/share/code/resources/app/out/vs/workbench/workbench.desktop.main.js:2821:250
    at processTicksAndRejections (internal/process/task_queues.js:89:5)
    at async R.sendBreakpoints (file:///usr/share/code/resources/app/out/vs/workbench/workbench.desktop.main.js:5114:210)
    at async Y.sendToOneOrAllSessions (file:///usr/share/code/resources/app/out/vs/workbench/workbench.desktop.main.js:5146:591)
    at async Promise.all (index 0)
    at async Y.sendAllBreakpoints (file:///usr/share/code/resources/app/out/vs/workbench/workbench.desktop.main.js:5145:441)
    at async file:///usr/share/code/resources/app/out/vs/workbench/workbench.desktop.main.js:5120:850

Output from Console under the Developer Tools panel (toggle Developer Tools on under Help; turn on source maps to make any tracebacks be useful by running Enable source map support for extension debugging)


log.ts:196   ERR timeout after 500 ms: Error: timeout after 500 ms
    at t.RawDebugSession.handleErrorResponse (file:///usr/share/code/resources/app/out/vs/workbench/workbench.desktop.main.js:2821:819)
    at file:///usr/share/code/resources/app/out/vs/workbench/workbench.desktop.main.js:2821:250
    at async t.RawDebugSession.shutdown (file:///usr/share/code/resources/app/out/vs/workbench/workbench.desktop.main.js:2819:336)
log.ts:196   ERR 'setBreakpoints' request must be issued after 'launch' or 'attach' request.: Error: 'setBreakpoints' request must be issued after 'launch' or 'attach' request.
    at t.RawDebugSession.handleErrorResponse (file:///usr/share/code/resources/app/out/vs/workbench/workbench.desktop.main.js:2821:819)
    at file:///usr/share/code/resources/app/out/vs/workbench/workbench.desktop.main.js:2821:250
    at processTicksAndRejections (internal/process/task_queues.js:89:5)
    at async R.sendBreakpoints (file:///usr/share/code/resources/app/out/vs/workbench/workbench.desktop.main.js:5114:210)
    at async Y.sendToOneOrAllSessions (file:///usr/share/code/resources/app/out/vs/workbench/workbench.desktop.main.js:5146:591)
    at async Promise.all (index 0)
    at async Y.sendAllBreakpoints (file:///usr/share/code/resources/app/out/vs/workbench/workbench.desktop.main.js:5145:441)
    at async file:///usr/share/code/resources/app/out/vs/workbench/workbench.desktop.main.js:5120:850

I am using Remote-Explorer to SSH into an Ubuntu 18.04 VM then trying to debug a docker container on that VM.

@eohlde can you share your attach configuration?

-edit: had an incorrect 'remoteRoot'-
edit 2: I was correct the first time. Sorry, Monday morning, early, no coffee yet.

launch.json:
I am using 'Python Attach"

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python Attach",
            "type": "python",
            "request": "attach",
            "justMyCode": false,
            "pathMappings": [
                {
                    "localRoot": "${workspaceFolder}/src",
                    "remoteRoot": "/opt"
                }
            ],
            "port": 5678,
            "host": "127.0.0.1"
        },
        {
            "name": "Python: Current File",
            "type": "python",
            "request": "launch",
            "program": "${file}",
            "console": "integratedTerminal"
        },
        {
            "name": "Debug Tests",
            "type": "python",
            "request": "test",
            "console": "integratedTerminal",
            "justMyCode": false
        }
    ]
}

Can you try enabling logging for ptvsd, and share the logs? The command line switch is --log-dir ..., and it must be a path to some existing directory. A bunch of .log files should be created there; the one that I need is ptvsd.adapter*.log.

Logs attached,

ptvsd-1.txt

@int19h it seems that the issue is that the attach request is only sent after the setBreakpoints request (we need the attach first to get the path mappings), so, the adapter, is failing the request (which is correct for the debugger).

Now, I was reading https://microsoft.github.io/debug-adapter-protocol/overview and it seems it can receive setBreakpoints after the debugger sends the initialized event... right now we return the initialized event in response to the initialize event, but maybe we should return it in response to the attach or launch event? (if that's not the case, then the issue is in the client).

The DAP spec is unfortunately rather confusing on this. This diagram is much clearer on the relative ordering of "initialize", "initialized", "configurationDone", and "launch"/"attach".

But note that the adapter handles the initialization sequence itself, and it does so according to the diagram above - i.e. it only sends "initialized" after receiving "attach" or "launch". It also suppresses the "initialized" event from pydevd. So this should work properly.

I think the problem here is that this is ptvsd 4 on the server side, but PVSC is in the new-ptvsd experiment, and so assumes that it can just talk to it directly over a socket, without going through the old debug adapter (which took care of initialization order).

@eohlde, can you please try updating ptvsd in the container to the most recent pre-release (i.e. use pip install -U --pre ptvsd), and see if that resolves your issue? You can check the version number from within the app by printing ptvsd.__version__.

edit: my home internet connection tanked. Thanks Comcast

I did receive:
Error: unrecognized switch --multiprocess

so I changed my Dockerfile line to:
CMD python -m ptvsd --host 0.0.0.0 --port 5678 --log-dir /opt/logs/ptvsd --wait ./server.py

Now when I try to attach with VSCode I get this:
image

The version of ptvsd in the container:

  • ptvsd==5.0.0a12

The version of ptvsd on the machine I'm ssh'd into (I don't know/think this has any effect):

  • ptvsd==5.0.0a12

The version of ptvsd on my machine:

  • ptvsd==5.0.0a12

@karthiknadig, can you think of anything that could possibly cause this? The error message looks like it's coming out of the debug adapter factory in PVSC, but how and why would either local or remote version of ptvsd affect that?

I think something went wrong while debug adapter factory was being registered or when we trying to create the debug adapter factory. This error is possible when extension fails to activate fully.

@int19h, upgrading to the pre released ptvsd fixes the problem. my debugger now stops at my defined breakpoints!

Perfect, thank you! I'll go ahead and close the issue.