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

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Fri Nov 7 12:02:20 PST 2025


================
@@ -200,41 +200,1341 @@ class lldb_private::BreakpointOptionGroup : public OptionGroup {
   BreakpointOptions m_bp_opts;
 };
 
+// This is the Breakpoint Names option group - used to add Names to breakpoints
+// while making them.  Not to be confused with the "Breakpoint Name" option
+// group which is the common options of various "breakpoint name" commands.
+#define LLDB_OPTIONS_breakpoint_names
+#include "CommandOptions.inc"
+
+class BreakpointNamesOptionGroup : public OptionGroup {
+public:
+  BreakpointNamesOptionGroup() = default;
+
+  ~BreakpointNamesOptionGroup() override = default;
+
+  llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+    return g_breakpoint_names_options;
+  }
+
+  Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value,
+                        ExecutionContext *execution_context) override {
+    Status error;
+    const int short_option = GetDefinitions()[option_idx].short_option;
+    const char *long_option = GetDefinitions()[option_idx].long_option;
+
+    switch (short_option) {
+    case 'N':
+      if (BreakpointID::StringIsBreakpointName(option_value, error))
+        m_breakpoint_names.push_back(std::string(option_value));
+      else
+        error = Status::FromError(
+            CreateOptionParsingError(option_value, short_option, long_option,
+                                     "Invalid breakpoint name"));
+      break;
+    }
+    return error;
+  }
+
+  void OptionParsingStarting(ExecutionContext *execution_context) override {
+    m_breakpoint_names.clear();
+  }
+
+  const std::vector<std::string> &GetBreakpointNames() {
+    return m_breakpoint_names;
+  }
+
+protected:
+  std::vector<std::string> m_breakpoint_names;
+};
+
 #define LLDB_OPTIONS_breakpoint_dummy
 #include "CommandOptions.inc"
 
-class BreakpointDummyOptionGroup : public OptionGroup {
+class BreakpointDummyOptionGroup : public OptionGroup {
+public:
+  BreakpointDummyOptionGroup() = default;
+
+  ~BreakpointDummyOptionGroup() override = default;
+
+  llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+    return llvm::ArrayRef(g_breakpoint_dummy_options);
+  }
+
+  Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+                        ExecutionContext *execution_context) override {
+    Status error;
+    const int short_option =
+        g_breakpoint_dummy_options[option_idx].short_option;
+
+    switch (short_option) {
+    case 'D':
+      m_use_dummy = true;
+      break;
+    default:
+      llvm_unreachable("Unimplemented option");
+    }
+
+    return error;
+  }
+
+  void OptionParsingStarting(ExecutionContext *execution_context) override {
+    m_use_dummy = false;
+  }
+
+  bool m_use_dummy;
+};
+
+#pragma mark AddAddress::CommandOptions
+#define LLDB_OPTIONS_breakpoint_add_address
+#include "CommandOptions.inc"
+
+#pragma mark Add Address
+
+static bool CopyOverBreakpointOptions(BreakpointSP bp_sp,
+                                      BreakpointOptionGroup &bp_opts,
+                                      const std::vector<std::string> &bp_names,
+                                      CommandReturnObject &result) {
+  assert(bp_sp && "CopyOverBreakpointOptions called with no breakpoint");
+
+  bp_sp->GetOptions().CopyOverSetOptions(bp_opts.GetBreakpointOptions());
+  Target &target = bp_sp->GetTarget();
+  if (!bp_names.empty()) {
+    Status name_error;
+    for (auto name : bp_names) {
+      target.AddNameToBreakpoint(bp_sp, name.c_str(), name_error);
+      if (name_error.Fail()) {
+        result.AppendErrorWithFormat("Invalid breakpoint name: %s",
+                                     name.c_str());
+        target.RemoveBreakpointByID(bp_sp->GetID());
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+static llvm::Expected<LanguageType>
+GetExceptionLanguageForLanguage(llvm::StringRef lang_name,
+                                char short_option = '\0',
+                                llvm::StringRef long_option = {}) {
+  LanguageType language = Language::GetLanguageTypeFromString(lang_name);
+  LanguageType exception_language = eLanguageTypeUnknown;
+
+  llvm::StringRef error_context;
+  switch (language) {
+  case eLanguageTypeC89:
+  case eLanguageTypeC:
+  case eLanguageTypeC99:
+  case eLanguageTypeC11:
+    exception_language = eLanguageTypeC;
+    break;
+  case eLanguageTypeC_plus_plus:
+  case eLanguageTypeC_plus_plus_03:
+  case eLanguageTypeC_plus_plus_11:
+  case eLanguageTypeC_plus_plus_14:
+    exception_language = eLanguageTypeC_plus_plus;
+    break;
+  case eLanguageTypeObjC_plus_plus:
+    error_context =
+        "Set exception breakpoints separately for c++ and objective-c";
+    break;
+  case eLanguageTypeUnknown:
+    error_context = "Unknown language type for exception breakpoint";
+    break;
+  default:
+    if (Language *languagePlugin = Language::FindPlugin(language)) {
+      if (languagePlugin->SupportsExceptionBreakpointsOnThrow() ||
+          languagePlugin->SupportsExceptionBreakpointsOnCatch()) {
+        exception_language = language;
+        break;
+      }
+    }
+    error_context = "Unsupported language type for exception breakpoint";
+  }
+  if (!error_context.empty())
+    return CreateOptionParsingError(lang_name, short_option, long_option,
+                                    error_context);
+  return exception_language;
+}
+
+static bool GetDefaultFile(ExecutionContext exe_ctx, FileSpec &file,
+                           std::string &error_msg) {
+  // First use the Source Manager's default file. Then use the current stack
+  // frame's file.
+  if (!exe_ctx.HasTargetScope()) {
+    error_msg = "Can't get a default file with no target.";
+    return false;
+  }
+  Target &target = exe_ctx.GetTargetRef();
+
+  if (auto maybe_file_and_line =
+          target.GetSourceManager().GetDefaultFileAndLine()) {
+    file = maybe_file_and_line->support_file_sp->GetSpecOnly();
+    return true;
+  }
+
+  StackFrame *cur_frame = exe_ctx.GetFramePtr();
+  if (cur_frame == nullptr) {
+    error_msg = "No selected frame to use to find the default file.";
+    return false;
+  }
+  if (!cur_frame->HasDebugInformation()) {
+    error_msg = "Cannot use the selected frame to find the default "
+                "file, it has no debug info.";
+    return false;
+  }
+
+  const SymbolContext &sc =
+      cur_frame->GetSymbolContext(eSymbolContextLineEntry);
+  if (sc.line_entry.GetFile()) {
+    file = sc.line_entry.GetFile();
+  } else {
+    error_msg = "Can't find the file for the selected frame to "
+                "use as the default file.";
+    return false;
+  }
+  return true;
+}
+
+static bool GetDefaultFile(ExecutionContext exe_ctx, FileSpec &file,
+                           CommandReturnObject &result) {
+  std::string error_msg;
+  if (!GetDefaultFile(exe_ctx, file, error_msg)) {
+    result.AppendError(error_msg);
+    return false;
+  }
+  return true;
+}
+
+static Status CompleteLineEntry(ExecutionContext &exe_ctx,
+                                OptionValueFileColonLine &line_entry) {
+  Status error;
+  uint32_t line_num = line_entry.GetLineNumber();
+  if (!line_entry.GetFileSpec()) {
+    FileSpec default_file_spec;
+    std::string error_msg;
+    if (!GetDefaultFile(exe_ctx, default_file_spec, error_msg)) {
+      error.FromErrorStringWithFormatv("Couldn't get default file for "
+                                       "line {0}: {1}",
+                                       line_num, error_msg);
+      return error;
+    }
+    line_entry.SetFile(default_file_spec);
+  }
+  return error;
+}
+
+class CommandObjectBreakpointAddAddress : public CommandObjectParsed {
+public:
+  CommandObjectBreakpointAddAddress(CommandInterpreter &interpreter)
+      : CommandObjectParsed(interpreter, "breakpoint add address",
+                            "Add breakpoints by raw address", nullptr) {
+    CommandArgumentData bp_id_arg;
+
+    // Define the first (and only) variant of this arg.
+    m_all_options.Append(&m_bp_opts, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+    m_all_options.Append(&m_name_opts);
+    m_all_options.Append(&m_dummy_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+    m_all_options.Append(&m_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
+    m_all_options.Finalize();
+
+    AddSimpleArgumentList(eArgTypeAddress, eArgRepeatPlus);
+  }
+
+  ~CommandObjectBreakpointAddAddress() override = default;
+
+  Options *GetOptions() override { return &m_all_options; }
+
+  class CommandOptions : public OptionGroup {
+  public:
+    CommandOptions() = default;
+
+    ~CommandOptions() override = default;
+
+    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+                          ExecutionContext *execution_context) override {
+      Status error;
+      const int short_option = GetDefinitions()[option_idx].short_option;
+      const char *long_option = GetDefinitions()[option_idx].long_option;
+
+      switch (short_option) {
+      case 'H':
+        m_hardware = true;
+        break;
+
+      case 's':
+        if (m_modules.GetSize() == 0)
+          m_modules.AppendIfUnique(FileSpec(option_arg));
+        else
+          error = Status::FromError(
+              CreateOptionParsingError(option_arg, short_option, long_option,
+                                       "Only one shared library can be "
----------------
JDevlieghere wrote:

```suggestion
                                       "only one shared library can be "
```

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


More information about the lldb-commits mailing list