[Lldb-commits] [lldb] Add a `breakpoint add` command to fix the option-madness that is `breakpoint set` (PR #156067)

via lldb-commits lldb-commits at lists.llvm.org
Fri Aug 29 10:26:51 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: None (jimingham)

<details>
<summary>Changes</summary>

Someone came up with a clever idea for a new breakpoint type, but we couldn't figure out how to add it in an ergonomic way because `breakpoint set` has used up all the short-option characters.  And even if they did find a way to add it, the help for `break set` is so confusing - because of the way it is implemented - that very few people would detect the addition.  The basic problem is that `break set` is distinguishes amongst the fundamental breakpoint types it offers by which "required option" you provide.  If you pass a `-a` you are setting an address breakpoint, if `-n`, `-F`, etc. a symbol name based breakpoint.  And so forth.  That is however pretty hard to discern from the option grouping printing from `help break set`.  `break set` also suffers from the problem that it uses common options in different ways depending on which "required" option is present, which makes documenting the various behaviors difficult.  And as we run out of single letters it makes extending it difficult to impossible.

This PR fixes that situation by adding a new command for adding breakpoints - `break add`.  The new command specifies the "breakpoint types" as subcommands of `break add` rather than distinguishing them by their being one of the "required" options the way `break set` does.  That both makes it much clearer what the breakpoint types actually are, and means that the option set can be dedicated to that particular breakpoint type, and so the help for each is less cluttered, and can be documented properly for each usage.

Instead of trying to parse the meaning of:

```
(lldb) help break set
Sets a breakpoint or set of breakpoints in the executable.

Syntax: breakpoint set <cmd-options>

Command Options Usage:
  breakpoint set [-DHd] -l <linenum> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-R <address>] [-N <breakpoint-name>] [-u <column>] [-f <filename>] [-m <boolean>] [-s <shlib-name>] [-K <boolean>]
  breakpoint set [-DHd] -a <address-expression> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-N <breakpoint-name>] [-s <shlib-name>]
  breakpoint set [-DHd] -n <function-name> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-R <address>] [-N <breakpoint-name>] [-f <filename>] [-L <source-language>] [-s <shlib-name>] [-K <boolean>]
  breakpoint set [-DHd] -F <fullname> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-R <address>] [-N <breakpoint-name>] [-f <filename>] [-L <source-language>] [-s <shlib-name>] [-K <boolean>]
  breakpoint set [-DHd] -S <selector> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-R <address>] [-N <breakpoint-name>] [-f <filename>] [-L <source-language>] [-s <shlib-name>] [-K <boolean>]
  breakpoint set [-DHd] -M <method> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-R <address>] [-N <breakpoint-name>] [-f <filename>] [-L <source-language>] [-s <shlib-name>] [-K <boolean>]
  breakpoint set [-DHd] -r <regular-expression> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-R <address>] [-N <breakpoint-name>] [-f <filename>] [-L <source-language>] [-s <shlib-name>] [-K <boolean>]
  breakpoint set [-DHd] -b <function-name> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-R <address>] [-N <breakpoint-name>] [-f <filename>] [-L <source-language>] [-s <shlib-name>] [-K <boolean>]
  breakpoint set [-ADHd] -p <regular-expression> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-N <breakpoint-name>] [-f <filename>] [-m <boolean>] [-s <shlib-name>] [-X <function-name>]
  breakpoint set [-DHd] -E <source-language> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-N <breakpoint-name>] [-h <boolean>] [-w <boolean>]
  breakpoint set [-DHd] -P <python-class> [-k <none>] [-v <none>] [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-N <breakpoint-name>] [-f <filename>] [-s <shlib-name>]
  breakpoint set [-DHd] -y <linespec> [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-R <address>] [-N <breakpoint-name>] [-m <boolean>] [-s <shlib-name>] [-K <boolean>]

```

We instead offer:

```
(lldb) help break add
Commands to add breakpoints of various types

Syntax: breakpoint add

Access the breakpoint search kernels built into lldb.  Along with specifying the
search kernel, each breakpoint add operation can specify a common set of 
"reaction" options for each breakpoint.  The reaction options can also be
modified after breakpoint creation using the "breakpoint modify" command.       
        

The following subcommands are supported:

      address   -- Add breakpoints by raw address
      exception -- Add breakpoints on language exceptions.  If no language is specified, break on exceptions for all supported languages
      file      -- Add breakpoints on lines in specified source files
      name      -- Add breakpoints matching function or symbol names
      pattern   -- Add breakpoints matching patterns in the source text  Expects 'raw' input (see 'help raw-input'.)
      scripted  -- Add breakpoints using a scripted breakpoint resolver.

For more help on any particular subcommand, type 'help <command> <subcommand>'.

```

The individual subcommand helps are also easier to read.  They are still a little too verbose because they all repeat the options for the `reactions`.  A general fix for our help system would be when a command incorporates an OptionGroup whole into the command options, help would show the option group name - which you could separately look up.  But even without that:

```
(lldb) help b a a
Add breakpoints by raw address

Syntax: breakpoint add address <cmd-options> <address> [<address> [...]]

Command Options Usage:
  breakpoint add address [-DHde] [-G <boolean>] [-C <command>] [-c <expr>] [-Y <source-language>] [-i <count>] [-o <boolean>] [-q <queue-name>] [-t <thread-id>] [-x <thread-index>] [-T <thread-name>] [-N <breakpoint-name>] [-s <shlib-name>] <address> [<address> [...]]

       -C <command> ( --command <command> )
            A command to run when the breakpoint is hit, can be provided more than once, the commands will be run in left-to-right order.

       -D ( --dummy-breakpoints )
            Act on Dummy breakpoints - i.e. breakpoints set before a file is provided, which prime new targets.

       -G <boolean> ( --auto-continue <boolean> )
            The breakpoint will auto-continue after running its commands.

       -H ( --hardware )
            Require the breakpoint to use hardware breakpoints.

       -N <breakpoint-name> ( --breakpoint-name <breakpoint-name> )
            Adds this name to the list of names for this breakpoint.  Can be specified more than once.

       -T <thread-name> ( --thread-name <thread-name> )
            The breakpoint stops only for the thread whose thread name matches this argument.

       -Y <source-language> ( --condition-language <source-language> )
            Specifies the Language to use when executing the breakpoint's condition expression.

       -c <expr> ( --condition <expr> )
            The breakpoint stops only if this condition expression evaluates to true.

       -d ( --disable )
            Disable the breakpoint.

       -e ( --enable )
            Enable the breakpoint.

       -i <count> ( --ignore-count <count> )
            Set the number of times this breakpoint is skipped before stopping.

       -o <boolean> ( --one-shot <boolean> )
            The breakpoint is deleted the first time it stop causes a stop.

       -q <queue-name> ( --queue-name <queue-name> )
            The breakpoint stops only for threads in the queue whose name is given by this argument.

       -s <shlib-name> ( --shlib <shlib-name> )
            Set the breakpoint at an address relative to sections in this shared library.

       -t <thread-id> ( --thread-id <thread-id> )
            The breakpoint stops only for the thread whose TID matches this argument.  The token 'current' resolves to the current thread's ID.

       -x <thread-index> ( --thread-index <thread-index> )
            The breakpoint stops only for the thread whose index matches this argument.
     
     This command takes options and free-form arguments.  If your arguments resemble option specifiers (i.e., they start with a - or --), you must use ' --
     ' between the end of the command options and the beginning of the arguments.

'b' is an abbreviation for 'breakpoint'

```

is pretty readable.

---

Patch is 73.51 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/156067.diff


8 Files Affected:

- (modified) lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h (+17) 
- (modified) lldb/include/lldb/Interpreter/OptionValueFileColonLine.h (+4) 
- (modified) lldb/include/lldb/lldb-enumerations.h (+18) 
- (modified) lldb/packages/Python/lldbsuite/test/lldbutil.py (+48-10) 
- (modified) lldb/source/Commands/CommandObjectBreakpoint.cpp (+1354-93) 
- (modified) lldb/source/Commands/Options.td (+126) 
- (modified) lldb/source/Interpreter/Options.cpp (+3-1) 
- (modified) lldb/test/Shell/Commands/command-wrong-subcommand-error-msg.test (+1-1) 


``````````diff
diff --git a/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h b/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h
index 4face717531b1..8ae074c5314b3 100644
--- a/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h
+++ b/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h
@@ -154,6 +154,21 @@ static constexpr OptionEnumValueElement g_running_mode[] = {
      "Run only this thread while stepping"},
 };
 
