[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 &current_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 &current_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