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

Investigate debug UI for highly concurrent languages #116109

Open
weinand opened this issue Feb 8, 2021 · 11 comments
Open

Investigate debug UI for highly concurrent languages #116109

weinand opened this issue Feb 8, 2021 · 11 comments
Assignees
Labels
debug Debug viewlet, configurations, breakpoints, adapter issues feature-request Request for new features or functionality
Milestone

Comments

@weinand
Copy link
Contributor

weinand commented Feb 8, 2021

The need for this item arises from a discussion initiated by the Go debugger team and others for the debug adapter protocol:

Since it is difficult to work on the DAP spec without having a concrete UI in mind, I decided to start with a discussion of a concrete debug UI in VS Code for highly concurrent languages first.

It would be great if interested parties could provide input for this discussion.

/cc @testforstephen @polinasok @gregg-miskelly @hyangah @connor4312

@weinand weinand added feature-request Request for new features or functionality debug Debug viewlet, configurations, breakpoints, adapter issues labels Feb 8, 2021
@weinand weinand added this to the February 2021 milestone Feb 8, 2021
@weinand weinand assigned weinand, isidorn and connor4312 and unassigned isidorn Feb 8, 2021
@weinand weinand modified the milestones: February 2021, March 2021 Feb 22, 2021
@weinand weinand modified the milestones: March 2021, On Deck Mar 24, 2021
@hyangah
Copy link

hyangah commented Jun 21, 2021

Thanks @weinand

We were also talking about server-side filtering & grouping features that Delve debug cli or other major Go IDE's debug UI supports or will soon support:

  • Group by strack traces.
  • Search/filter goroutines by function names in stack traces.
  • Group/search/filter goroutines by arbitrary key=value info Go runtime associates with goroutines internally.
  • Search/filter goroutines by goroutine ids

cc @suzmue for vscode go debug UI

@connor4312
Copy link
Member

My experience with green threads come from Go. Thinking about the user experience, when debugging a Go program I would like to:

  1. See how many threads are currently executing each line of code, like what pprof in Go gives.
  2. Have ability to step into or introspect thread(s) on each line of code, i.e. issue a "pause" for a thread when it resumes.
  3. 'Track' or 'isolate' a thread so I can easily step back into it. For example, if I have a websocket server I usually have at least one goroutine per connection, but might be interested in only debugging one of them. If the debugger supports it, being able to set a breakpoint so that it only hit by 'tracked' threads would be phenomenal.

The main protocol change is that threads would not be updated ambiently in an individually addressable way in the threads event. I think this is something we want to avoid since dealing with events for millions of threads could become problematic from a performance standpoint.

Perhaps instead the debug adapter could provide location metadata as in (1), and a mechanism to "realize" threads. Realization would happen when:

  • a breakpoint is hit by a thread
  • the ui requests an (arbitrary?) thread at a location to be realized

A thread would get a DAP threadId and should get threads events only after it's realized. This would allow (2) and (3). The UI could also request to "unrealize" a thread.

Something this doesn't cover is cases where additional metadata for threads, outside of location, is available. For example in Elixir/Erlang users might be want to explore the supervisor tree -- though I don't think this structure is relevant to m/any other languages.

Originally posted by @connor4312 in microsoft/debug-adapter-protocol#169 (comment)

@gregg-miskelly
Copy link
Member

gregg-miskelly commented Jun 26, 2021

In C# (coreclr debug adapter) and Windows C++ (cppvsdbg debug adapter) the threads request / ThreadEvent will return physical (OS) threads rather than logical (green) threads. While the DAP certainly doesn't/shouldn't require that threads be OS threads, I would suggest that we should come up with a new concept rather than trying to support millions of items with the existing 'thread' concept. I also think the existing threads/stack view in VS Code or the existing threads view in Visual Studio would completely fall over if we tried to do this anyway.

A few concrete suggestions:

  • In VS Code, it feels like we should have a new promises/tasks/green threads view that is in the document well rather than trying to fit it in the side bar.
  • I feel like we want to allow the debug adapter to group together stacks which are the same or partially the same like the parallel stacks window which many IDEs have (example: Visual Studio) and have the UI display these pre-grouped nodes.

Maybe something like:

