Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issues with ipykernel debugging in the Interactive Window #12407

Closed
roblourens opened this issue Dec 15, 2022 · 8 comments · Fixed by #12622
Closed

Issues with ipykernel debugging in the Interactive Window #12407

roblourens opened this issue Dec 15, 2022 · 8 comments · Fixed by #12622
Assignees
Labels
bug Issue identified by VS Code Team member as probable bug interactive-window-debugging verified Verification succeeded
Milestone

Comments

@roblourens
Copy link
Member

There are some issues with ipykernel debugging (using "jupyter.forceIPyKernelDebugger": true or on a remote jupyter server) in the Interactive Window that need to be fixed as prereqs to #11777. I'll make this list more specific when I get back into it

  • Mapping breakpoints to cells is not quite correct
  • Setting a breakpoint in a cell after the debugged cell can hang the extension host
  • Sometimes the line number translation goes to the wrong lines
@roblourens roblourens added bug Issue identified by VS Code Team member as probable bug interactive-window-debugging labels Dec 15, 2022
@roblourens roblourens added this to the January 2023 milestone Dec 15, 2022
@roblourens roblourens self-assigned this Dec 15, 2022
@roblourens
Copy link
Member Author

Since we have all these issues with mapping lines and cell paths, I was looking into using setPydevdSourceMap, as we used to do. It's not quite clear why we are doing the mapping in the extension, rather than using this feature to have debugpy do it. I tried to do this copying the way we used it previously but the maps are not taking effect, and I can confirm that by looking for the debugpy log lines that include (after source mapping) and (after path translation).

@int19h - Should setPydevdSourceMap still work with the way we now talk to debugpy through ipykernel? Do you know of anything I'd need to do differently? My guess is that I'm sending the wrong thing for runtimeSource.path but I am copying the same pattern from our old code. If you would expect it to work then I'll try to add some more debug logs to debugpy to work out why they aren't applied.

@roblourens
Copy link
Member Author

One thing I'm trying to understand is, what's the difference between using paths that look like <ipython-input-1-09853089e9ea> and the paths that I'm more familiar with, like /var/folders/tx/p0ycbfpj37786p760wwdg6y80000gn/T/ipykernel_4963/3029800661.py? @DonJayamanne do you know?

@roblourens
Copy link
Member Author

I have this API working now, but I'm still not sure whether I will use it or keep the manual remapping in the extension code.

One more question @int19h, I would like to use setPydevdSourceMap to map between vscode notebook cell URIs and the runtime path (as well as using it for the interactive window which is a real single .py file). But since the cell is not a real file on disk, it fails this check

https://github.com/microsoft/debugpy/blob/main/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_api.py#L505-L508

I notice that if I just prepend a < to the cell URI, then debugpy will happily map correctly to my cell URI, I just have to clean up that character in responses. Would it be safe to rely on this behavior? Or would I be abusing the API?

@DonJayamanne
Copy link
Contributor

DonJayamanne commented Jan 22, 2023

d is, what's the difference between using paths that look like and the paths that I'm more familiar with, like

I wasn't involved in any of the debugger work, hence my knowledge is limited to what I've learnt from my own research.
I believe ipython-input-1-09853089e9ea are paths generated when using the old approach of the debugging (our old approach) and paths such as ipykernel_4963/3029800661.py are generated by ipykernel/ipython module that has support for debugging built into it (i.e. using built in debugger using Jupyter protocol).