+static constexpr OptionEnumValueElement g_exception_stage[] = {
+    {lldb::eExceptionStageThrow, "throw", "Stop when the exception is thrown."},
+    {lldb::eExceptionStageReThrow, "re-throw", "Stop when the exception is re-thrown."},
+    {lldb::eExceptionStageCatch, "catch", "Stop when the exception is caught."},
+};
+
+static constexpr OptionEnumValueElement g_name_match_style[] = {
+    {lldb::eNameMatchStyleAuto, "auto", "Match against the leaf nodes of the identifier, or against methods or selectors."},
+    {lldb::eNameMatchStyleFull, "full", "Match the full identifier name."},
+    {lldb::eNameMatchStyleBase, "base", "Match against the leaf node of the identifier."},
+    {lldb::eNameMatchStyleMethod, "method", "Match only against method names."},
+    {lldb::eNameMatchStyleSelector, "selector", "Match only against selector names."},
+    {lldb::eNameMatchStyleRegex, "regex", "Match the identifier using a regular expression."},
+};
+
 static constexpr OptionEnumValueElement g_completion_type[] = {
     {lldb::eNoCompletion, "none", "No completion."},
     {lldb::eSourceFileCompletion, "source-file", "Completes to a source file."},
@@ -316,6 +331,8 @@ static constexpr CommandObject::ArgumentTableEntry g_argument_table[] = {
     { lldb::eArgTypeCPUFeatures, "cpu-features", lldb::CompletionType::eNoCompletion, {}, { nullptr, false }, "The CPU feature string." },
     { lldb::eArgTypeManagedPlugin, "managed-plugin", lldb::CompletionType::eNoCompletion, {}, { nullptr, false }, "Plugins managed by the PluginManager" },
     { lldb::eArgTypeProtocol, "protocol", lldb::CompletionType::eNoCompletion, {}, { nullptr, false }, "The name of the protocol." },
+    { lldb::eArgTypeExceptionStage, "exception-stage", lldb::CompletionType::eNoCompletion, g_exception_stage, { nullptr, false }, "Specify at which stage of the exception raise to stop." },
+    { lldb::eArgTypeNameMatchStyle, "match-style", lldb::CompletionType::eNoCompletion, g_name_match_style, { nullptr, false }, "Specify the kind of match to use when looking up names." },
     // clang-format on
 };
 
diff --git a/lldb/include/lldb/Interpreter/OptionValueFileColonLine.h b/lldb/include/lldb/Interpreter/OptionValueFileColonLine.h
index 70f035da649e7..edfaab8b27e98 100644
--- a/lldb/include/lldb/Interpreter/OptionValueFileColonLine.h
+++ b/lldb/include/lldb/Interpreter/OptionValueFileColonLine.h
@@ -40,6 +40,10 @@ class OptionValueFileColonLine :
     m_line_number = LLDB_INVALID_LINE_NUMBER;
     m_column_number = LLDB_INVALID_COLUMN_NUMBER;
   }
