diff --git a/lldb/source/Commands/CommandObjectBreakpoint.cpp b/lldb/source/Commands/CommandObjectBreakpoint.cpp --- a/lldb/source/Commands/CommandObjectBreakpoint.cpp +++ b/lldb/source/Commands/CommandObjectBreakpoint.cpp @@ -1423,7 +1423,8 @@ class CommandOptions : public Options { public: - CommandOptions() : Options(), m_use_dummy(false), m_force(false) {} + CommandOptions() : Options(), m_use_dummy(false), m_force(false), + m_delete_disabled(false) {} ~CommandOptions() override = default; @@ -1440,6 +1441,10 @@ case 'D': m_use_dummy = true; break; + + case 'd': + m_delete_disabled = true; + break; default: llvm_unreachable("Unimplemented option"); @@ -1451,6 +1456,7 @@ void OptionParsingStarting(ExecutionContext *execution_context) override { m_use_dummy = false; m_force = false; + m_delete_disabled = false; } llvm::ArrayRef GetDefinitions() override { @@ -1460,16 +1466,18 @@ // Instance variables to hold the values for command options. bool m_use_dummy; bool m_force; + bool m_delete_disabled; }; protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Target &target = GetSelectedOrDummyTarget(m_options.m_use_dummy); - + result.Clear(); + std::unique_lock lock; target.GetBreakpointList().GetListMutex(lock); - const BreakpointList &breakpoints = target.GetBreakpointList(); + BreakpointList &breakpoints = target.GetBreakpointList(); size_t num_breakpoints = breakpoints.GetSize(); @@ -1479,7 +1487,7 @@ return false; } - if (command.empty()) { + if (command.empty() && !m_options.m_delete_disabled) { if (!m_options.m_force && !m_interpreter.Confirm( "About to delete all breakpoints, do you want to do that?", @@ -1495,10 +1503,34 @@ } else { // Particular breakpoint selected; disable that breakpoint. BreakpointIDList valid_bp_ids; - CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( - command, &target, result, &valid_bp_ids, - BreakpointName::Permissions::PermissionKinds::deletePerm); + + if (m_options.m_delete_disabled) { + BreakpointIDList excluded_bp_ids; + if (!command.empty()) { + CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( + command, &target, result, &excluded_bp_ids, + BreakpointName::Permissions::PermissionKinds::deletePerm); + } + for (auto breakpoint_sp : breakpoints.Breakpoints()) { + if (!breakpoint_sp->IsEnabled() && breakpoint_sp->AllowDelete()) { + BreakpointID bp_id(breakpoint_sp->GetID()); + size_t pos = 0; + if (!excluded_bp_ids.FindBreakpointID(bp_id, &pos)) + valid_bp_ids.AddBreakpointID(breakpoint_sp->GetID()); + } + } + if (valid_bp_ids.GetSize() == 0) { + result.AppendError("No disabled breakpoints."); + result.SetStatus(eReturnStatusFailed); + return false; + } + } else { + CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( + command, &target, result, &valid_bp_ids, + BreakpointName::Permissions::PermissionKinds::deletePerm); + } + if (result.Succeeded()) { int delete_count = 0; int disable_count = 0; diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -227,6 +227,9 @@ def breakpoint_delete_dummy_breakpoints : Option<"dummy-breakpoints", "D">, Group<1>, Desc<"Delete Dummy breakpoints - i.e. breakpoints set before a " "file is provided, which prime new targets.">; + def breakpoint_delete_disabled : Option<"disabled", "d">, Group<1>, + Desc<"Delete all breakpoints which are currently disabled. When using the disabled option " + "any breakpoints listed on the command line are EXCLUDED from deletion.">; } let Command = "breakpoint name" in { diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py b/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py --- a/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py +++ b/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py @@ -287,3 +287,33 @@ self.assertEqual(com_list.GetStringAtIndex(0), "bt", "First bt") self.assertEqual(com_list.GetStringAtIndex(1), "thread list", "Next thread list") self.assertEqual(com_list.GetStringAtIndex(2), "continue", "Last continue") + + def test_breakpoint_delete_disabled(self): + """Test 'break delete --disabled' works""" + self.build() + exe = self.getBuildArtifact("a.out") + target = self.dbg.CreateTarget(exe) + self.assertTrue(target.IsValid(), "Created an invalid target.") + + bp_1 = target.BreakpointCreateByName("main") + bp_2 = target.BreakpointCreateByName("not_here") + bp_3 = target.BreakpointCreateByName("main") + bp_3.AddName("DeleteMeNot") + + bp_1.SetEnabled(False) + bp_3.SetEnabled(False) + + bp_id_1 = bp_1.GetID() + bp_id_2 = bp_2.GetID() + bp_id_3 = bp_3.GetID() + + self.runCmd("breakpoint delete --disabled DeleteMeNot") + + bp_1 = target.FindBreakpointByID(bp_id_1) + self.assertFalse(bp_1.IsValid(), "Didn't delete disabled breakpoint 1") + + bp_2 = target.FindBreakpointByID(bp_id_2) + self.assertTrue(bp_2.IsValid(), "Deleted enabled breakpoint 2") + + bp_3 = target.FindBreakpointByID(bp_id_3) + self.assertTrue(bp_3.IsValid(), "DeleteMeNot didn't protect disabled breakpoint 3")