Skip to content
This repository has been archived by the owner on Jan 20, 2024. It is now read-only.

Commit

Permalink
[lldb-dap] Updating VariableDescription to use GetDescription() as a …
Browse files Browse the repository at this point in the history
…fallback. (#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>`.
  • Loading branch information
ashgti authored Jan 12, 2024
1 parent 060505a commit 40a361a
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 66 deletions.
61 changes: 47 additions & 14 deletions lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Test lldb-dap completions request
"""

import re

import lldbdap_testcase
import dap_server
Expand All @@ -10,7 +11,7 @@
from lldbsuite.test.lldbtest import *


class TestDAP_variables(lldbdap_testcase.DAPTestCaseBase):
class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase):
def assertEvaluate(self, expression, regex):
self.assertRegexpMatches(
self.dap_server.request_evaluate(expression, context=self.context)["body"][
Expand All @@ -25,6 +26,9 @@ def assertEvaluateFailure(self, expression):
self.dap_server.request_evaluate(expression, context=self.context)["body"],
)

def isResultExpandedDescription(self):
return self.context == "repl" or self.context == "hover"

def isExpressionParsedExpected(self):
return self.context != "hover"

Expand Down Expand Up @@ -59,16 +63,30 @@ def run_test_evaluate_expressions(
self.assertEvaluate("var2", "21")
self.assertEvaluate("static_int", "42")
self.assertEvaluate("non_static_int", "43")
self.assertEvaluate(
"struct1", "{foo:15}" if enableAutoVariableSummaries else "my_struct @ 0x"
)
self.assertEvaluate(
"struct2", "0x.* {foo:16}" if enableAutoVariableSummaries else "0x.*"
)
self.assertEvaluate("struct3", "0x.*0")
self.assertEvaluate("struct1.foo", "15")
self.assertEvaluate("struct2->foo", "16")

if self.isResultExpandedDescription():
self.assertEvaluate(
"struct1",
r"\(my_struct\) (struct1|\$\d+) = \(foo = 15\)",
)
self.assertEvaluate("struct2", r"\(my_struct \*\) (struct2|\$\d+) = 0x.*")
self.assertEvaluate(
"struct3", r"\(my_struct \*\) (struct3|\$\d+) = nullptr"
)
else:
self.assertEvaluate(
"struct1",
re.escape("{foo:15}")
if enableAutoVariableSummaries
else "my_struct @ 0x",
)
self.assertEvaluate(
"struct2", "0x.* {foo:16}" if enableAutoVariableSummaries else "0x.*"
)
self.assertEvaluate("struct3", "0x.*0")

self.assertEvaluateFailure("var") # local variable of a_function
self.assertEvaluateFailure("my_struct") # type name
self.assertEvaluateFailure("int") # type name
Expand All @@ -95,9 +113,18 @@ def run_test_evaluate_expressions(
self.assertEvaluate(
"non_static_int", "10"
) # different variable with the same name
self.assertEvaluate(
"struct1", "{foo:15}" if enableAutoVariableSummaries else "my_struct @ 0x"
)
if self.isResultExpandedDescription():
self.assertEvaluate(
"struct1",
r"\(my_struct\) (struct1|\$\d+) = \(foo = 15\)",
)
else:
self.assertEvaluate(
"struct1",
re.escape("{foo:15}")
if enableAutoVariableSummaries
else "my_struct @ 0x",
)
self.assertEvaluate("struct1.foo", "15")
self.assertEvaluate("struct2->foo", "16")

Expand Down Expand Up @@ -164,16 +191,22 @@ def test_generic_evaluate_expressions(self):
@skipIfRemote
def test_repl_evaluate_expressions(self):
# Tests expression evaluations that are triggered from the Debug Console
self.run_test_evaluate_expressions("repl", enableAutoVariableSummaries=True)
self.run_test_evaluate_expressions("repl", enableAutoVariableSummaries=False)

@skipIfWindows
@skipIfRemote
def test_watch_evaluate_expressions(self):
# Tests expression evaluations that are triggered from a watch expression
self.run_test_evaluate_expressions("watch", enableAutoVariableSummaries=False)
self.run_test_evaluate_expressions("watch", enableAutoVariableSummaries=True)

@skipIfWindows
@skipIfRemote
def test_hover_evaluate_expressions(self):
# Tests expression evaluations that are triggered when hovering on the editor
self.run_test_evaluate_expressions("hover", enableAutoVariableSummaries=True)
self.run_test_evaluate_expressions("hover", enableAutoVariableSummaries=False)

@skipIfWindows
@skipIfRemote
def test_variable_evaluate_expressions(self):
# Tests expression evaluations that are triggered in the variable explorer
self.run_test_evaluate_expressions("variable", enableAutoVariableSummaries=True)
149 changes: 112 additions & 37 deletions lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,18 @@ def verify_values(self, verify_dict, actual, varref_dict=None, expression=None):
startswith = actual_value.startswith(verify_value)
self.assertTrue(
startswith,
('"%s" value "%s" doesn\'t start with' ' "%s")')
('"%s" value "%s" doesn\'t start with "%s")')
% (key, actual_value, verify_value),
)
if "matches" in verify_dict:
verify = verify_dict["matches"]
for key in verify:
verify_value = verify[key]
actual_value = actual[key]
self.assertRegex(
actual_value,
verify_value,
('"%s" value "%s" doesn\'t match pattern "%s")')
% (key, actual_value, verify_value),
)
if "contains" in verify_dict:
Expand Down Expand Up @@ -150,7 +161,7 @@ def do_test_scopes_variables_setVariable_evaluate(
self.continue_to_breakpoints(breakpoint_ids)
locals = self.dap_server.get_local_variables()
globals = self.dap_server.get_global_variables()
buffer_children = make_buffer_verify_dict(0, 32)
buffer_children = make_buffer_verify_dict(0, 16)
verify_locals = {
"argc": {
"equals": {
Expand Down Expand Up @@ -243,18 +254,18 @@ def do_test_scopes_variables_setVariable_evaluate(
"pt": {
"equals": {"type": "PointType"},
"startswith": {
"result": "{x:11, y:22}"
"result": "{x:11, y:22, buffer:{...}}"
if enableAutoVariableSummaries
else "PointType @ 0x"
},
"hasVariablesReference": True,
},
"pt.buffer": {
"equals": {"type": "int[32]"},
"equals": {"type": "int[16]"},
"startswith": {
"result": "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...}"
if enableAutoVariableSummaries
else "int[32] @ 0x"
else "int[16] @ 0x"
},
"hasVariablesReference": True,
},
Expand Down Expand Up @@ -440,7 +451,7 @@ def do_test_scopes_and_evaluate_expansion(self, enableAutoVariableSummaries: boo
},
"buffer": {
"children": buffer_children,
"equals": {"indexedVariables": 32},
"equals": {"indexedVariables": 16},
},
},
},
Expand All @@ -455,15 +466,85 @@ def do_test_scopes_and_evaluate_expansion(self, enableAutoVariableSummaries: boo
# the other temporary (from other UI).
expandable_expression = {
"name": "pt",
"response": {
"equals": {"type": "PointType"},
"startswith": {
"result": "{x:11, y:22}"
if enableAutoVariableSummaries
else "PointType @ 0x"
"context": {
"repl": {
"equals": {"type": "PointType"},
"equals": {
"result": """(PointType) $0 = {
x = 11
y = 22
buffer = {
[0] = 0
[1] = 1
[2] = 2
[3] = 3
[4] = 4
[5] = 5
[6] = 6
[7] = 7
[8] = 8
[9] = 9
[10] = 10
[11] = 11
[12] = 12
[13] = 13
[14] = 14
[15] = 15
}
}"""
},
"missing": ["indexedVariables"],
"hasVariablesReference": True,
},
"hover": {
"equals": {"type": "PointType"},
"equals": {
"result": """(PointType) pt = {
x = 11
y = 22
buffer = {
[0] = 0
[1] = 1
[2] = 2
[3] = 3
[4] = 4
[5] = 5
[6] = 6
[7] = 7
[8] = 8
[9] = 9
[10] = 10
[11] = 11
[12] = 12
[13] = 13
[14] = 14
[15] = 15
}
}"""
},
"missing": ["indexedVariables"],
"hasVariablesReference": True,
},
"watch": {
"equals": {"type": "PointType"},
"startswith": {
"result": "{x:11, y:22, buffer:{...}}"
if enableAutoVariableSummaries
else "PointType @ 0x"
},
"missing": ["indexedVariables"],
"hasVariablesReference": True,
},
"variables": {
"equals": {"type": "PointType"},
"startswith": {
"result": "{x:11, y:22, buffer:{...}}"
if enableAutoVariableSummaries
else "PointType @ 0x"
},
"missing": ["indexedVariables"],
"hasVariablesReference": True,
},
"missing": ["indexedVariables"],
"hasVariablesReference": True,
},
"children": {
"x": {"equals": {"type": "int", "value": "11"}},
Expand All @@ -472,35 +553,29 @@ def do_test_scopes_and_evaluate_expansion(self, enableAutoVariableSummaries: boo
},
}

# Evaluate from permanent UI.
permanent_expr_varref_dict = {}
response = self.dap_server.request_evaluate(
expandable_expression["name"], frameIndex=0, threadId=None, context="repl"
)
self.verify_values(
expandable_expression["response"],
response["body"],
permanent_expr_varref_dict,
expandable_expression["name"],
)

# Evaluate from temporary UI.
temporary_expr_varref_dict = {}
response = self.dap_server.request_evaluate(expandable_expression["name"])
self.verify_values(
expandable_expression["response"],
response["body"],
temporary_expr_varref_dict,
expandable_expression["name"],
)
# Evaluate from known contexts.
expr_varref_dict = {}
for context, verify_dict in expandable_expression["context"].items():
response = self.dap_server.request_evaluate(
expandable_expression["name"],
frameIndex=0,
threadId=None,
context=context,
)
self.verify_values(
verify_dict,
response["body"],
expr_varref_dict,
expandable_expression["name"],
)

# Evaluate locals again.
locals = self.dap_server.get_local_variables()
self.verify_variables(verify_locals, locals)

# Verify the evaluated expressions before second locals evaluation
# can be expanded.
var_ref = temporary_expr_varref_dict[expandable_expression["name"]]
var_ref = expr_varref_dict[expandable_expression["name"]]
response = self.dap_server.request_variables(var_ref)
self.verify_variables(
expandable_expression["children"], response["body"]["variables"]
Expand All @@ -516,7 +591,7 @@ def do_test_scopes_and_evaluate_expansion(self, enableAutoVariableSummaries: boo
)
self.continue_to_breakpoints(breakpoint_ids)

var_ref = permanent_expr_varref_dict[expandable_expression["name"]]
var_ref = expr_varref_dict[expandable_expression["name"]]
response = self.dap_server.request_variables(var_ref)
self.verify_variables(
expandable_expression["children"], response["body"]["variables"]
Expand Down
2 changes: 1 addition & 1 deletion lldb/test/API/tools/lldb-dap/variables/main.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

#define BUFFER_SIZE 32
#define BUFFER_SIZE 16
struct PointType {
int x;
int y;
Expand Down
Loading

0 comments on commit 40a361a

Please sign in to comment.