+  
+  void SetFile(const FileSpec &file_spec) { m_file_spec = file_spec; }
+  void SetLine(uint32_t line) { m_line_number = line; }
+  void SetColumn(uint32_t column) { m_column_number = column; }
 
   void AutoComplete(CommandInterpreter &interpreter,
                     CompletionRequest &request) override;
diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h
index fec9fdef44df9..3e68101e36d05 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -668,6 +668,8 @@ enum CommandArgumentType {
   eArgTypeCPUFeatures,
   eArgTypeManagedPlugin,
   eArgTypeProtocol,
+  eArgTypeExceptionStage,
+  eArgTypeNameMatchStyle,
   eArgTypeLastArg // Always keep this entry as the last entry in this
                   // enumeration!!
 };
@@ -1399,6 +1401,22 @@ enum StopDisassemblyType {
   eStopDisassemblyTypeAlways
 };
 
+enum ExceptionStage {
+  eExceptionStageCreate = (1 << 0),
+  eExceptionStageThrow = (1 << 1),
+  eExceptionStageReThrow = (1 << 2),
+  eExceptionStageCatch = (1 << 3)
+};
+
+enum NameMatchStyle {
+  eNameMatchStyleAuto = eFunctionNameTypeAuto,
+  eNameMatchStyleFull = eFunctionNameTypeFull,
+  eNameMatchStyleBase = eFunctionNameTypeBase,
+  eNameMatchStyleMethod = eFunctionNameTypeMethod,
+  eNameMatchStyleSelector = eFunctionNameTypeSelector,
+  eNameMatchStyleRegex = eFunctionNameTypeSelector << 1
+};
+
 } // namespace lldb
 
 #endif // LLDB_LLDB_ENUMERATIONS_H
