[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:16 PDT 2025


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

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.

>From fb2e2dffcb6472711b1d009a77849cfc76fbbfa0 Mon Sep 17 00:00:00 2001
From: Jim Ingham <jingham at apple.com>
Date: Thu, 28 Aug 2025 15:50:47 -0700
Subject: [PATCH] breakpoint add - all subcommands implemented.  Testsuite
 clean but no new tests.

---
 .../Interpreter/CommandOptionArgumentTable.h  |   17 +
 .../Interpreter/OptionValueFileColonLine.h    |    4 +
 lldb/include/lldb/lldb-enumerations.h         |   18 +
 .../Python/lldbsuite/test/lldbutil.py         |   58 +-
 .../Commands/CommandObjectBreakpoint.cpp      | 1447 +++++++++++++++--
 lldb/source/Commands/Options.td               |  126 ++
 lldb/source/Interpreter/Options.cpp           |    4 +-
 .../command-wrong-subcommand-error-msg.test   |    2 +-
 8 files changed, 1571 insertions(+), 105 deletions(-)

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_module = false;
+    if (m_options.m_modules.GetSize() != 0) {
+      has_module = true;
+      module_spec = m_options.m_modules.GetFileSpecAtIndex(0);
+    }
+    BreakpointSP bp_sp;
+    // Let's process the arguments first so we can short-circuit if there are
+    // any errors:
+    std::vector<lldb::addr_t> bp_addrs;
+    for (const Args::ArgEntry &arg_entry : command) {
+      Address bp_address;
+      Status error;
+      lldb::addr_t bp_load_addr = OptionArgParser::ToAddress(&exe_ctx, arg_entry.ref(),
+                                                 LLDB_INVALID_ADDRESS, &error);
+      if (error.Fail()) {
+        result.AppendErrorWithFormatv("invalid argument value '{0}': {1}",
+                                      arg_entry.ref(), error);
+        return;
+      }
+      bp_addrs.push_back(bp_load_addr);
+    }
+    for (auto bp_addr : bp_addrs) {
+      if (has_module)
+        bp_sp = target.CreateAddressInModuleBreakpoint(
+           bp_addr, internal, module_spec, m_options.m_hardware);
+      else
+        // ENHANCEMENT: we should see if bp_addr is in a single loaded module,
+        // and pass that module in if it is.
+        bp_sp = target.CreateBreakpoint(bp_addr, internal,
+                                    m_options.m_hardware);
+    }
+    
+    if (bp_sp) {
+      CopyOverBreakpointOptions(bp_sp, m_bp_opts, 
+                                m_name_opts.GetBreakpointNames(),
+                                result);
+      Stream &output_stream = result.GetOutputStream();
+      bp_sp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial,
+                            /*show_locations=*/ false);
+      result.SetStatus(eReturnStatusSuccessFinishResult);
+    } else {
+      result.AppendError("Breakpoint creation failed: No breakpoint created.");
+    }
+  }
+
+private:
+  BreakpointOptionGroup m_bp_opts;
+  BreakpointNamesOptionGroup m_name_opts;
+  BreakpointDummyOptionGroup m_dummy_options;
+  CommandOptions m_options;
+  OptionGroupOptions m_all_options;
+};
+
+#pragma mark AddException::CommandOptions
+#define LLDB_OPTIONS_breakpoint_add_exception
+#include "CommandOptions.inc"
+
+#pragma mark Add Exception
+
+class CommandObjectBreakpointAddException : public CommandObjectParsed {
+public:
+  CommandObjectBreakpointAddException(CommandInterpreter &interpreter)
+      : CommandObjectParsed(
+            interpreter, "breakpoint add exception",
+            "Add breakpoints on language exceptions.  If no language is "
+            "specified, break on exceptions for all supported languages",
+            nullptr) {
+    // Define the first (and only) variant of this arg.
+    AddSimpleArgumentList(eArgTypeLanguage, eArgRepeatStar);
+
+    // Next add all the options.
+    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);
+    m_all_options.Finalize();
+  }
+
+  ~CommandObjectBreakpointAddException() 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;
+
+      switch (short_option) {
+      case 'E': {
+        uint32_t this_val = (uint32_t)OptionArgParser::ToOptionEnum(
+            option_arg, GetDefinitions()[option_idx].enum_values,
+            eExceptionStageThrow, error);
+        if (error.Fail())
+          return error;
+        m_exception_stage |= this_val;
+      }
+      break;
+      case 'H':
+        m_hardware = true;
+        break;
+
+      case 'O':
+        m_exception_extra_args.AppendArgument("-O");
+        m_exception_extra_args.AppendArgument(option_arg);
+        break;
+
+      default:
+        llvm_unreachable("Unimplemented option");
+      }
+
+      return error;
+    }
+
+    void OptionParsingStarting(ExecutionContext *execution_context) override {
+      m_hardware = false;
+      m_exception_extra_args.Clear();
+      m_exception_stage = eExceptionStageThrow;
+    }
+
+    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+      return llvm::ArrayRef(g_breakpoint_add_exception_options);
+    }
+
+    // Instance variables to hold the values for command options.
+    bool m_hardware = false; //FIXME - this can go in the "modify" options.
+    Args m_exception_extra_args;
+    uint32_t m_exception_stage = eExceptionStageThrow;
+  };
+
+protected:
+  void DoExecute(Args &command, CommandReturnObject &result) override {
+    Target &target =
+        m_dummy_options.m_use_dummy ? GetDummyTarget() : GetTarget();
+    BreakpointSP bp_sp;
+    LanguageType exception_language = eLanguageTypeUnknown;
+    
+    if (command.size() == 0) {
+      result.AppendError("no languages specified.");
+    } else if (command.size() > 1) {
+      result.AppendError("can only set exception breakpoints on one language at a time.");
+    } else {
+      llvm::Expected<LanguageType> language 
+          = GetExceptionLanguageForLanguage(command[0].ref());
+      if (language)
+        exception_language = *language;
+      else {
+        result.SetError(language.takeError());
+        return;
+      }
+    }
+    Status precond_error;
+    const bool internal = false;
+    bool catch_bp = (m_options.m_exception_stage & eExceptionStageCatch) != 0;
+    bool throw_bp = (m_options.m_exception_stage & eExceptionStageThrow) != 0;
+    bp_sp = target.CreateExceptionBreakpoint(
+        exception_language, catch_bp,
+        throw_bp, internal, &m_options.m_exception_extra_args,
+        &precond_error);
+    if (precond_error.Fail()) {
+      result.AppendErrorWithFormat(
+          "Error setting extra exception arguments: %s",
+          precond_error.AsCString());
+      target.RemoveBreakpointByID(bp_sp->GetID());
+      return;
+    }
+    
+    
+    if (bp_sp) {
+      CopyOverBreakpointOptions(bp_sp, m_bp_opts, 
+                                m_name_opts.GetBreakpointNames(),
+                                result);
+      Stream &output_stream = result.GetOutputStream();
+      bp_sp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial,
+                            /*show_locations=*/ false);
+        // Note, we don't print a "got no locations" warning for exception 
+        // breakpoints.  They can get set in the dummy target, and we won't know
+        // how to actually set the breakpoint till we know what version of the
+        // relevant LanguageRuntime gets loaded.
+      if (&target == &GetDummyTarget())
+        output_stream.Printf("Breakpoint set in dummy target, will get copied "
+                             "into future targets.\n");
+      result.SetStatus(eReturnStatusSuccessFinishResult);
+    } else {
+      result.AppendError("Breakpoint creation failed: No breakpoint created.");
+    }
+  }
+
+private:
+  BreakpointOptionGroup m_bp_opts;
+  BreakpointNamesOptionGroup m_name_opts;
+  BreakpointDummyOptionGroup m_dummy_options;
+  CommandOptions m_options;
+  OptionGroupOptions m_all_options;
+};
+
+#pragma mark AddFile::CommandOptions
+#define LLDB_OPTIONS_breakpoint_add_file
+#include "CommandOptions.inc"
+
+#pragma mark Add File
+
+class CommandObjectBreakpointAddFile : public CommandObjectParsed {
+public:
+  CommandObjectBreakpointAddFile(CommandInterpreter &interpreter)
+      : CommandObjectParsed(
+            interpreter, "breakpoint add file",
+            "Add breakpoints on lines in specified source files",
+            nullptr) {
+    CommandArgumentEntry arg1;
+    CommandArgumentData linespec_arg;
+    CommandArgumentData no_arg;
+
+    // Any number of linespecs in group 1:
+    linespec_arg.arg_type = eArgTypeFileLineColumn;
+    linespec_arg.arg_repetition = eArgRepeatPlus;
+    linespec_arg.arg_opt_set_association = LLDB_OPT_SET_1;
+
+    arg1.push_back(linespec_arg);
+
+    // Leave arg2 empty, there are no arguments to this variant.
+    CommandArgumentEntry arg2;
+    no_arg.arg_type = eArgTypeNone;
+    no_arg.arg_repetition = eArgRepeatOptional;
+    no_arg.arg_opt_set_association = LLDB_OPT_SET_2;
+    
+    arg2.push_back(linespec_arg);
+    
+    // Push the data for the first argument into the m_arguments vector.
+    m_arguments.push_back(arg1);
+    m_arguments.push_back(arg2);
+
+    // Define the first (and only) variant of this arg.
+    m_all_options.Append(&m_bp_opts, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1|LLDB_OPT_SET_2);
+    m_all_options.Append(&m_name_opts);
+    m_all_options.Append(&m_dummy_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1|LLDB_OPT_SET_2);
+    m_all_options.Append(&m_options);
+    m_all_options.Finalize();
+  }
+
+  ~CommandObjectBreakpointAddFile() 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 'f':
+        m_cur_value.SetFile(FileSpec(option_arg));
+        break;
+      case 'l':
+        uint32_t line_num;
+        if (option_arg.getAsInteger(0, line_num))
+          error = Status::FromError(
+              CreateOptionParsingError(option_arg, short_option, long_option,
+                                       g_int_parsing_error_message));
+        else {
+          // The line number is the only required part of the options for a
+          // specifying the location - since we will fill in the file with the
+          // default file.  So when we see a new line, the old line entry we
+          // were building is done.  If we haven't gotten a file, try to fill
+          // in the default file, and then finish up this linespec and start
+          // the next one.
+          if (m_cur_value.GetLineNumber() != LLDB_INVALID_LINE_NUMBER) {
+            // FIXME: It should be possible to create a breakpoint with a list
+            // of file, line, column values.  But for now we can only create one,
+            // so return an error here.  The commented out code is what we 
+            // will do when I come back to add that capability.
+            return Status::FromErrorString("Can only specify one file and line "
+                                           "pair at a time.");
+#if 0   // This code will be appropriate once we have a resolver that can take
+        // more than one linespec at a time.         
+            error = CompleteLineEntry(*execution_context, m_cur_value);
+            if (error.Fail())
+              return error;
+          
+            m_line_specs.push_back(m_cur_value);
+            m_cur_value.Clear();
+#endif
+          }
+          m_cur_value.SetLine(line_num);
+          
+        }
+        break;
+      case 'c':
+        uint32_t column_num;
+        if (option_arg.getAsInteger(0, column_num))
+          error = Status::FromError(
+              CreateOptionParsingError(option_arg, short_option, long_option,
+                                       g_int_parsing_error_message));
+        else
+          m_cur_value.SetColumn(column_num);
+        break;
+      case 'K': {
+        bool success;
+        bool value;
+        value = OptionArgParser::ToBoolean(option_arg, true, &success);
+        if (value)
+          m_skip_prologue = eLazyBoolYes;
+        else
+          m_skip_prologue = eLazyBoolNo;
+
+        if (!success)
+          error = Status::FromError(
+              CreateOptionParsingError(option_arg, short_option, long_option,
+                                       g_bool_parsing_error_message));
+      } break;
+      case 'm': {
+        bool success;
+        bool value;
+        value = OptionArgParser::ToBoolean(option_arg, true, &success);
+        if (value)
+          m_move_to_nearest_code = eLazyBoolYes;
+        else
+          m_move_to_nearest_code = eLazyBoolNo;
+
+        if (!success)
+          error = Status::FromError(
+              CreateOptionParsingError(option_arg, short_option, long_option,
+                                       g_bool_parsing_error_message));
+      } break;
+      case 's':
+        m_modules.AppendIfUnique(FileSpec(option_arg));
+        break;
+      case 'H':
+        m_hardware = true;
+        break;
+      case 'S': {
+        lldb::addr_t tmp_offset_addr;
+        tmp_offset_addr = OptionArgParser::ToAddress(execution_context,
+                                                     option_arg, 0, &error);
+        if (error.Success())
+          m_offset_addr = tmp_offset_addr;
+      } break;
+
+      default:
+        llvm_unreachable("Unimplemented option");
+      }
+
+      return error;
+    }
+
+    void OptionParsingStarting(ExecutionContext *execution_context) override {
+      m_hardware = false;
+      m_line_specs.clear();
+      m_cur_value.Clear();
+      m_skip_prologue = eLazyBoolCalculate;
+      m_modules.Clear();
+      m_move_to_nearest_code = eLazyBoolCalculate;
+      m_offset_addr = 0;
+    }
+    
+    Status OptionParsingFinished(ExecutionContext *execution_context)  override 
+    {
+      // We were supplied at least a line from the options, so fill in the
+      // default file if needed.
+      if (m_cur_value.GetLineNumber() != LLDB_INVALID_LINE_NUMBER) {
+        Status error = CompleteLineEntry(*execution_context, m_cur_value);
+        if (error.Fail())
+          return error;
+        m_line_specs.push_back(m_cur_value);
+        m_cur_value.Clear();
+      }
+      return {};
+    }
+
+    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+      return llvm::ArrayRef(g_breakpoint_add_file_options);
+    }
+
+    // Instance variables to hold the values for command options.
+    bool m_hardware = false; //FIXME - this can go in the "modify" options.
+    std::vector<OptionValueFileColonLine> m_line_specs;
+    LazyBool m_skip_prologue = eLazyBoolCalculate;
+    OptionValueFileColonLine m_cur_value;
+    FileSpecList m_modules;
+    LazyBool m_move_to_nearest_code = eLazyBoolCalculate;
+    lldb::addr_t m_offset_addr = 0;
+    
+  };
+
+protected:
+  void DoExecute(Args &command, CommandReturnObject &result) override {
+    bool internal = false;
+    Target &target =
+        m_dummy_options.m_use_dummy ? GetDummyTarget() : GetTarget();
+    // FIXME: At present we can only make file & line breakpoints for one file
+    // and line pair. It wouldn't be hard to extend that, but I'm not adding
+    // features at this point so I'll leave that for a future patch.  For now, 
+    // flag this as an error.
+    
+    // I'm leaving this as a loop since that's how it should be when we can
+    // do more than one linespec at a time.
+    FileSpec default_file;
+    for (const Args::ArgEntry &this_arg : command) {
+      OptionValueFileColonLine value;
+      uint32_t line_value = LLDB_INVALID_LINE_NUMBER;
+      if (!this_arg.ref().getAsInteger(0, line_value)) {
+        // The argument is a plain number.  Treat that as a line number, and
+        // allow it if we can find a default file & line.
+        std::string error_msg;
+        if (!GetDefaultFile(m_exe_ctx, default_file, error_msg)) {
+          result.AppendErrorWithFormatv("Couldn't find default file for line "
+                                        "input: {0} - {1}", line_value, error_msg);
+          return;
+        }
+        value.SetLine(line_value);
+        value.SetFile(default_file);
+      } else {
+        Status error = value.SetValueFromString(this_arg.c_str());
+        if (error.Fail()) {
+          result.AppendErrorWithFormatv("Failed to parse linespec: {0}", error);
+          return;
+        }
+      }
+      m_options.m_line_specs.push_back(value);
+    }
+    
+    if (m_options.m_line_specs.size() != 1) {
+      result.AppendError("Can only make file and line breakpoints with one "
+                         "specification at a time.");
+      return;
+    }
+
+    BreakpointSP bp_sp;
+      // Only check for inline functions if
+    LazyBool check_inlines = eLazyBoolCalculate;
+
+    OptionValueFileColonLine &this_spec = m_options.m_line_specs[0];
+    bp_sp = target.CreateBreakpoint(
+        &(m_options.m_modules), this_spec.GetFileSpec(), this_spec.GetLineNumber(),
+        this_spec.GetColumnNumber(), m_options.m_offset_addr, check_inlines,
+        m_options.m_skip_prologue, internal, m_options.m_hardware,
+        m_options.m_move_to_nearest_code);
+
+    if (bp_sp) {
+      CopyOverBreakpointOptions(bp_sp, m_bp_opts, 
+                                m_name_opts.GetBreakpointNames(),
+                                result);
+      Stream &output_stream = result.GetOutputStream();
+      bp_sp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial,
+                            /*show_locations=*/ false);
+      if (&target == &GetDummyTarget())
+        output_stream.Printf("Breakpoint set in dummy target, will get copied "
+                             "into future targets.\n");
+      else {
+        // Don't print out this warning for exception breakpoints.  They can
+        // get set before the target is set, but we won't know how to actually
+        // set the breakpoint till we run.
+        if (bp_sp->GetNumLocations() == 0) {
+          output_stream.Printf("WARNING:  Unable to resolve breakpoint to any "
+                               "actual locations.\n");
+        }
+      }
+      result.SetStatus(eReturnStatusSuccessFinishResult);
+    } else {
+      result.AppendError("Breakpoint creation failed: No breakpoint created.");
+    }
+  }
+
+private:
+  BreakpointOptionGroup m_bp_opts;
+  BreakpointNamesOptionGroup m_name_opts;
+  BreakpointDummyOptionGroup m_dummy_options;
+  CommandOptions m_options;
+  OptionGroupOptions m_all_options;
+};
+
+#pragma mark AddName::CommandOptions
+#define LLDB_OPTIONS_breakpoint_add_name
+#include "CommandOptions.inc"
+
+#pragma mark Add Name
+
+class CommandObjectBreakpointAddName : public CommandObjectParsed {
+public:
+  CommandObjectBreakpointAddName(CommandInterpreter &interpreter)
+      : CommandObjectParsed(
+            interpreter, "breakpoint add name",
+            "Add breakpoints matching function or symbol names",
+            nullptr) {
+    // FIXME: Add a completer that's aware of the name match style.
+    // Define the first (and only) variant of this arg.
+    AddSimpleArgumentList(eArgTypeFunctionOrSymbol, eArgRepeatPlus);
+
+    // Now add all the options groups.
+    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);
+    m_all_options.Finalize();
+  }
+
+  ~CommandObjectBreakpointAddName() 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 'f':
+        m_files.AppendIfUnique(FileSpec(option_arg));
+        break;
+      case 'K': {
+        bool success;
+        bool value;
+        value = OptionArgParser::ToBoolean(option_arg, true, &success);
+        if (!success)
+          error = Status::FromError(
+              CreateOptionParsingError(option_arg, short_option, long_option,
+                                       g_bool_parsing_error_message));
+        else {
+          if (value)
+            m_skip_prologue = eLazyBoolYes;
+          else
+            m_skip_prologue = eLazyBoolNo;
+        }
+      } break;
+      case 'L': {
+        m_language = Language::GetLanguageTypeFromString(option_arg);
+        if (m_language == eLanguageTypeUnknown)
+          error = Status::FromError(
+              CreateOptionParsingError(option_arg, short_option, long_option,
+                                       g_language_parsing_error_message));
+      }
+      break;
+      case 'm': {
+        uint32_t this_val = (uint32_t)OptionArgParser::ToOptionEnum(
+            option_arg, GetDefinitions()[option_idx].enum_values,
+            eNameMatchStyleAuto, error);
+        if (error.Fail())
+          return error;
+        m_lookup_style = (NameMatchStyle) this_val;
+      }
+      break;
+      case 's':
+        m_modules.AppendIfUnique(FileSpec(option_arg));
+        break;
+      case 'H':
+        m_hardware = true;
+        break;
+      case 'S': {
+        lldb::addr_t tmp_offset_addr;
+        tmp_offset_addr = OptionArgParser::ToAddress(execution_context,
+                                                     option_arg, 0, &error);
+        if (error.Success())
+          m_offset_addr = tmp_offset_addr;
+      } break;
+
+      default:
+        llvm_unreachable("Unimplemented option");
+      }
+
+      return error;
+    }
+
+    void OptionParsingStarting(ExecutionContext *execution_context) override {
+      m_hardware = false;
+      m_skip_prologue = eLazyBoolCalculate;
+      m_files.Clear();
+      m_language = eLanguageTypeUnknown;
+      m_modules.Clear();
+      m_offset_addr = 0;
+      m_lookup_style = eNameMatchStyleAuto;
+    }
+    
+    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+      return llvm::ArrayRef(g_breakpoint_add_name_options);
+    }
+
+    // Instance variables to hold the values for command options.
+    bool m_hardware = false; //FIXME - this can go in the "modify" options.
+    LazyBool m_skip_prologue = eLazyBoolCalculate;
+    FileSpecList m_modules;
+    LanguageType m_language = eLanguageTypeUnknown;
+    FileSpecList m_files;
+    LazyBool m_move_to_nearest_code = eLazyBoolCalculate;
+    lldb::addr_t m_offset_addr = 0;
+    NameMatchStyle m_lookup_style = eNameMatchStyleAuto;
+    
+  };
+
+protected:
+  void DoExecute(Args &command, CommandReturnObject &result) override {
+    const bool internal = false;
+    Target &target =
+        m_dummy_options.m_use_dummy ? GetDummyTarget() : GetTarget();
+    // Parse the argument list - this is a simple list of names.
+    std::vector<std::string> func_names;
+    for (const Args::ArgEntry &this_arg : command) {
+      func_names.push_back(this_arg.ref().str());
+    }
+    BreakpointSP bp_sp;
+    if (!(m_options.m_lookup_style&eNameMatchStyleRegex))
+      bp_sp = target.CreateBreakpoint(
+        &m_options.m_modules, &m_options.m_files, func_names, 
+        (FunctionNameType) m_options.m_lookup_style,
+        m_options.m_language, m_options.m_offset_addr, m_options.m_skip_prologue,
+        internal, m_options.m_hardware);
+    else {
+      if (func_names.size() != 1) {
+        result.AppendError("Can only set function regular expression "
+        "breakpoints on one regex at a time.");
+        return;
+      }
+      std::string &func_regexp = func_names[0];
+      RegularExpression regexp(func_regexp);
+      if (llvm::Error err = regexp.GetError()) {
+        result.AppendErrorWithFormat(
+            "Function name regular expression could not be compiled: %s",
+            llvm::toString(std::move(err)).c_str());
+        // Check if the incorrect regex looks like a globbing expression and
+        // warn the user about it.
+        if (!func_regexp.empty()) {
+          if (func_regexp[0] == '*' ||
+              func_regexp[0] == '?')
+            result.AppendWarning(
+                "Function name regex does not accept glob patterns.");
+        }
+        return;
+      }
+
+      bp_sp = target.CreateFuncRegexBreakpoint(
+          &(m_options.m_modules), &(m_options.m_files), std::move(regexp),
+          m_options.m_language, m_options.m_skip_prologue, internal,
+          m_options.m_hardware);
+    }
+    if (bp_sp) {
+      CopyOverBreakpointOptions(bp_sp, m_bp_opts, 
+                                m_name_opts.GetBreakpointNames(),
+                                result);
+      Stream &output_stream = result.GetOutputStream();
+      bp_sp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial,
+                            /*show_locations=*/ false);
+      if (&target == &GetDummyTarget())
+        output_stream.Printf("Breakpoint set in dummy target, will get copied "
+                             "into future targets.\n");
+      else {
+        if (bp_sp->GetNumLocations() == 0) {
+          output_stream.Printf("WARNING:  Unable to resolve breakpoint to any "
+                               "actual locations.\n");
+        }
+      }
+      result.SetStatus(eReturnStatusSuccessFinishResult);
+    } else {
+      result.AppendError("Breakpoint creation failed: No breakpoint created.");
+    }
+  }
+
+private:
+  BreakpointOptionGroup m_bp_opts;
+  BreakpointNamesOptionGroup m_name_opts;
+  BreakpointDummyOptionGroup m_dummy_options;
+  CommandOptions m_options;
+  OptionGroupOptions m_all_options;
+};
+
+#pragma mark AddPattern::CommandOptions
+#define LLDB_OPTIONS_breakpoint_add_pattern
+#include "CommandOptions.inc"
+
+#pragma mark Add Pattern
+
+class CommandObjectBreakpointAddPattern : public CommandObjectRaw {
+public:
+  CommandObjectBreakpointAddPattern(CommandInterpreter &interpreter)
+      : CommandObjectRaw(
+            interpreter, "breakpoint add pattern",
+            "Add breakpoints matching patterns in the source text",
+            "breakpoint add pattern [options] -- <pattern>") {
+    AddSimpleArgumentList(eArgTypeRegularExpression, eArgRepeatPlain);
+    // Now add all the options groups.
+    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);
+    m_all_options.Finalize();
+  }
+
+  ~CommandObjectBreakpointAddPattern() 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 'a': {
+        bool success;
+        bool value;
+        value = OptionArgParser::ToBoolean(option_arg, true, &success);
+        if (!success)
+          error = Status::FromError(
+              CreateOptionParsingError(option_arg, short_option, long_option,
+                                       g_bool_parsing_error_message));
+        else
+          m_all_files = value;
+      } break;
+      case 'f':
+        m_files.AppendIfUnique(FileSpec(option_arg));
+        break;
+      case 'm': {
+        bool success;
+        bool value;
+        value = OptionArgParser::ToBoolean(option_arg, true, &success);
+        if (!success)
+          error = Status::FromError(
+              CreateOptionParsingError(option_arg, short_option, long_option,
+                                       g_bool_parsing_error_message));
+        else {
+          if (value)
+            m_move_to_nearest_code = eLazyBoolYes;
+          else
+            m_move_to_nearest_code = eLazyBoolNo;
+        }
+      } break;
+      case 'n':
+        m_func_names.insert(option_arg.str());
+        break;
+      case 's':
+        m_modules.AppendIfUnique(FileSpec(option_arg));
+        break;
+      case 'H':
+        m_hardware = true;
+        break;
+      default:
+        llvm_unreachable("Unimplemented option");
+      }
+
+      return error;
+    }
+
+    void OptionParsingStarting(ExecutionContext *execution_context) override {
+      m_hardware = false;
+      m_skip_prologue = eLazyBoolCalculate;
+      m_modules.Clear();
+      m_files.Clear();
+      m_func_names.clear();
+      m_all_files = false;
+      m_move_to_nearest_code = eLazyBoolCalculate;
+    }
+    
+    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+      return llvm::ArrayRef(g_breakpoint_add_pattern_options);
+    }
+
+    // Instance variables to hold the values for command options.
+    bool m_hardware = false; //FIXME - this can go in the "modify" options.
+    LazyBool m_skip_prologue = eLazyBoolCalculate;
+    FileSpecList m_modules;
+    FileSpecList m_files;
+    std::unordered_set<std::string> m_func_names;
+    bool m_all_files = false;
+    LazyBool m_move_to_nearest_code = eLazyBoolCalculate;
+    
+  };
+
+protected:
+  void DoExecute(llvm::StringRef command, CommandReturnObject &result) override {
+    const bool internal = false;
+    ExecutionContext exe_ctx = GetCommandInterpreter().GetExecutionContext();
+    Target &target =
+        m_dummy_options.m_use_dummy ? GetDummyTarget() : GetTarget();
+    m_all_options.NotifyOptionParsingStarting(&exe_ctx);
+
+    if (command.empty()) {
+      result.AppendError("no pattern to seek.");
+      return;
+    }
+
+    OptionsWithRaw args(command);
+    llvm::StringRef expr = args.GetRawPart();
+
+    if (args.HasArgs()) {
+      if (!ParseOptionsAndNotify(args.GetArgs(), result, m_all_options, exe_ctx))
+        return;
+    }
+    llvm::StringRef pattern = args.GetRawPart();
+    if (pattern.empty()) {
+      result.AppendError("no pattern to seek");
+      return;
+    }
+    printf("Pattern: '%s'\n", pattern.str().c_str());
+    
+    BreakpointSP bp_sp;
+    const size_t num_files = m_options.m_files.GetSize();
+
+    if (num_files == 0 && !m_options.m_all_files) {
+      FileSpec file;
+      if (!GetDefaultFile(m_exe_ctx, file, result)) {
+        result.AppendError(
+            "No files provided and could not find default file.");
+        return;
+      } else {
+        m_options.m_files.Append(file);
+      }
+    }
+
+    RegularExpression regexp(pattern);
+    if (llvm::Error err = regexp.GetError()) {
+      result.AppendErrorWithFormat(
+          "Source text regular expression could not be compiled: \"%s\"",
+          llvm::toString(std::move(err)).c_str());
+      return;
+    }
+    bp_sp = target.CreateSourceRegexBreakpoint(
+        &(m_options.m_modules), &(m_options.m_files),
+        m_options.m_func_names, std::move(regexp), internal,
+        m_options.m_hardware, m_options.m_move_to_nearest_code);
+
+    if (bp_sp) {
+      CopyOverBreakpointOptions(bp_sp, m_bp_opts, 
+                                m_name_opts.GetBreakpointNames(),
+                                result);
+      Stream &output_stream = result.GetOutputStream();
+      bp_sp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial,
+                            /*show_locations=*/ false);
+      if (&target == &GetDummyTarget())
+        output_stream.Printf("Breakpoint set in dummy target, will get copied "
+                             "into future targets.\n");
+      else {
+        // Don't print out this warning for exception breakpoints.  They can
+        // get set before the target is set, but we won't know how to actually
+        // set the breakpoint till we run.
+        if (bp_sp->GetNumLocations() == 0) {
+          output_stream.Printf("WARNING:  Unable to resolve breakpoint to any "
+                               "actual locations.\n");
+        }
+      }
+      result.SetStatus(eReturnStatusSuccessFinishResult);
+    } else {
+      result.AppendError("Breakpoint creation failed: No breakpoint created.");
+    }
+  }
+
+private:
+  BreakpointOptionGroup m_bp_opts;
+  BreakpointNamesOptionGroup m_name_opts;
+  BreakpointDummyOptionGroup m_dummy_options;
+  CommandOptions m_options;
+  OptionGroupOptions m_all_options;
+};
+
+#pragma mark AddScripted::CommandOptions
+#define LLDB_OPTIONS_breakpoint_add_scripted
+#include "CommandOptions.inc"
+
+#pragma mark Add Scripted
+
+class CommandObjectBreakpointAddScripted : public CommandObjectParsed {
 public:
-  BreakpointDummyOptionGroup() = default;
+  CommandObjectBreakpointAddScripted(CommandInterpreter &interpreter)
+      : CommandObjectParsed(
+            interpreter, "breakpoint add scripted",
+            "Add breakpoints using a scripted breakpoint resolver.",
+            nullptr),
+        m_python_class_options("scripted breakpoint", true, 'P') {
+    // We're picking up all the normal options, commands and disable.
+    m_all_options.Append(&m_python_class_options,
+                         LLDB_OPT_SET_1 | LLDB_OPT_SET_2, LLDB_OPT_SET_1);
+    // 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);
+    m_all_options.Finalize();
+  }
 
-  ~BreakpointDummyOptionGroup() override = default;
+  ~CommandObjectBreakpointAddScripted() override = default;
 
-  llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
-    return llvm::ArrayRef(g_breakpoint_dummy_options);
-  }
+  Options *GetOptions() override { return &m_all_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;
+  class CommandOptions : public OptionGroup {
+  public:
+    CommandOptions() = default;
 
-    switch (short_option) {
-    case 'D':
-      m_use_dummy = true;
-      break;
-    default:
-      llvm_unreachable("Unimplemented option");
+    ~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;
+
+      switch (short_option) {
+      case 'f':
+        m_files.Append(FileSpec(option_arg));
+        break;
+      case 's':
+        m_modules.AppendIfUnique(FileSpec(option_arg));
+        break;
+      case 'H':
+        m_hardware = true;
+        break;
+
+      default:
+        llvm_unreachable("Unimplemented option");
+      }
+
+      return error;
     }
 
-    return error;
-  }
+    void OptionParsingStarting(ExecutionContext *execution_context) override {
+      m_hardware = false;
+      m_files.Clear();
+      m_modules.Clear();
+    }
+    
 
-  void OptionParsingStarting(ExecutionContext *execution_context) override {
-    m_use_dummy = false;
+    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+      return llvm::ArrayRef(g_breakpoint_add_scripted_options);
+    }
+
+    // Instance variables to hold the values for command options.
+    bool m_hardware = false; //FIXME - this can go in the "modify" options.
+    FileSpecList m_files;
+    FileSpecList m_modules;
+    
+  };
+
+protected:
+  void DoExecute(Args &command, CommandReturnObject &result) override {
+    Target &target =
+        m_dummy_options.m_use_dummy ? GetDummyTarget() : GetTarget();
+
+    BreakpointSP bp_sp;
+      Status error;
+      bp_sp = target.CreateScriptedBreakpoint(
+          m_python_class_options.GetName().c_str(), &(m_options.m_modules),
+          &(m_options.m_files), false, m_options.m_hardware,
+          m_python_class_options.GetStructuredData(), &error);
+      if (error.Fail()) {
+        result.AppendErrorWithFormat(
+            "Error setting extra exception arguments: %s", error.AsCString());
+        target.RemoveBreakpointByID(bp_sp->GetID());
+        return;
+      }
+
+    if (bp_sp) {
+      CopyOverBreakpointOptions(bp_sp, m_bp_opts, 
+                                m_name_opts.GetBreakpointNames(),
+                                result);
+      Stream &output_stream = result.GetOutputStream();
+      bp_sp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial,
+                            /*show_locations=*/ false);
+      if (&target == &GetDummyTarget())
+        output_stream.Printf("Breakpoint set in dummy target, will get copied "
+                             "into future targets.\n");
+      else {
+        // Don't print out this warning for exception breakpoints.  They can
+        // get set before the target is set, but we won't know how to actually
+        // set the breakpoint till we run.
+        if (bp_sp->GetNumLocations() == 0) {
+          output_stream.Printf("WARNING:  Unable to resolve breakpoint to any "
+                               "actual locations.\n");
+        }
+      }
+      result.SetStatus(eReturnStatusSuccessFinishResult);
+    } else {
+      result.AppendError("Breakpoint creation failed: No breakpoint created.");
+    }
   }
 
