-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
[lldb-dap] Updating VariableDescription to use GetDescription() as a fallback. #77026
Conversation
@llvm/pr-subscribers-lldb Author: John Harrison (ashgti) ChangesWhen generating a For swift types, lldb includes a langauge runtime plugin that can generate a description of the object but this is only used with For example:
With this change, if GetValue and GetSummary return empty then we try Full diff: https://github.com/llvm/llvm-project/pull/77026.diff 1 Files Affected:
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index df17ac9d849176..f8ac53ef809e6e 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -1042,10 +1042,14 @@ VariableDescription::VariableDescription(lldb::SBValue v, bool format_hex,
os_display_value << " " << *effective_summary;
} else if (effective_summary) {
os_display_value << *effective_summary;
-
- // As last resort, we print its type and address if available.
} else {
- if (!raw_display_type_name.empty()) {
+ lldb::SBStream description;
+ // Try letting lldb generate a description.
+ if (v.GetDescription(description) && description.GetSize()) {
+ os_display_value << description.GetData();
+
+ // As last resort, we print its type and address if available.
+ } else if (!raw_display_type_name.empty()) {
os_display_value << raw_display_type_name;
lldb::addr_t address = v.GetLoadAddress();
if (address != LLDB_INVALID_ADDRESS)
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks fine to me. Can we add a test?
✅ With the latest revision this PR passed the C/C++ code formatter. |
cc @walter-erquinigo since this looks like it has similar overlap to the auto generated summeries. |
lldb/tools/lldb-dap/JSONUtils.cpp
Outdated
@@ -135,6 +135,18 @@ std::vector<std::string> GetStrings(const llvm::json::Object *obj, | |||
return strs; | |||
} | |||
|
|||
static std::string GetDescriptionTrimmed(lldb::SBValue &value) { | |||
lldb::SBStream stream; | |||
value.GetDescription(stream); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method bool SBValue::GetDescription(SBStream &description)
returns true
always. Could you make it return false if the description couldn't be generated, and also abort early in this function in such case?
I've also noticed that GetDescription
dumps "No value"
in some cases, which would be better not to display via lldb-dap
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated bool SBValue::GetDescription(SBStream &description)
to return false
for the "No value"
case and this helper should return an empty string in that situation fallback back to the <type> @ <pointer>
format.
"result": "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...}" | ||
if enableAutoVariableSummaries | ||
else "int[32] @ 0x" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
out of curiosity, how is this rendered in the variables tab on VSCode? Something I tried to achieve with auto summaries is to use a single line for readability, so I'd like to know how this looks like in the UI
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quite an interesting coincidence that it looks nice when the children are expanded.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just two minor things left, but otherwise LGTM
lldb/tools/lldb-dap/JSONUtils.cpp
Outdated
@@ -135,6 +135,21 @@ std::vector<std::string> GetStrings(const llvm::json::Object *obj, | |||
return strs; | |||
} | |||
|
|||
static std::string GetDescriptionTrimmed(lldb::SBValue &value) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add a comment for this function mentioning that this returns an empty string if the description is not available. You can also change the return type to optionalstd::string, which might be cleaner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
lldb/tools/lldb-dap/JSONUtils.cpp
Outdated
if (!value.GetDescription(stream)) { | ||
return ""; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove unnecessary braces
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the braces (and well simplified this function).
Ah, this looks like swift's description generator actually prints out My main concern is that the SBValue::GetDescription() can create multi-line output, and as you have discovered, when displayed in the GUI, it will chop off the value at the first newline which makes this less useful. I am not sure how I feel about this as this description could be quite large and it isn't doing much better than what used to be there before with the |
Is there a way we can tell that the request is from the console and only enable this feature if we are going to dump it to the debug console? |
Now that I think of it, it would be really nice if GetDescription() could get a |
Yea, the DAP has a context we can use to limit this https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Evaluate
I'll work on an update to this logic to only use GetDescription for |
That will work nicely. |
Done, the latest revision will only use the description for hovers and repl contexts. |
lldb/tools/lldb-dap/JSONUtils.cpp
Outdated
os << separator << name << ":" << value; | ||
separator = ", "; | ||
} | ||
desc = "{...}"; // Fallback for nested types. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"{...}" should only be used for structs unions or classes. We are processing the children of an item. You will need to check the type class so I would recommend:
static bool IsClassStructOrUnionType(lldb::SBType t) {
return (t.GetTypeClass() & (lldb::eTypeClassUnion | lldb::eTypeClassStruct | lldb::eTypeClassUnion)) != 0
}
Then in the above code:
if (IsClassStructOrUnionType(child.GetType())
desc = "{...}"
It seems we would want this description for a top level class/struct/union as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated this to use the {...}
for nested types.
…over and repl DAP evaluate requests. When generating a `display_value` for a variable the current approach calls `SBValue::GetValue()` and `SBValue::GetSummary()` to generate a `display_value` for the `SBValue`. However, there are cases where both of these return an empty string and the fallback is to print a pointer and type name instead (e.g. "FooBarType @ 0x00321"). For swift types, lldb includes a langauge runtime plugin that can generate a user description of the object but this is only used with `SBValue::GetDescription()`. For example: ``` $ lldb swift-binary ... stop at breakpoint ... lldb> script >>> event = lldb.frame.GetValueForVariablePath("event") >>> print("Value", event.GetValue()) Value None >>> print("Summary", event.GetSummary()) Summary None >>> print("Description", event) Description (main.Event) event = (name = "Greetings", time = 2024-01-04 23:38:06 UTC) ``` With this change we now call SBValue::GetDescription() which will internally include a summary (if configured) or call into a language runtime to return a formatted description of the value.
…fallback. (llvm#77026) When generating a `display_value` for a variable the current approach calls `SBValue::GetValue()` and `SBValue::GetSummary()` to generate a `display_value` for the `SBValue`. However, there are cases where both of these return an empty string and the fallback is to print a pointer and type name instead (e.g. `FooBarType @ 0x00321`). For swift types, lldb includes a langauge runtime plugin that can generate a description of the object but this is only used with `SBValue::GetDescription()`. For example: ``` $ lldb swift-binary ... stop at breakpoint ... lldb> script >>> event = lldb.frame.GetValueForVariablePath("event") >>> print("Value", event.GetValue()) Value None >>> print("Summary", event.GetSummary()) Summary None >>> print("Description", event) # __str__ calls SBValue::GetDescription() Description (main.Event) event = (name = "Greetings", time = 2024-01-04 23:38:06 UTC) ``` With this change, if GetValue and GetSummary return empty then we try `SBValue::GetDescription()` as a fallback before using the previous logic of printing `<type> @ <addr>`.
I'm currently optimizing our data formatters for some fairly complex data structures, and I've ran into something I don't understand. My DAP packet sequence for a hover request consists of two (mostly redundant) packets:
The first response contains the contents of the formatted variable (a vector, in this example) as a string, while the second one contains the same data in a structured form. Looking at the history, I've found this PR, which seems to indicate that the first packet is necessary in order to provide the contents of data structure in the hover window. However, that does not appear to match what I'm seeing: From what I can tell by this screenshot, the only part that VSCode uses from the first response is the first line (which it uses as the "summary" of the value, while the rest of the data (the vector contents) comes from the second response. The first response is problematic for me, because that output can contain a lot more information (if the vector contained structures rather than ints, those structures would get expanded recursively) than what's displayed on the screen, which makes the popups slower than necessary. From what I'm seeing, it would be ideal to remove the expansion in the first response completely (which would essentially amount to reverting this PR), given that the information is already contained in the second one. However, I am assuming there is a reason for why that code exists -- but then I don't understand why does my experience (and screenshots) differ so much from the screenshots on this PR. Could something have changed (over the last 4 months) in how VSCode behaves that would mean this PR is not necessary? Can anyone shed any light on this? |
I don't think anything has changed on VSCode proper. I've just verified I have the same experience as you. Given what you said, I'm in favor of reverting this or at least gating this feature under a json initialization option until the original author can look at this. |
Thanks for checking this out. I'll try to whip something up tomorrow. |
I just checked and I'm not seeing the hover's in the same format as they were when I made the pull request. The expression context should still have the expanded forms though for example: I think the VSCode team is working on making the Debug view more extensible for visualizations, see https://github.com/microsoft/vscode/blob/main/src/vscode-dts/vscode.proposed.debugVisualization.d.ts and microsoft/vscode#197287 |
I'm okay with anything that ensures hovering is fast. |
Sounds good to me |
VSCode will automatically ask for the children (in structured form) so there's no point in sending the textual representation. This can make displaying hover popups for complex variables with complicated data formatters much faster. See discussion on llvm#77026 for context.
When generating a
display_value
for a variable the current approach callsSBValue::GetValue()
andSBValue::GetSummary()
to generate adisplay_value
for theSBValue
. However, there are cases where both of these return an empty string and the fallback is to print a pointer and type name instead (e.g.FooBarType @ 0x00321
).For swift types, lldb includes a langauge runtime plugin that can generate a description of the object but this is only used with
SBValue::GetDescription()
.For example:
With this change, if GetValue and GetSummary return empty then we try
SBValue::GetDescription()
as a fallback before using the previous logic of printing<type> @ <addr>
.