text: //===-- CommandObjectBreakpoint.cpp -----------------------------*- C++ -*-===//
  //
  // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  // See https://llvm.org/LICENSE.txt for license information.
  // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  //
  //===----------------------------------------------------------------------===//
  
  #include "CommandObjectBreakpoint.h"
  #include "CommandObjectBreakpointCommand.h"
  #include "lldb/Breakpoint/Breakpoint.h"
  #include "lldb/Breakpoint/BreakpointIDList.h"
  #include "lldb/Breakpoint/BreakpointLocation.h"
  #include "lldb/Host/OptionParser.h"
  #include "lldb/Interpreter/CommandCompletions.h"
  #include "lldb/Interpreter/CommandInterpreter.h"
  #include "lldb/Interpreter/CommandReturnObject.h"
  #include "lldb/Interpreter/OptionArgParser.h"
  #include "lldb/Interpreter/OptionValueBoolean.h"
  #include "lldb/Interpreter/OptionValueString.h"
  #include "lldb/Interpreter/OptionValueUInt64.h"
  #include "lldb/Interpreter/Options.h"
  #include "lldb/Target/Language.h"
  #include "lldb/Target/StackFrame.h"
  #include "lldb/Target/Target.h"
  #include "lldb/Target/Thread.h"
  #include "lldb/Target/ThreadSpec.h"
  #include "lldb/Utility/RegularExpression.h"
  #include "lldb/Utility/StreamString.h"
  
  #include <memory>
  #include <vector>
  
  using namespace lldb;
  using namespace lldb_private;
  
  static void AddBreakpointDescription(Stream *s, Breakpoint *bp,
                                       lldb::DescriptionLevel level) {
    s->IndentMore();
    bp->GetDescription(s, level, true);
    s->IndentLess();
    s->EOL();
  }
  
  // Modifiable Breakpoint Options
  #pragma mark Modify::CommandOptions
  #define LLDB_OPTIONS_breakpoint_modify
  #include "CommandOptions.inc"
  
  class lldb_private::BreakpointOptionGroup : public OptionGroup
  {
  public:
    BreakpointOptionGroup() :
            OptionGroup(),
            m_bp_opts(false) {}
    
    ~BreakpointOptionGroup() override = default;
  
    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
      return llvm::makeArrayRef(g_breakpoint_modify_options);
    }
  
    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
                            ExecutionContext *execution_context) override {
      Status error;
      const int short_option = g_breakpoint_modify_options[option_idx].short_option;
  
      switch (short_option) {
      case 'c':
        // Normally an empty breakpoint condition marks is as unset. But we need
        // to say it was passed in.
        m_bp_opts.SetCondition(option_arg.str().c_str());
        m_bp_opts.m_set_flags.Set(BreakpointOptions::eCondition);
        break;
      case 'C':
        m_commands.push_back(option_arg);
        break;
      case 'd':
        m_bp_opts.SetEnabled(false);
        break;
      case 'e':
        m_bp_opts.SetEnabled(true);
        break;
      case 'G': {
        bool value, success;
        value = OptionArgParser::ToBoolean(option_arg, false, &success);
        if (success) {
          m_bp_opts.SetAutoContinue(value);
        } else
          error.SetErrorStringWithFormat(
              "invalid boolean value '%s' passed for -G option",
              option_arg.str().c_str());
      }
      break;
      case 'i':
      {
        uint32_t ignore_count;
        if (option_arg.getAsInteger(0, ignore_count))
          error.SetErrorStringWithFormat("invalid ignore count '%s'",
                                         option_arg.str().c_str());
        else
          m_bp_opts.SetIgnoreCount(ignore_count);
      }
      break;
      case 'o': {
        bool value, success;
        value = OptionArgParser::ToBoolean(option_arg, false, &success);
        if (success) {
          m_bp_opts.SetOneShot(value);
        } else
          error.SetErrorStringWithFormat(
              "invalid boolean value '%s' passed for -o option",
              option_arg.str().c_str());
      } break;
      case 't':
      {
        lldb::tid_t thread_id = LLDB_INVALID_THREAD_ID;
        if (option_arg[0] != '\0') {
          if (option_arg.getAsInteger(0, thread_id))
            error.SetErrorStringWithFormat("invalid thread id string '%s'",
                                           option_arg.str().c_str());
        }
        m_bp_opts.SetThreadID(thread_id);
      }
      break;
      case 'T':
        m_bp_opts.GetThreadSpec()->SetName(option_arg.str().c_str());
        break;
      case 'q':
        m_bp_opts.GetThreadSpec()->SetQueueName(option_arg.str().c_str());
        break;
      case 'x':
      {
        uint32_t thread_index = UINT32_MAX;
        if (option_arg[0] != '\n') {
          if (option_arg.getAsInteger(0, thread_index))
            error.SetErrorStringWithFormat("invalid thread index string '%s'",
                                           option_arg.str().c_str());
        }
        m_bp_opts.GetThreadSpec()->SetIndex(thread_index);
      }
      break;
      default:
        error.SetErrorStringWithFormat("unrecognized option '%c'",
                                       short_option);
        break;
      }
  
      return error;
    }
  
    void OptionParsingStarting(ExecutionContext *execution_context) override {
      m_bp_opts.Clear();
      m_commands.clear();
    }
    
    Status OptionParsingFinished(ExecutionContext *execution_context) override {
      if (!m_commands.empty())
      {
        if (!m_commands.empty())
        {
            auto cmd_data = llvm::make_unique<BreakpointOptions::CommandData>();
          
            for (std::string &str : m_commands)
              cmd_data->user_source.AppendString(str); 
  
            cmd_data->stop_on_error = true;
            m_bp_opts.SetCommandDataCallback(cmd_data);
        }
      }
      return Status();
    }
    
    const BreakpointOptions &GetBreakpointOptions()
    {
      return m_bp_opts;
    }
  
    std::vector<std::string> m_commands;
    BreakpointOptions m_bp_opts;
  
  };
  
  #define LLDB_OPTIONS_breakpoint_dummy
  #include "CommandOptions.inc"
  
  class BreakpointDummyOptionGroup : public OptionGroup
  {
  public:
    BreakpointDummyOptionGroup() :
            OptionGroup() {}
    
    ~BreakpointDummyOptionGroup() override = default;
  
    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
      return llvm::makeArrayRef(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_modify_options[option_idx].short_option;
  
      switch (short_option) {
        case 'D':
          m_use_dummy = true;
          break;
      default:
        error.SetErrorStringWithFormat("unrecognized option '%c'",
                                       short_option);
        break;
      }
  
      return error;
    }
  
    void OptionParsingStarting(ExecutionContext *execution_context) override {
      m_use_dummy = false;
    }
  
    bool m_use_dummy;
  
  };
  
  #define LLDB_OPTIONS_breakpoint_set
  #include "CommandOptions.inc"
  
  // CommandObjectBreakpointSet
  
  class CommandObjectBreakpointSet : public CommandObjectParsed {
  public:
    enum BreakpointSetType {
      eSetTypeInvalid,
      eSetTypeFileAndLine,
      eSetTypeAddress,
      eSetTypeFunctionName,
      eSetTypeFunctionRegexp,
      eSetTypeSourceRegexp,
      eSetTypeException,
      eSetTypeScripted,
    };
  
    CommandObjectBreakpointSet(CommandInterpreter &interpreter)
        : CommandObjectParsed(
              interpreter, "breakpoint set",
              "Sets a breakpoint or set of breakpoints in the executable.",
              "breakpoint set <cmd-options>"),
          m_bp_opts(), m_options() {
            // We're picking up all the normal options, commands and disable.
            m_all_options.Append(&m_bp_opts, 
                                 LLDB_OPT_SET_1 | LLDB_OPT_SET_3 | LLDB_OPT_SET_4, 
                                 LLDB_OPT_SET_ALL);
            m_all_options.Append(&m_dummy_options, LLDB_OPT_SET_1, LLDB_OPT_SET_ALL);
            m_all_options.Append(&m_options);
            m_all_options.Finalize();
          }
  
    ~CommandObjectBreakpointSet() override = default;
  
    Options *GetOptions() override { return &m_all_options; }
  
    class CommandOptions : public OptionGroup {
    public:
      CommandOptions()
          : OptionGroup(), m_condition(), m_filenames(), m_line_num(0), m_column(0),
            m_func_names(), m_func_name_type_mask(eFunctionNameTypeNone),
            m_func_regexp(), m_source_text_regexp(), m_modules(), m_load_addr(),
            m_catch_bp(false), m_throw_bp(true), m_hardware(false),
            m_exception_language(eLanguageTypeUnknown),
            m_language(lldb::eLanguageTypeUnknown),
            m_skip_prologue(eLazyBoolCalculate),
            m_all_files(false), m_move_to_nearest_code(eLazyBoolCalculate) {}
  
      ~CommandOptions() override = default;
  
      Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
                            ExecutionContext *execution_context) override {
        Status error;
        const int short_option = g_breakpoint_set_options[option_idx].short_option;
  
        switch (short_option) {
        case 'a': {
          m_load_addr = OptionArgParser::ToAddress(execution_context, option_arg,
                                                   LLDB_INVALID_ADDRESS, &error);
        } break;
  
        case 'A':
          m_all_files = true;
          break;
  
        case 'b':
          m_func_names.push_back(option_arg);
          m_func_name_type_mask |= eFunctionNameTypeBase;
          break;
  
        case 'C':
          if (option_arg.getAsInteger(0, m_column))
            error.SetErrorStringWithFormat("invalid column number: %s",
                                           option_arg.str().c_str());
          break;
  
        case 'E': {
          LanguageType language = Language::GetLanguageTypeFromString(option_arg);
  
          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:
            m_exception_language = eLanguageTypeObjC;
            break;
          case eLanguageTypeObjC_plus_plus:
            error.SetErrorStringWithFormat(
                "Set exception breakpoints separately for c++ and objective-c");
            break;
          case eLanguageTypeUnknown:
            error.SetErrorStringWithFormat(
                "Unknown language type: '%s' for exception breakpoint",
                option_arg.str().c_str());
            break;
          default:
            error.SetErrorStringWithFormat(
                "Unsupported language type: '%s' for exception breakpoint",
                option_arg.str().c_str());
          }
        } break;
  
        case 'f':
          m_filenames.AppendIfUnique(FileSpec(option_arg));
          break;
  
        case 'F':
          m_func_names.push_back(option_arg);
          m_func_name_type_mask |= eFunctionNameTypeFull;
          break;
          
        case 'h': {
          bool success;
          m_catch_bp = OptionArgParser::ToBoolean(option_arg, true, &success);
          if (!success)
            error.SetErrorStringWithFormat(
                "Invalid boolean value for on-catch option: '%s'",
                option_arg.str().c_str());
        } break;
  
        case 'H':
          m_hardware = true;
          break;
          
        case 'k': {
            if (m_current_key.empty())
              m_current_key.assign(option_arg);
            else
              error.SetErrorStringWithFormat("Key: %s missing value.",
                                              m_current_key.c_str());
          
        } 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.SetErrorStringWithFormat(
                "Invalid boolean value for skip prologue option: '%s'",
                option_arg.str().c_str());
        } break;
  
        case 'l':
          if (option_arg.getAsInteger(0, m_line_num))
            error.SetErrorStringWithFormat("invalid line number: %s.",
                                           option_arg.str().c_str());
          break;
  
        case 'L':
          m_language = Language::GetLanguageTypeFromString(option_arg);
          if (m_language == eLanguageTypeUnknown)
            error.SetErrorStringWithFormat(
                "Unknown language type: '%s' for breakpoint",
                option_arg.str().c_str());
          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.SetErrorStringWithFormat(
                "Invalid boolean value for move-to-nearest-code option: '%s'",
                option_arg.str().c_str());
          break;
        }
  
        case 'M':
          m_func_names.push_back(option_arg);
          m_func_name_type_mask |= eFunctionNameTypeMethod;
          break;
  
        case 'n':
          m_func_names.push_back(option_arg);
          m_func_name_type_mask |= eFunctionNameTypeAuto;
          break;
  
        case 'N': {
          if (BreakpointID::StringIsBreakpointName(option_arg, error))
            m_breakpoint_names.push_back(option_arg);
          else
            error.SetErrorStringWithFormat("Invalid breakpoint name: %s",
                                           option_arg.str().c_str());
          break;
        }
  
        case 'R': {
          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;
  
        case 'O':
          m_exception_extra_args.AppendArgument("-O");
          m_exception_extra_args.AppendArgument(option_arg);
          break;
  
        case 'p':
          m_source_text_regexp.assign(option_arg);
          break;
          
        case 'P':
          m_python_class.assign(option_arg);
          break;
  
        case 'r':
          m_func_regexp.assign(option_arg);
          break;
  
        case 's':
          m_modules.AppendIfUnique(FileSpec(option_arg));
          break;
  
        case 'S':
          m_func_names.push_back(option_arg);
          m_func_name_type_mask |= eFunctionNameTypeSelector;
          break;
  
        case 'v': {
            if (!m_current_key.empty()) {
                m_extra_args_sp->AddStringItem(m_current_key, option_arg);
                m_current_key.clear();
            }
            else
              error.SetErrorStringWithFormat("Value \"%s\" missing matching key.",
                                             option_arg.str().c_str());
        } break;
          
        case 'w': {
          bool success;
          m_throw_bp = OptionArgParser::ToBoolean(option_arg, true, &success);
          if (!success)
            error.SetErrorStringWithFormat(
                "Invalid boolean value for on-throw option: '%s'",
                option_arg.str().c_str());
        } break;
  
        case 'X':
          m_source_regex_func_names.insert(option_arg);
          break;
  
        default:
          error.SetErrorStringWithFormat("unrecognized option '%c'",
                                         short_option);
          break;
        }
  
        return error;
      }
  
      void OptionParsingStarting(ExecutionContext *execution_context) override {
        m_filenames.Clear();
        m_line_num = 0;
        m_column = 0;
        m_func_names.clear();
        m_func_name_type_mask = eFunctionNameTypeNone;
        m_func_regexp.clear();
        m_source_text_regexp.clear();
        m_modules.Clear();
        m_load_addr = LLDB_INVALID_ADDRESS;
        m_offset_addr = 0;
        m_catch_bp = false;
        m_throw_bp = true;
        m_hardware = false;
        m_exception_language = eLanguageTypeUnknown;
        m_language = lldb::eLanguageTypeUnknown;
        m_skip_prologue = eLazyBoolCalculate;
        m_breakpoint_names.clear();
        m_all_files = false;
        m_exception_extra_args.Clear();
        m_move_to_nearest_code = eLazyBoolCalculate;
        m_source_regex_func_names.clear();
        m_python_class.clear();
        m_extra_args_sp = std::make_shared<StructuredData::Dictionary>();
        m_current_key.clear();
      }
  
      llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
        return llvm::makeArrayRef(g_breakpoint_set_options);
      }
  
      // Instance variables to hold the values for command options.
  
      std::string m_condition;
      FileSpecList m_filenames;
      uint32_t m_line_num;
      uint32_t m_column;
      std::vector<std::string> m_func_names;
      std::vector<std::string> m_breakpoint_names;
      lldb::FunctionNameType m_func_name_type_mask;
      std::string m_func_regexp;
      std::string m_source_text_regexp;
      FileSpecList m_modules;
      lldb::addr_t m_load_addr;
      lldb::addr_t m_offset_addr;
      bool m_catch_bp;
      bool m_throw_bp;
      bool m_hardware; // Request to use hardware breakpoints
      lldb::LanguageType m_exception_language;
      lldb::LanguageType m_language;
      LazyBool m_skip_prologue;
      bool m_all_files;
      Args m_exception_extra_args;
      LazyBool m_move_to_nearest_code;
      std::unordered_set<std::string> m_source_regex_func_names;
      std::string m_python_class;
      StructuredData::DictionarySP m_extra_args_sp;
      std::string m_current_key;
    };
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      Target *target = GetSelectedOrDummyTarget(m_dummy_options.m_use_dummy);
  
      if (target == nullptr) {
        result.AppendError("Invalid target.  Must set target before setting "
                           "breakpoints (see 'target create' command).");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      // The following are the various types of breakpoints that could be set:
      //   1).  -f -l -p  [-s -g]   (setting breakpoint by source location)
      //   2).  -a  [-s -g]         (setting breakpoint by address)
      //   3).  -n  [-s -g]         (setting breakpoint by function name)
      //   4).  -r  [-s -g]         (setting breakpoint by function name regular
      //   expression)
      //   5).  -p -f               (setting a breakpoint by comparing a reg-exp
      //   to source text)
      //   6).  -E [-w -h]          (setting a breakpoint for exceptions for a
      //   given language.)
  
      BreakpointSetType break_type = eSetTypeInvalid;
  
      if (!m_options.m_python_class.empty())
        break_type = eSetTypeScripted;
      else if (m_options.m_line_num != 0)
        break_type = eSetTypeFileAndLine;
      else if (m_options.m_load_addr != LLDB_INVALID_ADDRESS)
        break_type = eSetTypeAddress;
      else if (!m_options.m_func_names.empty())
        break_type = eSetTypeFunctionName;
      else if (!m_options.m_func_regexp.empty())
        break_type = eSetTypeFunctionRegexp;
      else if (!m_options.m_source_text_regexp.empty())
        break_type = eSetTypeSourceRegexp;
      else if (m_options.m_exception_language != eLanguageTypeUnknown)
        break_type = eSetTypeException;
  
      BreakpointSP bp_sp = nullptr;
      FileSpec module_spec;
      const bool internal = false;
  
      // If the user didn't specify skip-prologue, having an offset should turn
      // that off.
      if (m_options.m_offset_addr != 0 &&
          m_options.m_skip_prologue == eLazyBoolCalculate)
        m_options.m_skip_prologue = eLazyBoolNo;
  
      switch (break_type) {
      case eSetTypeFileAndLine: // Breakpoint by source position
      {
        FileSpec file;
        const size_t num_files = m_options.m_filenames.GetSize();
        if (num_files == 0) {
          if (!GetDefaultFile(target, file, result)) {
            result.AppendError("No file supplied and no default file available.");
            result.SetStatus(eReturnStatusFailed);
            return false;
          }
        } else if (num_files > 1) {
          result.AppendError("Only one file at a time is allowed for file and "
                             "line breakpoints.");
          result.SetStatus(eReturnStatusFailed);
          return false;
        } else
          file = m_options.m_filenames.GetFileSpecAtIndex(0);
  
        // Only check for inline functions if
        LazyBool check_inlines = eLazyBoolCalculate;
  
        bp_sp = target->CreateBreakpoint(&(m_options.m_modules), 
                                         file,
                                         m_options.m_line_num,
                                         m_options.m_column,
                                         m_options.m_offset_addr,
                                         check_inlines, 
                                         m_options.m_skip_prologue,
                                         internal, 
                                         m_options.m_hardware,
                                         m_options.m_move_to_nearest_code);
      } break;
  
      case eSetTypeAddress: // Breakpoint by address
      {
        // If a shared library has been specified, make an lldb_private::Address
        // with the library, and use that.  That way the address breakpoint
        //  will track the load location of the library.
        size_t num_modules_specified = m_options.m_modules.GetSize();
        if (num_modules_specified == 1) {
          const FileSpec *file_spec =
              m_options.m_modules.GetFileSpecPointerAtIndex(0);
          bp_sp = target->CreateAddressInModuleBreakpoint(m_options.m_load_addr,
                                                          internal, file_spec,
                                                          m_options.m_hardware);
        } else if (num_modules_specified == 0) {
          bp_sp = target->CreateBreakpoint(m_options.m_load_addr, internal,
                                           m_options.m_hardware);
        } else {
          result.AppendError("Only one shared library can be specified for "
                             "address breakpoints.");
          result.SetStatus(eReturnStatusFailed);
          return false;
        }
        break;
      }
      case eSetTypeFunctionName: // Breakpoint by function name
      {
        FunctionNameType name_type_mask = m_options.m_func_name_type_mask;
  
        if (name_type_mask == 0)
          name_type_mask = eFunctionNameTypeAuto;
  
        bp_sp = target->CreateBreakpoint(&(m_options.m_modules), 
                                         &(m_options.m_filenames),
                                         m_options.m_func_names, 
                                         name_type_mask, 
                                         m_options.m_language,
                                         m_options.m_offset_addr, 
                                         m_options.m_skip_prologue, 
                                         internal,
                                         m_options.m_hardware);
      } break;
  
      case eSetTypeFunctionRegexp: // Breakpoint by regular expression function
                                   // name
        {
          RegularExpression regexp(m_options.m_func_regexp);
          if (!regexp.IsValid()) {
            char err_str[1024];
            regexp.GetErrorAsCString(err_str, sizeof(err_str));
            result.AppendErrorWithFormat(
                "Function name regular expression could not be compiled: \"%s\"",
                err_str);
            result.SetStatus(eReturnStatusFailed);
            return false;
          }
  
          bp_sp = target->CreateFuncRegexBreakpoint(&(m_options.m_modules), 
                                                    &(m_options.m_filenames), 
                                                    regexp,
                                                    m_options.m_language, 
                                                    m_options.m_skip_prologue, 
                                                    internal,
                                                    m_options.m_hardware);
        }
        break;
      case eSetTypeSourceRegexp: // Breakpoint by regexp on source text.
      {
        const size_t num_files = m_options.m_filenames.GetSize();
  
        if (num_files == 0 && !m_options.m_all_files) {
          FileSpec file;
          if (!GetDefaultFile(target, file, result)) {
            result.AppendError(
                "No files provided and could not find default file.");
            result.SetStatus(eReturnStatusFailed);
            return false;
          } else {
            m_options.m_filenames.Append(file);
          }
        }
  
        RegularExpression regexp(m_options.m_source_text_regexp);
        if (!regexp.IsValid()) {
          char err_str[1024];
          regexp.GetErrorAsCString(err_str, sizeof(err_str));
          result.AppendErrorWithFormat(
              "Source text regular expression could not be compiled: \"%s\"",
              err_str);
          result.SetStatus(eReturnStatusFailed);
          return false;
        }
        bp_sp = 
            target->CreateSourceRegexBreakpoint(&(m_options.m_modules), 
                                                &(m_options.m_filenames),
                                                m_options
                                                    .m_source_regex_func_names,
                                                regexp,
                                                internal,
                                                m_options.m_hardware,
                                                m_options.m_move_to_nearest_code);
      } break;
      case eSetTypeException: {
        Status precond_error;
        bp_sp = target->CreateExceptionBreakpoint(m_options.m_exception_language, 
                                                  m_options.m_catch_bp,
                                                  m_options.m_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());
          result.SetStatus(eReturnStatusFailed);
          return false;
        }
      } break;
      case eSetTypeScripted: {
  
        Status error;
        bp_sp = target->CreateScriptedBreakpoint(m_options.m_python_class,
                                                 &(m_options.m_modules), 
                                                 &(m_options.m_filenames),
                                                 false,
                                                 m_options.m_hardware,
                                                 m_options.m_extra_args_sp,
                                                 &error);
        if (error.Fail()) {
          result.AppendErrorWithFormat(
              "Error setting extra exception arguments: %s",
              error.AsCString());
          target->RemoveBreakpointByID(bp_sp->GetID());
          result.SetStatus(eReturnStatusFailed);
          return false;
        }
      } break;
      default:
        break;
      }
  
      // Now set the various options that were passed in:
      if (bp_sp) {
        bp_sp->GetOptions()->CopyOverSetOptions(m_bp_opts.GetBreakpointOptions());
  
        if (!m_options.m_breakpoint_names.empty()) {
          Status name_error;
          for (auto name : m_options.m_breakpoint_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());
              result.SetStatus(eReturnStatusFailed);
              return false;
            }
          }
        }
      }
      
      if (bp_sp) {
        Stream &output_stream = result.GetOutputStream();
        const bool show_locations = false;
        bp_sp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial,
                           show_locations);
        if (target == GetDebugger().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 && break_type != eSetTypeException) {
            output_stream.Printf("WARNING:  Unable to resolve breakpoint to any "
                                 "actual locations.\n");
          }
        }
        result.SetStatus(eReturnStatusSuccessFinishResult);
      } else if (!bp_sp) {
        result.AppendError("Breakpoint creation failed: No breakpoint created.");
        result.SetStatus(eReturnStatusFailed);
      }
  
      return result.Succeeded();
    }
  
  private:
    bool GetDefaultFile(Target *target, FileSpec &file,
                        CommandReturnObject &result) {
      uint32_t default_line;
      // First use the Source Manager's default file. Then use the current stack
      // frame's file.
      if (!target->GetSourceManager().GetDefaultFileAndLine(file, default_line)) {
        StackFrame *cur_frame = m_exe_ctx.GetFramePtr();
        if (cur_frame == nullptr) {
          result.AppendError(
              "No selected frame to use to find the default file.");
          result.SetStatus(eReturnStatusFailed);
          return false;
        } else if (!cur_frame->HasDebugInformation()) {
          result.AppendError("Cannot use the selected frame to find the default "
                             "file, it has no debug info.");
          result.SetStatus(eReturnStatusFailed);
          return false;
        } else {
          const SymbolContext &sc =
              cur_frame->GetSymbolContext(eSymbolContextLineEntry);
          if (sc.line_entry.file) {
            file = sc.line_entry.file;
          } else {
            result.AppendError("Can't find the file for the selected frame to "
                               "use as the default file.");
            result.SetStatus(eReturnStatusFailed);
            return false;
          }
        }
      }
      return true;
    }
  
    BreakpointOptionGroup m_bp_opts;
    BreakpointDummyOptionGroup m_dummy_options;
    CommandOptions m_options;
    OptionGroupOptions m_all_options;
  };
  
  // CommandObjectBreakpointModify
  #pragma mark Modify
  
  class CommandObjectBreakpointModify : public CommandObjectParsed {
  public:
    CommandObjectBreakpointModify(CommandInterpreter &interpreter)
        : CommandObjectParsed(interpreter, "breakpoint modify",
                              "Modify the options on a breakpoint or set of "
                              "breakpoints in the executable.  "
                              "If no breakpoint is specified, acts on the last "
                              "created breakpoint.  "
                              "With the exception of -e, -d and -i, passing an "
                              "empty argument clears the modification.",
                              nullptr),
          m_options() {
      CommandArgumentEntry arg;
      CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID,
                                        eArgTypeBreakpointIDRange);
      // Add the entry for the first argument for this command to the object's
      // arguments vector.
      m_arguments.push_back(arg);
      
      m_options.Append(&m_bp_opts, 
                       LLDB_OPT_SET_1 | LLDB_OPT_SET_2 | LLDB_OPT_SET_3, 
                       LLDB_OPT_SET_ALL);
      m_options.Append(&m_dummy_opts, LLDB_OPT_SET_1, LLDB_OPT_SET_ALL);
      m_options.Finalize();
    }
  
    ~CommandObjectBreakpointModify() override = default;
  
    Options *GetOptions() override { return &m_options; }
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      Target *target = GetSelectedOrDummyTarget(m_dummy_opts.m_use_dummy);
      if (target == nullptr) {
        result.AppendError("Invalid target.  No existing target or breakpoints.");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      std::unique_lock<std::recursive_mutex> lock;
      target->GetBreakpointList().GetListMutex(lock);
  
      BreakpointIDList valid_bp_ids;
  
      CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
          command, target, result, &valid_bp_ids, 
          BreakpointName::Permissions::PermissionKinds::disablePerm);
  
      if (result.Succeeded()) {
        const size_t count = valid_bp_ids.GetSize();
        for (size_t i = 0; i < count; ++i) {
          BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
  
          if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
            Breakpoint *bp =
                target->GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
            if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {
              BreakpointLocation *location =
                  bp->FindLocationByID(cur_bp_id.GetLocationID()).get();
              if (location)
                location->GetLocationOptions()
                    ->CopyOverSetOptions(m_bp_opts.GetBreakpointOptions());
            } else {
              bp->GetOptions()
                  ->CopyOverSetOptions(m_bp_opts.GetBreakpointOptions());
            }
          }
        }
      }
  
      return result.Succeeded();
    }
  
  private:
    BreakpointOptionGroup m_bp_opts;
    BreakpointDummyOptionGroup m_dummy_opts;
    OptionGroupOptions m_options;
  };
  
  // CommandObjectBreakpointEnable
  #pragma mark Enable
  
  class CommandObjectBreakpointEnable : public CommandObjectParsed {
  public:
    CommandObjectBreakpointEnable(CommandInterpreter &interpreter)
        : CommandObjectParsed(interpreter, "enable",
                              "Enable the specified disabled breakpoint(s). If "
                              "no breakpoints are specified, enable all of them.",
                              nullptr) {
      CommandArgumentEntry arg;
      CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID,
                                        eArgTypeBreakpointIDRange);
      // Add the entry for the first argument for this command to the object's
      // arguments vector.
      m_arguments.push_back(arg);
    }
  
    ~CommandObjectBreakpointEnable() override = default;
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      Target *target = GetSelectedOrDummyTarget();
      if (target == nullptr) {
        result.AppendError("Invalid target.  No existing target or breakpoints.");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      std::unique_lock<std::recursive_mutex> lock;
      target->GetBreakpointList().GetListMutex(lock);
  
      const BreakpointList &breakpoints = target->GetBreakpointList();
  
      size_t num_breakpoints = breakpoints.GetSize();
  
      if (num_breakpoints == 0) {
        result.AppendError("No breakpoints exist to be enabled.");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      if (command.empty()) {
        // No breakpoint selected; enable all currently set breakpoints.
        target->EnableAllowedBreakpoints();
        result.AppendMessageWithFormat("All breakpoints enabled. (%" PRIu64
                                       " breakpoints)\n",
                                       (uint64_t)num_breakpoints);
        result.SetStatus(eReturnStatusSuccessFinishNoResult);
      } else {
        // Particular breakpoint selected; enable that breakpoint.
        BreakpointIDList valid_bp_ids;
        CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
            command, target, result, &valid_bp_ids, 
            BreakpointName::Permissions::PermissionKinds::disablePerm);
  
        if (result.Succeeded()) {
          int enable_count = 0;
          int loc_count = 0;
          const size_t count = valid_bp_ids.GetSize();
          for (size_t i = 0; i < count; ++i) {
            BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
  
            if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
              Breakpoint *breakpoint =
                  target->GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
              if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {
                BreakpointLocation *location =
                    breakpoint->FindLocationByID(cur_bp_id.GetLocationID()).get();
                if (location) {
                  location->SetEnabled(true);
                  ++loc_count;
                }
              } else {
                breakpoint->SetEnabled(true);
                ++enable_count;
              }
            }
          }
          result.AppendMessageWithFormat("%d breakpoints enabled.\n",
                                         enable_count + loc_count);
          result.SetStatus(eReturnStatusSuccessFinishNoResult);
        }
      }
  
      return result.Succeeded();
    }
  };
  
  // CommandObjectBreakpointDisable
  #pragma mark Disable
  
  class CommandObjectBreakpointDisable : public CommandObjectParsed {
  public:
    CommandObjectBreakpointDisable(CommandInterpreter &interpreter)
        : CommandObjectParsed(
              interpreter, "breakpoint disable",
              "Disable the specified breakpoint(s) without deleting "
              "them.  If none are specified, disable all "
              "breakpoints.",
              nullptr) {
      SetHelpLong(
          "Disable the specified breakpoint(s) without deleting them.  \
  If none are specified, disable all breakpoints."
          R"(
  
  )"
          "Note: disabling a breakpoint will cause none of its locations to be hit \
  regardless of whether individual locations are enabled or disabled.  After the sequence:"
          R"(
  
      (lldb) break disable 1
      (lldb) break enable 1.1
  
  execution will NOT stop at location 1.1.  To achieve that, type:
  
      (lldb) break disable 1.*
      (lldb) break enable 1.1
  
  )"
          "The first command disables all locations for breakpoint 1, \
  the second re-enables the first location.");
  
      CommandArgumentEntry arg;
      CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID,
                                        eArgTypeBreakpointIDRange);
      // Add the entry for the first argument for this command to the object's
      // arguments vector.
      m_arguments.push_back(arg);
    }
  
    ~CommandObjectBreakpointDisable() override = default;
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      Target *target = GetSelectedOrDummyTarget();
      if (target == nullptr) {
        result.AppendError("Invalid target.  No existing target or breakpoints.");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      std::unique_lock<std::recursive_mutex> lock;
      target->GetBreakpointList().GetListMutex(lock);
  
      const BreakpointList &breakpoints = target->GetBreakpointList();
      size_t num_breakpoints = breakpoints.GetSize();
  
      if (num_breakpoints == 0) {
        result.AppendError("No breakpoints exist to be disabled.");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      if (command.empty()) {
        // No breakpoint selected; disable all currently set breakpoints.
        target->DisableAllowedBreakpoints();
        result.AppendMessageWithFormat("All breakpoints disabled. (%" PRIu64
                                       " breakpoints)\n",
                                       (uint64_t)num_breakpoints);
        result.SetStatus(eReturnStatusSuccessFinishNoResult);
      } else {
        // Particular breakpoint selected; disable that breakpoint.
        BreakpointIDList valid_bp_ids;
  
        CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
            command, target, result, &valid_bp_ids, 
            BreakpointName::Permissions::PermissionKinds::disablePerm);
  
        if (result.Succeeded()) {
          int disable_count = 0;
          int loc_count = 0;
          const size_t count = valid_bp_ids.GetSize();
          for (size_t i = 0; i < count; ++i) {
            BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
  
            if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
              Breakpoint *breakpoint =
                  target->GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
              if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {
                BreakpointLocation *location =
                    breakpoint->FindLocationByID(cur_bp_id.GetLocationID()).get();
                if (location) {
                  location->SetEnabled(false);
                  ++loc_count;
                }
              } else {
                breakpoint->SetEnabled(false);
                ++disable_count;
              }
            }
          }
          result.AppendMessageWithFormat("%d breakpoints disabled.\n",
                                         disable_count + loc_count);
          result.SetStatus(eReturnStatusSuccessFinishNoResult);
        }
      }
  
      return result.Succeeded();
    }
  };
  
  // CommandObjectBreakpointList
  
  #pragma mark List::CommandOptions
  #define LLDB_OPTIONS_breakpoint_list
  #include "CommandOptions.inc"
  
  #pragma mark List
  
  class CommandObjectBreakpointList : public CommandObjectParsed {
  public:
    CommandObjectBreakpointList(CommandInterpreter &interpreter)
        : CommandObjectParsed(
              interpreter, "breakpoint list",
              "List some or all breakpoints at configurable levels of detail.",
              nullptr),
          m_options() {
      CommandArgumentEntry arg;
      CommandArgumentData bp_id_arg;
  
      // Define the first (and only) variant of this arg.
      bp_id_arg.arg_type = eArgTypeBreakpointID;
      bp_id_arg.arg_repetition = eArgRepeatOptional;
  
      // There is only one variant this argument could be; put it into the
      // argument entry.
      arg.push_back(bp_id_arg);
  
      // Push the data for the first argument into the m_arguments vector.
      m_arguments.push_back(arg);
    }
  
    ~CommandObjectBreakpointList() override = default;
  
    Options *GetOptions() override { return &m_options; }
  
    class CommandOptions : public Options {
    public:
      CommandOptions()
          : Options(), m_level(lldb::eDescriptionLevelBrief), m_use_dummy(false) {
      }
  
      ~CommandOptions() override = default;
  
      Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
                            ExecutionContext *execution_context) override {
        Status error;
        const int short_option = m_getopt_table[option_idx].val;
  
        switch (short_option) {
        case 'b':
          m_level = lldb::eDescriptionLevelBrief;
          break;
        case 'D':
          m_use_dummy = true;
          break;
        case 'f':
          m_level = lldb::eDescriptionLevelFull;
          break;
        case 'v':
          m_level = lldb::eDescriptionLevelVerbose;
          break;
        case 'i':
          m_internal = true;
          break;
        default:
          error.SetErrorStringWithFormat("unrecognized option '%c'",
                                         short_option);
          break;
        }
  
        return error;
      }
  
      void OptionParsingStarting(ExecutionContext *execution_context) override {
        m_level = lldb::eDescriptionLevelFull;
        m_internal = false;
        m_use_dummy = false;
      }
  
      llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
        return llvm::makeArrayRef(g_breakpoint_list_options);
      }
  
      // Instance variables to hold the values for command options.
  
      lldb::DescriptionLevel m_level;
  
      bool m_internal;
      bool m_use_dummy;
    };
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      Target *target = GetSelectedOrDummyTarget(m_options.m_use_dummy);
  
      if (target == nullptr) {
        result.AppendError("Invalid target. No current target or breakpoints.");
        result.SetStatus(eReturnStatusSuccessFinishNoResult);
        return true;
      }
  
      const BreakpointList &breakpoints =
          target->GetBreakpointList(m_options.m_internal);
      std::unique_lock<std::recursive_mutex> lock;
      target->GetBreakpointList(m_options.m_internal).GetListMutex(lock);
  
      size_t num_breakpoints = breakpoints.GetSize();
  
      if (num_breakpoints == 0) {
        result.AppendMessage("No breakpoints currently set.");
        result.SetStatus(eReturnStatusSuccessFinishNoResult);
        return true;
      }
  
      Stream &output_stream = result.GetOutputStream();
  
      if (command.empty()) {
        // No breakpoint selected; show info about all currently set breakpoints.
        result.AppendMessage("Current breakpoints:");
        for (size_t i = 0; i < num_breakpoints; ++i) {
          Breakpoint *breakpoint = breakpoints.GetBreakpointAtIndex(i).get();
          if (breakpoint->AllowList())
            AddBreakpointDescription(&output_stream, breakpoint, 
                                     m_options.m_level);
        }
        result.SetStatus(eReturnStatusSuccessFinishNoResult);
      } else {
        // Particular breakpoints selected; show info about that breakpoint.
        BreakpointIDList valid_bp_ids;
        CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
            command, target, result, &valid_bp_ids, 
            BreakpointName::Permissions::PermissionKinds::listPerm);
  
        if (result.Succeeded()) {
          for (size_t i = 0; i < valid_bp_ids.GetSize(); ++i) {
            BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
            Breakpoint *breakpoint =
                target->GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
            AddBreakpointDescription(&output_stream, breakpoint,
                                     m_options.m_level);
          }
          result.SetStatus(eReturnStatusSuccessFinishNoResult);
        } else {
          result.AppendError("Invalid breakpoint ID.");
          result.SetStatus(eReturnStatusFailed);
        }
      }
  
      return result.Succeeded();
    }
  
  private:
    CommandOptions m_options;
  };
  
  // CommandObjectBreakpointClear
  #pragma mark Clear::CommandOptions
  
  #define LLDB_OPTIONS_breakpoint_clear
  #include "CommandOptions.inc"
  
  #pragma mark Clear
  
  class CommandObjectBreakpointClear : public CommandObjectParsed {
  public:
    enum BreakpointClearType { eClearTypeInvalid, eClearTypeFileAndLine };
  
    CommandObjectBreakpointClear(CommandInterpreter &interpreter)
        : CommandObjectParsed(interpreter, "breakpoint clear",
                              "Delete or disable breakpoints matching the "
                              "specified source file and line.",
                              "breakpoint clear <cmd-options>"),
          m_options() {}
  
    ~CommandObjectBreakpointClear() override = default;
  
    Options *GetOptions() override { return &m_options; }
  
    class CommandOptions : public Options {
    public:
      CommandOptions() : Options(), m_filename(), m_line_num(0) {}
  
      ~CommandOptions() override = default;
  
      Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
                            ExecutionContext *execution_context) override {
        Status error;
        const int short_option = m_getopt_table[option_idx].val;
  
        switch (short_option) {
        case 'f':
          m_filename.assign(option_arg);
          break;
  
        case 'l':
          option_arg.getAsInteger(0, m_line_num);
          break;
  
        default:
          error.SetErrorStringWithFormat("unrecognized option '%c'",
                                         short_option);
          break;
        }
  
        return error;
      }
  
      void OptionParsingStarting(ExecutionContext *execution_context) override {
        m_filename.clear();
        m_line_num = 0;
      }
  
      llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
        return llvm::makeArrayRef(g_breakpoint_clear_options);
      }
  
      // Instance variables to hold the values for command options.
  
      std::string m_filename;
      uint32_t m_line_num;
    };
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      Target *target = GetSelectedOrDummyTarget();
      if (target == nullptr) {
        result.AppendError("Invalid target. No existing target or breakpoints.");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      // The following are the various types of breakpoints that could be
      // cleared:
      //   1). -f -l (clearing breakpoint by source location)
  
      BreakpointClearType break_type = eClearTypeInvalid;
  
      if (m_options.m_line_num != 0)
        break_type = eClearTypeFileAndLine;
  
      std::unique_lock<std::recursive_mutex> lock;
      target->GetBreakpointList().GetListMutex(lock);
  
      BreakpointList &breakpoints = target->GetBreakpointList();
      size_t num_breakpoints = breakpoints.GetSize();
  
      // Early return if there's no breakpoint at all.
      if (num_breakpoints == 0) {
        result.AppendError("Breakpoint clear: No breakpoint cleared.");
        result.SetStatus(eReturnStatusFailed);
        return result.Succeeded();
      }
  
      // Find matching breakpoints and delete them.
  
      // First create a copy of all the IDs.
      std::vector<break_id_t> BreakIDs;
      for (size_t i = 0; i < num_breakpoints; ++i)
        BreakIDs.push_back(breakpoints.GetBreakpointAtIndex(i)->GetID());
  
      int num_cleared = 0;
      StreamString ss;
      switch (break_type) {
      case eClearTypeFileAndLine: // Breakpoint by source position
      {
        const ConstString filename(m_options.m_filename.c_str());
        BreakpointLocationCollection loc_coll;
  
        for (size_t i = 0; i < num_breakpoints; ++i) {
          Breakpoint *bp = breakpoints.FindBreakpointByID(BreakIDs[i]).get();
  
          if (bp->GetMatchingFileLine(filename, m_options.m_line_num, loc_coll)) {
            // If the collection size is 0, it's a full match and we can just
            // remove the breakpoint.
            if (loc_coll.GetSize() == 0) {
              bp->GetDescription(&ss, lldb::eDescriptionLevelBrief);
              ss.EOL();
              target->RemoveBreakpointByID(bp->GetID());
              ++num_cleared;
            }
          }
        }
      } break;
  
      default:
        break;
      }
  
      if (num_cleared > 0) {
        Stream &output_stream = result.GetOutputStream();
        output_stream.Printf("%d breakpoints cleared:\n", num_cleared);
        output_stream << ss.GetString();
        output_stream.EOL();
        result.SetStatus(eReturnStatusSuccessFinishNoResult);
      } else {
        result.AppendError("Breakpoint clear: No breakpoint cleared.");
        result.SetStatus(eReturnStatusFailed);
      }
  
      return result.Succeeded();
    }
  
  private:
    CommandOptions m_options;
  };
  
  // CommandObjectBreakpointDelete
  #define LLDB_OPTIONS_breakpoint_delete
  #include "CommandOptions.inc"
  
  #pragma mark Delete
  
  class CommandObjectBreakpointDelete : public CommandObjectParsed {
  public:
    CommandObjectBreakpointDelete(CommandInterpreter &interpreter)
        : CommandObjectParsed(interpreter, "breakpoint delete",
                              "Delete the specified breakpoint(s).  If no "
                              "breakpoints are specified, delete them all.",
                              nullptr),
          m_options() {
      CommandArgumentEntry arg;
      CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID,
                                        eArgTypeBreakpointIDRange);
      // Add the entry for the first argument for this command to the object's
      // arguments vector.
      m_arguments.push_back(arg);
    }
  
    ~CommandObjectBreakpointDelete() override = default;
  
    Options *GetOptions() override { return &m_options; }
  
    class CommandOptions : public Options {
    public:
      CommandOptions() : Options(), m_use_dummy(false), m_force(false) {}
  
      ~CommandOptions() override = default;
  
      Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
                            ExecutionContext *execution_context) override {
        Status error;
        const int short_option = m_getopt_table[option_idx].val;
  
        switch (short_option) {
        case 'f':
          m_force = true;
          break;
  
        case 'D':
          m_use_dummy = true;
          break;
  
        default:
          error.SetErrorStringWithFormat("unrecognized option '%c'",
                                         short_option);
          break;
        }
  
        return error;
      }
  
      void OptionParsingStarting(ExecutionContext *execution_context) override {
        m_use_dummy = false;
        m_force = false;
      }
  
      llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
        return llvm::makeArrayRef(g_breakpoint_delete_options);
      }
  
      // Instance variables to hold the values for command options.
      bool m_use_dummy;
      bool m_force;
    };
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      Target *target = GetSelectedOrDummyTarget(m_options.m_use_dummy);
  
      if (target == nullptr) {
        result.AppendError("Invalid target. No existing target or breakpoints.");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      std::unique_lock<std::recursive_mutex> lock;
      target->GetBreakpointList().GetListMutex(lock);
  
      const BreakpointList &breakpoints = target->GetBreakpointList();
  
      size_t num_breakpoints = breakpoints.GetSize();
  
      if (num_breakpoints == 0) {
        result.AppendError("No breakpoints exist to be deleted.");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      if (command.empty()) {
        if (!m_options.m_force &&
            !m_interpreter.Confirm(
                "About to delete all breakpoints, do you want to do that?",
                true)) {
          result.AppendMessage("Operation cancelled...");
        } else {
          target->RemoveAllowedBreakpoints();
          result.AppendMessageWithFormat(
              "All breakpoints removed. (%" PRIu64 " breakpoint%s)\n",
              (uint64_t)num_breakpoints, num_breakpoints > 1 ? "s" : "");
        }
        result.SetStatus(eReturnStatusSuccessFinishNoResult);
      } else {
        // Particular breakpoint selected; disable that breakpoint.
        BreakpointIDList valid_bp_ids;
        CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
            command, target, result, &valid_bp_ids, 
            BreakpointName::Permissions::PermissionKinds::deletePerm);
  
        if (result.Succeeded()) {
          int delete_count = 0;
          int disable_count = 0;
          const size_t count = valid_bp_ids.GetSize();
          for (size_t i = 0; i < count; ++i) {
            BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
  
            if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
              if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {
                Breakpoint *breakpoint =
                    target->GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
                BreakpointLocation *location =
                    breakpoint->FindLocationByID(cur_bp_id.GetLocationID()).get();
                // It makes no sense to try to delete individual locations, so we
                // disable them instead.
                if (location) {
                  location->SetEnabled(false);
                  ++disable_count;
                }
              } else {
                target->RemoveBreakpointByID(cur_bp_id.GetBreakpointID());
                ++delete_count;
              }
            }
          }
          result.AppendMessageWithFormat(
              "%d breakpoints deleted; %d breakpoint locations disabled.\n",
              delete_count, disable_count);
          result.SetStatus(eReturnStatusSuccessFinishNoResult);
        }
      }
      return result.Succeeded();
    }
  
  private:
    CommandOptions m_options;
  };
  
  // CommandObjectBreakpointName
  #define LLDB_OPTIONS_breakpoint_name
  #include "CommandOptions.inc"
  
  class BreakpointNameOptionGroup : public OptionGroup {
  public:
    BreakpointNameOptionGroup()
        : OptionGroup(), m_breakpoint(LLDB_INVALID_BREAK_ID), m_use_dummy(false) {
    }
  
    ~BreakpointNameOptionGroup() override = default;
  
    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
      return llvm::makeArrayRef(g_breakpoint_name_options);
    }
  
    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
                          ExecutionContext *execution_context) override {
      Status error;
      const int short_option = g_breakpoint_name_options[option_idx].short_option;
  
      switch (short_option) {
      case 'N':
        if (BreakpointID::StringIsBreakpointName(option_arg, error) &&
            error.Success())
          m_name.SetValueFromString(option_arg);
        break;
      case 'B':
        if (m_breakpoint.SetValueFromString(option_arg).Fail())
          error.SetErrorStringWithFormat(
              "unrecognized value \"%s\" for breakpoint",
              option_arg.str().c_str());
        break;
      case 'D':
        if (m_use_dummy.SetValueFromString(option_arg).Fail())
          error.SetErrorStringWithFormat(
              "unrecognized value \"%s\" for use-dummy",
              option_arg.str().c_str());
        break;
      case 'H':
        m_help_string.SetValueFromString(option_arg);
        break;
  
      default:
        error.SetErrorStringWithFormat("unrecognized short option '%c'",
                                       short_option);
        break;
      }
      return error;
    }
  
    void OptionParsingStarting(ExecutionContext *execution_context) override {
      m_name.Clear();
      m_breakpoint.Clear();
      m_use_dummy.Clear();
      m_use_dummy.SetDefaultValue(false);
      m_help_string.Clear();
    }
  
    OptionValueString m_name;
    OptionValueUInt64 m_breakpoint;
    OptionValueBoolean m_use_dummy;
    OptionValueString m_help_string;
  };
  
  #define LLDB_OPTIONS_breakpoint_access
  #include "CommandOptions.inc"
  
  class BreakpointAccessOptionGroup : public OptionGroup {
  public:
    BreakpointAccessOptionGroup() : OptionGroup() {}
  
    ~BreakpointAccessOptionGroup() override = default;
  
    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
      return llvm::makeArrayRef(g_breakpoint_access_options);
    }
    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
                          ExecutionContext *execution_context) override {
      Status error;
      const int short_option 
          = g_breakpoint_access_options[option_idx].short_option;
  
      switch (short_option) {
        case 'L': {
          bool value, success;
          value = OptionArgParser::ToBoolean(option_arg, false, &success);
          if (success) {
            m_permissions.SetAllowList(value);
          } else
            error.SetErrorStringWithFormat(
                "invalid boolean value '%s' passed for -L option",
                option_arg.str().c_str());
        } break;
        case 'A': {
          bool value, success;
          value = OptionArgParser::ToBoolean(option_arg, false, &success);
          if (success) {
            m_permissions.SetAllowDisable(value);
          } else
            error.SetErrorStringWithFormat(
                "invalid boolean value '%s' passed for -L option",
                option_arg.str().c_str());
        } break;
        case 'D': {
          bool value, success;
          value = OptionArgParser::ToBoolean(option_arg, false, &success);
          if (success) {
            m_permissions.SetAllowDelete(value);
          } else
            error.SetErrorStringWithFormat(
                "invalid boolean value '%s' passed for -L option",
                option_arg.str().c_str());
        } break;
  
      }
      
      return error;
    }
    
    void OptionParsingStarting(ExecutionContext *execution_context) override {
    }
    
    const BreakpointName::Permissions &GetPermissions() const
    {
      return m_permissions;
    }
    BreakpointName::Permissions m_permissions;  
  };
  
  class CommandObjectBreakpointNameConfigure : public CommandObjectParsed {
  public:
    CommandObjectBreakpointNameConfigure(CommandInterpreter &interpreter)
        : CommandObjectParsed(
              interpreter, "configure", "Configure the options for the breakpoint"
              " name provided.  "
              "If you provide a breakpoint id, the options will be copied from "
              "the breakpoint, otherwise only the options specified will be set "
              "on the name.",
              "breakpoint name configure <command-options> "
              "<breakpoint-name-list>"),
          m_bp_opts(), m_option_group() {
      // Create the first variant for the first (and only) argument for this
      // command.
      CommandArgumentEntry arg1;
      CommandArgumentData id_arg;
      id_arg.arg_type = eArgTypeBreakpointName;
      id_arg.arg_repetition = eArgRepeatOptional;
      arg1.push_back(id_arg);
      m_arguments.push_back(arg1);
  
      m_option_group.Append(&m_bp_opts, 
                            LLDB_OPT_SET_ALL, 
                            LLDB_OPT_SET_1);
      m_option_group.Append(&m_access_options, 
                            LLDB_OPT_SET_ALL, 
                            LLDB_OPT_SET_ALL);
      m_option_group.Append(&m_bp_id, 
                            LLDB_OPT_SET_2|LLDB_OPT_SET_4, 
                            LLDB_OPT_SET_ALL);
      m_option_group.Finalize();
    }
  
    ~CommandObjectBreakpointNameConfigure() override = default;
  
    Options *GetOptions() override { return &m_option_group; }
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override {
  
      const size_t argc = command.GetArgumentCount();
      if (argc == 0) {
        result.AppendError("No names provided.");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
      
      Target *target =
          GetSelectedOrDummyTarget(false);
  
      if (target == nullptr) {
        result.AppendError("Invalid target. No existing target or breakpoints.");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      std::unique_lock<std::recursive_mutex> lock;
      target->GetBreakpointList().GetListMutex(lock);
  
      // Make a pass through first to see that all the names are legal.
      for (auto &entry : command.entries()) {
        Status error;
        if (!BreakpointID::StringIsBreakpointName(entry.ref, error))
        {
          result.AppendErrorWithFormat("Invalid breakpoint name: %s - %s",
                                       entry.c_str(), error.AsCString());
          result.SetStatus(eReturnStatusFailed);
          return false;
        }
      }
      // Now configure them, we already pre-checked the names so we don't need to
      // check the error:
      BreakpointSP bp_sp;
      if (m_bp_id.m_breakpoint.OptionWasSet())
      {
        lldb::break_id_t bp_id = m_bp_id.m_breakpoint.GetUInt64Value();
        bp_sp = target->GetBreakpointByID(bp_id);
        if (!bp_sp)
        {
          result.AppendErrorWithFormatv("Could not find specified breakpoint {0}",
                             bp_id);
          result.SetStatus(eReturnStatusFailed);
          return false;
        }
      }
  
      Status error;
      for (auto &entry : command.entries()) {
        ConstString name(entry.c_str());
        BreakpointName *bp_name = target->FindBreakpointName(name, true, error);
        if (!bp_name)
          continue;
        if (m_bp_id.m_help_string.OptionWasSet())
          bp_name->SetHelp(m_bp_id.m_help_string.GetStringValue().str().c_str());
        
        if (bp_sp)
          target->ConfigureBreakpointName(*bp_name,
                                         *bp_sp->GetOptions(),
                                         m_access_options.GetPermissions());
        else
          target->ConfigureBreakpointName(*bp_name,
                                         m_bp_opts.GetBreakpointOptions(),
                                         m_access_options.GetPermissions());
      }
      return true;
    }
  
  private:
    BreakpointNameOptionGroup m_bp_id; // Only using the id part of this.
    BreakpointOptionGroup m_bp_opts;
    BreakpointAccessOptionGroup m_access_options;
    OptionGroupOptions m_option_group;
  };
  
  class CommandObjectBreakpointNameAdd : public CommandObjectParsed {
  public:
    CommandObjectBreakpointNameAdd(CommandInterpreter &interpreter)
        : CommandObjectParsed(
              interpreter, "add", "Add a name to the breakpoints provided.",
              "breakpoint name add <command-options> <breakpoint-id-list>"),
          m_name_options(), m_option_group() {
      // Create the first variant for the first (and only) argument for this
      // command.
      CommandArgumentEntry arg1;
      CommandArgumentData id_arg;
      id_arg.arg_type = eArgTypeBreakpointID;
      id_arg.arg_repetition = eArgRepeatOptional;
      arg1.push_back(id_arg);
      m_arguments.push_back(arg1);
  
      m_option_group.Append(&m_name_options, LLDB_OPT_SET_1, LLDB_OPT_SET_ALL);
      m_option_group.Finalize();
    }
  
    ~CommandObjectBreakpointNameAdd() override = default;
  
    Options *GetOptions() override { return &m_option_group; }
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      if (!m_name_options.m_name.OptionWasSet()) {
        result.SetError("No name option provided.");
        return false;
      }
  
      Target *target =
          GetSelectedOrDummyTarget(m_name_options.m_use_dummy.GetCurrentValue());
  
      if (target == nullptr) {
        result.AppendError("Invalid target. No existing target or breakpoints.");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      std::unique_lock<std::recursive_mutex> lock;
      target->GetBreakpointList().GetListMutex(lock);
  
      const BreakpointList &breakpoints = target->GetBreakpointList();
  
      size_t num_breakpoints = breakpoints.GetSize();
      if (num_breakpoints == 0) {
        result.SetError("No breakpoints, cannot add names.");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      // Particular breakpoint selected; disable that breakpoint.
      BreakpointIDList valid_bp_ids;
      CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs(
          command, target, result, &valid_bp_ids, 
          BreakpointName::Permissions::PermissionKinds::listPerm);
  
      if (result.Succeeded()) {
        if (valid_bp_ids.GetSize() == 0) {
          result.SetError("No breakpoints specified, cannot add names.");
          result.SetStatus(eReturnStatusFailed);
          return false;
        }
        size_t num_valid_ids = valid_bp_ids.GetSize();
        const char *bp_name = m_name_options.m_name.GetCurrentValue();
        Status error; // This error reports illegal names, but we've already 
                      // checked that, so we don't need to check it again here.
        for (size_t index = 0; index < num_valid_ids; index++) {
          lldb::break_id_t bp_id =
              valid_bp_ids.GetBreakpointIDAtIndex(index).GetBreakpointID();
          BreakpointSP bp_sp = breakpoints.FindBreakpointByID(bp_id);
          target->AddNameToBreakpoint(bp_sp, bp_name, error);
        }
      }
  
      return true;
    }
  
  private:
    BreakpointNameOptionGroup m_name_options;
    OptionGroupOptions m_option_group;
  };
  
  class CommandObjectBreakpointNameDelete : public CommandObjectParsed {
  public:
    CommandObjectBreakpointNameDelete(CommandInterpreter &interpreter)
        : CommandObjectParsed(
              interpreter, "delete",
              "Delete a name from the breakpoints provided.",
              "breakpoint name delete <command-options> <breakpoint-id-list>"),
          m_name_options(), m_option_group() {
      // Create the first variant for the first (and only) argument for this
      // command.
      CommandArgumentEntry arg1;
      CommandArgumentData id_arg;
      id_arg.arg_type = eArgTypeBreakpointID;
      id_arg.arg_repetition = eArgRepeatOptional;
      arg1.push_back(id_arg);
      m_arguments.push_back(arg1);
  
      m_option_group.Append(&m_name_options, LLDB_OPT_SET_1, LLDB_OPT_SET_ALL);
      m_option_group.Finalize();
    }
  
    ~CommandObjectBreakpointNameDelete() override = default;
  
    Options *GetOptions() override { return &m_option_group; }
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      if (!m_name_options.m_name.OptionWasSet()) {
        result.SetError("No name option provided.");
        return false;
      }
  
      Target *target =
          GetSelectedOrDummyTarget(m_name_options.m_use_dummy.GetCurrentValue());
  
      if (target == nullptr) {
        result.AppendError("Invalid target. No existing target or breakpoints.");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      std::unique_lock<std::recursive_mutex> lock;
      target->GetBreakpointList().GetListMutex(lock);
  
      const BreakpointList &breakpoints = target->GetBreakpointList();
  
      size_t num_breakpoints = breakpoints.GetSize();
      if (num_breakpoints == 0) {
        result.SetError("No breakpoints, cannot delete names.");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      // Particular breakpoint selected; disable that breakpoint.
      BreakpointIDList valid_bp_ids;
      CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs(
          command, target, result, &valid_bp_ids, 
          BreakpointName::Permissions::PermissionKinds::deletePerm);
  
      if (result.Succeeded()) {
        if (valid_bp_ids.GetSize() == 0) {
          result.SetError("No breakpoints specified, cannot delete names.");
          result.SetStatus(eReturnStatusFailed);
          return false;
        }
        ConstString bp_name(m_name_options.m_name.GetCurrentValue());
        size_t num_valid_ids = valid_bp_ids.GetSize();
        for (size_t index = 0; index < num_valid_ids; index++) {
          lldb::break_id_t bp_id =
              valid_bp_ids.GetBreakpointIDAtIndex(index).GetBreakpointID();
          BreakpointSP bp_sp = breakpoints.FindBreakpointByID(bp_id);
          target->RemoveNameFromBreakpoint(bp_sp, bp_name);
        }
      }
  
      return true;
    }
  
  private:
    BreakpointNameOptionGroup m_name_options;
    OptionGroupOptions m_option_group;
  };
  
  class CommandObjectBreakpointNameList : public CommandObjectParsed {
  public:
    CommandObjectBreakpointNameList(CommandInterpreter &interpreter)
        : CommandObjectParsed(interpreter, "list",
                              "List either the names for a breakpoint or info "
                              "about a given name.  With no arguments, lists all "
                              "names",
                              "breakpoint name list <command-options>"),
          m_name_options(), m_option_group() {
      m_option_group.Append(&m_name_options, LLDB_OPT_SET_3, LLDB_OPT_SET_ALL);
      m_option_group.Finalize();
    }
  
    ~CommandObjectBreakpointNameList() override = default;
  
    Options *GetOptions() override { return &m_option_group; }
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      Target *target =
          GetSelectedOrDummyTarget(m_name_options.m_use_dummy.GetCurrentValue());
  
      if (target == nullptr) {
        result.AppendError("Invalid target. No existing target or breakpoints.");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
      
      
      std::vector<std::string> name_list;
      if (command.empty()) {
        target->GetBreakpointNames(name_list);
      } else {
        for (const Args::ArgEntry &arg : command)
        {
          name_list.push_back(arg.c_str());
        }
      }
      
      if (name_list.empty()) {
        result.AppendMessage("No breakpoint names found.");
      } else {
        for (const std::string &name_str : name_list) {
          const char *name = name_str.c_str();
          // First print out the options for the name:
          Status error;
          BreakpointName *bp_name = target->FindBreakpointName(ConstString(name),
                                                               false,
                                                               error);
          if (bp_name)
          {
            StreamString s;
            result.AppendMessageWithFormat("Name: %s\n", name);
            if (bp_name->GetDescription(&s, eDescriptionLevelFull))
            {
              result.AppendMessage(s.GetString());
            }
          
            std::unique_lock<std::recursive_mutex> lock;
            target->GetBreakpointList().GetListMutex(lock);
  
            BreakpointList &breakpoints = target->GetBreakpointList();
            bool any_set = false;
            for (BreakpointSP bp_sp : breakpoints.Breakpoints()) {
              if (bp_sp->MatchesName(name)) {
                StreamString s;
                any_set = true;
                bp_sp->GetDescription(&s, eDescriptionLevelBrief);
                s.EOL();
                result.AppendMessage(s.GetString());
              }
            }
            if (!any_set)
              result.AppendMessage("No breakpoints using this name.");
          } else {
            result.AppendMessageWithFormat("Name: %s not found.\n", name);
          }
        }
      }
      return true;
    }
  
  private:
    BreakpointNameOptionGroup m_name_options;
    OptionGroupOptions m_option_group;
  };
  
  // CommandObjectBreakpointName
  class CommandObjectBreakpointName : public CommandObjectMultiword {
  public:
    CommandObjectBreakpointName(CommandInterpreter &interpreter)
        : CommandObjectMultiword(
              interpreter, "name", "Commands to manage name tags for breakpoints",
              "breakpoint name <subcommand> [<command-options>]") {
      CommandObjectSP add_command_object(
          new CommandObjectBreakpointNameAdd(interpreter));
      CommandObjectSP delete_command_object(
          new CommandObjectBreakpointNameDelete(interpreter));
      CommandObjectSP list_command_object(
          new CommandObjectBreakpointNameList(interpreter));
      CommandObjectSP configure_command_object(
          new CommandObjectBreakpointNameConfigure(interpreter));
  
      LoadSubCommand("add", add_command_object);
      LoadSubCommand("delete", delete_command_object);
      LoadSubCommand("list", list_command_object);
      LoadSubCommand("configure", configure_command_object);
    }
  
    ~CommandObjectBreakpointName() override = default;
  };
  
  // CommandObjectBreakpointRead
  #pragma mark Read::CommandOptions
  #define LLDB_OPTIONS_breakpoint_read
  #include "CommandOptions.inc"
  
  #pragma mark Read
  
  class CommandObjectBreakpointRead : public CommandObjectParsed {
  public:
    CommandObjectBreakpointRead(CommandInterpreter &interpreter)
        : CommandObjectParsed(interpreter, "breakpoint read",
                              "Read and set the breakpoints previously saved to "
                              "a file with \"breakpoint write\".  ",
                              nullptr),
          m_options() {
      CommandArgumentEntry arg;
      CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID,
                                        eArgTypeBreakpointIDRange);
      // Add the entry for the first argument for this command to the object's
      // arguments vector.
      m_arguments.push_back(arg);
    }
  
    ~CommandObjectBreakpointRead() override = default;
  
    Options *GetOptions() override { return &m_options; }
  
    class CommandOptions : public Options {
    public:
      CommandOptions() : Options() {}
  
      ~CommandOptions() override = default;
  
      Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
                            ExecutionContext *execution_context) override {
        Status error;
        const int short_option = m_getopt_table[option_idx].val;
  
        switch (short_option) {
        case 'f':
          m_filename.assign(option_arg);
          break;
        case 'N': {
          Status name_error;
          if (!BreakpointID::StringIsBreakpointName(llvm::StringRef(option_arg),
                                                    name_error)) {
            error.SetErrorStringWithFormat("Invalid breakpoint name: %s",
                                           name_error.AsCString());
          }
          m_names.push_back(option_arg);
          break;
        }
        default:
          error.SetErrorStringWithFormat("unrecognized option '%c'",
                                         short_option);
          break;
        }
  
        return error;
      }
  
      void OptionParsingStarting(ExecutionContext *execution_context) override {
        m_filename.clear();
        m_names.clear();
      }
  
      llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
        return llvm::makeArrayRef(g_breakpoint_read_options);
      }
  
      // Instance variables to hold the values for command options.
  
      std::string m_filename;
      std::vector<std::string> m_names;
    };
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      Target *target = GetSelectedOrDummyTarget();
      if (target == nullptr) {
        result.AppendError("Invalid target.  No existing target or breakpoints.");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      std::unique_lock<std::recursive_mutex> lock;
      target->GetBreakpointList().GetListMutex(lock);
  
      FileSpec input_spec(m_options.m_filename);
      FileSystem::Instance().Resolve(input_spec);
      BreakpointIDList new_bps;
      Status error = target->CreateBreakpointsFromFile(
          input_spec, m_options.m_names, new_bps);
  
      if (!error.Success()) {
        result.AppendError(error.AsCString());
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      Stream &output_stream = result.GetOutputStream();
  
      size_t num_breakpoints = new_bps.GetSize();
      if (num_breakpoints == 0) {
        result.AppendMessage("No breakpoints added.");
      } else {
        // No breakpoint selected; show info about all currently set breakpoints.
        result.AppendMessage("New breakpoints:");
        for (size_t i = 0; i < num_breakpoints; ++i) {
          BreakpointID bp_id = new_bps.GetBreakpointIDAtIndex(i);
          Breakpoint *bp = target->GetBreakpointList()
                               .FindBreakpointByID(bp_id.GetBreakpointID())
                               .get();
          if (bp)
            bp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial,
                               false);
        }
      }
      return result.Succeeded();
    }
  
  private:
    CommandOptions m_options;
  };
  
  // CommandObjectBreakpointWrite
  #pragma mark Write::CommandOptions
  #define LLDB_OPTIONS_breakpoint_write
  #include "CommandOptions.inc"
  
  #pragma mark Write
  class CommandObjectBreakpointWrite : public CommandObjectParsed {
  public:
    CommandObjectBreakpointWrite(CommandInterpreter &interpreter)
        : CommandObjectParsed(interpreter, "breakpoint write",
                              "Write the breakpoints listed to a file that can "
                              "be read in with \"breakpoint read\".  "
                              "If given no arguments, writes all breakpoints.",
                              nullptr),
          m_options() {
      CommandArgumentEntry arg;
      CommandObject::AddIDsArgumentData(arg, eArgTypeBreakpointID,
                                        eArgTypeBreakpointIDRange);
      // Add the entry for the first argument for this command to the object's
      // arguments vector.
      m_arguments.push_back(arg);
    }
  
    ~CommandObjectBreakpointWrite() override = default;
  
    Options *GetOptions() override { return &m_options; }
  
    class CommandOptions : public Options {
    public:
      CommandOptions() : Options() {}
  
      ~CommandOptions() override = default;
  
      Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
                            ExecutionContext *execution_context) override {
        Status error;
        const int short_option = m_getopt_table[option_idx].val;
  
        switch (short_option) {
        case 'f':
          m_filename.assign(option_arg);
          break;
        case 'a':
          m_append = true;
          break;
        default:
          error.SetErrorStringWithFormat("unrecognized option '%c'",
                                         short_option);
          break;
        }
  
        return error;
      }
  
      void OptionParsingStarting(ExecutionContext *execution_context) override {
        m_filename.clear();
        m_append = false;
      }
  
      llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
        return llvm::makeArrayRef(g_breakpoint_write_options);
      }
  
      // Instance variables to hold the values for command options.
  
      std::string m_filename;
      bool m_append = false;
    };
  
  protected:
    bool DoExecute(Args &command, CommandReturnObject &result) override {
      Target *target = GetSelectedOrDummyTarget();
      if (target == nullptr) {
        result.AppendError("Invalid target.  No existing target or breakpoints.");
        result.SetStatus(eReturnStatusFailed);
        return false;
      }
  
      std::unique_lock<std::recursive_mutex> lock;
      target->GetBreakpointList().GetListMutex(lock);
  
      BreakpointIDList valid_bp_ids;
      if (!command.empty()) {
        CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs(
            command, target, result, &valid_bp_ids, 
            BreakpointName::Permissions::PermissionKinds::listPerm);
  
        if (!result.Succeeded()) {
          result.SetStatus(eReturnStatusFailed);
          return false;
        }
      }
      FileSpec file_spec(m_options.m_filename);
      FileSystem::Instance().Resolve(file_spec);
      Status error = target->SerializeBreakpointsToFile(file_spec, valid_bp_ids,
                                                        m_options.m_append);
      if (!error.Success()) {
        result.AppendErrorWithFormat("error serializing breakpoints: %s.",
                                     error.AsCString());
        result.SetStatus(eReturnStatusFailed);
      }
      return result.Succeeded();
    }
  
  private:
    CommandOptions m_options;
  };
  
  // CommandObjectMultiwordBreakpoint
  #pragma mark MultiwordBreakpoint
  
  CommandObjectMultiwordBreakpoint::CommandObjectMultiwordBreakpoint(
      CommandInterpreter &interpreter)
      : CommandObjectMultiword(
            interpreter, "breakpoint",
            "Commands for operating on breakpoints (see 'help b' for shorthand.)",
            "breakpoint <subcommand> [<command-options>]") {
    CommandObjectSP list_command_object(
        new CommandObjectBreakpointList(interpreter));
    CommandObjectSP enable_command_object(
        new CommandObjectBreakpointEnable(interpreter));
    CommandObjectSP disable_command_object(
        new CommandObjectBreakpointDisable(interpreter));
    CommandObjectSP clear_command_object(
        new CommandObjectBreakpointClear(interpreter));
    CommandObjectSP delete_command_object(
        new CommandObjectBreakpointDelete(interpreter));
    CommandObjectSP set_command_object(
        new CommandObjectBreakpointSet(interpreter));
    CommandObjectSP command_command_object(
        new CommandObjectBreakpointCommand(interpreter));
    CommandObjectSP modify_command_object(
        new CommandObjectBreakpointModify(interpreter));
    CommandObjectSP name_command_object(
        new CommandObjectBreakpointName(interpreter));
    CommandObjectSP write_command_object(
        new CommandObjectBreakpointWrite(interpreter));
    CommandObjectSP read_command_object(
        new CommandObjectBreakpointRead(interpreter));
  
    list_command_object->SetCommandName("breakpoint list");
    enable_command_object->SetCommandName("breakpoint enable");
    disable_command_object->SetCommandName("breakpoint disable");
    clear_command_object->SetCommandName("breakpoint clear");
    delete_command_object->SetCommandName("breakpoint delete");
    set_command_object->SetCommandName("breakpoint set");
    command_command_object->SetCommandName("breakpoint command");
    modify_command_object->SetCommandName("breakpoint modify");
    name_command_object->SetCommandName("breakpoint name");
    write_command_object->SetCommandName("breakpoint write");
    read_command_object->SetCommandName("breakpoint read");
  
    LoadSubCommand("list", list_command_object);
    LoadSubCommand("enable", enable_command_object);
    LoadSubCommand("disable", disable_command_object);
    LoadSubCommand("clear", clear_command_object);
    LoadSubCommand("delete", delete_command_object);
    LoadSubCommand("set", set_command_object);
    LoadSubCommand("command", command_command_object);
    LoadSubCommand("modify", modify_command_object);
    LoadSubCommand("name", name_command_object);
    LoadSubCommand("write", write_command_object);
    LoadSubCommand("read", read_command_object);
  }
  
  CommandObjectMultiwordBreakpoint::~CommandObjectMultiwordBreakpoint() = default;
  
  void CommandObjectMultiwordBreakpoint::VerifyIDs(Args &args, Target *target,
                                                   bool allow_locations,
                                                   CommandReturnObject &result,
                                                   BreakpointIDList *valid_ids,
                                                   BreakpointName::Permissions
                                                       ::PermissionKinds 
                                                       purpose) {
    // args can be strings representing 1). integers (for breakpoint ids)
    //                                  2). the full breakpoint & location
    //                                  canonical representation
    //                                  3). the word "to" or a hyphen,
    //                                  representing a range (in which case there
    //                                      had *better* be an entry both before &
    //                                      after of one of the first two types.
    //                                  4). A breakpoint name
    // If args is empty, we will use the last created breakpoint (if there is
    // one.)
  
    Args temp_args;
  
    if (args.empty()) {
      if (target->GetLastCreatedBreakpoint()) {
        valid_ids->AddBreakpointID(BreakpointID(
            target->GetLastCreatedBreakpoint()->GetID(), LLDB_INVALID_BREAK_ID));
        result.SetStatus(eReturnStatusSuccessFinishNoResult);
      } else {
        result.AppendError(
            "No breakpoint specified and no last created breakpoint.");
        result.SetStatus(eReturnStatusFailed);
      }
      return;
    }
  
    // Create a new Args variable to use; copy any non-breakpoint-id-ranges stuff
    // directly from the old ARGS to the new TEMP_ARGS.  Do not copy breakpoint
    // id range strings over; instead generate a list of strings for all the
    // breakpoint ids in the range, and shove all of those breakpoint id strings
    // into TEMP_ARGS.
  
    BreakpointIDList::FindAndReplaceIDRanges(args, target, allow_locations, 
                                             purpose, result, temp_args);
  
    // NOW, convert the list of breakpoint id strings in TEMP_ARGS into an actual
    // BreakpointIDList:
  
    valid_ids->InsertStringArray(temp_args.GetArgumentArrayRef(), result);
  
    // At this point,  all of the breakpoint ids that the user passed in have
    // been converted to breakpoint IDs and put into valid_ids.
  
    if (result.Succeeded()) {
      // Now that we've converted everything from args into a list of breakpoint
      // ids, go through our tentative list of breakpoint id's and verify that
      // they correspond to valid/currently set breakpoints.
  
      const size_t count = valid_ids->GetSize();
      for (size_t i = 0; i < count; ++i) {
        BreakpointID cur_bp_id = valid_ids->GetBreakpointIDAtIndex(i);
        Breakpoint *breakpoint =
            target->GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
        if (breakpoint != nullptr) {
          const size_t num_locations = breakpoint->GetNumLocations();
          if (static_cast<size_t>(cur_bp_id.GetLocationID()) > num_locations) {
            StreamString id_str;
            BreakpointID::GetCanonicalReference(
                &id_str, cur_bp_id.GetBreakpointID(), cur_bp_id.GetLocationID());
            i = valid_ids->GetSize() + 1;
            result.AppendErrorWithFormat(
                "'%s' is not a currently valid breakpoint/location id.\n",
                id_str.GetData());
            result.SetStatus(eReturnStatusFailed);
          }
        } else {
          i = valid_ids->GetSize() + 1;
          result.AppendErrorWithFormat(
              "'%d' is not a currently valid breakpoint ID.\n",
              cur_bp_id.GetBreakpointID());
          result.SetStatus(eReturnStatusFailed);
        }
      }
    }
  }
  