-
Notifications
You must be signed in to change notification settings - Fork 131
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
Proposal: Add new reason finished
or stepOut
and optional field returnValue
to Stopped Event
#458
Comments
I guess this work just as well. I suppose one way my proposed way would be preferred, is if in some universe, someone decides to log return values from a function (or set of functions). Automating this process would probably be easier if a "return value" was standardized in the protocol from stepOut, but even in that universe, that functionality could be solved using the same approach as you mentioned. So I guess we can close this issue really. |
Hm, nothing in particular comes to mind for usages of it in VS Code's UI (cc @roblourens) but let's leave it open for a bit to see if any other client implementors want to weigh in. In general DAP is a protocol for specifying what UI should be available during debugging, so most of our thinking starts at the client experience we want to build, and from there the minimal protocol necessary to describe it. |
I personally find the ‘put the return value in the scopes’ to be a hack that’s only sporadically implemented and not intuitive for users. I would like to offer a better ui to my users. The obvious UI for this is to inline it as an inlay hint or virtual test next to or around the function call. This is not trivial and I don’t think there’s enough info in the proposal for clients to implement that well. In order for this to work well we would need it whether the user pressed step out or step over etc. (Parity with the ‘scopes’ approach). It could be enough just to provide a variablesReference and sourceReference/line number or something. In summary I think it would be a good addition, but needs more iteration on the UI side to decide what data are worthwhile. Alternatively, a statement that “typically the return value from the last function call should be reported as a Scope named ‘return’” might help with consistency for users. |
This would definitely be an improvement -- for my gdb implementation, I was completely unaware that this was a convention. Adding a variable reference to the stop event also seems reasonable to me. Alternatively I suppose an adapter could also do this via an output event. (Though again, the key thing is to document any such convention.) |
Output event is very bad. Probably not even visible to user in most cases. |
I think this makes sense. However, I suggest that we put it on the stack frame rather than the "stopped". This allows multiple stack frames to represent that they're in the process of returning data. This is semantically sensible in some cases, such as when one is stepped through code called in a interface StackFrame {
// ...
/**
* A value being returned by from this stack frame.
*/
returnValue?: Variable; Though not strictly necessary, we should also introduce a client capability so that adapters that have been adding ad-hoc return variables avoid duplicating data: interface InitializeRequestArguments {
/**
* Client supports reading the `returnValue` from a `StackFrame`.
*/
supportStackFrameReturnValue?: boolean; cc @roblourens |
A few problems with putting it on the stack frame:
My suggestion: these should be in a scope. I understand some folks on this thread have considered this a 'hack' though it doesn't seem like one to me. Would folks consider this less hacky if scope.presentationHint was extended with a value for this? |
I like that idea. That also allows for a graceful fallback for existing clients without introducing any new capabilities. That would be extending the presentationHint on the Scope as such: interface Scope
/**
* A hint for how to present this scope in the UI. If this attribute is
* missing, the scope is shown with a generic UI.
* Values:
* 'arguments': Scope contains method arguments.
* 'locals': Scope contains local variables.
* 'registers': Scope contains registers. Only a single `registers` scope
* should be returned from a `scopes` request.
* 'returnValue': Scope contains a return value. This may represent a value
* about to be returned, or a value just returned from a function call, for example.
*/
presentationHint?: 'arguments' | 'locals' | 'registers' | 'returnValue' | string; There is a bit of looseness in that this doesn't say where the return value is coming from. In cases like I mentioned above, there may be a return value prepared on the current call frame. In other cases (e.g. what Python does today) they may want to display the value returned from a function the user just stepped out of. Possibly both: the scope |
After having processed this further, these are my thoughts: Scope comes with problems but mainly, the biggest problem is: The client may or may not decide to show the scope automatically, after a stepOut has completed and for the debug adapter maintainers, we are out of luck, we have no way to influence that it lands in a scope that's displayed (let's use VSCode as an example since it's the biggest client in the wild) - unless we artificially make it so it's in the first scope, in the scope list which VSCode generally tends to display automatically in my experience. This is counter intuitive. After having thought about it, I actually think my initial example is superior (@tromey suggested an But I have come to realize that adding a new reason is what is not good with my initial proposal - it should just be one of the existing reasons. The reason for that, is adding just an optional field to interface StoppedEvent {
/**
* A value that is related to this stopped event. Used, not exclusively, to signal return values
* or data breakpoint values.
*/
result?: Variable;
/// ...
} I also don't see how this could not work in the scenario that @connor4312 mentions - take for instance a C++ exception, and the debugger & adapter has The thing is - we want to be able to represent a value with a stop. I don't know how Go and Zig does with their This will also work very well with logging purposes (which sticking on Stop events are also always processed and must be supported (although a client doesn't have to support the optional Additionally, this could potentially open up for debuggers and debug adapters, that |
If I had any say, I would vote a hard no on the scope idea. I guess we would have to reach out to more maintainers of debug adapters, but I don't think scope works as well as stopped event does. It doesn't compose well or extend well. The reason why |
I'm not sure what you're referring to that I said, but clients still don't know where or how to display this Variable. What would you do if you were a client/UI implemented? Where would you surface this data? |
I was referring to you mentioning that OutputEvent would be bad and how that wouldn't fly and how I agreed that was the problem with OutputEvent, because an OutputEvent is by it's very nature asynchronous (and it does not stop the state machine which is an UI/Debugger session in the same fashion a StoppedEvent does since it is the defacto way to trigger an update chain, i.e. [threads, stacktrace, scopes, variables_1 ... variables_n]) However, representing it in the UI would be the debug adapter's responsibility - it would also be trivial, since a stop event always precedes an update chain, it would be the DA's responsibility to surface the data. If you want to use a scope, use a scope. If you want to use, for instance, VSCode's exception UI - if you want to give room for VSCode to actually develop new UI widgets, StoppedEvent would be the go to solution as it's composable, extendable by default. Locking returnValue into Scopes, means locking every single other "stop-associated value" as well, where you have to keep adding it to scopes - and if, for some reason, the debug adapter wants to automate something, or build some fancy tool, using the scopes approach would now require it to report the stop event to the client, and then read the update chain results. I don't see any composability arguments in Scopes favor. Could an argument be made that perhaps it's easier for VSCode to implement? Sure. But I would argue it comes at the cost of the protocol's quality and it would be another ad-hoc solution, instead of the more generic one. |
Could a StoppedEvent value, be made to always be displayed in the I guess, this would marry the two worlds - any value seen in a |
In any case, whether we add a
Separate issue, but I think that data breakpoint values are better tracked in the
Take the following Go code: func c() int {
return 3 // breakpoint here
}
func b() int {
defer c()
return 2
}
func a() int {
defer b()
return 1
} Stopped in |
My point about the stoped event is context. Debugger UI gets a stoped event with no context. I want to display the return value somewhere on the screen that has some meaning to the user such as inline with the function that was just called. From a stopped event I have no way to do that. Indeed that's true of scope and output event too, both of which are arguably worse, but at least there is a logical place to display the return value. I could just stuff the stopped event 'return' value somewhere in the stack frame window. But that's not really intuitive either. I'm not saying don't use a stopped event, I'm saying provide the UI with more context like a source range associated with where the rerun value came from or something like that. Iirc it's valid to get multiple stopped events (one per thread eg) but the treadId is optional. So I just think that implementation in a client in practice should be considered while designing the API |
I think that case is covered by #343, which I'm anticipating taking up in the near future |
Ah yes. Hadn't seen that. If we had that source/line info in Variable then I think this works well as a Stopped event argument. |
I'm still leaning toward use of a scope with
Are there UX scenarios folks see that it isn't satisfied by this case, or are satisfied poorly? Re further up the thread:
I'm not sure what you mean here, or that I get your point around lacking extensibility. A In contrast,
I don't think this is very fancy, a client reading the scopes for a stack frame is bread and butter of debugging. |
|
It's worth noting that a I personally tend to lean toward the new scope idea as well. In the current implementation in gdb, I think I misunderstood the earlier discussion and so right now gdb just sticks a |
The DAP spec recently changed to add a new scope for the return value from a "stepOut" request. This new scope uses the "returnValue" presentation hint. See: microsoft/debug-adapter-protocol#458 This patch implements this for gdb. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31945
The DAP spec recently changed to add a new scope for the return value from a "stepOut" request. This new scope uses the "returnValue" presentation hint. See: microsoft/debug-adapter-protocol#458 This patch implements this for gdb. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31945
The DAP spec recently changed to add a new scope for the return value from a "stepOut" request. This new scope uses the "returnValue" presentation hint. See: microsoft/debug-adapter-protocol#458 This patch implements this for gdb. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31945 Reviewed-By: Eli Zaretskii <eliz@gnu.org>
As a maintainer of a debug adapter, I've been asked a few times about being able to display return values somehow, and even though it's doable with some minor hacking about (actually implemented sort of like it's described here, thus being non-standard compliant), it would be nice if it got standardized in the protocol.
Since we have
stepOut
request, which explicitly "finishes" the current function (in GDB lingo), a newreason
value should be added to reflect that this has happened, instead of just reportingstep
. Due to this new reason, an optional field of typeVariable
should also be added to theStoppedEvent
, where a structured representation of the return value is returned.An explicit
stepOut
reason I think is to prefer, than just having it remainstep
and then have an optionalvalue
or something like that onStoppedEvent
because it doesn't signal as clearly intention. Especially sincestep
stops come in so many forms (next line, next instruction, next statement, etc).Also, adding an additional
stepOut
reason, means that legacy adapters can continue signallingstep
if they so choose if they are unwilling or don't want to support the feature of displaying return values after astepOut
request.The text was updated successfully, but these errors were encountered: