[Lldb-commits] [lldb] [lldb] Add repeat command for `frame variable` (PR #194195)
Dave Lee via lldb-commits
lldb-commits at lists.llvm.org
Mon Apr 27 09:46:21 PDT 2026
https://github.com/kastiglione updated https://github.com/llvm/llvm-project/pull/194195
>From 584945c03da75c9e31e4850ff2dcef2abb7e96ee Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Sat, 25 Apr 2026 16:39:11 -0700
Subject: [PATCH 1/4] [lldb] Add repeat command for `frame variable`
---
lldb/source/Commands/CommandObjectFrame.cpp | 28 +++++++++++++++++
.../API/commands/frame/var/repeat/Makefile | 3 ++
.../frame/var/repeat/TestFrameVarRepeat.py | 31 +++++++++++++++++++
.../API/commands/frame/var/repeat/main.cpp | 20 ++++++++++++
4 files changed, 82 insertions(+)
create mode 100644 lldb/test/API/commands/frame/var/repeat/Makefile
create mode 100644 lldb/test/API/commands/frame/var/repeat/TestFrameVarRepeat.py
create mode 100644 lldb/test/API/commands/frame/var/repeat/main.cpp
diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp
index 2a726f8e0fd8f..88e8e9e32cff1 100644
--- a/lldb/source/Commands/CommandObjectFrame.cpp
+++ b/lldb/source/Commands/CommandObjectFrame.cpp
@@ -32,6 +32,7 @@
#include "lldb/Utility/ValueType.h"
#include "lldb/ValueObject/ValueObject.h"
#include "lldb/lldb-enumerations.h"
+#include "llvm/ADT/StringRef.h"
#include <memory>
#include <optional>
@@ -435,6 +436,33 @@ may even involve JITing and running code in the target program.)");
Options *GetOptions() override { return &m_option_group; }
+ // `frame variable` repeats by incrementing the printing depth. When the depth
+ // is too shallow, hitting enter a few times will quickly expand the data.
+ std::optional<std::string> GetRepeatCommand(Args ¤t_command_args,
+ uint32_t index) override {
+ uint32_t new_depth = m_varobj_options.max_depth + 1;
+ std::string cmd;
+ llvm::raw_string_ostream os(cmd);
+ bool skip_next = false;
+ for (const auto &entry : current_command_args) {
+ if (skip_next) {
+ skip_next = false;
+ continue;
+ }
+
+ llvm::StringRef arg = entry.ref();
+ if (arg == "--depth" || arg == "-D") {
+ skip_next = true;
+ os << " " << arg << " " << new_depth;
+ } else if (arg.starts_with("-D")) {
+ os << "-D" << new_depth;
+ } else {
+ os << " " << arg;
+ }
+ }
+ return cmd;
+ }
+
protected:
llvm::StringRef GetScopeString(VariableSP var_sp) {
if (!var_sp)
diff --git a/lldb/test/API/commands/frame/var/repeat/Makefile b/lldb/test/API/commands/frame/var/repeat/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var/repeat/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/frame/var/repeat/TestFrameVarRepeat.py b/lldb/test/API/commands/frame/var/repeat/TestFrameVarRepeat.py
new file mode 100644
index 0000000000000..89d0f6feec529
--- /dev/null
+++ b/lldb/test/API/commands/frame/var/repeat/TestFrameVarRepeat.py
@@ -0,0 +1,31 @@
+import lldb
+from lldbsuite.test.lldbtest import TestBase
+from lldbsuite.test import lldbutil
+
+
+class TestCase(TestBase):
+ def test(self):
+ """Test that repeating 'frame variable' increments --depth."""
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "break here", lldb.SBFileSpec("main.cpp")
+ )
+
+ # Start with --depth 0 showing a, but not b.
+ self.expect(
+ "frame variable --depth 0 a",
+ inHistory=True,
+ patterns=[r"\(A\) a = {", r"(?!.*b = {)"],
+ )
+
+ # First repeat: --depth 1 showing b, but not c.
+ self.expect("", patterns=["b = {", "(?!.*c = {)"])
+
+ # Second repeat: --depth 2, showing c, but not d.
+ self.expect("", patterns=["c = {", "(?!.*d = {)"])
+
+ # Third repeat: --depth 3, showing d, but not value.
+ self.expect("", patterns=["d = {", "(?!.*value = 42)"])
+
+ # Fourth repeat: --depth 4, showing value, the deepest value.
+ self.expect("", substrs=["value = 42"])
diff --git a/lldb/test/API/commands/frame/var/repeat/main.cpp b/lldb/test/API/commands/frame/var/repeat/main.cpp
new file mode 100644
index 0000000000000..78225fad724eb
--- /dev/null
+++ b/lldb/test/API/commands/frame/var/repeat/main.cpp
@@ -0,0 +1,20 @@
+struct D {
+ int value = 42;
+};
+
+struct C {
+ D d;
+};
+
+struct B {
+ C c;
+};
+
+struct A {
+ B b;
+};
+
+int main() {
+ A a;
+ return 0; // break here
+}
>From 6ff75eda18343f2b45823d9e50ea6331914e9738 Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Mon, 27 Apr 2026 09:41:37 -0700
Subject: [PATCH 2/4] Handle incrementing of default depth
---
lldb/source/Commands/CommandObjectFrame.cpp | 10 ++++-
.../frame/var/repeat/TestFrameVarRepeat.py | 42 +++++++++++++------
.../API/commands/frame/var/repeat/main.cpp | 10 ++++-
3 files changed, 48 insertions(+), 14 deletions(-)
diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp
index 88e8e9e32cff1..c5e1f7abf5bf1 100644
--- a/lldb/source/Commands/CommandObjectFrame.cpp
+++ b/lldb/source/Commands/CommandObjectFrame.cpp
@@ -440,9 +440,11 @@ may even involve JITing and running code in the target program.)");
// is too shallow, hitting enter a few times will quickly expand the data.
std::optional<std::string> GetRepeatCommand(Args ¤t_command_args,
uint32_t index) override {
- uint32_t new_depth = m_varobj_options.max_depth + 1;
std::string cmd;
llvm::raw_string_ostream os(cmd);
+
+ uint32_t new_depth = m_varobj_options.max_depth + 1;
+ bool has_depth_option = false;
bool skip_next = false;
for (const auto &entry : current_command_args) {
if (skip_next) {
@@ -454,12 +456,18 @@ may even involve JITing and running code in the target program.)");
if (arg == "--depth" || arg == "-D") {
skip_next = true;
os << " " << arg << " " << new_depth;
+ has_depth_option = true;
} else if (arg.starts_with("-D")) {
os << "-D" << new_depth;
+ has_depth_option = true;
} else {
os << " " << arg;
}
}
+
+ if (!has_depth_option)
+ os << " --depth " << new_depth;
+
return cmd;
}
diff --git a/lldb/test/API/commands/frame/var/repeat/TestFrameVarRepeat.py b/lldb/test/API/commands/frame/var/repeat/TestFrameVarRepeat.py
index 89d0f6feec529..bfbd80bfb5c27 100644
--- a/lldb/test/API/commands/frame/var/repeat/TestFrameVarRepeat.py
+++ b/lldb/test/API/commands/frame/var/repeat/TestFrameVarRepeat.py
@@ -4,28 +4,46 @@
class TestCase(TestBase):
- def test(self):
+
+ def test_explicit_depth(self):
"""Test that repeating 'frame variable' increments --depth."""
self.build()
lldbutil.run_to_source_breakpoint(
self, "break here", lldb.SBFileSpec("main.cpp")
)
- # Start with --depth 0 showing a, but not b.
+ # Start with --depth 2 showing a, b, and c, but but not d.
self.expect(
- "frame variable --depth 0 a",
+ "frame variable --depth 2 a",
inHistory=True,
- patterns=[r"\(A\) a = {", r"(?!.*b = {)"],
+ patterns=[r"\(A\) a = {", "b = {", "c = {", r"(?!.*d = {)"],
)
- # First repeat: --depth 1 showing b, but not c.
- self.expect("", patterns=["b = {", "(?!.*c = {)"])
+ # First repeat: --depth 4, showing d, but not e.
+ self.expect("", patterns=["d = {", "(?!.*e = {)"])
+
+ # Second repeat: --depth 5, showing e, but not f.
+ self.expect("", patterns=["e = {", "(?!.*f = {)"])
+
+ # Third repeat: --depth 6, showing d, but not f.
+ self.expect("", patterns=["e = {", "(?!.*leaf = 42)"])
- # Second repeat: --depth 2, showing c, but not d.
- self.expect("", patterns=["c = {", "(?!.*d = {)"])
+ # Fourth repeat: --depth 7, showing leaf, the deepest child.
+ self.expect("", substrs=["leaf = 42"])
- # Third repeat: --depth 3, showing d, but not value.
- self.expect("", patterns=["d = {", "(?!.*value = 42)"])
+ def test_default_depth(self):
+ """Test that repeating 'frame variable' adds a --depth option."""
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "break here", lldb.SBFileSpec("main.cpp")
+ )
+
+ # Default depth shows f but not leaf.
+ self.expect(
+ "frame variable a",
+ inHistory=True,
+ patterns=[r"f = \{...\}", r"(?!.*leaf = 42)"],
+ )
- # Fourth repeat: --depth 4, showing value, the deepest value.
- self.expect("", substrs=["value = 42"])
+ # Repeat
+ self.expect("", substrs=["leaf = 42"])
diff --git a/lldb/test/API/commands/frame/var/repeat/main.cpp b/lldb/test/API/commands/frame/var/repeat/main.cpp
index 78225fad724eb..7f95251776b62 100644
--- a/lldb/test/API/commands/frame/var/repeat/main.cpp
+++ b/lldb/test/API/commands/frame/var/repeat/main.cpp
@@ -1,5 +1,12 @@
+struct F {
+ int leaf = 42;
+};
+
+struct E {
+ F f;
+};
struct D {
- int value = 42;
+ E e;
};
struct C {
@@ -16,5 +23,6 @@ struct A {
int main() {
A a;
+ (void)a;
return 0; // break here
}
>From 7cfb14e953b78c099db0d6735d9d59ddf48ebea8 Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Mon, 27 Apr 2026 09:44:41 -0700
Subject: [PATCH 3/4] Fixes to test
---
.../frame/var/repeat/TestFrameVarRepeat.py | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/lldb/test/API/commands/frame/var/repeat/TestFrameVarRepeat.py b/lldb/test/API/commands/frame/var/repeat/TestFrameVarRepeat.py
index bfbd80bfb5c27..de8412085b2ff 100644
--- a/lldb/test/API/commands/frame/var/repeat/TestFrameVarRepeat.py
+++ b/lldb/test/API/commands/frame/var/repeat/TestFrameVarRepeat.py
@@ -12,23 +12,23 @@ def test_explicit_depth(self):
self, "break here", lldb.SBFileSpec("main.cpp")
)
- # Start with --depth 2 showing a, b, and c, but but not d.
+ # Start with --depth 2, shows a, b, and c, but but not d.
self.expect(
"frame variable --depth 2 a",
inHistory=True,
patterns=[r"\(A\) a = {", "b = {", "c = {", r"(?!.*d = {)"],
)
- # First repeat: --depth 4, showing d, but not e.
+ # First repeat: shows d, but not e.
self.expect("", patterns=["d = {", "(?!.*e = {)"])
- # Second repeat: --depth 5, showing e, but not f.
+ # Second repeat: shows e, but not f.
self.expect("", patterns=["e = {", "(?!.*f = {)"])
- # Third repeat: --depth 6, showing d, but not f.
- self.expect("", patterns=["e = {", "(?!.*leaf = 42)"])
+ # Third repeat: shows f, but not leaf.
+ self.expect("", patterns=["f = {", "(?!.*leaf = 42)"])
- # Fourth repeat: --depth 7, showing leaf, the deepest child.
+ # Fourth repeat: shows leaf, the deepest child.
self.expect("", substrs=["leaf = 42"])
def test_default_depth(self):
@@ -45,5 +45,5 @@ def test_default_depth(self):
patterns=[r"f = \{...\}", r"(?!.*leaf = 42)"],
)
- # Repeat
+ # Repeat to show leaf.
self.expect("", substrs=["leaf = 42"])
>From cea3e6d8e5351fdc246a34fdf6cf863b62e262a7 Mon Sep 17 00:00:00 2001
From: Dave Lee <davelee.com at gmail.com>
Date: Mon, 27 Apr 2026 09:46:02 -0700
Subject: [PATCH 4/4] formatting
---
lldb/test/API/commands/frame/var/repeat/TestFrameVarRepeat.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/lldb/test/API/commands/frame/var/repeat/TestFrameVarRepeat.py b/lldb/test/API/commands/frame/var/repeat/TestFrameVarRepeat.py
index de8412085b2ff..755dd6e28e0fa 100644
--- a/lldb/test/API/commands/frame/var/repeat/TestFrameVarRepeat.py
+++ b/lldb/test/API/commands/frame/var/repeat/TestFrameVarRepeat.py
@@ -4,7 +4,6 @@
class TestCase(TestBase):
-
def test_explicit_depth(self):
"""Test that repeating 'frame variable' increments --depth."""
self.build()
More information about the lldb-commits
mailing list