diff --git a/lldb/tools/lldb-vscode/BreakpointBase.h b/lldb/tools/lldb-vscode/BreakpointBase.h --- a/lldb/tools/lldb-vscode/BreakpointBase.h +++ b/lldb/tools/lldb-vscode/BreakpointBase.h @@ -15,7 +15,7 @@ #include namespace lldb_vscode { - + struct BreakpointBase { // An optional expression for conditional breakpoints. @@ -36,6 +36,7 @@ void SetCondition(); void SetHitCondition(); void UpdateBreakpoint(const BreakpointBase &request_bp); + static const char *GetBreakpointLabel(); }; } // namespace lldb_vscode diff --git a/lldb/tools/lldb-vscode/BreakpointBase.cpp b/lldb/tools/lldb-vscode/BreakpointBase.cpp --- a/lldb/tools/lldb-vscode/BreakpointBase.cpp +++ b/lldb/tools/lldb-vscode/BreakpointBase.cpp @@ -18,7 +18,7 @@ void BreakpointBase::SetCondition() { bp.SetCondition(condition.c_str()); } -void BreakpointBase::SetHitCondition() { +void BreakpointBase::SetHitCondition() { uint64_t hitCount = 0; if (llvm::to_integer(hitCondition, hitCount)) bp.SetIgnoreCount(hitCount - 1); @@ -34,3 +34,19 @@ SetHitCondition(); } } + +const char *BreakpointBase::GetBreakpointLabel() { + // Breakpoints in LLDB can have names added to them which are kind of like + // labels or categories. All breakpoints that are set through the IDE UI get + // sent through the various VS code DAP set*Breakpoint packets, and these + // breakpoints will be labeled with this name so if breakpoint update events + // come in for breakpoints that the IDE doesn't know about, like if a + // breakpoint is set manually using the debugger console, we won't report any + // updates on them and confused the IDE. This function gets called by all of + // the breakpoint classes after they set breakpoints to mark a breakpoint as + // a UI breakpoint. We can later check a lldb::SBBreakpoint object that comes + // in via LLDB breakpoint changed events and check the breakpoint by calling + // "bool lldb::SBBreakpoint::MatchesName(const char *)" to check if a + // breakpoint in one of the UI breakpoints that we should report changes for. + return "vscode"; +} diff --git a/lldb/tools/lldb-vscode/ExceptionBreakpoint.cpp b/lldb/tools/lldb-vscode/ExceptionBreakpoint.cpp --- a/lldb/tools/lldb-vscode/ExceptionBreakpoint.cpp +++ b/lldb/tools/lldb-vscode/ExceptionBreakpoint.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "ExceptionBreakpoint.h" +#include "BreakpointBase.h" #include "VSCode.h" namespace lldb_vscode { @@ -18,6 +19,9 @@ bool throw_value = filter.find("_throw") != std::string::npos; bp = g_vsc.target.BreakpointCreateForException(language, catch_value, throw_value); + // See comments in BreakpointBase::GetBreakpointLabel() for details of why + // we add a label to our breakpoints. + bp.AddName(BreakpointBase::GetBreakpointLabel()); } void ExceptionBreakpoint::ClearBreakpoint() { @@ -28,4 +32,3 @@ } } // namespace lldb_vscode - diff --git a/lldb/tools/lldb-vscode/FunctionBreakpoint.cpp b/lldb/tools/lldb-vscode/FunctionBreakpoint.cpp --- a/lldb/tools/lldb-vscode/FunctionBreakpoint.cpp +++ b/lldb/tools/lldb-vscode/FunctionBreakpoint.cpp @@ -18,6 +18,9 @@ if (functionName.empty()) return; bp = g_vsc.target.BreakpointCreateByName(functionName.c_str()); + // See comments in BreakpointBase::GetBreakpointLabel() for details of why + // we add a label to our breakpoints. + bp.AddName(GetBreakpointLabel()); if (!condition.empty()) SetCondition(); if (!hitCondition.empty()) diff --git a/lldb/tools/lldb-vscode/JSONUtils.h b/lldb/tools/lldb-vscode/JSONUtils.h --- a/lldb/tools/lldb-vscode/JSONUtils.h +++ b/lldb/tools/lldb-vscode/JSONUtils.h @@ -199,13 +199,13 @@ /// Converts breakpoint location to a Visual Studio Code "Breakpoint" /// JSON object and appends it to the \a breakpoints array. /// -/// \param[in] bp_loc -/// A LLDB breakpoint location object to convert into a JSON value +/// \param[in] bp +/// A LLDB breakpoint object to convert into a JSON value /// /// \return /// A "Breakpoint" JSON object with that follows the formal JSON /// definition outlined by Microsoft. -llvm::json::Value CreateBreakpoint(lldb::SBBreakpointLocation &bp_loc); +llvm::json::Value CreateBreakpoint(lldb::SBBreakpoint &bp); /// Create a "Event" JSON object using \a event_name as the event name /// diff --git a/lldb/tools/lldb-vscode/JSONUtils.cpp b/lldb/tools/lldb-vscode/JSONUtils.cpp --- a/lldb/tools/lldb-vscode/JSONUtils.cpp +++ b/lldb/tools/lldb-vscode/JSONUtils.cpp @@ -281,16 +281,33 @@ // }, // "required": [ "verified" ] // } -llvm::json::Value CreateBreakpoint(lldb::SBBreakpointLocation &bp_loc) { +llvm::json::Value CreateBreakpoint(lldb::SBBreakpoint &bp) { // Each breakpoint location is treated as a separate breakpoint for VS code. // They don't have the notion of a single breakpoint with multiple locations. llvm::json::Object object; - if (!bp_loc.IsValid()) + if (!bp.IsValid()) return llvm::json::Value(std::move(object)); - object.try_emplace("verified", true); - const auto vs_id = MakeVSCodeBreakpointID(bp_loc); - object.try_emplace("id", vs_id); + object.try_emplace("verified", bp.GetNumResolvedLocations() > 0); + object.try_emplace("id", bp.GetID()); + // VS Code DAP doesn't currently allow one breakpoint to have multiple + // locations so we just report the first one. If we report all locations + // then the IDE starts showing the wrong line numbers and locations for + // other source file and line breakpoints in the same file. + + // Below we search for the first resolved location in a breakpoint and report + // this as the breakpoint location since it will have a complete location + // that is at least loaded in the current process. + lldb::SBBreakpointLocation bp_loc; + const auto num_locs = bp.GetNumLocations(); + for (size_t i=0; i> BREAKPOINT_ID_SHIFT; -} - -uint32_t GetLLDBBreakpointLocationID(uint64_t dap_breakpoint_id) { - return dap_breakpoint_id & ((1u << BREAKPOINT_ID_SHIFT) - 1); -} - -int64_t MakeVSCodeBreakpointID(lldb::SBBreakpointLocation &bp_loc) { - return (int64_t)(bp_loc.GetBreakpoint().GetID() << BREAKPOINT_ID_SHIFT | - bp_loc.GetID()); -} - } // namespace lldb_vscode diff --git a/lldb/tools/lldb-vscode/SourceBreakpoint.cpp b/lldb/tools/lldb-vscode/SourceBreakpoint.cpp --- a/lldb/tools/lldb-vscode/SourceBreakpoint.cpp +++ b/lldb/tools/lldb-vscode/SourceBreakpoint.cpp @@ -17,6 +17,9 @@ void SourceBreakpoint::SetBreakpoint(const llvm::StringRef source_path) { bp = g_vsc.target.BreakpointCreateByLocation(source_path.str().c_str(), line); + // See comments in BreakpointBase::GetBreakpointLabel() for details of why + // we add a label to our breakpoints. + bp.AddName(GetBreakpointLabel()); if (!condition.empty()) SetCondition(); if (!hitCondition.empty()) diff --git a/lldb/tools/lldb-vscode/lldb-vscode.cpp b/lldb/tools/lldb-vscode/lldb-vscode.cpp --- a/lldb/tools/lldb-vscode/lldb-vscode.cpp +++ b/lldb/tools/lldb-vscode/lldb-vscode.cpp @@ -379,27 +379,20 @@ if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) { auto event_type = lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event); - const auto num_locs = - lldb::SBBreakpoint::GetNumBreakpointLocationsFromEvent(event); auto bp = lldb::SBBreakpoint::GetBreakpointFromEvent(event); - bool added = event_type & lldb::eBreakpointEventTypeLocationsAdded; - bool removed = - event_type & lldb::eBreakpointEventTypeLocationsRemoved; - if (added || removed) { - for (size_t i = 0; i < num_locs; ++i) { - auto bp_loc = - lldb::SBBreakpoint::GetBreakpointLocationAtIndexFromEvent( - event, i); - auto bp_event = CreateEventObject("breakpoint"); - llvm::json::Object body; - body.try_emplace("breakpoint", CreateBreakpoint(bp_loc)); - if (added) - body.try_emplace("reason", "new"); - else - body.try_emplace("reason", "removed"); - bp_event.try_emplace("body", std::move(body)); - g_vsc.SendJSON(llvm::json::Value(std::move(bp_event))); - } + // If the breakpoint was originated from the IDE, it will have the + // BreakpointBase::GetBreakpointLabel() label attached. Regardless + // of wether the locations were added or removed, the breakpoint + // ins't going away, so we the reason is always "changed". + if ((event_type & lldb::eBreakpointEventTypeLocationsAdded || + event_type & lldb::eBreakpointEventTypeLocationsRemoved) && + bp.MatchesName(BreakpointBase::GetBreakpointLabel())) { + auto bp_event = CreateEventObject("breakpoint"); + llvm::json::Object body; + body.try_emplace("breakpoint", CreateBreakpoint(bp)); + body.try_emplace("reason", "changed"); + bp_event.try_emplace("body", std::move(body)); + g_vsc.SendJSON(llvm::json::Value(std::move(bp_event))); } } } else if (event.BroadcasterMatchesRef(g_vsc.broadcaster)) {