Skip to content

Commit

Permalink
[lldb] Update dwim-print to support limited variable expression paths
Browse files Browse the repository at this point in the history
`frame variable` supports nested variable access, which the API calls "variable
expression paths". This change updates `dwim-print` to support a subset of supported
variable expression paths.

Consider the expression `a->b`. In C++, the arrow operator can be overloaded, and where
that is the case, expression evaluation must be used to evaluate it, not frame variable.
Likewise, the subscript operator can be overloaded.

To avoid those cases, this change introduces a limited support for variable expression
paths. Use of the dot operator is allowed.

Additionally, this change allows `dwim-print` to directly access children of `this` and
`self` (see AllowDirectIVarAccess). This functionality is also provided by the same
`GetValueForVariableExpressionPath` method.

rdar://104348908
  • Loading branch information
kastiglione committed Nov 23, 2024
1 parent a5af621 commit cce676f
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 26 deletions.
21 changes: 17 additions & 4 deletions lldb/source/Commands/CommandObjectDWIMPrint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,23 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
result.SetStatus(eReturnStatusSuccessFinishResult);
};

// First, try `expr` as the name of a frame variable.
if (frame) {
auto valobj_sp = frame->FindVariable(ConstString(expr));
if (valobj_sp && valobj_sp->GetError().Success()) {
// First, try `expr` as a _limited_ frame variable expression path: only the
// dot operator (`.`) is permitted for this case.
//
// This is limited to support only unambiguous expression paths. Of note,
// expression paths are not attempted if the expression contain either the
// arrow operator (`->`) or the subscript operator (`[]`). This is because
// both operators can be overloaded in C++, and could result in ambiguity in
// how the expression is handled. Additionally, `*` and `&` are not supported.
bool try_variable_path = expr.find_first_of("*&->[]") == StringRef::npos;
if (frame && try_variable_path) {
Status status;
VariableSP var_sp;
auto valobj_sp = frame->GetValueForVariableExpressionPath(
expr, eDynamicDontRunTarget,
StackFrame::eExpressionPathOptionsAllowDirectIVarAccess, var_sp,
status);
if (valobj_sp && status.Success() && valobj_sp->GetError().Success()) {
if (!suppress_result) {
if (auto persisted_valobj = valobj_sp->Persist())
valobj_sp = persisted_valobj;
Expand Down
2 changes: 1 addition & 1 deletion lldb/test/API/commands/dwim-print/Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
C_SOURCES := main.c
CXX_SOURCES := main.cpp

include Makefile.rules
43 changes: 36 additions & 7 deletions lldb/test/API/commands/dwim-print/TestDWIMPrint.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def _run_cmd(self, cmd: str) -> str:
self.ci.HandleCommand(cmd, result)
return result.GetOutput().rstrip()

VAR_IDENT = re.compile(r"(?:\$\d+|\w+) = ")
VAR_IDENT = re.compile(r"(?:\$\d+|[\w.]+) = ")

def _strip_result_var(self, string: str) -> str:
"""
Expand Down Expand Up @@ -121,30 +121,39 @@ def test_empty_expression(self):
def test_nested_values(self):
"""Test dwim-print with nested values (structs, etc)."""
self.build()
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
lldbutil.run_to_source_breakpoint(
self, "break here", lldb.SBFileSpec("main.cpp")
)
self.runCmd("settings set auto-one-line-summaries false")
self._expect_cmd(f"dwim-print s", "frame variable")
self._expect_cmd(f"dwim-print (struct Structure)s", "expression")

def test_summary_strings(self):
"""Test dwim-print with nested values (structs, etc)."""
"""Test dwim-print with type summaries."""
self.build()
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
lldbutil.run_to_source_breakpoint(
self, "break here", lldb.SBFileSpec("main.cpp")
)
self.runCmd("settings set auto-one-line-summaries false")
self.runCmd("type summary add -e -s 'stub summary' Structure")
self._expect_cmd(f"dwim-print s", "frame variable")
self._expect_cmd(f"dwim-print (struct Structure)s", "expression")
self.runCmd("type summary delete Structure")

def test_void_result(self):
"""Test dwim-print does not surface an error message for void expressions."""
self.build()
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
lldbutil.run_to_source_breakpoint(
self, "break here", lldb.SBFileSpec("main.cpp")
)
self.expect("dwim-print (void)15", matching=False, patterns=["(?i)error"])

def test_preserves_persistent_variables(self):
"""Test dwim-print does not delete persistent variables."""
self.build()
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
lldbutil.run_to_source_breakpoint(
self, "break here", lldb.SBFileSpec("main.cpp")
)
self.expect("dwim-print int $i = 15")
# Run the same expression twice and verify success. This ensures the
# first command does not delete the persistent variable.
Expand All @@ -154,5 +163,25 @@ def test_preserves_persistent_variables(self):
def test_missing_type(self):
"""The expected output of po opaque is its address (no error)"""
self.build()
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
lldbutil.run_to_source_breakpoint(
self, "break here", lldb.SBFileSpec("main.cpp")
)
self.expect("dwim-print -O -- opaque", substrs=["0x"])

def test_variable_expression_path(self):
"""Test dwim-print supports certain variable expression paths."""
self.build()
lldbutil.run_to_source_breakpoint(
self, "break here", lldb.SBFileSpec("main.cpp")
)
self.runCmd("settings set auto-one-line-summaries false")
self._expect_cmd("dwim-print w.s", "frame variable")
self._expect_cmd("dwim-print wp->s", "expression")

def test_direct_child_access(self):
"""Test dwim-print supports accessing members/ivars without qualification."""
self.build()
lldbutil.run_to_source_breakpoint(
self, "break inside", lldb.SBFileSpec("main.cpp")
)
self._expect_cmd("dwim-print number", "frame variable")
14 changes: 0 additions & 14 deletions lldb/test/API/commands/dwim-print/main.c

This file was deleted.

22 changes: 22 additions & 0 deletions lldb/test/API/commands/dwim-print/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
extern "C" int puts(const char *s);

struct Structure {
int number = 30;
void f() { puts("break inside"); }
};

struct Wrapper {
Structure s;
};

struct Opaque;

int main(int argc, char **argv) {
Structure s;
Wrapper w;
Wrapper *wp = &w;
Opaque *opaque = (Opaque *)(void *)&s;
puts("break here");
s.f();
return 0;
}

0 comments on commit cce676f

Please sign in to comment.