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

Detecting which breakpoint caused a Stopped event #178

Closed
logancollins opened this issue Feb 8, 2021 · 11 comments
Closed

Detecting which breakpoint caused a Stopped event #178

logancollins opened this issue Feb 8, 2021 · 11 comments
Assignees
Labels
feature-request Request for new features or functionality
Milestone

Comments

@logancollins
Copy link

logancollins commented Feb 8, 2021

Background: I'm attempting to implement actions that are run by the IDE when specific breakpoints are hit; Actions that are a bit more complex than the protocol provides (which is fine… these actions are purely IDE-specific and aren't really the concern of the adapter). They are actions like "show a specific view when a specific function breakpoint is hit" or "play a specific sound".

With the Stopped event, the reason field can denote that a thread stopped because of a breakpoint, function breakpoint, etc. However, I have been running into difficulty planning the best way to know which breakpoint the debugger hit with any accuracy.

Once the Stopped event arrives, it is possible to request the Thread that stopped, then its individual stack frames (and then even scopes). Then you can begin to correlate on which source line debugging stopped to a Breakpoint object that was returned from the adapter. But this only works as long as the adapter was able to provide the line parameter. This is likely to be returned for source breakpoints, but function breakpoints seem to be another matter (as not all adapters would likely be able to accurately resolve line / column information for a function purely by name when adding them.)

If this indeed is not directly possible right now, I'd like to propose something like a breakpointId attribute of the Stopped event, based on the IDs returned from the SetBreakpoints or SetFunctionBreakpoints requests. This way, the IDE can know exactly which resolved breakpoint was hit by the debug adapter. As far as my limited knowledge goes, this would still determine whether a source or function breakpoints was hit, as well.

It's also possible that I've entirely overlooked something in the protocol definition that makes this more clear or concrete. If so, I'd be very thankful for any clarification!

@weinand weinand self-assigned this Feb 9, 2021
@weinand weinand added the feature-request Request for new features or functionality label Feb 9, 2021
@weinand
Copy link
Contributor

weinand commented Feb 9, 2021

@logancollins Your request makes sense.

But please be aware that a breakpointId on the stopped event would have to be optional (for backward compatibility).
So in the implementation of your feature you are at the mercy of debug extension owners that have opted into providing the breakpointId.

BTW, you can already "simulate" a breakpointId on the stopped event with a breakpoint condition: just call your "actions" via a function that you enter as a breakpoint condition and return true (so that the breakpoint is not skipped).

@logancollins
Copy link
Author

Thank you for considering! I agree that making the parameter optional makes sense for backwards compatibility. If there were a corresponding Capability, it could help IDEs to conditionally disable or enable certain functionality that requires that the breakpointId parameter be provided, if that level of specificity were wanted.

Also, sorry—I should've been more clear on the actions. They are not actions that are happening within the language being debugged. They are purely within the IDE's UI, so the debuggee has no access to invoke them in any way. But, this is a nice workaround for doing so with the language itself!

@gregg-miskelly
Copy link
Member

gregg-miskelly commented Feb 9, 2021

Suggestion if we decide to add this -- multiple breakpoints can be hit at the same time, so I would suggest something like:

            "hitBreakpointIds": {
              "type": "array",
              "items": {
                "type": "integer"
              },
              "description": "Array of the ids of the breakpoints that were hit."
            }

Visual Studio currently uses this as a protocol extension for the same purpose.

@puremourning
Copy link
Contributor

+1 to having breakpointIds. Vimspector has client-side emulation of temporary breakpoints (for "run-to-cursor" functionality) and currently guesses if the breakpoint hit was the "run to cursor" breakpoint based purely on matching the line number of the stopped event.

having an ID to link these would make it a lot more robust.

@weinand
Copy link
Contributor

weinand commented Mar 10, 2021

@gregg-miskelly
Thanks for your input. I think we could easily use what you already have as a protocol extension.

But in order to come up with a good description for the new property I need some help with the semantics of the multiplicity of "hitBreakpointIds".
In what exact situation can a single stopped event originate from multiple breakpoints being hit?
Duplicate or overlapping breakpoints?

@gregg-miskelly
Copy link
Member

gregg-miskelly commented Mar 10, 2021

This would happen if there were multiple breakpoint requests which wound up mapping to the same instruction. Examples:

  • A function breakpoint and a source file breakpoint map to the same point
  • Multiple source file breakpoints get collapsed to the same instruction by the compiler/runtime
  • Multiple function breakpoints with different strings but wind up mapping to the same location
  • An instruction breakpoint and a source line breakpoint that mapped to the same instruction
  • A data breakpoint hitting at the same instruction where there was already a breakpoint
  • Using the proposed AddBreakpointRequest API, multiple breakpoints can be at the same location. I can't think of any case where this would make sense for normal breakpoints (the ones that a user sets), but can be valid for hidden breakpoints set programmatically by the debugger UI or some extension to it. For example, in Visual Studio, you could use 'Run To Cursor' on a line that already had a conditional breakpoint, or a breakpoint with an action.

@weinand
Copy link
Contributor

weinand commented Mar 10, 2021

@gregg-miskelly thanks a lot, that was helpful.

Here is my proposal for what gets added to the body of the stopped event:

    /**
     * Ids of the breakpoints that triggered the event. In most cases there will
     * be only a single breakpoint but here are some examples for multiple
     * breakpoints:
     * - Different types of breakpoints map to the same location.
     * - Multiple source breakpoints get collapsed to the same instruction by
     * the compiler/runtime.
     * - Multiple function breakpoints with different function names map to the
     * same location.
     */
    hitBreakpointIds?: number[];

@gregg-miskelly
Copy link
Member

LGTM

@logancollins
Copy link
Author

This would also solve my initial request perfectly!

@weinand
Copy link
Contributor

weinand commented Mar 10, 2021

The feature is now available in the 1.46.0-pre.0 version of the vscode-debugadapter-node npm modules.

@weinand
Copy link
Contributor

weinand commented Mar 25, 2021

The feature is now available in the final 1.46.0 version of the vscode-debugadapter-node npm modules.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-request Request for new features or functionality
Projects
None yet
Development

No branches or pull requests

4 participants