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

Debugger: Exclude caller from breakpoint hit condition #127775

Closed
hediet opened this issue Jul 1, 2021 · 16 comments
Closed

Debugger: Exclude caller from breakpoint hit condition #127775

hediet opened this issue Jul 1, 2021 · 16 comments
Assignees
Labels
debug Debug viewlet, configurations, breakpoints, adapter issues feature-request Request for new features or functionality on-testplan
Milestone

Comments

@hediet
Copy link
Member

hediet commented Jul 1, 2021

It is now about the 20th time that I wished I had this feature, so I'm finally filing this feature request.

I think the UX around breakpoint hit conditions can be improved.
Similar to log points I find breakpoint conditions not very accessible (but this might be a personal preference). Thus, I rarely use them even though they could solve some problems I encounter during debugging.

Maybe this is because I'm usually using the mouse when debugging and "programming" the right stop condition requires context switches. Or because they require too many clicks to be setup. Whenever I use breakpoint hit conditions, I feel slow.

This feature is about making breakpoint hit conditions a little bit more accessible.

The Feature

It would be really nice if I could simply exclude a caller from a breakpoint hit condition:
If a breakpoint is hit and the statement that the breakpoint is on is about to be executed, I want to be able to right-click on some stack frame and have an option "Don't trigger breakpoint in this context".
If I click on it, the breakpoint hit condition should be extended to return false when the selected stack frame is still on the stack.

This way, this breakpoint would never be hit when called from renderLine (because renderLine is spammy, which makes it difficult to debug the actually problematic context):

image

Implementation Details

This requires a small UI part (an additional context menu entry) and a larger debug adapter part (figuring out how to formalute the exclusion as valid JS condition, probably involving new Error().stack).

More Ideas

I think these contextual actions make hit conditions way easier to use.
Maybe something like this could also make sense for the watch view - "Restrict hit condition to x === 1" (or x !== 1), if x currently is 1.

@isidorn
Copy link
Contributor

isidorn commented Jul 1, 2021

I like the overall push to make Conditional Breakpoints and Hit Count breakpoints easier to use, since currently they are a bit hidden and not so easy to use as you say.

As for this particular action: we could only do it when the debug adapter supports the hitBreakpointIds since then we can correlate between a stopped event and a breakpoint. So in case the hitBreakpointsIds are present we could show this action.

You idea to use conditions to control this would not easily work for every language, so I think we need something more general.

@weinand for ideas

@isidorn isidorn added debug Debug viewlet, configurations, breakpoints, adapter issues under-discussion Issue is under discussion for relevance, priority, approach labels Jul 1, 2021
@connor4312
Copy link
Member

I like this idea. This would probably only apply in the current session. Would there be UI to remove or clear skipped callers? That will increase the size of DAP surface area.

@hediet
Copy link
Member Author

hediet commented Jul 1, 2021

Is the context menu contributable by extensions? If so, I think for now this can be entirely implemented in vscode-debug. If users like it, it can get a proper UI.

@connor4312
Copy link
Member

Indeed, that should be possible given we have good enough context keys and data passed when the command is executed

@isidorn isidorn assigned roblourens and unassigned isidorn Aug 18, 2021
@weinand
Copy link
Contributor

weinand commented Aug 31, 2021

The feature request makes sense, but I don't think that the feature is related to (or needs to be based on) breakpoint conditions or hit counts.
Breakpoint conditions or hit counts rely on user code that is executed in the context of the running program whereas the requested feature relies on "meta" information (function name of direct or indirect caller) which typically is not available in the running program but only in the debugger (e.g. as source file and line number in stop event or stack frame).

IMO the requested feature is orthogonal to breakpoint conditions or hit counts (and it would not make breakpoint conditions or hit counts more discoverable).

Another similar mechanism which exists for JS/TS debugging is the "skipFiles" concept. Here a set of files can be specified in the launch config through glob patterns and if program execution stops in the specified files, the debug adapter automatically executes a continue request to effectively "skip" the file. In the legacy node debug adapters it was even possible to add a source file dynamically to the skipFiles set via a context menu command on the stack frame.

But this mechanism is not a good fit for the requested feature: "skipFiles" is too coarse grained and and it does not cover the concept of a caller context ("skip this breakpoint if the code is called directly or indirectly from method 'foo'").

So we can either try to extend the existing mechanisms from above for this feature or we can introduce a new mechanism.

Let's first try to to extend existing mechanisms:

  • we could make the meta information ("callstack has frame 'foo'") available in the breakpoint conditions via some pseudo statements/functions that are redirected to the debugger by preprocessing the condition. So a user could write something like i < 10 && $stackContains('foo'). This approach would require no changes on the VS Code side, but puts the burden on the debug extension.
  • we could extend the skipFiles mechanism by supporting special syntax to express a "calling context", e.g "skip this file if the call stack contains 'foo'". Again, no changes on the VS Code side, but burden is on the debug extension.

Of course for both cases context menu actions ("Ignore breakpoint in the context of this caller") would be provided as "short cuts", so a user does not have to edit a condition or the launch configuration.

Alternatively we could introduce a new mechanism either

  • in a debug extension as a skipContext mechanism (similar to skipFiles in js-debug), or
  • as a generic mechanism in VS Code that is implemented with existing DAP and without need for language/debugger specific code. E.g. when hitting a breakpoint the different frames of the stack would offer a "Ignore breakpoint in the context of this caller" context menu action. VS Code would remember this context for the breakpoint and when the breakpoint is hit in the context, VS Code would automatically issue a continue in order to skip the breakpoint.