CallGraphRequest: -- this allows a client to obtain a complete call graph of all green threads, returning just the info needed to draw the graph

  • Inputs:
    • Filter constraints like @hyangah mentioned above --
      • Some sort of promiseId (where the adapter gets to decide what a promise id is, ex: Task.Id in C#, or goroutine id in Go)
      • Function name
  • Outputs:
    • Array of call graph nodes:
      • Node id
      • Number of logical threads in the node
      • Array of grouped frames:
        • Name
        • Module Id
        • Presentation hint
    • Array of call graph edges: array of integer pairs to connect call graph nodes

CallGraphNodeDetailsRequest -- this obtains information about a node in the call graph so that the debugger UI can be navigated to a particular green thread node (even if it isn't active) and, if the DA supports it, evaluate expressions, obtain variables, etc.

  • Inputs:
    • Node id
    • Group frame index (which item in the 'Array of grouped frames' is being queried)
    • Logical thread index -- this is to enable paging, it is the start index of the logical output array
    • Max logical thread count -- this is to enable paging, it is the number of items requests
  • Outputs:
    • Array of:
      • promiseId
      • StackFrame

For the stepping scenarios that Connor mentioned, we could extend the execution control commands to take an optional promiseId that could be used in that case.

Since I am assuming CallGraphRequest could take minutes or even hours to complete, a DA could use the progress event stuff to let the user know what is going on.

@andyj513
Copy link

My suggestion for the Threads panel is for the ability to only show one thread at a time. I.e., when stopped on a Breakpoint, only show the thread that hit the breakpoint, with its callstack, and don't show the other threads. Of course, others, and sometimes I, will want to see the other threads at times. But I normally only care about the thread that hit the breakpoint. Perhaps a toggle button at the top to show One (i.e., when active) vs. All Threads.

@weinand
Copy link
Contributor Author

weinand commented Jan 17, 2022

related issue: #140845

@polinasok
Copy link

polinasok commented Apr 9, 2022

Hi, all, looks like this might be looked at this month although it has slipped a number of times before, so who knows. Can we please get an update?

Some of the things that our users would find helpful:

  • filter/search by id, name, method, source:line, etc - things that are already reported by various waterfall requests
  • filter/search by arbitrary implementation-specific criteria for the server to interpret
  • filter/search by user assigned tags so users can label/group interesting threads (these could be translated into id search by the editor)
  • sticky thread view settings or bookmarks like WATCH for variables, so users don't have to play with filters at every pause
  • have a setting to hide/unhide all threads other than the current one or at least keep them collapsed
    • the view does conveniently jump to the current thread, but as threads stay expanded after hitting breakpoints, things can start looking pretty messy (and too many stacktrace requests can slow things down)
  • support filtering by stop reason (depends on Support stop reasons for multiple threads (with one flagged as current) in a single stopped event debug-adapter-protocol#161)
  • support paging (depends on Paging support for threads debug-adapter-protocol#159) or at least have a field to report how many threads were not returned by the server
    • even with filtering there can be too many threads to return
    • knowing how many didn't make the list would help users get a sense of how creative they need to get with custom filtering

@davidfowl
Copy link

davidfowl commented Jun 11, 2024

I recently filed dotnet/vscode-csharp#7216

This is a significant usability issue for C# DevKit. Is there an MVP version of this feature? Show one thread at a time with the ability to switch threads via a drop down?

Solving this for golang is more difficult if you want to show goroutines in the same way mostly because of the scale (the amount of threads and stacks). The best way to visualize that in my biased experience is what Visual Studio has for parallel stacks/tasks.

I’m not asking for that though, for C#, assuming we want to improve the experience given the current design space, we could do with “one at a time” thread debugging (show one call stack) with the ability to switch threads.

Later we can address how to handle the scale the visualization for gourotines, or .NET tasks…

@davidfowl
Copy link

The suggestion from @gregg-miskelly #116109 (comment) looks like a good place to start?

@davidfowl
Copy link

@WardenGnaw moved my issue here #214824. I think these are fundamentally different requests.

@int19h
Copy link

int19h commented Jun 26, 2024

This is also of interest for CUDA debugging, where there is a similarly large number (possibly on the order of ~1M) of GPU threads running at any given time, but the user is usually interested in one or two while stepping thru or hitting breakpoints.

For https://github.com/NVIDIA/nsight-vscode-edition, we effectively implemented "show single thread at a time" by rolling our own - we present a single dummy thread at DAP level while using custom UI to allow the user to "set focus" (i.e. change which actual thread is being presented by the dummy DAP thread). We use invalidate events to force the client to refresh UI when focus changes. However, this is very much a hack, and ideally we'd prefer to drop it and use standard facilities instead.

Note that in our case those threads aren't "green" threads - they are actual physical hardware threads, so it makes sense to conceptually represent them as such. However, all the points regarding the need to lazily load them and provide some filtering experience apply.

@robertoaloi
Copy link

robertoaloi commented Nov 5, 2024

This is also of interest for Erlang, so that a large number of lightweight processes could be displayed.

As an additional requirement, it would be great to optionally view processes using a tree view, which would map well with Erlang supervision trees. An approach similar to the ones used for structured variables (with a variablesReference being 0 or positive) could be used to represent processes with "children".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
debug Debug viewlet, configurations, breakpoints, adapter issues feature-request Request for new features or functionality
Projects
None yet
Development

No branches or pull requests

10 participants