[Lldb-commits] [lldb] Add a _regexp-break-add and some more tests for the b alias. (PR #171236)

via lldb-commits lldb-commits at lists.llvm.org
Mon Dec 8 16:46:03 PST 2025


https://github.com/jimingham updated https://github.com/llvm/llvm-project/pull/171236

>From b9ebcb580ea13ee2f1a6491e03249062889eb12d Mon Sep 17 00:00:00 2001
From: Jim Ingham <jingham at apple.com>
Date: Mon, 8 Dec 2025 16:26:51 -0800
Subject: [PATCH 1/2] Add a _regexp-break-add and some more tests for the b
 alias. This commit leaves "b" aliased to the old _regexp-break for now.

---
 .../source/Interpreter/CommandInterpreter.cpp | 90 ++++++++++++++++++-
 .../TestRegexpBreakCommand.py                 | 33 +++++--
 .../API/terminal/TestEditlineCompletions.py   |  2 +-
 3 files changed, 117 insertions(+), 8 deletions(-)

diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index ffcc9ceeb2a93..28b868da26740 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -316,6 +316,11 @@ void CommandInterpreter::Initialize() {
     AddAlias("continue", cmd_obj_sp);
   }
 
+  // At this point, I'm leaving "b" command aliased to "_regexp-break".  There's
+  // a catch-all regexp in the command that takes any unrecognized input and
+  // runs it as `break set <input>` and switching the command to break add 
+  // would change that behavior.  People who want to use the break add for the
+  // "b" alias can do so in their .lldbinit.
   cmd_obj_sp = GetCommandSPExact("_regexp-break");
   if (cmd_obj_sp)
     AddAlias("b", cmd_obj_sp)->SetSyntax(cmd_obj_sp->GetSyntax());
@@ -610,7 +615,7 @@ void CommandInterpreter::LoadCommandDictionary() {
       {"^[\"']?(.*[^[:space:]\"'])[\"']?[[:space:]]*$",
        "breakpoint set --name '%1'"}};
   // clang-format on
-
+  
   size_t num_regexes = std::size(break_regexes);
 
   std::unique_ptr<CommandObjectRegexCommand> break_regex_cmd_up(
@@ -668,6 +673,89 @@ void CommandInterpreter::LoadCommandDictionary() {
     }
   }
 
+  // clang-format off
+  // FIXME: It would be simpler to just use the linespec's directly here, but
+  // the `b` alias allows "foo.c   :   12   :   45" but the linespec parser
+  // is more rigorous, and doesn't strip spaces, so the two are not equivalent.
+  const char *break_add_regexes[][2] = {
+      {"^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*$",
+       "breakpoint add file --file '%1' --line %2 --column %3"},
+      {"^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*$",
+       "breakpoint add file --file '%1' --line %2"},
+      {"^/([^/]+)/$", "breakpoint add pattern -- %1"},
+      {"^([[:digit:]]+)[[:space:]]*$",
+      "breakpoint add file --line %1"},
+      {"^\\*?(0x[[:xdigit:]]+)[[:space:]]*$",
+      "breakpoint add address %1"},
+      {"^[\"']?([-+]?\\[.*\\])[\"']?[[:space:]]*$",
+       "breakpoint add name '%1'"},
+      {"^(-.*)$",
+      "breakpoint add name '%1'"},
+      {"^(.*[^[:space:]])`(.*[^[:space:]])[[:space:]]*$",
+       "breakpoint add name '%2' --shlib '%1'"},
+      {"^\\&(.*[^[:space:]])[[:space:]]*$",
+       "breakpoint add name '%1' --skip-prologue=0"},
+      {"^[\"']?(.*[^[:space:]\"'])[\"']?[[:space:]]*$",
+       "breakpoint add name '%1'"}};
+  // clang-format on
+
+  size_t num_add_regexes = std::size(break_add_regexes);
+
+  std::unique_ptr<CommandObjectRegexCommand> break_add_regex_cmd_up(
+      new CommandObjectRegexCommand(
+          *this, "_regexp-break-add",
+          "Set a breakpoint using one of several shorthand formats, or list "
+          "the existing breakpoints if no arguments are provided.",
+          "\n"
+          "_regexp-break-add <filename>:<linenum>:<colnum>\n"
+          "              main.c:12:21          // Break at line 12 and column "
+          "21 of main.c\n\n"
+          "_regexp-break-add <filename>:<linenum>\n"
+          "              main.c:12             // Break at line 12 of "
+          "main.c\n\n"
+          "_regexp-break-add <linenum>\n"
+          "              12                    // Break at line 12 of current "
+          "file\n\n"
+          "_regexp-break-add 0x<address>\n"
+          "              0x1234000             // Break at address "
+          "0x1234000\n\n"
+          "_regexp-break-add <name>\n"
+          "              main                  // Break in 'main' after the "
+          "prologue\n\n"
+          "_regexp-break-add &<name>\n"
+          "              &main                 // Break at first instruction "
+          "in 'main'\n\n"
+          "_regexp-break-add <module>`<name>\n"
+          "              libc.so`malloc        // Break in 'malloc' from "
+          "'libc.so'\n\n"
+          "_regexp-break-add /<source-regex>/\n"
+          "              /break here/          // Break on source lines in "
+          "current file\n"
+          "                                    // containing text 'break "
+          "here'.\n"
+          "_regexp-break-add\n"
+          "                                    // List the existing "
+          "breakpoints\n",
+          lldb::eSymbolCompletion | lldb::eSourceFileCompletion, false));
+
+  if (break_add_regex_cmd_up) {
+    bool success = true;
+    for (size_t i = 0; i < num_add_regexes; i++) {
+      success = break_add_regex_cmd_up->AddRegexCommand(break_add_regexes[i][0],
+                                                    break_add_regexes[i][1]);
+      if (!success)
+        break;
+    }
+    success =
+        break_add_regex_cmd_up->AddRegexCommand("^$", "breakpoint list --full");
+
+    if (success) {
+      CommandObjectSP break_add_regex_cmd_sp(break_add_regex_cmd_up.release());
+      m_command_dict[std::string(break_add_regex_cmd_sp->GetCommandName())] =
+          break_add_regex_cmd_sp;
+    }
+  }
+
   std::unique_ptr<CommandObjectRegexCommand> tbreak_regex_cmd_up(
       new CommandObjectRegexCommand(
           *this, "_regexp-tbreak",
diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestRegexpBreakCommand.py b/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestRegexpBreakCommand.py
index 235a41d1adef3..adece955a492f 100644
--- a/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestRegexpBreakCommand.py
+++ b/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestRegexpBreakCommand.py
@@ -10,11 +10,16 @@
 
 
 class RegexpBreakCommandTestCase(TestBase):
-    def test(self):
+    def test_set_version(self):
         """Test _regexp-break command."""
         self.build()
-        self.regexp_break_command()
+        self.regexp_break_command("_regexp-break")
 
+    def test_add_version(self):
+        """Test _regexp-break-add command."""
+        self.build()
+        self.regexp_break_command("_regexp-break-add")
+        
     def setUp(self):
         # Call super's setUp().
         TestBase.setUp(self)
@@ -22,12 +27,12 @@ def setUp(self):
         self.source = "main.c"
         self.line = line_number(self.source, "// Set break point at this line.")
 
-    def regexp_break_command(self):
+    def regexp_break_command(self, cmd_name):
         """Test the super consie "b" command, which is analias for _regexp-break."""
         exe = self.getBuildArtifact("a.out")
         self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
 
-        break_results = lldbutil.run_break_set_command(self, "b %d" % self.line)
+        break_results = lldbutil.run_break_set_command(self, f"{cmd_name} {self.line}")
         lldbutil.check_breakpoint_result(
             self,
             break_results,
@@ -37,7 +42,7 @@ def regexp_break_command(self):
         )
 
         break_results = lldbutil.run_break_set_command(
-            self, "b %s:%d" % (self.source, self.line)
+            self, f"{cmd_name} {self.source}:{self.line}"
         )
         lldbutil.check_breakpoint_result(
             self,
@@ -50,7 +55,7 @@ def regexp_break_command(self):
         # Check breakpoint with full file path.
         full_path = os.path.join(self.getSourceDir(), self.source)
         break_results = lldbutil.run_break_set_command(
-            self, "b %s:%d" % (full_path, self.line)
+            self, f"{cmd_name} {full_path}:{self.line}"
         )
         lldbutil.check_breakpoint_result(
             self,
@@ -60,6 +65,22 @@ def regexp_break_command(self):
             num_locations=1,
         )
 
+        # Check breakpoint with symbol name.  I'm also passing in
+        # the module so I can check the number of locations.
+        exe_spec = lldb.SBFileSpec(exe)
+        exe_filename = exe_spec.basename
+        cmd = f"{cmd_name} {exe_filename}`main"
+        print(f"About to run: '{cmd}'")
+        break_results = lldbutil.run_break_set_command(
+            self, cmd 
+        )
+        lldbutil.check_breakpoint_result(
+            self,
+            break_results,
+            symbol_name="main",
+            num_locations=1
+        )
+
         self.runCmd("run", RUN_SUCCEEDED)
 
         # The stop reason of the thread should be breakpoint.
diff --git a/lldb/test/API/terminal/TestEditlineCompletions.py b/lldb/test/API/terminal/TestEditlineCompletions.py
index b4ea0f39ec10c..ac1d3f90e2970 100644
--- a/lldb/test/API/terminal/TestEditlineCompletions.py
+++ b/lldb/test/API/terminal/TestEditlineCompletions.py
@@ -72,11 +72,11 @@ def test_completion_pagination(self):
         self.child.expect("Available completions:")
         self.child.expect("        _regexp-attach")
         self.child.expect("        _regexp-break")
+        self.child.expect("        _regexp-break-add")
         self.child.expect("        _regexp-bt")
         self.child.expect("        _regexp-display")
         self.child.expect("        _regexp-down")
         self.child.expect("        _regexp-env")
-        self.child.expect("        _regexp-jump")
         self.child.expect("More")
 
     @skipIfAsan

>From 65c4a750f57d1f759dd7f1897c3f82d1ab4a1062 Mon Sep 17 00:00:00 2001
From: Jim Ingham <jingham at apple.com>
Date: Mon, 8 Dec 2025 16:45:45 -0800
Subject: [PATCH 2/2] formatting

---
 lldb/source/Interpreter/CommandInterpreter.cpp        |  8 ++++----
 .../breakpoint_command/TestRegexpBreakCommand.py      | 11 +++--------
 2 files changed, 7 insertions(+), 12 deletions(-)

diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index 28b868da26740..188b5f407cb40 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -318,7 +318,7 @@ void CommandInterpreter::Initialize() {
 
   // At this point, I'm leaving "b" command aliased to "_regexp-break".  There's
   // a catch-all regexp in the command that takes any unrecognized input and
-  // runs it as `break set <input>` and switching the command to break add 
+  // runs it as `break set <input>` and switching the command to break add
   // would change that behavior.  People who want to use the break add for the
   // "b" alias can do so in their .lldbinit.
   cmd_obj_sp = GetCommandSPExact("_regexp-break");
@@ -615,7 +615,7 @@ void CommandInterpreter::LoadCommandDictionary() {
       {"^[\"']?(.*[^[:space:]\"'])[\"']?[[:space:]]*$",
        "breakpoint set --name '%1'"}};
   // clang-format on
-  
+
   size_t num_regexes = std::size(break_regexes);
 
   std::unique_ptr<CommandObjectRegexCommand> break_regex_cmd_up(
@@ -741,8 +741,8 @@ void CommandInterpreter::LoadCommandDictionary() {
   if (break_add_regex_cmd_up) {
     bool success = true;
     for (size_t i = 0; i < num_add_regexes; i++) {
-      success = break_add_regex_cmd_up->AddRegexCommand(break_add_regexes[i][0],
-                                                    break_add_regexes[i][1]);
+      success = break_add_regex_cmd_up->AddRegexCommand(
+          break_add_regexes[i][0], break_add_regexes[i][1]);
       if (!success)
         break;
     }
diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestRegexpBreakCommand.py b/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestRegexpBreakCommand.py
index adece955a492f..930d497032171 100644
--- a/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestRegexpBreakCommand.py
+++ b/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestRegexpBreakCommand.py
@@ -19,7 +19,7 @@ def test_add_version(self):
         """Test _regexp-break-add command."""
         self.build()
         self.regexp_break_command("_regexp-break-add")
-        
+
     def setUp(self):
         # Call super's setUp().
         TestBase.setUp(self)
@@ -71,14 +71,9 @@ def regexp_break_command(self, cmd_name):
         exe_filename = exe_spec.basename
         cmd = f"{cmd_name} {exe_filename}`main"
         print(f"About to run: '{cmd}'")
-        break_results = lldbutil.run_break_set_command(
-            self, cmd 
-        )
+        break_results = lldbutil.run_break_set_command(self, cmd)
         lldbutil.check_breakpoint_result(
-            self,
-            break_results,
-            symbol_name="main",
-            num_locations=1
+            self, break_results, symbol_name="main", num_locations=1
         )
 
         self.runCmd("run", RUN_SUCCEEDED)



More information about the lldb-commits mailing list