-  bool m_use_dummy;
+private:
+  BreakpointOptionGroup m_bp_opts;
+  BreakpointNamesOptionGroup m_name_opts;
+  BreakpointDummyOptionGroup m_dummy_options;
+  OptionGroupPythonClassWithDict m_python_class_options;
+  CommandOptions m_options;
+  OptionGroupOptions m_all_options;
+};
+
+#pragma mark Add::CommandOptions
+#define LLDB_OPTIONS_breakpoint_add
+#include "CommandOptions.inc"
+
+#pragma mark Add
+
+class CommandObjectBreakpointAdd : public CommandObjectMultiword {
+public:
+  CommandObjectBreakpointAdd(CommandInterpreter &interpreter)
+      : CommandObjectMultiword(
+          interpreter, "add", "Commands to add breakpoints of various types") {
+    SetHelpLong(
+        R"(
+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.       
+        )");
+    CommandObjectSP address_command_object(
+        new CommandObjectBreakpointAddAddress(interpreter));
+    CommandObjectSP exception_command_object(
+        new CommandObjectBreakpointAddException(interpreter));
+    CommandObjectSP file_command_object(
+        new CommandObjectBreakpointAddFile(interpreter));
+    CommandObjectSP name_command_object(
+        new CommandObjectBreakpointAddName(interpreter));
+    CommandObjectSP pattern_command_object(
+        new CommandObjectBreakpointAddPattern(interpreter));
+    CommandObjectSP scripted_command_object(
+        new CommandObjectBreakpointAddScripted(interpreter));
+
+    LoadSubCommand("address", address_command_object);
+    LoadSubCommand("exception", exception_command_object);
+    LoadSubCommand("file", file_command_object);
+    LoadSubCommand("name", name_command_object);
+    LoadSubCommand("pattern", pattern_command_object);
+    LoadSubCommand("scripted", scripted_command_object);
+  }
 };
 
 #define LLDB_OPTIONS_breakpoint_set