BTW, I wonder whether (and how) feature request https://github.com/microsoft/vscode/issues/127886 is related...

@hediet @roblourens @connor4312 what do you think about this assessment?

@hediet
Copy link
Member Author

hediet commented Aug 31, 2021

In the legacy node debug adapters it was even possible to add a source file dynamically to the skipFiles set via a context menu command on the stack frame.

I dearly miss this feature.

But yeah, conceptually this is very similar.

we could make the meta information ("callstack has frame 'foo'") available in the breakpoint conditions via some pseudo statements/functions that are redirected to the debugger by preprocessing the condition. So a user could write something like i < 10 & $stackContains('foo'). This approach would require no changes on the VS Code side, but puts the burden on the debug extension.

I like this approach! But in general such language injections can cause problems.
Does it make sense to encode this as expression tree?

as a generic mechanism in VS Code that is implemented with existing DAP and without need for language/debugger specific code. E.g. when hitting a breakpoint the different frames of the stack would offer a "Ignore breakpoint in the context of this caller" context menu action. VS Code would remember this context for the breakpoint and when the breakpoint is hit in the context, VS Code would automatically issue a continue in order to skip the breakpoint.

I fear this could impact performance and debugging stability, especially when excluding hot paths.

@weinand
Copy link
Contributor

weinand commented Aug 31, 2021

I fear this could impact performance and debugging stability, especially when excluding hot paths.

Yeah, may be. But I'm not aware of any runtime/debugger that would allow for a native implementation of the "exclude caller" feature. So the "exclude caller" condition will be executed either in the DA or in VS Code.

Does it make sense to encode this as expression tree?

VS Code knows nothing about the syntax of breakpoint conditions. They are just passed as strings to the DA. The DA is free to represent them in whatever form it wants.

@connor4312
Copy link
Member

I agree that this shouldn't be based on conditional breakpoints. There is precedence for having "magic" in expressions, such as using the $returnValue or de-minified variables. However, a $stackContains is not discoverable unless you know about it (intellisense in conditional breakpoints is another issue) and you might have scenarios with multiple functions with the same name, or anonymous functions, which cannot be readily referenced.

Something I am unsure about is the longevity of this "Ignore Caller" action (and similarly the "Never Break Here" in microsoft/vscode-js-debug#1095). When I'm debugging a program, I'm examining a particular facet of the program, so while I may want to ignore a caller or file at one point this isn't necessarily something I was persisted or committed. I think instead they should be stored in memory or in workspace storage, and (thinking for the moment that this would be custom functionality in js-debug) I could register tree views which show skipped/ignored files and allow them to be mutated.

I fear this could impact performance and debugging stability, especially when excluding hot paths.

The presence of this feature alone would have a negligible impact on performance if unused--a couple extra if statements. When an ignored caller is his, though, there will be: this is something that has be to evaluated on the debugger side, so there'll be a round trip for a Debugger.paused and Debugger.resumed.

@isidorn
Copy link
Contributor

isidorn commented Oct 1, 2021

Not happening this milestone, moving to October

@isidorn isidorn modified the milestones: September 2021, October 2021 Oct 1, 2021
@connor4312
Copy link
Member

I think I want to do this, but next iteration is housekeeping. Bumping to Nov.

@connor4312 connor4312 modified the milestones: October 2021, November 2021 Oct 1, 2021
@connor4312 connor4312 added feature-request Request for new features or functionality and removed under-discussion Issue is under discussion for relevance, priority, approach labels Oct 1, 2021
@connor4312 connor4312 self-assigned this Oct 1, 2021
@weinand weinand modified the milestones: November 2021, On Deck Nov 29, 2021
@weinand weinand modified the milestones: On Deck, January 2022 Dec 14, 2021
@hediet
Copy link
Member Author

hediet commented Jan 4, 2022

Another use-case:
I want to debug _doHitTest when it is called by a click operation, but not on mouse-move.

image

@connor4312
Copy link
Member

connor4312 commented Jan 14, 2022

In JavaScript debugging, there's a context menu action to exclude a specific calling location from breaking at the current paused location.

image

The currently excluded callers are shown in a basic tree view, which allows going to and removing locations

image

These are only persisted in memory right now, so reloading will clear any currently-excluded locations.

@GitMensch
Copy link
Contributor

Looks like this was implemented - can you please reference the commits for the feature/docs allowing other debuggers to add the same feature?

@connor4312
Copy link
Member

This was implemented using existing VS Code extension points and APIs (the only tweak in core was to add additional context to actions on the call stack). E.g. actions were contributed to the context menu and view and then a tree view and TreeDataProvider was implemented in js-debug.

@justingrant
Copy link
Contributor

Great feature! Hey, does this also work for exception breakpoints, e.g. never break on lines where the exception is expected, like when checking browser capabilities by seeing if a particular piece of code will throw, or React Suspense's use of exceptions to interrupt rendering?

@connor4312
Copy link
Member

Yep, it works for every type of pause, including exception breakpoints.

@github-actions github-actions bot locked and limited conversation to collaborators Feb 28, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
debug Debug viewlet, configurations, breakpoints, adapter issues feature-request Request for new features or functionality on-testplan
Projects
None yet
Development

No branches or pull requests

7 participants