diff --git a/lldb/packages/Python/lldbsuite/test/lldbutil.py b/lldb/packages/Python/lldbsuite/test/lldbutil.py
index b8a78b71f5ec1..929d6915b4255 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbutil.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbutil.py
@@ -319,13 +319,24 @@ def sort_stopped_threads(
 # Utility functions for setting breakpoints
 # ==================================================
 
+g_use_break_add = True
+def set_use_break_add(use_it):
+    global g_use_break_add
+    g_use_break_add = use_it
+
+def get_use_break_add():
+    global g_use_break_add
+    return g_use_break_add
 
 def run_break_set_by_script(
     test, class_name, extra_options=None, num_expected_locations=1
 ):
     """Set a scripted breakpoint.  Check that it got the right number of locations."""
     test.assertTrue(class_name is not None, "Must pass in a class name.")
-    command = "breakpoint set -P " + class_name
+    if get_use_break_add():
+        command = f"breakpoint add scripted -P {class_name}"
+    else:
+        command = "breakpoint set -P " + class_name
     if extra_options is not None:
         command += " " + extra_options
 
@@ -333,7 +344,6 @@ def run_break_set_by_script(
     check_breakpoint_result(test, break_results, num_locations=num_expected_locations)
     return get_bpno_from_match(break_results)
 
-
 def run_break_set_by_file_and_line(
     test,
     file_name,
@@ -353,10 +363,16 @@ def run_break_set_by_file_and_line(
     If loc_exact is true, we check that there is one location, and that location must be at the input file and line number.
     """
 
-    if file_name is None:
-        command = "breakpoint set -l %d" % (line_number)
+    if get_use_break_add():
+        if file_name is None:
+            command = f"breakpoint add file {line_number} "
+        else:
+            command = f"breakpoint add file -f {file_name} -l {line_number} "
     else:
-        command = 'breakpoint set -f "%s" -l %d' % (file_name, line_number)
+        if file_name is None:
+            command = "breakpoint set -l %d" % (line_number)
+        else:
+            command = 'breakpoint set -f "%s" -l %d' % (file_name, line_number)
 
     if module_name:
         command += " --shlib '%s'" % (module_name)
@@ -395,7 +411,10 @@ def run_break_set_by_symbol(
 
     If sym_exact is true, then the output symbol must match the input exactly, otherwise we do a substring match.
     """
-    command = 'breakpoint set -n "%s"' % (symbol)
+    if get_use_break_add():
+        command = f"breakpoint add name"
+    else:
+        command = 'breakpoint set -n "%s"' % (symbol)
 
     if module_name:
         command += " --shlib '%s'" % (module_name)
@@ -403,6 +422,9 @@ def run_break_set_by_symbol(
     if extra_options:
         command += " " + extra_options
 
+    if get_use_break_add():
+        command += f" -- '{symbol}'" 
+
     break_results = run_break_set_command(test, command)
 
     if num_expected_locations == 1 and sym_exact:
@@ -426,7 +448,10 @@ def run_break_set_by_selector(
 ):
     """Set a breakpoint by selector.  Common options are the same as run_break_set_by_file_and_line."""
 
-    command = 'breakpoint set -S "%s"' % (selector)
+    if get_use_break_add():
+        command = f"breakpoint add name --match-style selector '{selector}'"
+    else:
+      command = 'breakpoint set -S "%s"' % (selector)
 
     if module_name:
         command += ' --shlib "%s"' % (module_name)
@@ -458,7 +483,10 @@ def run_break_set_by_regexp(
 ):
     """Set a breakpoint by regular expression match on symbol name.  Common options are the same as run_break_set_by_file_and_line."""
 
-    command = 'breakpoint set -r "%s"' % (regexp)
+    if get_use_break_add():
+        command = f"breakpoint add name --match-style regex '{regexp}'"
+    else:
+        command = 'breakpoint set -r "%s"' % (regexp)
     if extra_options:
         command += " " + extra_options
 
@@ -473,10 +501,16 @@ def run_break_set_by_source_regexp(
     test, regexp, extra_options=None, num_expected_locations=-1
 ):
     """Set a breakpoint by source regular expression.  Common options are the same as run_break_set_by_file_and_line."""
-    command = 'breakpoint set -p "%s"' % (regexp)
+    if get_use_break_add():
+        command = "breakpoint add pattern"
+    else:
+        command = 'breakpoint set -p "%s"' % (regexp)
     if extra_options:
         command += " " + extra_options
 
+    if get_use_break_add():
+        command += f" -- {regexp}"
+
     break_results = run_break_set_command(test, command)
 
     check_breakpoint_result(test, break_results, num_locations=num_expected_locations)
@@ -493,7 +527,11 @@ def run_break_set_by_file_colon_line(
     extra_options=None,
     num_expected_locations=-1,
 ):
-    command = 'breakpoint set -y "%s"' % (specifier)
+    if get_use_break_add():
+        command = f"breakpoint add file '{specifier}'"
+    else:
+        command = 'breakpoint set -y "%s"' % (specifier)
+
     if extra_options:
         command += " " + extra_options
 
diff --git a/lldb/source/Commands/CommandObjectBreakpoint.cpp b/lldb/source/Commands/CommandObjectBreakpoint.cpp
index 38ec375c03070..4306289e7b924 100644
--- a/lldb/source/Commands/CommandObjectBreakpoint.cpp
+++ b/lldb/source/Commands/CommandObjectBreakpoint.cpp
@@ -200,41 +200,1361 @@ class lldb_private::BreakpointOptionGroup : public OptionGroup {
   BreakpointOptions m_bp_opts;
 };
 
+// This is the Breakpoint Names option group - used to add Names to breakpoints
+// while making them.  Not to be confused with the "Breakpoint Name" option 
+// group which is the common options of various "breakpoint name" commands.
+#define LLDB_OPTIONS_breakpoint_names
+#include "CommandOptions.inc"
+
+class BreakpointNamesOptionGroup : public OptionGroup {
+public:
+  BreakpointNamesOptionGroup() = default;
+
+  ~BreakpointNamesOptionGroup() override = default;
+
+  llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+    return g_breakpoint_names_options;
+  }
+
+  Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+                        ExecutionContext *execution_context) override {
+    Status error;
+    const int short_option =
+          GetDefinitions()[option_idx].short_option;
+    const char *long_option =
+          GetDefinitions()[option_idx].long_option;
+
+    switch (short_option) {
+      case 'N':
+        if (BreakpointID::StringIsBreakpointName(option_value, error))
+          m_breakpoint_names.push_back(std::string(option_value));
+        else
+          error = Status::FromError(
+              CreateOptionParsingError(option_value, short_option,
+                                     long_option, "Invalid breakpoint name"));
+        break;
+    }
+      return error;
+   }
+
+  void OptionParsingStarting(ExecutionContext *execution_context) override {
+    m_breakpoint_names.clear();
+  }
+  
+  const std::vector<std::string> &GetBreakpointNames() {
+    return m_breakpoint_names;
+  }
+
+protected:
+  std::vector<std::string> m_breakpoint_names;
+};
+
 #define LLDB_OPTIONS_breakpoint_dummy
 #include "CommandOptions.inc"
 
-class BreakpointDummyOptionGroup : public OptionGroup {
+class BreakpointDummyOptionGroup : public OptionGroup {
+public:
+  BreakpointDummyOptionGroup() = default;
+
+  ~BreakpointDummyOptionGroup() override = default;
+
+  llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+    return llvm::ArrayRef(g_breakpoint_dummy_options);
+  }
+
+  Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+                        ExecutionContext *execution_context) override {
+    Status error;
+    const int short_option =
+        g_breakpoint_dummy_options[option_idx].short_option;
+
+    switch (short_option) {
+    case 'D':
+      m_use_dummy = true;
+      break;
+    default:
+      llvm_unreachable("Unimplemented option");
+    }
+
+    return error;
+  }
+
+  void OptionParsingStarting(ExecutionContext *execution_context) override {
+    m_use_dummy = false;
+  }
+
+  bool m_use_dummy;
+};
+
+#pragma mark AddAddress::CommandOptions
+#define LLDB_OPTIONS_breakpoint_add_address
+#include "CommandOptions.inc"
+
+#pragma mark Add Address
+
+static bool CopyOverBreakpointOptions(BreakpointSP bp_sp, 
+                                      BreakpointOptionGroup &bp_opts,
+                                      const std::vector<std::string> &bp_names,
+                                      CommandReturnObject &result) {
+  assert(bp_sp && "CopyOverBreakpointOptions called with no breakpoint");
+
+  bp_sp->GetOptions().CopyOverSetOptions(bp_opts.GetBreakpointOptions());
+  Target &target = bp_sp->GetTarget();
+  if (!bp_names.empty()) {
+    Status name_error;
+    for (auto name : bp_names) {
+      target.AddNameToBreakpoint(bp_sp, name.c_str(), name_error);
+      if (name_error.Fail()) {
+        result.AppendErrorWithFormat("Invalid breakpoint name: %s",
+                                     name.c_str());
+        target.RemoveBreakpointByID(bp_sp->GetID());
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+static llvm::Expected<LanguageType> GetExceptionLanguageForLanguage(
+    llvm::StringRef lang_name, char short_option = '\0', 
+    llvm::StringRef long_option = {}) {
+    LanguageType language = Language::GetLanguageTypeFromString(lang_name);
+    LanguageType exception_language = eLanguageTypeUnknown;
+
+    llvm::StringRef error_context;
+    switch (language) {
+    case eLanguageTypeC89:
+    case eLanguageTypeC:
+    case eLanguageTypeC99:
+    case eLanguageTypeC11:
+      exception_language = eLanguageTypeC;
+      break;
+    case eLanguageTypeC_plus_plus:
+    case eLanguageTypeC_plus_plus_03:
+    case eLanguageTypeC_plus_plus_11:
+    case eLanguageTypeC_plus_plus_14:
+      exception_language = eLanguageTypeC_plus_plus;
+      break;
+    case eLanguageTypeObjC_plus_plus:
+      error_context =
+          "Set exception breakpoints separately for c++ and objective-c";
+      break;
+    case eLanguageTypeUnknown:
+      error_context = "Unknown language type for exception breakpoint";
+      break;
+    default:
+      if (Language *languagePlugin = Language::FindPlugin(language)) {
+        if (languagePlugin->SupportsExceptionBreakpointsOnThrow() ||
+            languagePlugin->SupportsExceptionBreakpointsOnCatch()) {
+          exception_language = language;
+          break;
+        }
+      }
+      error_context = "Unsupported language type for exception breakpoint";
+    }
+    if (!error_context.empty())
+      return CreateOptionParsingError(
+          lang_name, short_option, long_option, error_context);
+    return exception_language;
+}
+
+static bool GetDefaultFile(ExecutionContext exe_ctx, FileSpec &file,
+                      std::string &error_msg) {
+  // First use the Source Manager's default file. Then use the current stack
+  // frame's file.
+  if (!exe_ctx.HasTargetScope()) {
+    error_msg = "Can't get a default file with no target.";
+    return false;
+  }
+  Target &target = exe_ctx.GetTargetRef();
+
+  if (auto maybe_file_and_line =
+          target.GetSourceManager().GetDefaultFileAndLine()) {
+    file = maybe_file_and_line->support_file_sp->GetSpecOnly();
+    return true;
+  }
+
+    StackFrame *cur_frame = exe_ctx.GetFramePtr();
+    if (cur_frame == nullptr) {
+      error_msg =
+          "No selected frame to use to find the default file.";
+      return false;
+    }
+    if (!cur_frame->HasDebugInformation()) {
+      error_msg = "Cannot use the selected frame to find the default "
+                         "file, it has no debug info.";
+      return false;
+    }
+
+      const SymbolContext &sc =
+          cur_frame->GetSymbolContext(eSymbolContextLineEntry);
+      if (sc.line_entry.GetFile()) {
+        file = sc.line_entry.GetFile();
+      } else {
+        error_msg = "Can't find the file for the selected frame to "
+                           "use as the default file.";
+        return false;
+  }
+  return true;
+}
+
+static bool GetDefaultFile(ExecutionContext exe_ctx, FileSpec &file,
+                      CommandReturnObject &result) {
+  std::string error_msg;
+  if (!GetDefaultFile(exe_ctx, file, error_msg)) {
+    result.AppendError(error_msg);
+    return false;
+  }
+  return true;
+}
+
+static Status CompleteLineEntry(ExecutionContext &exe_ctx, 
+    OptionValueFileColonLine &line_entry) {
+  Status error;
+  uint32_t line_num = line_entry.GetLineNumber();
+  if (!line_entry.GetFileSpec()) {
+    FileSpec default_file_spec;
+    std::string error_msg;
+    if (!GetDefaultFile(exe_ctx, default_file_spec, error_msg)) {
+      error.FromErrorStringWithFormatv("Couldn't get default file for "
+        "line {0}: {1}", line_num, error_msg);
+      return error;
+    }
+    line_entry.SetFile(default_file_spec);
+  }
+  return error;
+}
+
+class CommandObjectBreakpointAddAddress : public CommandObjectParsed {
+public:
+  CommandObjectBreakpointAddAddress(CommandInterpreter &interpreter)
+      : CommandObjectParsed(
+            interpreter, "breakpoint add address",
+            "Add breakpoints by raw address",
+            nullptr) {
+    CommandArgumentData bp_id_arg;
+
+    // Define the first (and only) variant of this arg.
+    m_all_options.Append(&m_bp_opts, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+    m_all_options.Append(&m_name_opts);
+    m_all_options.Append(&m_dummy_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+    m_all_options.Append(&m_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+    m_all_options.Finalize();
+
+    AddSimpleArgumentList(eArgTypeAddress, eArgRepeatPlus);
+  }
+
+  ~CommandObjectBreakpointAddAddress() override = default;
+
+  Options *GetOptions() override { return &m_all_options; }
+
+  class CommandOptions : public OptionGroup {
+  public:
+    CommandOptions() = default;
+
+    ~CommandOptions() override = default;
+
+    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+                          ExecutionContext *execution_context) override {
+      Status error;
+      const int short_option = GetDefinitions()[option_idx].short_option;
+      const char *long_option = GetDefinitions()[option_idx].long_option;
+
+      switch (short_option) {
+      case 'H':
+        m_hardware = true;
+        break;
+
+      case 's':
+        if (m_modules.GetSize() == 0)
+          m_modules.AppendIfUnique(FileSpec(option_arg));
+        else
+          error = Status::FromError(
+              CreateOptionParsingError(option_arg, short_option, long_option,
+                                       "Only one shared library can be "
+                                       "specified for address breakpoints."));
+        break;
+
+      default:
+        llvm_unreachable("Unimplemented option");
+      }
+
+      return error;
+    }
+
+    void OptionParsingStarting(ExecutionContext *execution_context) override {
+      m_hardware = false;
+      m_modules.Clear();
+    }
+
+    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+      return llvm::ArrayRef(g_breakpoint_add_address_options);
+    }
+
+    // Instance variables to hold the values for command options.
+    bool m_hardware = false; //FIXME - this can go in the "modify" options.
+    FileSpecList m_modules;
+    
+  };
+
+protected:
+  void DoExecute(Args &command, CommandReturnObject &result) override {
+    // We've already asserted that there can only be one entry in m_modules:
+    const ExecutionContext &exe_ctx = m_interpreter.GetExecutionContext();
+    // We don't set address breakpoints in the dummy target.
+    if (!exe_ctx.HasTargetScope() || exe_ctx.GetTargetPtr()->IsDummyTarget()) {
+      result.AppendError("can't set address breakpoints without a real target.");
+      return;
+    }
+    // Commands can't set internal breakpoints:
+    const bool internal = false;
+
+    Target &target = exe_ctx.GetTargetRef();
+    
+    FileSpec module_spec;
+    bool has_modu...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/156067


More information about the lldb-commits mailing list