diff --git a/lldb/include/lldb/Breakpoint/BreakpointIDList.h b/lldb/include/lldb/Breakpoint/BreakpointIDList.h index f2eaa60d95413..3354a3a24893a 100644 --- a/lldb/include/lldb/Breakpoint/BreakpointIDList.h +++ b/lldb/include/lldb/Breakpoint/BreakpointIDList.h @@ -50,10 +50,9 @@ class BreakpointIDList { static std::pair SplitIDRangeExpression(llvm::StringRef in_string); - static llvm::Error - FindAndReplaceIDRanges(Args &old_args, Target *target, bool allow_locations, - BreakpointName::Permissions ::PermissionKinds purpose, - Args &new_args); + static llvm::Error FindAndReplaceIDRanges( + Args &old_args, ExecutionContext &exe_ctx, bool allow_locations, + BreakpointName::Permissions ::PermissionKinds purpose, Args &new_args); private: BreakpointIDArray m_breakpoint_ids; diff --git a/lldb/source/Breakpoint/BreakpointIDList.cpp b/lldb/source/Breakpoint/BreakpointIDList.cpp index 8c46b39c02a9e..82850d9e10b3e 100644 --- a/lldb/source/Breakpoint/BreakpointIDList.cpp +++ b/lldb/source/Breakpoint/BreakpointIDList.cpp @@ -11,9 +11,13 @@ #include "lldb/Breakpoint/Breakpoint.h" #include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/StopInfo.h" #include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/StreamString.h" +#include "lldb/lldb-forward.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" @@ -55,6 +59,16 @@ bool BreakpointIDList::Contains(BreakpointID bp_id) const { return llvm::is_contained(m_breakpoint_ids, bp_id); } +static std::string LocationIDForStop(StopInfoSP stop_info_sp, uint32_t idx) { + assert(stop_info_sp->GetStopReason() == lldb::eStopReasonBreakpoint && + "expected breakpoint stop"); + break_id_t bp_id = stop_info_sp->GetStopReasonDataAtIndex(idx); + break_id_t loc_id = stop_info_sp->GetStopReasonDataAtIndex(idx + 1); + StreamString stream; + BreakpointID::GetCanonicalReference(&stream, bp_id, loc_id); + return stream.GetString().str(); +} + // This function takes OLD_ARGS, which is usually the result of breaking the // command string arguments into // an array of space-separated strings, and searches through the arguments for @@ -69,8 +83,9 @@ bool BreakpointIDList::Contains(BreakpointID bp_id) const { // by the members of the range. llvm::Error BreakpointIDList::FindAndReplaceIDRanges( - Args &old_args, Target *target, bool allow_locations, + Args &old_args, ExecutionContext &exe_ctx, bool allow_locations, BreakpointName::Permissions ::PermissionKinds purpose, Args &new_args) { + Target *target = exe_ctx.GetTargetPtr(); llvm::StringRef range_from; llvm::StringRef range_to; llvm::StringRef current_arg; @@ -80,6 +95,30 @@ llvm::Error BreakpointIDList::FindAndReplaceIDRanges( bool is_range = false; current_arg = old_args[i].ref(); + + if (allow_locations && current_arg == ".") { + Thread *thread = exe_ctx.GetThreadPtr(); + if (!thread) { + new_args.Clear(); + return llvm::createStringError("no current thread"); + } + StopInfoSP stop_info_sp = thread->GetStopInfo(); + if (!stop_info_sp || + stop_info_sp->GetStopReason() != eStopReasonBreakpoint) { + new_args.Clear(); + return llvm::createStringError( + "current thread is not stopped at a breakpoint"); + } + + uint32_t data_count = stop_info_sp->GetStopReasonDataCount(); + for (uint32_t j = 0; j < data_count; j += 2) { + std::string location_id = LocationIDForStop(stop_info_sp, j); + new_args.AppendArgument(location_id); + } + + continue; + } + if (!allow_locations && current_arg.contains('.')) { new_args.Clear(); return llvm::createStringError( diff --git a/lldb/source/Commands/CommandObjectBreakpoint.cpp b/lldb/source/Commands/CommandObjectBreakpoint.cpp index 8b1b99110eca2..d3e314efb41c0 100644 --- a/lldb/source/Commands/CommandObjectBreakpoint.cpp +++ b/lldb/source/Commands/CommandObjectBreakpoint.cpp @@ -2075,7 +2075,7 @@ class CommandObjectBreakpointModify : public CommandObjectParsed { BreakpointIDList valid_bp_ids; CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( - command, target, result, &valid_bp_ids, + command, m_exe_ctx, result, &valid_bp_ids, BreakpointName::Permissions::PermissionKinds::disablePerm); if (result.Succeeded()) { @@ -2156,7 +2156,7 @@ class CommandObjectBreakpointEnable : public CommandObjectParsed { // Particular breakpoint selected; enable that breakpoint. BreakpointIDList valid_bp_ids; CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( - command, target, result, &valid_bp_ids, + command, m_exe_ctx, result, &valid_bp_ids, BreakpointName::Permissions::PermissionKinds::disablePerm); if (result.Succeeded()) { @@ -2265,7 +2265,7 @@ the second re-enables the first location."); BreakpointIDList valid_bp_ids; CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( - command, target, result, &valid_bp_ids, + command, m_exe_ctx, result, &valid_bp_ids, BreakpointName::Permissions::PermissionKinds::disablePerm); if (result.Succeeded()) { @@ -2412,7 +2412,7 @@ class CommandObjectBreakpointList : public CommandObjectParsed { // Particular breakpoints selected; show info about that breakpoint. BreakpointIDList valid_bp_ids; CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( - command, target, result, &valid_bp_ids, + command, m_exe_ctx, result, &valid_bp_ids, BreakpointName::Permissions::PermissionKinds::listPerm); if (result.Succeeded()) { @@ -2691,7 +2691,7 @@ class CommandObjectBreakpointDelete : public CommandObjectParsed { if (!command.empty()) { CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( - command, target, result, &excluded_bp_ids, + command, m_exe_ctx, result, &excluded_bp_ids, BreakpointName::Permissions::PermissionKinds::deletePerm); if (!result.Succeeded()) return; @@ -2710,7 +2710,7 @@ class CommandObjectBreakpointDelete : public CommandObjectParsed { } } else { CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( - command, target, result, &valid_bp_ids, + command, m_exe_ctx, result, &valid_bp_ids, BreakpointName::Permissions::PermissionKinds::deletePerm); if (!result.Succeeded()) return; @@ -3021,7 +3021,7 @@ class CommandObjectBreakpointNameAdd : public CommandObjectParsed { // Particular breakpoint selected; disable that breakpoint. BreakpointIDList valid_bp_ids; CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs( - command, target, result, &valid_bp_ids, + command, m_exe_ctx, result, &valid_bp_ids, BreakpointName::Permissions::PermissionKinds::listPerm); if (result.Succeeded()) { @@ -3095,7 +3095,7 @@ class CommandObjectBreakpointNameDelete : public CommandObjectParsed { // Particular breakpoint selected; disable that breakpoint. BreakpointIDList valid_bp_ids; CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs( - command, target, result, &valid_bp_ids, + command, m_exe_ctx, result, &valid_bp_ids, BreakpointName::Permissions::PermissionKinds::deletePerm); if (result.Succeeded()) { @@ -3568,7 +3568,7 @@ class CommandObjectBreakpointWrite : public CommandObjectParsed { BreakpointIDList valid_bp_ids; if (!command.empty()) { CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs( - command, target, result, &valid_bp_ids, + command, m_exe_ctx, result, &valid_bp_ids, BreakpointName::Permissions::PermissionKinds::listPerm); if (!result.Succeeded()) { @@ -3654,7 +3654,7 @@ CommandObjectMultiwordBreakpoint::CommandObjectMultiwordBreakpoint( CommandObjectMultiwordBreakpoint::~CommandObjectMultiwordBreakpoint() = default; void CommandObjectMultiwordBreakpoint::VerifyIDs( - Args &args, Target &target, bool allow_locations, + Args &args, ExecutionContext &exe_ctx, bool allow_locations, CommandReturnObject &result, BreakpointIDList *valid_ids, BreakpointName::Permissions ::PermissionKinds purpose) { // args can be strings representing 1). integers (for breakpoint ids) @@ -3668,6 +3668,7 @@ void CommandObjectMultiwordBreakpoint::VerifyIDs( // If args is empty, we will use the last created breakpoint (if there is // one.) + Target &target = exe_ctx.GetTargetRef(); Args temp_args; if (args.empty()) { @@ -3689,7 +3690,7 @@ void CommandObjectMultiwordBreakpoint::VerifyIDs( // into TEMP_ARGS. if (llvm::Error err = BreakpointIDList::FindAndReplaceIDRanges( - args, &target, allow_locations, purpose, temp_args)) { + args, exe_ctx, allow_locations, purpose, temp_args)) { result.SetError(std::move(err)); return; } diff --git a/lldb/source/Commands/CommandObjectBreakpoint.h b/lldb/source/Commands/CommandObjectBreakpoint.h index 40c67157e07c4..7e2fa045eb744 100644 --- a/lldb/source/Commands/CommandObjectBreakpoint.h +++ b/lldb/source/Commands/CommandObjectBreakpoint.h @@ -23,22 +23,22 @@ class CommandObjectMultiwordBreakpoint : public CommandObjectMultiword { ~CommandObjectMultiwordBreakpoint() override; static void VerifyBreakpointOrLocationIDs( - Args &args, Target &target, CommandReturnObject &result, + Args &args, ExecutionContext &exe_ctx, CommandReturnObject &result, BreakpointIDList *valid_ids, BreakpointName::Permissions ::PermissionKinds purpose) { - VerifyIDs(args, target, true, result, valid_ids, purpose); + VerifyIDs(args, exe_ctx, true, result, valid_ids, purpose); } static void - VerifyBreakpointIDs(Args &args, Target &target, CommandReturnObject &result, - BreakpointIDList *valid_ids, + VerifyBreakpointIDs(Args &args, ExecutionContext &exe_ctx, + CommandReturnObject &result, BreakpointIDList *valid_ids, BreakpointName::Permissions::PermissionKinds purpose) { - VerifyIDs(args, target, false, result, valid_ids, purpose); + VerifyIDs(args, exe_ctx, false, result, valid_ids, purpose); } private: - static void VerifyIDs(Args &args, Target &target, bool allow_locations, - CommandReturnObject &result, + static void VerifyIDs(Args &args, ExecutionContext &exe_ctx, + bool allow_locations, CommandReturnObject &result, BreakpointIDList *valid_ids, BreakpointName::Permissions::PermissionKinds purpose); }; diff --git a/lldb/source/Commands/CommandObjectBreakpointCommand.cpp b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp index a913ed5fa12b3..12a5a441817ce 100644 --- a/lldb/source/Commands/CommandObjectBreakpointCommand.cpp +++ b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp @@ -344,7 +344,7 @@ are no syntax errors may indicate that a function was declared but never called. BreakpointIDList valid_bp_ids; CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( - command, target, result, &valid_bp_ids, + command, m_exe_ctx, result, &valid_bp_ids, BreakpointName::Permissions::PermissionKinds::listPerm); m_bp_options_vec.clear(); @@ -500,7 +500,7 @@ class CommandObjectBreakpointCommandDelete : public CommandObjectParsed { BreakpointIDList valid_bp_ids; CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( - command, target, result, &valid_bp_ids, + command, m_exe_ctx, result, &valid_bp_ids, BreakpointName::Permissions::PermissionKinds::listPerm); if (result.Succeeded()) { @@ -567,7 +567,7 @@ class CommandObjectBreakpointCommandList : public CommandObjectParsed { BreakpointIDList valid_bp_ids; CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( - command, target, result, &valid_bp_ids, + command, m_exe_ctx, result, &valid_bp_ids, BreakpointName::Permissions::PermissionKinds::listPerm); if (result.Succeeded()) { diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp index 886db785586a1..269ca89c8c56f 100644 --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -534,7 +534,7 @@ class CommandObjectProcessContinue : public CommandObjectParsed { // default breakpoint. if (m_options.m_run_to_bkpt_args.GetArgumentCount() > 0) CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs( - m_options.m_run_to_bkpt_args, target, result, &run_to_bkpt_ids, + m_options.m_run_to_bkpt_args, m_exe_ctx, result, &run_to_bkpt_ids, BreakpointName::Permissions::disablePerm); if (!result.Succeeded()) { return; diff --git a/lldb/source/Commands/CommandOptionArgumentTable.cpp b/lldb/source/Commands/CommandOptionArgumentTable.cpp index e8e9307b62919..4d267ba111e16 100644 --- a/lldb/source/Commands/CommandOptionArgumentTable.cpp +++ b/lldb/source/Commands/CommandOptionArgumentTable.cpp @@ -49,7 +49,10 @@ llvm::StringRef BreakpointIDHelpTextCallback() { "major " "number, or the major number followed by a dot and the location " "number (e.g. " - "3 or 3.2 could both be valid breakpoint IDs.)"; + "3 or 3.2 could both be valid breakpoint IDs.)\n" + "\n" + "You can use . to refer to the breakpoint location(s) at which the " + "current thread is stopped."; } llvm::StringRef BreakpointIDRangeHelpTextCallback() { diff --git a/lldb/test/API/commands/breakpoint/location-dot/Makefile b/lldb/test/API/commands/breakpoint/location-dot/Makefile new file mode 100644 index 0000000000000..10495940055b6 --- /dev/null +++ b/lldb/test/API/commands/breakpoint/location-dot/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules diff --git a/lldb/test/API/commands/breakpoint/location-dot/TestBreakpointLocationDot.py b/lldb/test/API/commands/breakpoint/location-dot/TestBreakpointLocationDot.py new file mode 100644 index 0000000000000..d2f0226699c6d --- /dev/null +++ b/lldb/test/API/commands/breakpoint/location-dot/TestBreakpointLocationDot.py @@ -0,0 +1,51 @@ +import lldb +from lldbsuite.test.lldbtest import TestBase +from lldbsuite.test import lldbutil + + +class TestCase(TestBase): + def test_disable_enable(self): + self.build() + _, _, _, bp = lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec("main.c") + ) + + self.assertTrue(bp.FindLocationByID(1).IsEnabled()) + self.expect("breakpoint disable .", startstr="1 breakpoints disabled.") + self.assertFalse(bp.FindLocationByID(1).IsEnabled()) + self.expect("breakpoint enable .", startstr="1 breakpoints enabled.") + self.assertTrue(bp.FindLocationByID(1).IsEnabled()) + + def test_delete(self): + self.build() + _, _, _, bp = lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec("main.c") + ) + + self.expect( + "breakpoint delete .", + startstr="0 breakpoints deleted; 1 breakpoint locations disabled", + ) + self.assertFalse(bp.FindLocationByID(1).IsEnabled()) + + def test_error_not_breakpoint_stop(self): + self.build() + _, _, thread, bp = lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec("main.c") + ) + + self.assertTrue(bp.FindLocationByID(1).IsEnabled()) + thread.StepOver() + self.assertNotEqual(thread.stop_reason, lldb.eStopReasonBreakpoint) + self.expect( + "breakpoint disable .", + error=True, + startstr="error: current thread is not stopped at a breakpoint", + ) + self.assertTrue(bp.FindLocationByID(1).IsEnabled()) + + def test_error_no_process(self): + self.build() + target = self.createTestTarget() + target.BreakpointCreateByLocation("main.c", 2) + self.expect("breakpoint disable .", error=True, substrs=["no current thread"]) diff --git a/lldb/test/API/commands/breakpoint/location-dot/main.c b/lldb/test/API/commands/breakpoint/location-dot/main.c new file mode 100644 index 0000000000000..f4a494dd4bc25 --- /dev/null +++ b/lldb/test/API/commands/breakpoint/location-dot/main.c @@ -0,0 +1,5 @@ +int main() { + int x = 1; // break here + int y = 2; + return x + y; +}