Based on what I recall, ipython/ipykernel (can't remember what module) will end up creating a file on disk with the contents of the cell and use that for debugging.

@int19h
Copy link
Contributor

int19h commented Jan 23, 2023

Relying on < would be safe as it is a well-established convention for __file__ in Python when it is not actually a filename - stuff like <string> or <frozen ...> coming from Python itself, or <ipython...> from Jupyter.

@roblourens
Copy link
Member Author

roblourens commented Jan 23, 2023

Thanks @int19h, another question- I think I am sending a correct sourcemap request, and it looks like I see the breakpoint path remapped, however the breakpoint is never actually hit. I thought this was working earlier, now it isn't, I might have just been confused by hitting a breakpoint() statement that we insert. Could you take a look at the logs and see if anything looks wrong to you?
debugpy.zip

I run a cell which looks like

#%%
print('cell 1 a')
print('cell 1 b')
print('cell 1 c')
print('cell 1 d')

which has a breakpoint() inserted like

breakpoint()
#%%
print('cell 1 a')
print('cell 1 b')
print('cell 1 c')
print('cell 1 d')

Here's an excerpt from the pydevd log

0.07s - Process SetPydevdSourceMapRequest: {
    "arguments": {
        "pydevdSourceMaps": [
            {
                "endLine": 6,
                "line": 1,
                "runtimeLine": 2,
                "runtimeSource": {
                    "path": "/var/folders/tx/p0ycbfpj37786p760wwdg6y80000gn/T/ipykernel_62300/1385174233.py"
                }
            }
        ],
        "source": {
            "path": "/Users/roblou/code/jupyter-local/test.py"
        }
    },
    "command": "setPydevdSourceMap",
    "seq": 10,
    "type": "request"
}

...

# Seems the breakpoint is remapped to the correct path and line
0.02s - Process SetBreakpointsRequest: {
    "arguments": {
        "breakpoints": [
            {
                "line": 4
            }
        ],
        "lines": [
            4
        ],
        "source": {
            "name": "test.py",
            "path": "/Users/roblou/code/jupyter-local/test.py"
        },
        "sourceModified": false
    },
    "command": "setBreakpoints",
    "seq": 11,
    "type": "request"
}

0.00s - Request for breakpoint in: /Users/roblou/code/jupyter-local/test.py line: 4
0.00s - Breakpoint (after path translation) in: /Users/roblou/code/jupyter-local/test.py line: 4
0.00s - Breakpoint (after source mapping) in: /var/folders/tx/p0ycbfpj37786p760wwdg6y80000gn/T/ipykernel_62300/1385174233.py line: 5
0.00s - File traced: /var/folders/tx/p0ycbfpj37786p760wwdg6y80000gn/T/ipykernel_62300/1385174233.py
0.00s - Added breakpoint:/var/folders/tx/p0ycbfpj37786p760wwdg6y80000gn/t/ipykernel_62300/1385174233.py - line:5 - func_name:None

...

# And we hit the inserted breakpoint() statement, and remap the stack frame path correctly
0.00s - sending cmd (http_json) -->           CMD_RETURN {"type": "response", "request_seq": 15, "success": true, "command": "stackTrace", "body": {"stackFrames": [{"id": 5, "name": "<module>", "line": 2, "column": 1, "source": {"path": "/Users/roblou/code/jupyter-local/test.py", "sourceReference": 0}}], "totalFrames": 1}, "seq": 70, "pydevd_cmd_id": 502}

...

# We then continue
1.63s - Process ContinueRequest: {
    "arguments": {
        "threadId": "*"
    },
    "command": "continue",
    "seq": 19,
    "type": "request"
}

# but never hit the breakpoint, it disconnects after here

@roblourens
Copy link
Member Author

roblourens commented Jan 23, 2023

Looked at the logs for notebook debugging, where we have a similar flow but no pydevd sourcemap, and noticed this

0.00s - File traced: /var/folders/tx/p0ycbfpj37786p760wwdg6y80000gn/T/ipykernel_65647/2595120235.py
0.00s - Added breakpoint:/private/var/folders/tx/p0ycbfpj37786p760wwdg6y80000gn/t/ipykernel_65647/2595120235.py - line:3 - func_name:None

This is the tmp directory on mac, which is symlinked from /var/... to /private/var/.... When you have a sourcemap, then set a breakpoint, debugpy doesn't try to resolve its symlinked path to a canonical path, but the breakpoint will only bind if it's on the resolved path. If I use the path with the resolved symlink in the sourcemap, then the sourcemap is not applied at all. 

Could work around it if we could have ipykernel (?) only use canonical paths, but I don't think we can have control over that.

I'd still like to use this API if this can be fixed in pydevd/debugpy, but unless there is a workaround, we will have to fix all this mapping code in the extension for this issue.

@roblourens
Copy link
Member Author

Don suggested that we can still control this path using IPYKERNEL_CELL_NAME, which would let us force the name to be the canonicalized tmp path, and could in theory work around this issue. I will attempt that for next month.

@amunger amunger added the verified Verification succeeded label Jan 25, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 10, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Issue identified by VS Code Team member as probable bug interactive-window-debugging verified Verification succeeded
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants