diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py index d823126e3e2fd..1567462839748 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py @@ -173,6 +173,28 @@ def verify_breakpoint_hit(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT): return self.assertTrue(False, f"breakpoint not hit, stopped_events={stopped_events}") + def verify_all_breakpoints_hit(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT): + """Wait for the process we are debugging to stop, and verify we hit + all of the breakpoint locations in the "breakpoint_ids" array. + "breakpoint_ids" should be a list of int breakpoint IDs ([1, 2]).""" + stopped_events = self.dap_server.wait_for_stopped(timeout) + for stopped_event in stopped_events: + if "body" in stopped_event: + body = stopped_event["body"] + if "reason" not in body: + continue + if ( + body["reason"] != "breakpoint" + and body["reason"] != "instruction breakpoint" + ): + continue + if "hitBreakpointIds" not in body: + continue + hit_bps = body["hitBreakpointIds"] + if all(breakpoint_id in hit_bps for breakpoint_id in breakpoint_ids): + return + self.assertTrue(False, f"breakpoints not hit, stopped_events={stopped_events}") + def verify_stop_exception_info(self, expected_description, timeout=DEFAULT_TIMEOUT): """Wait for the process we are debugging to stop, and verify the stop reason is 'exception' and that the description matches diff --git a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py index 831edd6494c1e..2e860ff5d5e17 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py +++ b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py @@ -398,3 +398,26 @@ def test_column_breakpoints(self): self.stepIn() func_name = self.get_stackFrames()[0]["name"] self.assertEqual(func_name, "a::fourteen(int)") + + @skipIfWindows + def test_hit_multiple_breakpoints(self): + """Test that if we hit multiple breakpoints at the same address, they + all appear in the stop reason.""" + breakpoint_lines = [ + line_number("main.cpp", "// break non-breakpointable line"), + line_number("main.cpp", "// before loop"), + ] + + program = self.getBuildArtifact("a.out") + self.build_and_launch(program) + + # Set a pair of breakpoints that will both resolve to the same address. + breakpoint_ids = [ + int(bp_id) + for bp_id in self.set_source_breakpoints(self.main_path, breakpoint_lines) + ] + self.assertEqual(len(breakpoint_ids), 2, "expected two breakpoints") + self.dap_server.request_continue() + print(breakpoint_ids) + # Verify we hit both of the breakpoints we just set + self.verify_all_breakpoints_hit(breakpoint_ids) diff --git a/lldb/test/API/tools/lldb-dap/breakpoint/main.cpp b/lldb/test/API/tools/lldb-dap/breakpoint/main.cpp index a84546a95af15..2206b07f19494 100644 --- a/lldb/test/API/tools/lldb-dap/breakpoint/main.cpp +++ b/lldb/test/API/tools/lldb-dap/breakpoint/main.cpp @@ -33,7 +33,7 @@ int main(int argc, char const *argv[]) { if (foo == nullptr) { fprintf(stderr, "%s\n", dlerror()); exit(2); - } + } // break non-breakpointable line foo(12); // before loop for (int i = 0; i < 10; ++i) { diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp index 41ca29a405ac9..f42c50236f19e 100644 --- a/lldb/tools/lldb-dap/JSONUtils.cpp +++ b/lldb/tools/lldb-dap/JSONUtils.cpp @@ -654,12 +654,17 @@ llvm::json::Value CreateThreadStopped(DAP &dap, lldb::SBThread &thread, } else { body.try_emplace("reason", "breakpoint"); } - lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(0); - lldb::break_id_t bp_loc_id = thread.GetStopReasonDataAtIndex(1); - std::string desc_str = - llvm::formatv("breakpoint {0}.{1}", bp_id, bp_loc_id); - body.try_emplace("hitBreakpointIds", - llvm::json::Array{llvm::json::Value(bp_id)}); + std::vector bp_ids; + std::ostringstream desc_sstream; + desc_sstream << "breakpoint"; + for (size_t idx = 0; idx < thread.GetStopReasonDataCount(); idx += 2) { + lldb::break_id_t bp_id = thread.GetStopReasonDataAtIndex(idx); + lldb::break_id_t bp_loc_id = thread.GetStopReasonDataAtIndex(idx + 1); + bp_ids.push_back(bp_id); + desc_sstream << " " << bp_id << "." << bp_loc_id; + } + std::string desc_str = desc_sstream.str(); + body.try_emplace("hitBreakpointIds", llvm::json::Array(bp_ids)); EmplaceSafeString(body, "description", desc_str); } } break;