@@ -313,42 +1633,12 @@ class CommandObjectBreakpointSet : public CommandObjectParsed {
         break;
 
       case 'E': {
-        LanguageType language = Language::GetLanguageTypeFromString(option_arg);
-
-        llvm::StringRef error_context;
-        switch (language) {
-        case eLanguageTypeC89:
-        case eLanguageTypeC:
-        case eLanguageTypeC99:
-        case eLanguageTypeC11:
-          m_exception_language = eLanguageTypeC;
-          break;
-        case eLanguageTypeC_plus_plus:
-        case eLanguageTypeC_plus_plus_03:
-        case eLanguageTypeC_plus_plus_11:
-        case eLanguageTypeC_plus_plus_14:
-          m_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()) {
-              m_exception_language = language;
-              break;
-            }
-          }
-          error_context = "Unsupported language type for exception breakpoint";
-        }
-        if (!error_context.empty())
-          error = Status::FromError(CreateOptionParsingError(
-              option_arg, short_option, long_option, error_context));
+        llvm::Expected<LanguageType> language = 
+            GetExceptionLanguageForLanguage(option_arg, short_option, long_option);
+        if (language)
+          m_exception_language = *language;
+        else
+          error = Status::FromError(language.takeError());
       } break;
 
       case 'f':
@@ -608,7 +1898,7 @@ class CommandObjectBreakpointSet : public CommandObjectParsed {
       FileSpec file;
       const size_t num_files = m_options.m_filenames.GetSize();
       if (num_files == 0) {
-        if (!GetDefaultFile(target, file, result)) {
+        if (!GetDefaultFile(m_exe_ctx, file, result)) {
           result.AppendError("No file supplied and no default file available.");
           return;
         }
@@ -694,7 +1984,7 @@ class CommandObjectBreakpointSet : public CommandObjectParsed {
 
       if (num_files == 0 && !m_options.m_all_files) {
         FileSpec file;
-        if (!GetDefaultFile(target, file, result)) {
+        if (!GetDefaultFile(m_exe_ctx, file, result)) {
           result.AppendError(
               "No files provided and could not find default file.");
           return;
@@ -789,39 +2079,6 @@ class CommandObjectBreakpointSet : public CommandObjectParsed {
   }
 
 private:
-  bool GetDefaultFile(Target &target, FileSpec &file,
-                      CommandReturnObject &result) {
-    // First use the Source Manager's default file. Then use the current stack
-    // frame's file.
-    if (auto maybe_file_and_line =
-            target.GetSourceManager().GetDefaultFileAndLine()) {
-      file = maybe_file_and_line->support_file_sp->GetSpecOnly();
-      return true;
-    }
-
-      StackFrame *cur_frame = m_exe_ctx.GetFramePtr();
-      if (cur_frame == nullptr) {
-        result.AppendError(
-            "No selected frame to use to find the default file.");
-        return false;
-      }
-      if (!cur_frame->HasDebugInformation()) {
-        result.AppendError("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 {
-          result.AppendError("Can't find the file for the selected frame to "
-                             "use as the default file.");
-          return false;
-    }
-    return true;
-  }
 
   BreakpointOptionGroup m_bp_opts;
   BreakpointDummyOptionGroup m_dummy_options;
@@ -2410,6 +3667,8 @@ CommandObjectMultiwordBreakpoint::CommandObjectMultiwordBreakpoint(
       new CommandObjectBreakpointDelete(interpreter));
   CommandObjectSP set_command_object(
       new CommandObjectBreakpointSet(interpreter));
+  CommandObjectSP add_command_object(
+      new CommandObjectBreakpointAdd(interpreter));
   CommandObjectSP command_command_object(
       new CommandObjectBreakpointCommand(interpreter));
   CommandObjectSP modify_command_object(
@@ -2427,6 +3686,7 @@ CommandObjectMultiwordBreakpoint::CommandObjectMultiwordBreakpoint(
   clear_command_object->SetCommandName("breakpoint clear");
   delete_command_object->SetCommandName("breakpoint delete");
   set_command_object->SetCommandName("breakpoint set");
+  add_command_object->SetCommandName("breakpoint add");
   command_command_object->SetCommandName("breakpoint command");
   modify_command_object->SetCommandName("breakpoint modify");
   name_command_object->SetCommandName("breakpoint name");
@@ -2439,6 +3699,7 @@ CommandObjectMultiwordBreakpoint::CommandObjectMultiwordBreakpoint(
   LoadSubCommand("clear", clear_command_object);
   LoadSubCommand("delete", delete_command_object);
   LoadSubCommand("set", set_command_object);
+  LoadSubCommand("add", add_command_object);
   LoadSubCommand("command", command_command_object);
   LoadSubCommand("modify", modify_command_object);
   LoadSubCommand("name", name_command_object);
diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index 3dbf65b0c02ff..cc6bd3d8c5aad 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -126,6 +126,14 @@ let Command = "breakpoint dummy" in {
     "provided, which prime new targets.">;
 }
 
+let Command = "breakpoint names" in {
+  def breakpoint_name_option_names :
+    Option<"breakpoint-name", "N">, Group<1>,
+    Arg<"BreakpointName">,
+    Desc<"Adds this name to the list of names for this breakpoint.  "
+    "Can be specified more than once.">;
+}
+
 let Command = "breakpoint set" in {
   def breakpoint_set_shlib : Option<"shlib", "s">, Arg<"ShlibName">,
     Completion<"Module">, Groups<[1,2,3,4,5,6,7,8,9,11,12]>, // *not* in group 10
@@ -235,6 +243,124 @@ let Command = "breakpoint set" in {
    */
 }
 
+let Command = "breakpoint add address" in {
+  def breakpoint_add_address_shlib : Option<"shlib", "s">, Arg<"ShlibName">,
+    Completion<"Module">,
+    Desc<"Set the breakpoint at an address relative to sections in this shared"
+    " library.">;
+  def breakpoint_add_address_hardware : Option<"hardware", "H">,
+    Desc<"Require the breakpoint to use hardware breakpoints.">;
+}
+
+let Command = "breakpoint add exception" in {
+  def breakpoint_add_exception_hardware : Option<"hardware", "H">,
+    Desc<"Require the breakpoint to use hardware breakpoints.">;
+  def breakpoint_add_exception_typename : Option<"exception-typename", "O">,
+    Arg<"TypeName">, Desc<"The breakpoint will only stop if an "
+    "exception Object of this type is thrown.  Can be repeated multiple times "
+    "to stop for multiple object types">;
+  def breakpoint_add_exception_stage : Option<"exception-stage", "E">,
+    Arg<"ExceptionStage">,
+    Desc<"Stop only at the specified exception stage.  Can be specified more "
+         "than once to create a mask of stages.">;
+}
+
+let Command = "breakpoint add file" in {
+  def breakpoint_add_file_hardware : Option<"hardware", "H">,
+    Desc<"Require the breakpoint to use hardware breakpoints.">;
+  def breakpoint_add_file_line : Option<"line", "l">, Group<2>, Arg<"LineNum">,
+    Required,
+    Desc<"Specifies the line number on which to set this breakpoint.">;
+  def breakpoint_add_file_filename : Option<"filename", "f">, Group<2>,
+    Arg<"Filename">, Completion<"SourceFile">, Desc<"The file in which to seek "
+    "the specified source line.">;
+  def breakpoint_add_file_column : Option<"column", "u">, Arg<"ColumnNum">, Group<2>,
+    Desc<"Specifies the column number on which to set this breakpoint.">;
+  def breakpoint_add_file_shlib : Option<"shlib", "s">, Arg<"ShlibName">,
+    Completion<"Module">,
+    Desc<"Set the breakpoint only in this shared library.  Can repeat this "
+    "option multiple times to specify multiple shared libraries.">;
+  def breakpoint_add_file_move_to_nearest_code : Option<"move-to-nearest-code", "m">,
+    Arg<"Boolean">,
+    Desc<"Move breakpoints to nearest code. If not set the "
+    "target.move-to-nearest-code setting is used.">;
+  def breakpoint_add_file_address_slide : Option<"address-slide", "S">,
+    Arg<"Offset">,
+    Desc<"Add the specified offset to whatever address(es) the breakpoint "
+    "resolves to. At present this applies the offset directly as given, and "
+    "doesn't try to align it to instruction boundaries.">;
+  def breakpoint_add_file_skip_prologue : Option<"skip-prologue", "K">,
+    Arg<"Boolean">,
+    Desc<"Skip the prologue if the breakpoint is at the beginning of a "
+    "function. If not set the target.skip-prologue setting is used.">;
+}
+
+let Command = "breakpoint add name" in {
+  def breakpoint_add_name_hardware : Option<"hardware", "H">,
+    Desc<"Require the breakpoint to use hardware breakpoints.">;
+  def breakpoint_add_name_address_slide : Option<"address-slide", "S">,
+    Arg<"Offset">,
+    Desc<"Add the specified offset to whatever address(es) the breakpoint "
+    "resolves to. At present this applies the offset directly as given, and "
+    "doesn't try to align it to instruction boundaries.">;
+  def breakpoint_add_name_shlib : Option<"shlib", "s">, Arg<"ShlibName">,
+    Completion<"Module">,
+    Desc<"Search for names only in this shared library.  Can repeat this "
+    "option multiple times to specify multiple shared libraries.">;
+  def breakpoint_add_name_filename : Option<"filename", "f">,
+    Arg<"Filename">, Completion<"SourceFile">, Desc<"Only search for functions "
+    "defined in the given source file.  Can be specified more than once.">;
+  def breakpoint_add_name_skip_prologue : Option<"skip-prologue", "K">,
+    Arg<"Boolean">,
+    Desc<"Skip the prologue if the breakpoint is at the beginning of a "
+    "function. If not set the target.skip-prologue setting is used.">;
+  def breakpoint_add_name_match_style : Option<"match-style", "m">,
+    Arg<"NameMatchStyle">, 
+    Desc<"The style of matching to do when looking for candidate symbols - "
+    "auto if not specified.">;
+  def breakpoint_add_name_language : Option<"name-language", "L">,
+    Arg<"Language">,
+    Desc<"Only consider indentifiers from the given language when looking for "
+    "match candidates.">;
+}
+
+let Command = "breakpoint add pattern" in {
+  def breakpoint_add_pattern_hardware : Option<"hardware", "H">,
+    Desc<"Require the breakpoint to use hardware breakpoints.">;
+  def breakpoint_add_pattern_move_to_nearest_code : Option<"move-to-nearest-code", "m">,
+    Arg<"Boolean">,
+    Desc<"Move breakpoints to nearest code. If not set the "
+    "target.move-to-nearest-code setting is used.">;
+  def breakpoint_add_pattern_shlib : Option<"shlib", "s">, Arg<"ShlibName">,
+    Completion<"Module">,
+    Desc<"Search for pattern matches only in this shared library.  Can repeat this "
+    "option multiple times to specify multiple shared libraries.">;
+  def breakpoint_add_pattern_filename : Option<"filename", "f">, Group<1>,
+    Arg<"Filename">, Completion<"SourceFile">, Desc<"Limit the pattern search "
+    "to the specified source file.  Can be specified more than once.">;
+  def breakpoint_add_pattern_name : Option<"name", "n">, Arg<"FunctionName">,
+    Completion<"Symbol">, Group<1>,
+    Desc<"Search for pattern matches only in functions matching the specified "
+    "function name using the 'auto' match style.  Can be specified more than "
+    "once, and composes with the filename option.">;
+  def breakpoint_add_pattern_all_files : Option<"all-files", "a">, Group<2>,
+    Desc<"All files are searched for source pattern matches, limited by the "
+    "shlib argument.">;
+}
+
+let Command = "breakpoint add scripted" in {
+  def breakpoint_add_scripted_hardware : Option<"hardware", "H">,
+    Desc<"Require the breakpoint to use hardware breakpoints.">;
+  def breakpoint_add_scripted_filename : Option<"filename", "f">,
+    Arg<"Filename">, Completion<"SourceFile">, Desc<"The files in which to apply "
+    "the scripted resolver callback.  Can repeat the option multiple times.">;
+  def breakpoint_add_scripted_shlib : Option<"shlib", "s">, Arg<"ShlibName">,
+    Completion<"Module">,
+    Desc<"The module in which to apply the scripted resolver callback.  Can "
+    "repeat this option multiple times to specify multiple shared libraries.">;
+}
+
+
 let Command = "breakpoint clear" in {
   def breakpoint_clear_file : Option<"file", "f">, Group<1>, Arg<"Filename">,
     Completion<"SourceFile">,
diff --git a/lldb/source/Interpreter/Options.cpp b/lldb/source/Interpreter/Options.cpp
index ec725428483ff..d0e978b7f740c 100644
--- a/lldb/source/Interpreter/Options.cpp
+++ b/lldb/source/Interpreter/Options.cpp
@@ -1401,7 +1401,9 @@ llvm::Error lldb_private::CreateOptionParsingError(
     llvm::StringRef long_option, llvm::StringRef additional_context) {
   std::string buffer;
   llvm::raw_string_ostream stream(buffer);
-  stream << "Invalid value ('" << option_arg << "') for -" << short_option;
+  stream << "Invalid value ('" << option_arg << "')"; 
+  if (short_option)
+    stream << " for -" << short_option;
   if (!long_option.empty())
     stream << " (" << long_option << ")";
   if (!additional_context.empty())
diff --git a/lldb/test/Shell/Commands/command-wrong-subcommand-error-msg.test b/lldb/test/Shell/Commands/command-wrong-subcommand-error-msg.test
index 445f8d1c8361c..b2dd64446221c 100644
--- a/lldb/test/Shell/Commands/command-wrong-subcommand-error-msg.test
+++ b/lldb/test/Shell/Commands/command-wrong-subcommand-error-msg.test
@@ -4,5 +4,5 @@
 # RUN: not %lldb -b -o 'breakpoint foo' %t.out -o exit 2>&1 | FileCheck %s --check-prefix BP-MSG
 # RUN: not %lldb -b -o 'watchpoint set foo' %t.out -o exit 2>&1 | FileCheck %s --check-prefix WP-MSG
 # CHECK: at main.c:2:21
-# BP-MSG: "foo" is not a valid subcommand of "breakpoint". Valid subcommands are: clear, command, delete, disable, enable, and others. Use "help breakpoint" to find out more.
+# BP-MSG: "foo" is not a valid subcommand of "breakpoint". Valid subcommands are: add, clear, command, delete, disable, enable, and others. Use "help breakpoint" to find out more.
 # WP-MSG: "foo" is not a valid subcommand of "watchpoint set". Valid subcommands are: expression, variable. Use "help watchpoint set" to find out more.
\ No newline at end of file



More information about the lldb-commits mailing list