[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