https://github.com/ashgti created https://github.com/llvm/llvm-project/pull/115208
Refactoring breakpoints to not use the `g_dap` reference. Instead, when a breakpoint is constructed it will be passed a DAP reference that it should use for its lifetime. This is part of a larger refactor to remove the global `g_dap` variable to allow us to create multiple DAP instances. >From a272a8d4189ffcf79052787ceaf23374a44de8f6 Mon Sep 17 00:00:00 2001 From: John Harrison <harj...@google.com> Date: Wed, 6 Nov 2024 09:36:48 -0800 Subject: [PATCH] [lldb-dap] Refactoring breakpoints to not use the `g_dap` reference. Instead, when a breakpoint is constructed it will be passed a DAP reference that it should use for its lifetime. This is part of a larger refactor to remove the global `g_dap` variable in favor of managing instances. --- lldb/tools/lldb-dap/Breakpoint.cpp | 8 +- lldb/tools/lldb-dap/Breakpoint.h | 9 +- lldb/tools/lldb-dap/BreakpointBase.cpp | 4 +- lldb/tools/lldb-dap/BreakpointBase.h | 12 +- lldb/tools/lldb-dap/DAP.cpp | 18 +- lldb/tools/lldb-dap/DAPForward.h | 1 + lldb/tools/lldb-dap/ExceptionBreakpoint.cpp | 6 +- lldb/tools/lldb-dap/ExceptionBreakpoint.h | 12 +- lldb/tools/lldb-dap/FunctionBreakpoint.cpp | 7 +- lldb/tools/lldb-dap/FunctionBreakpoint.h | 4 +- lldb/tools/lldb-dap/InstructionBreakpoint.cpp | 13 +- lldb/tools/lldb-dap/InstructionBreakpoint.h | 14 +- lldb/tools/lldb-dap/JSONUtils.cpp | 64 --- lldb/tools/lldb-dap/JSONUtils.h | 11 - lldb/tools/lldb-dap/SourceBreakpoint.cpp | 17 +- lldb/tools/lldb-dap/SourceBreakpoint.h | 3 +- lldb/tools/lldb-dap/Watchpoint.cpp | 9 +- lldb/tools/lldb-dap/Watchpoint.h | 8 +- lldb/tools/lldb-dap/lldb-dap.cpp | 475 ++++++++---------- 19 files changed, 307 insertions(+), 388 deletions(-) diff --git a/lldb/tools/lldb-dap/Breakpoint.cpp b/lldb/tools/lldb-dap/Breakpoint.cpp index 9ea7a42ca85a1e..aee2f87e2cc23e 100644 --- a/lldb/tools/lldb-dap/Breakpoint.cpp +++ b/lldb/tools/lldb-dap/Breakpoint.cpp @@ -7,11 +7,13 @@ //===----------------------------------------------------------------------===// #include "Breakpoint.h" -#include "DAP.h" -#include "JSONUtils.h" + #include "lldb/API/SBBreakpointLocation.h" #include "llvm/ADT/StringExtras.h" +#include "DAP.h" +#include "JSONUtils.h" + using namespace lldb_dap; void Breakpoint::SetCondition() { bp.SetCondition(condition.c_str()); } @@ -51,7 +53,7 @@ void Breakpoint::CreateJsonObject(llvm::json::Object &object) { if (bp_addr.IsValid()) { std::string formatted_addr = - "0x" + llvm::utohexstr(bp_addr.GetLoadAddress(g_dap.target)); + "0x" + llvm::utohexstr(bp_addr.GetLoadAddress(bp.GetTarget())); object.try_emplace("instructionReference", formatted_addr); auto line_entry = bp_addr.GetLineEntry(); const auto line = line_entry.GetLine(); diff --git a/lldb/tools/lldb-dap/Breakpoint.h b/lldb/tools/lldb-dap/Breakpoint.h index ee9d3736d6190f..cffeb2fab1f0ef 100644 --- a/lldb/tools/lldb-dap/Breakpoint.h +++ b/lldb/tools/lldb-dap/Breakpoint.h @@ -9,18 +9,19 @@ #ifndef LLDB_TOOLS_LLDB_DAP_BREAKPOINT_H #define LLDB_TOOLS_LLDB_DAP_BREAKPOINT_H -#include "BreakpointBase.h" #include "lldb/API/SBBreakpoint.h" +#include "BreakpointBase.h" + namespace lldb_dap { struct Breakpoint : public BreakpointBase { // The LLDB breakpoint associated wit this source breakpoint lldb::SBBreakpoint bp; - Breakpoint() = default; - Breakpoint(const llvm::json::Object &obj) : BreakpointBase(obj){}; - Breakpoint(lldb::SBBreakpoint bp) : bp(bp) {} + Breakpoint(DAP &d) : BreakpointBase(d) {} + Breakpoint(DAP &d, const llvm::json::Object &obj) : BreakpointBase(d, obj) {} + Breakpoint(DAP &d, lldb::SBBreakpoint bp) : BreakpointBase(d), bp(bp) {} void SetCondition() override; void SetHitCondition() override; diff --git a/lldb/tools/lldb-dap/BreakpointBase.cpp b/lldb/tools/lldb-dap/BreakpointBase.cpp index f3cb06a3562d48..c5d7a9778df018 100644 --- a/lldb/tools/lldb-dap/BreakpointBase.cpp +++ b/lldb/tools/lldb-dap/BreakpointBase.cpp @@ -11,8 +11,8 @@ using namespace lldb_dap; -BreakpointBase::BreakpointBase(const llvm::json::Object &obj) - : condition(std::string(GetString(obj, "condition"))), +BreakpointBase::BreakpointBase(DAP &d, const llvm::json::Object &obj) + : dap(d), condition(std::string(GetString(obj, "condition"))), hitCondition(std::string(GetString(obj, "hitCondition"))) {} void BreakpointBase::UpdateBreakpoint(const BreakpointBase &request_bp) { diff --git a/lldb/tools/lldb-dap/BreakpointBase.h b/lldb/tools/lldb-dap/BreakpointBase.h index 79301480e0e588..bb660ddc451bd5 100644 --- a/lldb/tools/lldb-dap/BreakpointBase.h +++ b/lldb/tools/lldb-dap/BreakpointBase.h @@ -9,12 +9,17 @@ #ifndef LLDB_TOOLS_LLDB_DAP_BREAKPOINTBASE_H #define LLDB_TOOLS_LLDB_DAP_BREAKPOINTBASE_H -#include "llvm/Support/JSON.h" #include <string> +#include "llvm/Support/JSON.h" + +#include "DAPForward.h" + namespace lldb_dap { struct BreakpointBase { + // Associated DAP session. + DAP &dap; // An optional expression for conditional breakpoints. std::string condition; @@ -22,8 +27,9 @@ struct BreakpointBase { // ignored. The backend is expected to interpret the expression as needed std::string hitCondition; - BreakpointBase() = default; - BreakpointBase(const llvm::json::Object &obj); + BreakpointBase(DAP &d) : dap(d) {} + BreakpointBase(DAP &d, const llvm::json::Object &obj); + BreakpointBase(const BreakpointBase &) = default; virtual ~BreakpointBase() = default; virtual void SetCondition() = 0; diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 283392270ba26c..9921e15e1e75cd 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -74,21 +74,21 @@ void DAP::PopulateExceptionBreakpoints() { exception_breakpoints = std::vector<ExceptionBreakpoint>{}; if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeC_plus_plus)) { - exception_breakpoints->emplace_back("cpp_catch", "C++ Catch", + exception_breakpoints->emplace_back(*this, "cpp_catch", "C++ Catch", lldb::eLanguageTypeC_plus_plus); - exception_breakpoints->emplace_back("cpp_throw", "C++ Throw", + exception_breakpoints->emplace_back(*this, "cpp_throw", "C++ Throw", lldb::eLanguageTypeC_plus_plus); } if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeObjC)) { - exception_breakpoints->emplace_back("objc_catch", "Objective-C Catch", + exception_breakpoints->emplace_back(*this, "objc_catch", "Objective-C Catch", lldb::eLanguageTypeObjC); - exception_breakpoints->emplace_back("objc_throw", "Objective-C Throw", + exception_breakpoints->emplace_back(*this, "objc_throw", "Objective-C Throw", lldb::eLanguageTypeObjC); } if (lldb::SBDebugger::SupportsLanguage(lldb::eLanguageTypeSwift)) { - exception_breakpoints->emplace_back("swift_catch", "Swift Catch", + exception_breakpoints->emplace_back(*this, "swift_catch", "Swift Catch", lldb::eLanguageTypeSwift); - exception_breakpoints->emplace_back("swift_throw", "Swift Throw", + exception_breakpoints->emplace_back(*this, "swift_throw", "Swift Throw", lldb::eLanguageTypeSwift); } // Besides handling the hardcoded list of languages from above, we try to @@ -118,7 +118,7 @@ void DAP::PopulateExceptionBreakpoints() { std::string throw_keyword = raw_throw_keyword ? raw_throw_keyword : "throw"; - exception_breakpoints->emplace_back( + exception_breakpoints->emplace_back(*this, raw_lang_name + "_" + throw_keyword, capitalized_lang_name + " " + capitalize(throw_keyword), lang); } @@ -129,7 +129,7 @@ void DAP::PopulateExceptionBreakpoints() { std::string catch_keyword = raw_catch_keyword ? raw_catch_keyword : "catch"; - exception_breakpoints->emplace_back( + exception_breakpoints->emplace_back(*this, raw_lang_name + "_" + catch_keyword, capitalized_lang_name + " " + capitalize(catch_keyword), lang); } @@ -1060,7 +1060,7 @@ void DAP::SetThreadFormat(llvm::StringRef format) { InstructionBreakpoint * DAP::GetInstructionBreakpoint(const lldb::break_id_t bp_id) { for (auto &bp : instruction_breakpoints) { - if (bp.second.id == bp_id) + if (bp.second.bp.GetID() == bp_id) return &bp.second; } return nullptr; diff --git a/lldb/tools/lldb-dap/DAPForward.h b/lldb/tools/lldb-dap/DAPForward.h index 159d999a63c820..2bb54d806fd6fb 100644 --- a/lldb/tools/lldb-dap/DAPForward.h +++ b/lldb/tools/lldb-dap/DAPForward.h @@ -16,6 +16,7 @@ struct FunctionBreakpoint; struct SourceBreakpoint; struct Watchpoint; struct InstructionBreakpoint; +struct DAP; } // namespace lldb_dap namespace lldb { diff --git a/lldb/tools/lldb-dap/ExceptionBreakpoint.cpp b/lldb/tools/lldb-dap/ExceptionBreakpoint.cpp index 130c237e65441d..e9bb11face49df 100644 --- a/lldb/tools/lldb-dap/ExceptionBreakpoint.cpp +++ b/lldb/tools/lldb-dap/ExceptionBreakpoint.cpp @@ -17,8 +17,8 @@ void ExceptionBreakpoint::SetBreakpoint() { return; bool catch_value = filter.find("_catch") != std::string::npos; bool throw_value = filter.find("_throw") != std::string::npos; - bp = g_dap.target.BreakpointCreateForException(language, catch_value, - throw_value); + bp = dap.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()); @@ -27,7 +27,7 @@ void ExceptionBreakpoint::SetBreakpoint() { void ExceptionBreakpoint::ClearBreakpoint() { if (!bp.IsValid()) return; - g_dap.target.BreakpointDelete(bp.GetID()); + dap.target.BreakpointDelete(bp.GetID()); bp = lldb::SBBreakpoint(); } diff --git a/lldb/tools/lldb-dap/ExceptionBreakpoint.h b/lldb/tools/lldb-dap/ExceptionBreakpoint.h index 7b81d845cb26be..7819bea726a1d0 100644 --- a/lldb/tools/lldb-dap/ExceptionBreakpoint.h +++ b/lldb/tools/lldb-dap/ExceptionBreakpoint.h @@ -13,17 +13,21 @@ #include "lldb/API/SBBreakpoint.h" +#include "DAPForward.h" + namespace lldb_dap { struct ExceptionBreakpoint { + DAP &dap; std::string filter; std::string label; lldb::LanguageType language; - bool default_value; + bool default_value = false; lldb::SBBreakpoint bp; - ExceptionBreakpoint(std::string f, std::string l, lldb::LanguageType lang) - : filter(std::move(f)), label(std::move(l)), language(lang), - default_value(false), bp() {} + ExceptionBreakpoint(DAP &d, std::string f, std::string l, + lldb::LanguageType lang) + : dap(d), filter(std::move(f)), label(std::move(l)), language(lang), + bp() {} void SetBreakpoint(); void ClearBreakpoint(); diff --git a/lldb/tools/lldb-dap/FunctionBreakpoint.cpp b/lldb/tools/lldb-dap/FunctionBreakpoint.cpp index 216c685f633da8..ef6df6c0dc91cc 100644 --- a/lldb/tools/lldb-dap/FunctionBreakpoint.cpp +++ b/lldb/tools/lldb-dap/FunctionBreakpoint.cpp @@ -7,18 +7,19 @@ //===----------------------------------------------------------------------===// #include "FunctionBreakpoint.h" + #include "DAP.h" #include "JSONUtils.h" namespace lldb_dap { -FunctionBreakpoint::FunctionBreakpoint(const llvm::json::Object &obj) - : Breakpoint(obj), functionName(std::string(GetString(obj, "name"))) {} +FunctionBreakpoint::FunctionBreakpoint(DAP &d, const llvm::json::Object &obj) + : Breakpoint(d, obj), functionName(std::string(GetString(obj, "name"))) {} void FunctionBreakpoint::SetBreakpoint() { if (functionName.empty()) return; - bp = g_dap.target.BreakpointCreateByName(functionName.c_str()); + bp = dap.target.BreakpointCreateByName(functionName.c_str()); Breakpoint::SetBreakpoint(); } diff --git a/lldb/tools/lldb-dap/FunctionBreakpoint.h b/lldb/tools/lldb-dap/FunctionBreakpoint.h index b15ff1931a6b22..93f0b93b35291d 100644 --- a/lldb/tools/lldb-dap/FunctionBreakpoint.h +++ b/lldb/tools/lldb-dap/FunctionBreakpoint.h @@ -10,14 +10,14 @@ #define LLDB_TOOLS_LLDB_DAP_FUNCTIONBREAKPOINT_H #include "Breakpoint.h" +#include "DAPForward.h" namespace lldb_dap { struct FunctionBreakpoint : public Breakpoint { std::string functionName; - FunctionBreakpoint() = default; - FunctionBreakpoint(const llvm::json::Object &obj); + FunctionBreakpoint(DAP &dap, const llvm::json::Object &obj); // Set this breakpoint in LLDB as a new breakpoint void SetBreakpoint(); diff --git a/lldb/tools/lldb-dap/InstructionBreakpoint.cpp b/lldb/tools/lldb-dap/InstructionBreakpoint.cpp index e3a8460bb7b301..8c5c32c9270b41 100644 --- a/lldb/tools/lldb-dap/InstructionBreakpoint.cpp +++ b/lldb/tools/lldb-dap/InstructionBreakpoint.cpp @@ -8,22 +8,25 @@ //===----------------------------------------------------------------------===// #include "InstructionBreakpoint.h" + #include "DAP.h" #include "JSONUtils.h" namespace lldb_dap { // Instruction Breakpoint -InstructionBreakpoint::InstructionBreakpoint(const llvm::json::Object &obj) - : Breakpoint(obj), instructionAddressReference(LLDB_INVALID_ADDRESS), id(0), +InstructionBreakpoint::InstructionBreakpoint(DAP &d, + const llvm::json::Object &obj) + : Breakpoint(d, obj), instructionAddressReference(LLDB_INVALID_ADDRESS), offset(GetSigned(obj, "offset", 0)) { GetString(obj, "instructionReference") .getAsInteger(0, instructionAddressReference); instructionAddressReference += offset; } -void InstructionBreakpoint::SetInstructionBreakpoint() { - bp = g_dap.target.BreakpointCreateByAddress(instructionAddressReference); - id = bp.GetID(); +void InstructionBreakpoint::SetBreakpoint() { + bp = dap.target.BreakpointCreateByAddress(instructionAddressReference); + Breakpoint::SetBreakpoint(); } + } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/InstructionBreakpoint.h b/lldb/tools/lldb-dap/InstructionBreakpoint.h index 53912af46ca148..cc251c96f5bdd8 100644 --- a/lldb/tools/lldb-dap/InstructionBreakpoint.h +++ b/lldb/tools/lldb-dap/InstructionBreakpoint.h @@ -10,7 +10,12 @@ #ifndef LLDB_TOOLS_LLDB_DAP_INSTRUCTIONBREAKPOINT_H #define LLDB_TOOLS_LLDB_DAP_INSTRUCTIONBREAKPOINT_H +#include <cstdint> + +#include "lldb/lldb-types.h" + #include "Breakpoint.h" +#include "DAPForward.h" namespace lldb_dap { @@ -18,16 +23,15 @@ namespace lldb_dap { struct InstructionBreakpoint : public Breakpoint { lldb::addr_t instructionAddressReference; - int32_t id; int32_t offset; - InstructionBreakpoint() - : Breakpoint(), instructionAddressReference(LLDB_INVALID_ADDRESS), id(0), + InstructionBreakpoint(DAP &d) + : Breakpoint(d), instructionAddressReference(LLDB_INVALID_ADDRESS), offset(0) {} - InstructionBreakpoint(const llvm::json::Object &obj); + InstructionBreakpoint(DAP &d, const llvm::json::Object &obj); // Set instruction breakpoint in LLDB as a new breakpoint - void SetInstructionBreakpoint(); + void SetBreakpoint(); }; } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp index 97fe6b4f9f05db..70bebddc91119d 100644 --- a/lldb/tools/lldb-dap/JSONUtils.cpp +++ b/lldb/tools/lldb-dap/JSONUtils.cpp @@ -831,70 +831,6 @@ llvm::json::Value CreateExtendedStackFrameLabel(lldb::SBThread &thread) { {"presentationHint", "label"}}); } -// Response to `setInstructionBreakpoints` request. -// "Breakpoint": { -// "type": "object", -// "description": "Response to `setInstructionBreakpoints` request.", -// "properties": { -// "id": { -// "type": "number", -// "description": "The identifier for the breakpoint. It is needed if -// breakpoint events are used to update or remove breakpoints." -// }, -// "verified": { -// "type": "boolean", -// "description": "If true, the breakpoint could be set (but not -// necessarily at the desired location." -// }, -// "message": { -// "type": "string", -// "description": "A message about the state of the breakpoint. -// This is shown to the user and can be used to explain why a breakpoint -// could not be verified." -// }, -// "source": { -// "type": "Source", -// "description": "The source where the breakpoint is located." -// }, -// "line": { -// "type": "number", -// "description": "The start line of the actual range covered by the -// breakpoint." -// }, -// "column": { -// "type": "number", -// "description": "The start column of the actual range covered by the -// breakpoint." -// }, -// "endLine": { -// "type": "number", -// "description": "The end line of the actual range covered by the -// breakpoint." -// }, -// "endColumn": { -// "type": "number", -// "description": "The end column of the actual range covered by the -// breakpoint. If no end line is given, then the end column is assumed to -// be in the start line." -// }, -// "instructionReference": { -// "type": "string", -// "description": "A memory reference to where the breakpoint is set." -// }, -// "offset": { -// "type": "number", -// "description": "The offset from the instruction reference. -// This can be negative." -// }, -// }, -// "required": [ "id", "verified", "line"] -// } -llvm::json::Value CreateInstructionBreakpoint(BreakpointBase *ibp) { - llvm::json::Object object; - ibp->CreateJsonObject(object); - return llvm::json::Value(std::move(object)); -} - // "Thread": { // "type": "object", // "description": "A Thread", diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h index 54fc4323475723..43056f3dc14566 100644 --- a/lldb/tools/lldb-dap/JSONUtils.h +++ b/lldb/tools/lldb-dap/JSONUtils.h @@ -380,17 +380,6 @@ llvm::json::Value CreateStackFrame(lldb::SBFrame &frame); /// definition outlined by Microsoft. llvm::json::Value CreateExtendedStackFrameLabel(lldb::SBThread &thread); -/// Create a "instruction" object for a LLDB disassemble object as described in -/// the Visual Studio Code debug adaptor definition. -/// -/// \param[in] bp -/// The LLDB instruction object used to populate the disassembly -/// instruction. -/// \return -/// A "Scope" JSON object with that follows the formal JSON -/// definition outlined by Microsoft. -llvm::json::Value CreateInstructionBreakpoint(BreakpointBase *ibp); - /// Create a "Thread" object for a LLDB thread object. /// /// This function will fill in the following keys in the returned diff --git a/lldb/tools/lldb-dap/SourceBreakpoint.cpp b/lldb/tools/lldb-dap/SourceBreakpoint.cpp index d1a3a5bedb0ae2..7415a0914dad43 100644 --- a/lldb/tools/lldb-dap/SourceBreakpoint.cpp +++ b/lldb/tools/lldb-dap/SourceBreakpoint.cpp @@ -12,15 +12,16 @@ namespace lldb_dap { -SourceBreakpoint::SourceBreakpoint(const llvm::json::Object &obj) - : Breakpoint(obj), logMessage(std::string(GetString(obj, "logMessage"))), +SourceBreakpoint::SourceBreakpoint(DAP &dap, const llvm::json::Object &obj) + : Breakpoint(dap, obj), + logMessage(std::string(GetString(obj, "logMessage"))), line(GetUnsigned(obj, "line", 0)), column(GetUnsigned(obj, "column", 0)) { } void SourceBreakpoint::SetBreakpoint(const llvm::StringRef source_path) { lldb::SBFileSpecList module_list; - bp = g_dap.target.BreakpointCreateByLocation(source_path.str().c_str(), line, - column, 0, module_list); + bp = dap.target.BreakpointCreateByLocation(source_path.str().c_str(), line, + column, 0, module_list); if (!logMessage.empty()) SetLogMessage(); Breakpoint::SetBreakpoint(); @@ -279,7 +280,7 @@ void SourceBreakpoint::SetLogMessage() { void SourceBreakpoint::NotifyLogMessageError(llvm::StringRef error) { std::string message = "Log message has error: "; message += error; - g_dap.SendOutput(OutputType::Console, message); + dap.SendOutput(OutputType::Console, message); } /*static*/ @@ -304,14 +305,16 @@ bool SourceBreakpoint::BreakpointHitCallback( frame.GetValueForVariablePath(expr, lldb::eDynamicDontRunTarget); if (value.GetError().Fail()) value = frame.EvaluateExpression(expr); - output += VariableDescription(value).display_value; + output += + VariableDescription(value, bp->dap.enable_auto_variable_summaries) + .display_value; } else { output += messagePart.text; } } if (!output.empty() && output.back() != '\n') output.push_back('\n'); // Ensure log message has line break. - g_dap.SendOutput(OutputType::Console, output.c_str()); + bp->dap.SendOutput(OutputType::Console, output.c_str()); // Do not stop. return false; diff --git a/lldb/tools/lldb-dap/SourceBreakpoint.h b/lldb/tools/lldb-dap/SourceBreakpoint.h index aa3fbe6d0f96d2..113c0efbddcc5c 100644 --- a/lldb/tools/lldb-dap/SourceBreakpoint.h +++ b/lldb/tools/lldb-dap/SourceBreakpoint.h @@ -31,8 +31,7 @@ struct SourceBreakpoint : public Breakpoint { uint32_t line; ///< The source line of the breakpoint or logpoint uint32_t column; ///< An optional source column of the breakpoint - SourceBreakpoint() : Breakpoint(), line(0), column(0) {} - SourceBreakpoint(const llvm::json::Object &obj); + SourceBreakpoint(DAP &d, const llvm::json::Object &obj); // Set this breakpoint in LLDB as a new breakpoint void SetBreakpoint(const llvm::StringRef source_path); diff --git a/lldb/tools/lldb-dap/Watchpoint.cpp b/lldb/tools/lldb-dap/Watchpoint.cpp index 21765509449140..56dd5434269151 100644 --- a/lldb/tools/lldb-dap/Watchpoint.cpp +++ b/lldb/tools/lldb-dap/Watchpoint.cpp @@ -7,12 +7,15 @@ //===----------------------------------------------------------------------===// #include "Watchpoint.h" + +#include "llvm/ADT/StringExtras.h" + #include "DAP.h" #include "JSONUtils.h" -#include "llvm/ADT/StringExtras.h" namespace lldb_dap { -Watchpoint::Watchpoint(const llvm::json::Object &obj) : BreakpointBase(obj) { +Watchpoint::Watchpoint(DAP &d, const llvm::json::Object &obj) + : BreakpointBase(d, obj) { llvm::StringRef dataId = GetString(obj, "dataId"); std::string accessType = GetString(obj, "accessType").str(); auto [addr_str, size_str] = dataId.split('/'); @@ -42,7 +45,7 @@ void Watchpoint::CreateJsonObject(llvm::json::Object &object) { } void Watchpoint::SetWatchpoint() { - wp = g_dap.target.WatchpointCreateByAddress(addr, size, options, error); + wp = dap.target.WatchpointCreateByAddress(addr, size, options, error); if (!condition.empty()) SetCondition(); if (!hitCondition.empty()) diff --git a/lldb/tools/lldb-dap/Watchpoint.h b/lldb/tools/lldb-dap/Watchpoint.h index 4d2e58ed753360..b84b4b1c9ff3d3 100644 --- a/lldb/tools/lldb-dap/Watchpoint.h +++ b/lldb/tools/lldb-dap/Watchpoint.h @@ -9,11 +9,12 @@ #ifndef LLDB_TOOLS_LLDB_DAP_WATCHPOINT_H #define LLDB_TOOLS_LLDB_DAP_WATCHPOINT_H -#include "BreakpointBase.h" #include "lldb/API/SBError.h" #include "lldb/API/SBWatchpoint.h" #include "lldb/API/SBWatchpointOptions.h" +#include "BreakpointBase.h" + namespace lldb_dap { struct Watchpoint : public BreakpointBase { @@ -24,9 +25,8 @@ struct Watchpoint : public BreakpointBase { lldb::SBWatchpoint wp; lldb::SBError error; - Watchpoint() = default; - Watchpoint(const llvm::json::Object &obj); - Watchpoint(lldb::SBWatchpoint wp) : wp(wp) {} + Watchpoint(DAP &d, const llvm::json::Object &obj); + Watchpoint(DAP &d, lldb::SBWatchpoint wp) : BreakpointBase(d), wp(wp) {} void SetCondition() override; void SetHitCondition() override; diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp index a2f7be2b214e4a..1983f1d57c27ee 100644 --- a/lldb/tools/lldb-dap/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/lldb-dap.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "DAP.h" +#include "DAPForward.h" #include "FifoFiles.h" #include "RunInTerminal.h" #include "Watchpoint.h" @@ -15,6 +16,7 @@ #include "lldb/API/SBListener.h" #include "lldb/API/SBMemoryRegionInfo.h" #include "lldb/API/SBStringList.h" +#include "llvm/ADT/SetVector.h" #include "llvm/Support/Base64.h" #include <cassert> @@ -526,8 +528,8 @@ void EventThreadFunction() { if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) { auto event_type = lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event); - auto bp = - Breakpoint(lldb::SBBreakpoint::GetBreakpointFromEvent(event)); + auto bp = Breakpoint( + g_dap, lldb::SBBreakpoint::GetBreakpointFromEvent(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 @@ -2689,10 +2691,10 @@ void request_setBreakpoints(const llvm::json::Object &request) { llvm::json::Object response; lldb::SBError error; FillResponse(request, response); - auto arguments = request.getObject("arguments"); - auto source = arguments->getObject("source"); + const auto *arguments = request.getObject("arguments"); + const auto *source = arguments->getObject("source"); const auto path = GetString(source, "path"); - auto breakpoints = arguments->getArray("breakpoints"); + const auto *breakpoints = arguments->getArray("breakpoints"); llvm::json::Array response_breakpoints; // Decode the source breakpoint infos for this "setBreakpoints" request @@ -2701,28 +2703,21 @@ void request_setBreakpoints(const llvm::json::Object &request) { // to an empty array. if (breakpoints) { for (const auto &bp : *breakpoints) { - auto bp_obj = bp.getAsObject(); + const auto *bp_obj = bp.getAsObject(); if (bp_obj) { - SourceBreakpoint src_bp(*bp_obj); - request_bps[src_bp.line] = src_bp; - + SourceBreakpoint src_bp(g_dap, *bp_obj); + request_bps.try_emplace(src_bp.line, src_bp); + const auto [kv, inserted] = + g_dap.source_breakpoints[path].try_emplace(src_bp.line, src_bp); // We check if this breakpoint already exists to update it - auto existing_source_bps = g_dap.source_breakpoints.find(path); - if (existing_source_bps != g_dap.source_breakpoints.end()) { - const auto &existing_bp = - existing_source_bps->second.find(src_bp.line); - if (existing_bp != existing_source_bps->second.end()) { - existing_bp->second.UpdateBreakpoint(src_bp); - AppendBreakpoint(&existing_bp->second, response_breakpoints, path, - src_bp.line); - continue; - } + if (inserted) { + kv->getSecond().SetBreakpoint(path.data()); + } else { + kv->getSecond().UpdateBreakpoint(src_bp); } - // At this point the breakpoint is new - g_dap.source_breakpoints[path][src_bp.line] = src_bp; - SourceBreakpoint &new_bp = g_dap.source_breakpoints[path][src_bp.line]; - new_bp.SetBreakpoint(path.data()); - AppendBreakpoint(&new_bp, response_breakpoints, path, new_bp.line); + + AppendBreakpoint(&kv->getSecond(), response_breakpoints, path, + src_bp.line); } } } @@ -2799,8 +2794,8 @@ void request_setExceptionBreakpoints(const llvm::json::Object &request) { llvm::json::Object response; lldb::SBError error; FillResponse(request, response); - auto arguments = request.getObject("arguments"); - auto filters = arguments->getArray("filters"); + const auto *arguments = request.getObject("arguments"); + const auto *filters = arguments->getArray("filters"); // Keep a list of any exception breakpoint filter names that weren't set // so we can clear any exception breakpoints if needed. std::set<std::string> unset_filters; @@ -2905,51 +2900,39 @@ void request_setFunctionBreakpoints(const llvm::json::Object &request) { llvm::json::Object response; lldb::SBError error; FillResponse(request, response); - auto arguments = request.getObject("arguments"); - auto breakpoints = arguments->getArray("breakpoints"); - FunctionBreakpointMap request_bps; + const auto *arguments = request.getObject("arguments"); + const auto *breakpoints = arguments->getArray("breakpoints"); llvm::json::Array response_breakpoints; - for (const auto &value : *breakpoints) { - auto bp_obj = value.getAsObject(); - if (bp_obj == nullptr) - continue; - FunctionBreakpoint func_bp(*bp_obj); - request_bps[func_bp.functionName] = std::move(func_bp); - } - std::vector<llvm::StringRef> remove_names; - // Disable any function breakpoints that aren't in the request_bps. + // Disable any function breakpoints that aren't in this request. // There is no call to remove function breakpoints other than calling this // function with a smaller or empty "breakpoints" list. - for (auto &pair : g_dap.function_breakpoints) { - auto request_pos = request_bps.find(pair.first()); - if (request_pos == request_bps.end()) { - // This function breakpoint no longer exists delete it from LLDB - g_dap.target.BreakpointDelete(pair.second.bp.GetID()); - remove_names.push_back(pair.first()); + const auto name_iter = g_dap.function_breakpoints.keys(); + llvm::SetVector<llvm::StringRef> seen(name_iter.begin(), name_iter.end()); + for (const auto &value : *breakpoints) { + const auto *bp_obj = value.getAsObject(); + if (!bp_obj) + continue; + FunctionBreakpoint fn_bp(g_dap, *bp_obj); + const auto [kv, inserted] = g_dap.function_breakpoints.try_emplace( + fn_bp.functionName, g_dap, *bp_obj); + if (inserted) { + kv->second.SetBreakpoint(); } else { - // Update the existing breakpoint as any setting withing the function - // breakpoint might have changed. - pair.second.UpdateBreakpoint(request_pos->second); - // Remove this breakpoint from the request breakpoints since we have - // handled it here and we don't need to set a new breakpoint below. - request_bps.erase(request_pos); - // Add this breakpoint info to the response - AppendBreakpoint(&pair.second, response_breakpoints); + kv->second.UpdateBreakpoint(fn_bp); } + + AppendBreakpoint(&kv->second, response_breakpoints); + seen.remove(fn_bp.functionName); } + // Remove any breakpoints that are no longer in our list - for (const auto &name : remove_names) + for (const auto &name : seen) { + auto fn_bp = g_dap.function_breakpoints.find(name); + if (fn_bp == g_dap.function_breakpoints.end()) + continue; + g_dap.target.BreakpointDelete(fn_bp->second.bp.GetID()); g_dap.function_breakpoints.erase(name); - - // Any breakpoints that are left in "request_bps" are breakpoints that - // need to be set. - for (auto &pair : request_bps) { - // Add this breakpoint info to the response - g_dap.function_breakpoints[pair.first()] = std::move(pair.second); - FunctionBreakpoint &new_bp = g_dap.function_breakpoints[pair.first()]; - new_bp.SetBreakpoint(); - AppendBreakpoint(&new_bp, response_breakpoints); } llvm::json::Object body; @@ -3202,8 +3185,7 @@ void request_setDataBreakpoints(const llvm::json::Object &request) { for (const auto &bp : *breakpoints) { const auto *bp_obj = bp.getAsObject(); if (bp_obj) { - Watchpoint wp(*bp_obj); - watchpoints.push_back(wp); + watchpoints.emplace_back(g_dap, *bp_obj); } } } @@ -4551,7 +4533,7 @@ void request__testGetTargetBreakpoints(const llvm::json::Object &request) { FillResponse(request, response); llvm::json::Array response_breakpoints; for (uint32_t i = 0; g_dap.target.GetBreakpointAtIndex(i).IsValid(); ++i) { - auto bp = Breakpoint(g_dap.target.GetBreakpointAtIndex(i)); + auto bp = Breakpoint(g_dap, g_dap.target.GetBreakpointAtIndex(i)); AppendBreakpoint(&bp, response_breakpoints); } llvm::json::Object body; @@ -4560,10 +4542,11 @@ void request__testGetTargetBreakpoints(const llvm::json::Object &request) { g_dap.SendJSON(llvm::json::Value(std::move(response))); } -// "SetInstructionBreakpointsRequest" : { -// "allOf" : [ -// {"$ref" : "#/definitions/Request"}, { -// "type" : "object", +// "SetInstructionBreakpointsRequest": { +// "allOf": [ +// {"$ref": "#/definitions/Request"}, +// { +// "type": "object", // "description" : // "Replaces all existing instruction breakpoints. Typically, " // "instruction breakpoints would be set from a disassembly window. " @@ -4572,235 +4555,219 @@ void request__testGetTargetBreakpoints(const llvm::json::Object &request) { // "(with reason `instruction breakpoint`) is generated.\nClients " // "should only call this request if the corresponding capability " // "`supportsInstructionBreakpoints` is true.", -// "properties" : { -// "command" : {"type" : "string", "enum" : -// ["setInstructionBreakpoints"]}, "arguments" : -// {"$ref" : "#/definitions/SetInstructionBreakpointsArguments"} +// "properties": { +// "command": { "type": "string", "enum": ["setInstructionBreakpoints"] +// }, "arguments": {"$ref": +// "#/definitions/SetInstructionBreakpointsArguments"} // }, -// "required" : [ "command", "arguments" ] +// "required": [ "command", "arguments" ] // } // ] // }, -// "SetInstructionBreakpointsArguments" -// : { -// "type" : "object", -// "description" : "Arguments for `setInstructionBreakpoints` request", -// "properties" : { -// "breakpoints" : { -// "type" : "array", -// "items" : {"$ref" : "#/definitions/InstructionBreakpoint"}, -// "description" : "The instruction references of the breakpoints" -// } -// }, -// "required" : ["breakpoints"] -// }, -// "SetInstructionBreakpointsResponse" -// : { -// "allOf" : [ -// {"$ref" : "#/definitions/Response"}, { -// "type" : "object", -// "description" : "Response to `setInstructionBreakpoints` request", -// "properties" : { -// "body" : { -// "type" : "object", -// "properties" : { -// "breakpoints" : { -// "type" : "array", -// "items" : {"$ref" : "#/definitions/Breakpoint"}, -// "description" : -// "Information about the breakpoints. The array elements -// " "correspond to the elements of the `breakpoints` -// array." -// } -// }, -// "required" : ["breakpoints"] +// "SetInstructionBreakpointsArguments": { +// "type": "object", +// "description": "Arguments for `setInstructionBreakpoints` request", +// "properties": { +// "breakpoints": { +// "type": "array", +// "items": {"$ref": "#/definitions/InstructionBreakpoint"}, +// "description": "The instruction references of the breakpoints" +// } +// }, +// "required": ["breakpoints"] +// }, +// "SetInstructionBreakpointsResponse": { +// "allOf": [ +// {"$ref": "#/definitions/Response"}, +// { +// "type": "object", +// "description": "Response to `setInstructionBreakpoints` request", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "breakpoints": { +// "type": "array", +// "items": {"$ref": "#/definitions/Breakpoint"}, +// "description": +// "Information about the breakpoints. The array elements +// " "correspond to the elements of the `breakpoints` +// array." // } // }, -// "required" : ["body"] +// "required": ["breakpoints"] // } -// ] -// }, -// "InstructionBreakpoint" : { -// "type" : "object", -// "description" : "Properties of a breakpoint passed to the " +// }, +// "required": ["body"] +// } +// ] +// }, +// "InstructionBreakpoint": { +// "type": "object", +// "description": "Properties of a breakpoint passed to the " // "`setInstructionBreakpoints` request", -// "properties" : { -// "instructionReference" : { -// "type" : "string", +// "properties": { +// "instructionReference": { +// "type": "string", // "description" : // "The instruction reference of the breakpoint.\nThis should be a " // "memory or instruction pointer reference from an // `EvaluateResponse`, " // "`Variable`, `StackFrame`, `GotoTarget`, or `Breakpoint`." // }, -// "offset" : { -// "type" : "integer", -// "description" : "The offset from the instruction reference in " +// "offset": { +// "type": "integer", +// "description": "The offset from the instruction reference in " // "bytes.\nThis can be negative." // }, -// "condition" : { -// "type" : "string", -// "description" : "An expression for conditional breakpoints.\nIt is only +// "condition": { +// "type": "string", +// "description": "An expression for conditional breakpoints.\nIt is only // " // "honored by a debug adapter if the corresponding " // "capability `supportsConditionalBreakpoints` is true." // }, -// "hitCondition" : { -// "type" : "string", -// "description" : "An expression that controls how many hits of the " +// "hitCondition": { +// "type": "string", +// "description": "An expression that controls how many hits of the " // "breakpoint are ignored.\nThe debug adapter is expected // " "to interpret the expression as needed.\nThe // attribute " "is only honored by a debug adapter if the // corresponding " "capability // `supportsHitConditionalBreakpoints` is true." // }, -// "mode" : { -// "type" : "string", -// "description" : "The mode of this breakpoint. If defined, this must be +// "mode": { +// "type": "string", +// "description": "The mode of this breakpoint. If defined, this must be // " // "one of the `breakpointModes` the debug adapter " // "advertised in its `Capabilities`." // } // }, -// "required" : ["instructionReference"] +// "required": ["instructionReference"] // }, -// "Breakpoint" -// : { -// "type" : "object", +// "Breakpoint": { +// "type": "object", +// "description" : +// "Information about a breakpoint created in `setBreakpoints`, " +// "`setFunctionBreakpoints`, `setInstructionBreakpoints`, or " +// "`setDataBreakpoints` requests.", +// "properties": { +// "id": { +// "type": "integer", // "description" : -// "Information about a breakpoint created in `setBreakpoints`, " -// "`setFunctionBreakpoints`, `setInstructionBreakpoints`, or " -// "`setDataBreakpoints` requests.", -// "properties" : { -// "id" : { -// "type" : "integer", -// "description" : -// "The identifier for the breakpoint. It is needed if breakpoint -// " "events are used to update or remove breakpoints." -// }, -// "verified" : { -// "type" : "boolean", -// "description" : "If true, the breakpoint could be set (but not " -// "necessarily at the desired location)." -// }, -// "message" : { -// "type" : "string", -// "description" : "A message about the state of the breakpoint.\nThis -// " -// "is shown to the user and can be used to explain -// why " "a breakpoint could not be verified." -// }, -// "source" : { -// "$ref" : "#/definitions/Source", -// "description" : "The source where the breakpoint is located." -// }, -// "line" : { -// "type" : "integer", -// "description" : -// "The start line of the actual range covered by the breakpoint." -// }, -// "column" : { -// "type" : "integer", -// "description" : -// "Start position of the source range covered by the breakpoint. -// " "It is measured in UTF-16 code units and the client -// capability " -// "`columnsStartAt1` determines whether it is 0- or 1-based." -// }, -// "endLine" : { -// "type" : "integer", -// "description" : -// "The end line of the actual range covered by the breakpoint." -// }, -// "endColumn" : { -// "type" : "integer", -// "description" : -// "End position of the source range covered by the breakpoint. It -// " "is measured in UTF-16 code units and the client capability " -// "`columnsStartAt1` determines whether it is 0- or 1-based.\nIf -// " "no end line is given, then the end column is assumed to be -// in " "the start line." -// }, -// "instructionReference" : { -// "type" : "string", -// "description" : "A memory reference to where the breakpoint is -// set." -// }, -// "offset" : { -// "type" : "integer", -// "description" : "The offset from the instruction reference.\nThis " -// "can be negative." -// }, -// "reason" : { -// "type" : "string", -// "description" : -// "A machine-readable explanation of why a breakpoint may not be -// " "verified. If a breakpoint is verified or a specific reason -// is " "not known, the adapter should omit this property. -// Possible " "values include:\n\n- `pending`: Indicates a -// breakpoint might be " "verified in the future, but the adapter -// cannot verify it in the " "current state.\n - `failed`: -// Indicates a breakpoint was not " "able to be verified, and the -// adapter does not believe it can be " "verified without -// intervention.", -// "enum" : [ "pending", "failed" ] -// } -// }, -// "required" : ["verified"] +// "The identifier for the breakpoint. It is needed if breakpoint +// " "events are used to update or remove breakpoints." // }, - +// "verified": { +// "type": "boolean", +// "description": "If true, the breakpoint could be set (but not " +// "necessarily at the desired location)." +// }, +// "message": { +// "type": "string", +// "description": "A message about the state of the breakpoint.\nThis +// " +// "is shown to the user and can be used to explain +// why " "a breakpoint could not be verified." +// }, +// "source": { +// "$ref": "#/definitions/Source", +// "description": "The source where the breakpoint is located." +// }, +// "line": { +// "type": "integer", +// "description" : +// "The start line of the actual range covered by the breakpoint." +// }, +// "column": { +// "type": "integer", +// "description" : +// "Start position of the source range covered by the breakpoint. +// " "It is measured in UTF-16 code units and the client +// capability " +// "`columnsStartAt1` determines whether it is 0- or 1-based." +// }, +// "endLine": { +// "type": "integer", +// "description" : +// "The end line of the actual range covered by the breakpoint." +// }, +// "endColumn": { +// "type": "integer", +// "description" : +// "End position of the source range covered by the breakpoint. It +// " "is measured in UTF-16 code units and the client capability " +// "`columnsStartAt1` determines whether it is 0- or 1-based.\nIf +// " "no end line is given, then the end column is assumed to be +// in " "the start line." +// }, +// "instructionReference": { +// "type": "string", +// "description": "A memory reference to where the breakpoint is +// set." +// }, +// "offset": { +// "type": "integer", +// "description": "The offset from the instruction reference.\nThis " +// "can be negative." +// }, +// "reason": { +// "type": "string", +// "description" : +// "A machine-readable explanation of why a breakpoint may not be +// " "verified. If a breakpoint is verified or a specific reason +// is " "not known, the adapter should omit this property. +// Possible " "values include:\n\n- `pending`: Indicates a +// breakpoint might be " "verified in the future, but the adapter +// cannot verify it in the " "current state.\n - `failed`: +// Indicates a breakpoint was not " "able to be verified, and the +// adapter does not believe it can be " "verified without +// intervention.", +// "enum": [ "pending", "failed" ] +// } +// }, +// "required": ["verified"] +// }, void request_setInstructionBreakpoints(const llvm::json::Object &request) { llvm::json::Object response; llvm::json::Array response_breakpoints; llvm::json::Object body; FillResponse(request, response); - auto arguments = request.getObject("arguments"); - auto breakpoints = arguments->getArray("breakpoints"); + const auto *arguments = request.getObject("arguments"); + const auto *breakpoints = arguments->getArray("breakpoints"); - // It holds active instruction breakpoint list received from DAP. - InstructionBreakpointMap request_ibp; - if (breakpoints) { - for (const auto &bp : *breakpoints) { - auto bp_obj = bp.getAsObject(); - if (bp_obj) { - // Read instruction breakpoint request. - InstructionBreakpoint inst_bp(*bp_obj); - // Store them into map for reference. - request_ibp[inst_bp.instructionAddressReference] = std::move(inst_bp); - } - } + // Disable any instruction breakpoints that aren't in this request. + // There is no call to remove instruction breakpoints other than calling this + // function with a smaller or empty "breakpoints" list. + llvm::SetVector<lldb::addr_t> seen; + for (const auto &addr : g_dap.instruction_breakpoints) + seen.insert(addr.first); - // Iterate previous active instruction breakpoint list. - for (auto &prev_ibp : g_dap.instruction_breakpoints) { - // Find previous instruction breakpoint reference address in newly - // received instruction breakpoint list. - auto inst_reference = request_ibp.find(prev_ibp.first); - // Request for remove and delete the breakpoint, if the prev instruction - // breakpoint ID is not available in active instrcation breakpoint list. - // Means delete removed breakpoint instance. - if (inst_reference == request_ibp.end()) { - g_dap.target.BreakpointDelete(prev_ibp.second.id); - // Update Prev instruction breakpoint list. - g_dap.instruction_breakpoints.erase(prev_ibp.first); - } else { - // Instead of recreating breakpoint instance, update the breakpoint if - // there are any conditional changes. - prev_ibp.second.UpdateBreakpoint(inst_reference->second); - request_ibp.erase(inst_reference); - response_breakpoints.emplace_back( - CreateInstructionBreakpoint(&prev_ibp.second)); - } + for (const auto &bp : *breakpoints) { + const auto *bp_obj = bp.getAsObject(); + if (!bp_obj) + continue; + // Read instruction breakpoint request. + InstructionBreakpoint inst_bp(g_dap, *bp_obj); + const auto [kv, inserted] = g_dap.instruction_breakpoints.try_emplace( + inst_bp.instructionAddressReference, g_dap, *bp_obj); + if (inserted) { + kv->second.SetBreakpoint(); + } else { + kv->second.UpdateBreakpoint(inst_bp); } + AppendBreakpoint(&kv->second, response_breakpoints); + seen.remove(inst_bp.instructionAddressReference); + } - for (auto &req_bpi : request_ibp) { - // Add this breakpoint info to the response - g_dap.instruction_breakpoints[req_bpi.first] = std::move(req_bpi.second); - InstructionBreakpoint &new_bp = - g_dap.instruction_breakpoints[req_bpi.first]; - new_bp.SetInstructionBreakpoint(); - response_breakpoints.emplace_back(CreateInstructionBreakpoint(&new_bp)); - } + for (const auto &addr : seen) { + auto inst_bp = g_dap.instruction_breakpoints.find(addr); + if (inst_bp == g_dap.instruction_breakpoints.end()) + continue; + g_dap.target.BreakpointDelete(inst_bp->second.bp.GetID()); + g_dap.instruction_breakpoints.erase(addr); } body.try_emplace("breakpoints", std::move(response_breakpoints)); _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits