mfussenegger / nvim-dap

Debug Adapter Protocol client implementation for Neovim
GNU General Public License v3.0
5.31k stars 187 forks source link

Cannot toggle/list breakpoints #185

Closed yingzhu146 closed 3 years ago

yingzhu146 commented 3 years ago

Hi there,

When I use

dap.list_breakpoints()

I always get nil, even though I can clearly see the breakpoints in the sign column and it is being stopped at them. Additionally, often I can't toggle the breakpoints ( dap.toggle_breakpoint() will not do anything to sign column/debugger stopping where it should˙)

NVIM v0.5.0-dev+1025-g0fad952a2
Build type: Debug
LuaJIT 2.1.0-beta3

OS is macOS 11.4, using an M1 based MBA

mfussenegger commented 3 years ago

From the docs:

list_breakpoints() Lists all breakpoints and log points in quickfix window.

It doesn't actually return the breakpoints, but adds them to the quickfix window.

Additionally, often I can't toggle the breakpoints ( dap.toggle_breakpoint() will not do anything to sign column/debugger stopping where it should˙)

The debug adapter can reject breakpoints if they're not applicable. For example some debuggers cannot stop on a declaration without assignment.

You should see the breakpoint flicker - popping up and disappearing - if that happens, and it only happens if you toggle breakpoints during an active debug session.

If you set the up-front and then launch the session, they can of course also disappear if the debug adapter rejects them.

Not sure how to best communicate this better to the user. It should also result in a log entry at info level if a breakpoint is rejected.

yingzhu146 commented 3 years ago

From the docs:

list_breakpoints() Lists all breakpoints and log points in quickfix window.

It doesn't actually return the breakpoints, but adds them to the quickfix window.

Apologies, I had been imprecise there - nothing is showing in the quickfix either - just literally nothing happening, however the debug adapter still stops at the point

Additionally, often I can't toggle the breakpoints ( dap.toggle_breakpoint() will not do anything to sign column/debugger stopping where it should˙)

The debug adapter can reject breakpoints if they're not applicable. For example some debuggers cannot stop on a declaration without assignment.

Actually I think this might be related to me using an external debug session and attaching to it - if I restart the session it works. This is a little cumbersome of a workflow right now but at least it works.

Not sure how to best communicate this better to the user. It should also result in a log entry at info level if a breakpoint is rejected.

Ah - just saw the documentation part about the logging, this is very helpful. Generally I think two things could help:

a) transparently communicate things that are "errors" or do not result in immediate results (e.g. "you will need to restart", "breakpoint can't be set here")

b) Show breakpoints as a pane in dap-ui - I have submitted a Feature Request

c) Have a short help section on debugging workflow, e.g. How to troubleshoot unexpected behavior, where you mention the log file

Thanks so much for this awesome package and your help!

mfussenegger commented 3 years ago

Apologies, I had been imprecise there - nothing is showing in the quickfix either - just literally nothing happening, however the debug adapter still stops at the point

Does :lua print(vim.inspect(require('dap.breakpoints').get())) print anything?

I can't manage to reproduce this.

Actually I think this might be related to me using an external debug session and attaching to it - if I restart the session it works

Restarting shouldn't really be necessary to change breakpoints. Which debug adapter are you using?

transparently communicate things that are "errors" or do not result in immediate results (e.g. "you will need to restart", "breakpoint can't be set here")

My only concern here is that if you set multiple breakpoints upfront and multiple of them are rejected the messages will be flooded. But yes - maybe that's better then having no visible feedback (other than the sign disappearing)

yingzhu146 commented 3 years ago

It doesn't return anything - I'm also experiencing other odd behavior, so maybe something generally is off. Just to give you a little background info, maybe important/connected

I'm running python -m debugpy --listen 127.0.0.1:5678 --wait-for-client /Users/yingzhu/miniconda3/envs/python38/bin/euporie ~/test.ipynb - euporie is a terminal UI, so it runs an async an event loop.

Other weird behavior (I don't know if this is dap-ui or dap related, feel free to punt me over if this is wrong):

If I print memory objects on the dap repl, or, interact with the UI (which maybe under the hood does the same thing) it appears to manipulate the state. E.g., I try to inspect properties of the self object in the current stackframe and print it with the repl/press enter in the ui (such that it de-colapses it's method/property dictionary), this appears to be destructive. With that I mean: it will crash the debugged euporie process ("not found" error on the inspected element on the terminal window where euporie is running), and I can do it exactly once (when printing again or trying to print another object, I get Unable to find thread for evaluation within dap-ui). It's like as if the event loop quits after printing the object.

To see if this is DAP related or not, I tried to use python's built in breakpoint() and try with an alternative terminal-based debugger (https://github.com/inducer/pudb) I can normally interact with those objects in a "non-destructive" way (neither euporie will crash, nor can I only interact once) - so this appears to be either dap or dap-ui related.

Apologies if I'm fundamentally not grasping something (if there's any background reading you can recommend to understand this better, let me know. I've read through the DAP protocol and I've tried to follow the lua implementation but might have overlooked some key parts)

mfussenegger commented 3 years ago

Are you trying to debug the code of euporie, or the code within the jupyter notebook?

This could be a problem of debugpy itself. Jupyter probably spawns another process to run it's kernel - not sure if debugpy can handle that.

If I print memory objects on the dap repl, or, interact with the UI (which maybe under the hood does the same thing) it appears to manipulate the state

If you type expressions in the REPL it uses the evaluate feature of the debug adapter protocol (and some debug adapters, like debugpy, allow to manipulate the state). But if you type a simple variable name it shouldn't cause state changes - although that's up to the debug adapter implementation

If you use something like .scopes it uses the scopes received from the stackframe / and variable requests. That shouldn't cause any state changes - but what exactly it does internally is up to the debug adapter.

Did you see anything interesting in the logs? Otherwise you could try the same thing using vscode, to see if it is a client issue, or if it is a problem with debugpy.

yingzhu146 commented 3 years ago

I recompiled neovim and uninstalled/reinstalled this plugin and somehow all the weirdness is gone now - not sure why this happened but thanks for the debug help!