[Lldb-commits] [lldb] [lldb-dap] Refactoring lldb-dap to support running in a server mode, allowing multiple connections. (PR #114881)
John Harrison via lldb-commits
lldb-commits at lists.llvm.org
Tue Nov 5 17:57:09 PST 2024
https://github.com/ashgti updated https://github.com/llvm/llvm-project/pull/114881
>From b65c30f2d2cd31298cf4dba7ae91eaadb44bf999 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Wed, 30 Oct 2024 17:35:36 -0700
Subject: [PATCH 1/5] [lldb-dap] Refactoring the lldb-dap to create a new DAP
object per connection, or a single object when not in server mode.
This is a fairly large refactor of the lldb-dap to allow multiple DAP sessions from a single lldb-dap server.
The majority of the changes are around the removal of the g_dap instance and instead requiring all functions to either accept a DAP instance as a parameter or refactoring some code to not directly rely on the DAP object.
When running without a port, this should behavior the same as it did before.
When running with a port, the server will now accept each new connection and run the debug session to completion. In this mode, the server will continue until interrupted.
---
lldb/tools/lldb-dap/Breakpoint.cpp | 8 +-
lldb/tools/lldb-dap/Breakpoint.h | 9 +-
lldb/tools/lldb-dap/BreakpointBase.cpp | 6 +-
lldb/tools/lldb-dap/BreakpointBase.h | 11 +-
lldb/tools/lldb-dap/DAP.cpp | 111 +-
lldb/tools/lldb-dap/DAP.h | 19 +-
lldb/tools/lldb-dap/DAPForward.h | 4 +-
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 | 11 +-
lldb/tools/lldb-dap/InstructionBreakpoint.h | 11 +-
lldb/tools/lldb-dap/JSONUtils.cpp | 80 +-
lldb/tools/lldb-dap/JSONUtils.h | 60 +-
lldb/tools/lldb-dap/LLDBUtils.cpp | 20 +-
lldb/tools/lldb-dap/LLDBUtils.h | 12 +-
lldb/tools/lldb-dap/OutputRedirector.cpp | 6 +-
lldb/tools/lldb-dap/OutputRedirector.h | 12 +-
lldb/tools/lldb-dap/SourceBreakpoint.cpp | 17 +-
lldb/tools/lldb-dap/SourceBreakpoint.h | 4 +-
lldb/tools/lldb-dap/Watchpoint.cpp | 9 +-
lldb/tools/lldb-dap/Watchpoint.h | 8 +-
lldb/tools/lldb-dap/lldb-dap.cpp | 1302 +++++++++--------
24 files changed, 947 insertions(+), 802 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..f07921783aa985 100644
--- a/lldb/tools/lldb-dap/BreakpointBase.cpp
+++ b/lldb/tools/lldb-dap/BreakpointBase.cpp
@@ -11,8 +11,10 @@
using namespace lldb_dap;
-BreakpointBase::BreakpointBase(const llvm::json::Object &obj)
- : condition(std::string(GetString(obj, "condition"))),
+BreakpointBase::BreakpointBase(DAP &d) : dap(d) {}
+
+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..121688d08f1524 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,8 @@ 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);
+ BreakpointBase(DAP &d, const llvm::json::Object &obj);
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..c394d4d1a7990a 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -32,10 +32,11 @@ using namespace lldb_dap;
namespace lldb_dap {
-DAP g_dap;
-
-DAP::DAP()
- : broadcaster("lldb-dap"), exception_breakpoints(),
+DAP::DAP(llvm::StringRef debug_adapter_path, std::shared_ptr<std::ofstream> log,
+ ReplMode repl_mode)
+ : debug_adaptor_path(debug_adapter_path),
+ in(lldb::SBFile(std::fopen("/dev/null", "w"), true)),
+ broadcaster("lldb-dap"), log(std::move(log)), exception_breakpoints(),
focus_tid(LLDB_INVALID_THREAD_ID), stop_at_entry(false), is_attach(false),
enable_auto_variable_summaries(false),
enable_synthetic_child_debugging(false),
@@ -44,21 +45,7 @@ DAP::DAP()
configuration_done_sent(false), waiting_for_run_in_terminal(false),
progress_event_reporter(
[&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }),
- reverse_request_seq(0), repl_mode(ReplMode::Auto) {
- const char *log_file_path = getenv("LLDBDAP_LOG");
-#if defined(_WIN32)
- // Windows opens stdout and stdin in text mode which converts \n to 13,10
- // while the value is just 10 on Darwin/Linux. Setting the file mode to binary
- // fixes this.
- int result = _setmode(fileno(stdout), _O_BINARY);
- assert(result);
- result = _setmode(fileno(stdin), _O_BINARY);
- UNUSED_IF_ASSERT_DISABLED(result);
- assert(result);
-#endif
- if (log_file_path)
- log.reset(new std::ofstream(log_file_path));
-}
+ reverse_request_seq(0), repl_mode(repl_mode) {}
DAP::~DAP() = default;
@@ -74,21 +61,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",
- lldb::eLanguageTypeObjC);
- exception_breakpoints->emplace_back("objc_throw", "Objective-C Throw",
- lldb::eLanguageTypeObjC);
+ exception_breakpoints->emplace_back(
+ *this, "objc_catch", "Objective-C Catch", lldb::eLanguageTypeObjC);
+ 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
@@ -119,7 +106,7 @@ void DAP::PopulateExceptionBreakpoints() {
raw_throw_keyword ? raw_throw_keyword : "throw";
exception_breakpoints->emplace_back(
- raw_lang_name + "_" + throw_keyword,
+ *this, raw_lang_name + "_" + throw_keyword,
capitalized_lang_name + " " + capitalize(throw_keyword), lang);
}
@@ -130,7 +117,7 @@ void DAP::PopulateExceptionBreakpoints() {
raw_catch_keyword ? raw_catch_keyword : "catch";
exception_breakpoints->emplace_back(
- raw_lang_name + "_" + catch_keyword,
+ *this, raw_lang_name + "_" + catch_keyword,
capitalized_lang_name + " " + capitalize(catch_keyword), lang);
}
}
@@ -477,12 +464,12 @@ lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) {
llvm::json::Value DAP::CreateTopLevelScopes() {
llvm::json::Array scopes;
- scopes.emplace_back(CreateScope("Locals", VARREF_LOCALS,
- g_dap.variables.locals.GetSize(), false));
+ scopes.emplace_back(
+ CreateScope("Locals", VARREF_LOCALS, variables.locals.GetSize(), false));
scopes.emplace_back(CreateScope("Globals", VARREF_GLOBALS,
- g_dap.variables.globals.GetSize(), false));
+ variables.globals.GetSize(), false));
scopes.emplace_back(CreateScope("Registers", VARREF_REGS,
- g_dap.variables.registers.GetSize(), false));
+ variables.registers.GetSize(), false));
return llvm::json::Value(std::move(scopes));
}
@@ -490,8 +477,8 @@ ReplMode DAP::DetectReplMode(lldb::SBFrame frame, std::string &expression,
bool partial_expression) {
// Check for the escape hatch prefix.
if (!expression.empty() &&
- llvm::StringRef(expression).starts_with(g_dap.command_escape_prefix)) {
- expression = expression.substr(g_dap.command_escape_prefix.size());
+ llvm::StringRef(expression).starts_with(command_escape_prefix)) {
+ expression = expression.substr(command_escape_prefix.size());
return ReplMode::Command;
}
@@ -531,7 +518,7 @@ ReplMode DAP::DetectReplMode(lldb::SBFrame frame, std::string &expression,
<< "Warning: Expression '" << term
<< "' is both an LLDB command and variable. It will be evaluated as "
"a variable. To evaluate the expression as an LLDB command, use '"
- << g_dap.command_escape_prefix << "' as a prefix.\n";
+ << command_escape_prefix << "' as a prefix.\n";
}
// Variables take preference to commands in auto, since commands can always
@@ -548,7 +535,7 @@ bool DAP::RunLLDBCommands(llvm::StringRef prefix,
llvm::ArrayRef<std::string> commands) {
bool required_command_failed = false;
std::string output =
- ::RunLLDBCommands(prefix, commands, required_command_failed);
+ ::RunLLDBCommands(*this, prefix, commands, required_command_failed);
SendOutput(OutputType::Console, output);
return !required_command_failed;
}
@@ -694,14 +681,14 @@ bool DAP::HandleObject(const llvm::json::Object &object) {
const auto command = GetString(object, "command");
auto handler_pos = request_handlers.find(std::string(command));
if (handler_pos != request_handlers.end()) {
- handler_pos->second(object);
+ handler_pos->second(*this, object);
return true; // Success
- } else {
- if (log)
- *log << "error: unhandled command \"" << command.data() << "\""
- << std::endl;
- return false; // Fail
}
+
+ if (log)
+ *log << "error: unhandled command \"" << command.data() << "\""
+ << std::endl;
+ return false; // Fail
}
if (packet_type == "response") {
@@ -901,7 +888,7 @@ bool StartDebuggingRequestHandler::DoExecute(
return false;
}
- g_dap.SendReverseRequest(
+ dap.SendReverseRequest(
"startDebugging",
llvm::json::Object{{"request", request},
{"configuration", std::move(*configuration)}},
@@ -925,7 +912,7 @@ bool ReplModeRequestHandler::DoExecute(lldb::SBDebugger debugger,
// If a new mode is not specified report the current mode.
if (!command || llvm::StringRef(command[0]).empty()) {
std::string mode;
- switch (g_dap.repl_mode) {
+ switch (dap.repl_mode) {
case ReplMode::Variable:
mode = "variable";
break;
@@ -946,11 +933,11 @@ bool ReplModeRequestHandler::DoExecute(lldb::SBDebugger debugger,
llvm::StringRef new_mode{command[0]};
if (new_mode == "variable") {
- g_dap.repl_mode = ReplMode::Variable;
+ dap.repl_mode = ReplMode::Variable;
} else if (new_mode == "command") {
- g_dap.repl_mode = ReplMode::Command;
+ dap.repl_mode = ReplMode::Command;
} else if (new_mode == "auto") {
- g_dap.repl_mode = ReplMode::Auto;
+ dap.repl_mode = ReplMode::Auto;
} else {
lldb::SBStream error_message;
error_message.Printf("Invalid repl-mode '%s'. Expected one of 'variable', "
@@ -1022,7 +1009,7 @@ bool SendEventRequestHandler::DoExecute(lldb::SBDebugger debugger,
event.try_emplace("body", std::move(*body));
}
- g_dap.SendJSON(llvm::json::Value(std::move(event)));
+ dap.SendJSON(llvm::json::Value(std::move(event)));
result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
return true;
}
@@ -1031,14 +1018,13 @@ void DAP::SetFrameFormat(llvm::StringRef format) {
if (format.empty())
return;
lldb::SBError error;
- g_dap.frame_format = lldb::SBFormat(format.str().c_str(), error);
+ frame_format = lldb::SBFormat(format.str().c_str(), error);
if (error.Fail()) {
- g_dap.SendOutput(
- OutputType::Console,
- llvm::formatv(
- "The provided frame format '{0}' couldn't be parsed: {1}\n", format,
- error.GetCString())
- .str());
+ SendOutput(OutputType::Console,
+ llvm::formatv(
+ "The provided frame format '{0}' couldn't be parsed: {1}\n",
+ format, error.GetCString())
+ .str());
}
}
@@ -1046,14 +1032,13 @@ void DAP::SetThreadFormat(llvm::StringRef format) {
if (format.empty())
return;
lldb::SBError error;
- g_dap.thread_format = lldb::SBFormat(format.str().c_str(), error);
+ thread_format = lldb::SBFormat(format.str().c_str(), error);
if (error.Fail()) {
- g_dap.SendOutput(
- OutputType::Console,
- llvm::formatv(
- "The provided thread format '{0}' couldn't be parsed: {1}\n",
- format, error.GetCString())
- .str());
+ SendOutput(OutputType::Console,
+ llvm::formatv(
+ "The provided thread format '{0}' couldn't be parsed: {1}\n",
+ format, error.GetCString())
+ .str());
}
}
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index dab4ce44ab202c..04a747721e86af 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -28,6 +28,7 @@
#include "lldb/API/SBCommandReturnObject.h"
#include "lldb/API/SBDebugger.h"
#include "lldb/API/SBEvent.h"
+#include "lldb/API/SBFile.h"
#include "lldb/API/SBFormat.h"
#include "lldb/API/SBLaunchInfo.h"
#include "lldb/API/SBTarget.h"
@@ -63,7 +64,7 @@ enum DAPBroadcasterBits {
eBroadcastBitStopProgressThread = 1u << 1
};
-typedef void (*RequestCallback)(const llvm::json::Object &command);
+typedef void (*RequestCallback)(DAP &dap, const llvm::json::Object &command);
typedef void (*ResponseCallback)(llvm::Expected<llvm::json::Value> value);
enum class PacketStatus {
@@ -116,16 +117,22 @@ struct Variables {
};
struct StartDebuggingRequestHandler : public lldb::SBCommandPluginInterface {
+ DAP &dap;
+ StartDebuggingRequestHandler(DAP &dap) : dap(dap) {}
bool DoExecute(lldb::SBDebugger debugger, char **command,
lldb::SBCommandReturnObject &result) override;
};
struct ReplModeRequestHandler : public lldb::SBCommandPluginInterface {
+ DAP &dap;
+ ReplModeRequestHandler(DAP &dap) : dap(dap) {}
bool DoExecute(lldb::SBDebugger debugger, char **command,
lldb::SBCommandReturnObject &result) override;
};
struct SendEventRequestHandler : public lldb::SBCommandPluginInterface {
+ DAP &dap;
+ SendEventRequestHandler(DAP &dap) : dap(dap) {}
bool DoExecute(lldb::SBDebugger debugger, char **command,
lldb::SBCommandReturnObject &result) override;
};
@@ -134,13 +141,16 @@ struct DAP {
std::string debug_adaptor_path;
InputStream input;
OutputStream output;
+ lldb::SBFile in;
+ lldb::SBFile out;
+ lldb::SBFile err;
lldb::SBDebugger debugger;
lldb::SBTarget target;
Variables variables;
lldb::SBBroadcaster broadcaster;
std::thread event_thread;
std::thread progress_event_thread;
- std::unique_ptr<std::ofstream> log;
+ std::shared_ptr<std::ofstream> log;
llvm::StringMap<SourceBreakpointMap> source_breakpoints;
FunctionBreakpointMap function_breakpoints;
InstructionBreakpointMap instruction_breakpoints;
@@ -192,7 +202,8 @@ struct DAP {
// will contain that expression.
std::string last_nonempty_var_expression;
- DAP();
+ DAP(llvm::StringRef debug_adapter_path, std::shared_ptr<std::ofstream> log,
+ ReplMode repl_mode);
~DAP();
DAP(const DAP &rhs) = delete;
void operator=(const DAP &rhs) = delete;
@@ -347,8 +358,6 @@ struct DAP {
void SendJSON(const std::string &json_str);
};
-extern DAP g_dap;
-
} // namespace lldb_dap
#endif
diff --git a/lldb/tools/lldb-dap/DAPForward.h b/lldb/tools/lldb-dap/DAPForward.h
index 159d999a63c820..ed7664526847b1 100644
--- a/lldb/tools/lldb-dap/DAPForward.h
+++ b/lldb/tools/lldb-dap/DAPForward.h
@@ -11,11 +11,12 @@
namespace lldb_dap {
struct BreakpointBase;
+struct DAP;
struct ExceptionBreakpoint;
struct FunctionBreakpoint;
+struct InstructionBreakpoint;
struct SourceBreakpoint;
struct Watchpoint;
-struct InstructionBreakpoint;
} // namespace lldb_dap
namespace lldb {
@@ -27,6 +28,7 @@ class SBCommandReturnObject;
class SBCommunication;
class SBDebugger;
class SBEvent;
+class SBFile;
class SBFrame;
class SBHostOS;
class SBInstruction;
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..d6ccb2dfc29111 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),
- offset(GetSigned(obj, "offset", 0)) {
+InstructionBreakpoint::InstructionBreakpoint(DAP &d,
+ const llvm::json::Object &obj)
+ : Breakpoint(d, obj), instructionAddressReference(LLDB_INVALID_ADDRESS),
+ id(0), offset(GetSigned(obj, "offset", 0)) {
GetString(obj, "instructionReference")
.getAsInteger(0, instructionAddressReference);
instructionAddressReference += offset;
}
void InstructionBreakpoint::SetInstructionBreakpoint() {
- bp = g_dap.target.BreakpointCreateByAddress(instructionAddressReference);
+ bp = dap.target.BreakpointCreateByAddress(instructionAddressReference);
id = bp.GetID();
}
+
} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/InstructionBreakpoint.h b/lldb/tools/lldb-dap/InstructionBreakpoint.h
index 53912af46ca148..d7f3fb8ad3a4eb 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 {
@@ -21,10 +26,10 @@ struct InstructionBreakpoint : public Breakpoint {
int32_t id;
int32_t offset;
- InstructionBreakpoint()
- : Breakpoint(), instructionAddressReference(LLDB_INVALID_ADDRESS), id(0),
+ InstructionBreakpoint(DAP &d)
+ : Breakpoint(d), instructionAddressReference(LLDB_INVALID_ADDRESS), id(0),
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();
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 97fe6b4f9f05db..c6d9e86f0d8bf1 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -184,12 +184,6 @@ static bool IsClassStructOrUnionType(lldb::SBType t) {
/// glance.
static std::optional<std::string>
TryCreateAutoSummaryForContainer(lldb::SBValue &v) {
- // We gate this feature because it performs GetNumChildren(), which can
- // cause performance issues because LLDB needs to complete possibly huge
- // types.
- if (!g_dap.enable_auto_variable_summaries)
- return std::nullopt;
-
if (!v.MightHaveChildren())
return std::nullopt;
/// As this operation can be potentially slow, we limit the total time spent
@@ -245,10 +239,7 @@ TryCreateAutoSummaryForContainer(lldb::SBValue &v) {
/// Try to create a summary string for the given value that doesn't have a
/// summary of its own.
-static std::optional<std::string> TryCreateAutoSummary(lldb::SBValue value) {
- if (!g_dap.enable_auto_variable_summaries)
- return std::nullopt;
-
+static std::optional<std::string> TryCreateAutoSummary(lldb::SBValue &value) {
// We use the dereferenced value for generating the summary.
if (value.GetType().IsPointerType() || value.GetType().IsReferenceType())
value = value.Dereference();
@@ -459,9 +450,10 @@ static std::string ConvertDebugInfoSizeToString(uint64_t debug_info) {
}
return oss.str();
}
-llvm::json::Value CreateModule(lldb::SBModule &module) {
+
+llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module) {
llvm::json::Object object;
- if (!module.IsValid())
+ if (!target.IsValid() && !module.IsValid())
return llvm::json::Value(std::move(object));
const char *uuid = module.GetUUIDString();
object.try_emplace("id", uuid ? std::string(uuid) : std::string(""));
@@ -488,7 +480,7 @@ llvm::json::Value CreateModule(lldb::SBModule &module) {
object.try_emplace("symbolStatus", "Symbols not found.");
}
std::string loaded_addr = std::to_string(
- module.GetObjectFileHeaderAddress().GetLoadAddress(g_dap.target));
+ module.GetObjectFileHeaderAddress().GetLoadAddress(target));
object.try_emplace("addressRange", loaded_addr);
std::string version_str;
uint32_t version_nums[3];
@@ -750,15 +742,15 @@ std::optional<llvm::json::Value> CreateSource(lldb::SBFrame &frame) {
// },
// "required": [ "id", "name", "line", "column" ]
// }
-llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) {
+llvm::json::Value CreateStackFrame(lldb::SBFrame &frame,
+ lldb::SBFormat &format) {
llvm::json::Object object;
int64_t frame_id = MakeDAPFrameID(frame);
object.try_emplace("id", frame_id);
std::string frame_name;
lldb::SBStream stream;
- if (g_dap.frame_format &&
- frame.GetDescriptionWithFormat(g_dap.frame_format, stream).Success()) {
+ if (format && frame.GetDescriptionWithFormat(format, stream).Success()) {
frame_name = stream.GetData();
// `function_name` can be a nullptr, which throws an error when assigned to
@@ -775,7 +767,7 @@ llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) {
}
// We only include `[opt]` if a custom frame format is not specified.
- if (!g_dap.frame_format && frame.GetFunction().GetIsOptimized())
+ if (!format && frame.GetFunction().GetIsOptimized())
frame_name += " [opt]";
EmplaceSafeString(object, "name", frame_name);
@@ -809,11 +801,11 @@ llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) {
return llvm::json::Value(std::move(object));
}
-llvm::json::Value CreateExtendedStackFrameLabel(lldb::SBThread &thread) {
+llvm::json::Value CreateExtendedStackFrameLabel(lldb::SBThread &thread,
+ lldb::SBFormat &format) {
std::string name;
lldb::SBStream stream;
- if (g_dap.thread_format &&
- thread.GetDescriptionWithFormat(g_dap.thread_format, stream).Success()) {
+ if (format && thread.GetDescriptionWithFormat(format, stream).Success()) {
name = stream.GetData();
} else {
const uint32_t thread_idx = thread.GetExtendedBacktraceOriginatingIndexID();
@@ -910,13 +902,12 @@ llvm::json::Value CreateInstructionBreakpoint(BreakpointBase *ibp) {
// },
// "required": [ "id", "name" ]
// }
-llvm::json::Value CreateThread(lldb::SBThread &thread) {
+llvm::json::Value CreateThread(lldb::SBThread &thread, lldb::SBFormat &format) {
llvm::json::Object object;
object.try_emplace("id", (int64_t)thread.GetThreadID());
std::string thread_str;
lldb::SBStream stream;
- if (g_dap.thread_format &&
- thread.GetDescriptionWithFormat(g_dap.thread_format, stream).Success()) {
+ if (format && thread.GetDescriptionWithFormat(format, stream).Success()) {
thread_str = stream.GetData();
} else {
const char *thread_name = thread.GetName();
@@ -1004,7 +995,7 @@ llvm::json::Value CreateThread(lldb::SBThread &thread) {
// "required": [ "event", "body" ]
// }]
// }
-llvm::json::Value CreateThreadStopped(lldb::SBThread &thread,
+llvm::json::Value CreateThreadStopped(DAP &dap, lldb::SBThread &thread,
uint32_t stop_id) {
llvm::json::Object event(CreateEventObject("stopped"));
llvm::json::Object body;
@@ -1014,13 +1005,13 @@ llvm::json::Value CreateThreadStopped(lldb::SBThread &thread,
body.try_emplace("reason", "step");
break;
case lldb::eStopReasonBreakpoint: {
- ExceptionBreakpoint *exc_bp = g_dap.GetExceptionBPFromStopReason(thread);
+ ExceptionBreakpoint *exc_bp = dap.GetExceptionBPFromStopReason(thread);
if (exc_bp) {
body.try_emplace("reason", "exception");
EmplaceSafeString(body, "description", exc_bp->label);
} else {
InstructionBreakpoint *inst_bp =
- g_dap.GetInstructionBPFromStopReason(thread);
+ dap.GetInstructionBPFromStopReason(thread);
if (inst_bp) {
body.try_emplace("reason", "instruction breakpoint");
} else {
@@ -1080,10 +1071,10 @@ llvm::json::Value CreateThreadStopped(lldb::SBThread &thread,
}
}
// "threadCausedFocus" is used in tests to validate breaking behavior.
- if (tid == g_dap.focus_tid) {
+ if (tid == dap.focus_tid) {
body.try_emplace("threadCausedFocus", true);
}
- body.try_emplace("preserveFocusHint", tid != g_dap.focus_tid);
+ body.try_emplace("preserveFocusHint", tid != dap.focus_tid);
body.try_emplace("allThreadsStopped", true);
event.try_emplace("body", std::move(body));
return llvm::json::Value(std::move(event));
@@ -1111,7 +1102,9 @@ std::string CreateUniqueVariableNameForDisplay(lldb::SBValue v,
return name_builder.GetData();
}
-VariableDescription::VariableDescription(lldb::SBValue v, bool format_hex,
+VariableDescription::VariableDescription(lldb::SBValue &v,
+ bool auto_variable_summaries,
+ bool format_hex,
bool is_name_duplicated,
std::optional<std::string> custom_name)
: v(v) {
@@ -1142,7 +1135,7 @@ VariableDescription::VariableDescription(lldb::SBValue v, bool format_hex,
} else {
value = llvm::StringRef(v.GetValue()).str();
summary = llvm::StringRef(v.GetSummary()).str();
- if (summary.empty())
+ if (summary.empty() && auto_variable_summaries)
auto_summary = TryCreateAutoSummary(v);
std::optional<std::string> effective_summary =
@@ -1220,13 +1213,13 @@ std::string VariableDescription::GetResult(llvm::StringRef context) {
return description.trim().str();
}
-bool ValuePointsToCode(lldb::SBValue v) {
+bool ValuePointsToCode(lldb::SBValue &v) {
if (!v.GetType().GetPointeeType().IsFunctionType())
return false;
lldb::addr_t addr = v.GetValueAsAddress();
lldb::SBLineEntry line_entry =
- g_dap.target.ResolveLoadAddress(addr).GetLineEntry();
+ v.GetTarget().ResolveLoadAddress(addr).GetLineEntry();
return line_entry.IsValid();
}
@@ -1386,10 +1379,13 @@ std::pair<int64_t, bool> UnpackLocation(int64_t location_id) {
// },
// "required": [ "name", "value", "variablesReference" ]
// }
-llvm::json::Value CreateVariable(lldb::SBValue v, int64_t var_ref,
- bool format_hex, bool is_name_duplicated,
+llvm::json::Value CreateVariable(lldb::SBValue &v, int64_t var_ref,
+ bool format_hex, bool auto_variable_summaries,
+ bool synthetic_child_debugging,
+ bool is_name_duplicated,
std::optional<std::string> custom_name) {
- VariableDescription desc(v, format_hex, is_name_duplicated, custom_name);
+ VariableDescription desc(v, auto_variable_summaries, format_hex,
+ is_name_duplicated, custom_name);
llvm::json::Object object;
EmplaceSafeString(object, "name", desc.name);
EmplaceSafeString(object, "value", desc.display_value);
@@ -1425,7 +1421,7 @@ llvm::json::Value CreateVariable(lldb::SBValue v, int64_t var_ref,
size_t num_children = v.GetNumChildren();
// If we are creating a "[raw]" fake child for each synthetic type, we
// have to account for it when returning indexed variables.
- if (g_dap.enable_synthetic_child_debugging)
+ if (synthetic_child_debugging)
++num_children;
object.try_emplace("indexedVariables", num_children);
}
@@ -1504,9 +1500,7 @@ CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request,
llvm::StringRef value = envs.GetValueAtIndex(index);
if (key.empty())
- g_dap.SendOutput(OutputType::Stderr,
- "empty environment variable for value: \"" +
- value.str() + '\"');
+ continue;
else
env_json.try_emplace(key, value);
}
@@ -1562,8 +1556,8 @@ void FilterAndGetValueForKey(const lldb::SBStructuredData data, const char *key,
}
}
-void addStatistic(llvm::json::Object &event) {
- lldb::SBStructuredData statistics = g_dap.target.GetStatistics();
+void addStatistic(lldb::SBTarget &target, llvm::json::Object &event) {
+ lldb::SBStructuredData statistics = target.GetStatistics();
bool is_dictionary =
statistics.GetType() == lldb::eStructuredDataTypeDictionary;
if (!is_dictionary)
@@ -1580,9 +1574,9 @@ void addStatistic(llvm::json::Object &event) {
event.try_emplace("statistics", std::move(stats_body));
}
-llvm::json::Object CreateTerminatedEventObject() {
+llvm::json::Object CreateTerminatedEventObject(lldb::SBTarget &target) {
llvm::json::Object event(CreateEventObject("terminated"));
- addStatistic(event);
+ addStatistic(target, event);
return event;
}
diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h
index 54fc4323475723..d06c0ddb9e638e 100644
--- a/lldb/tools/lldb-dap/JSONUtils.h
+++ b/lldb/tools/lldb-dap/JSONUtils.h
@@ -9,14 +9,15 @@
#ifndef LLDB_TOOLS_LLDB_DAP_JSONUTILS_H
#define LLDB_TOOLS_LLDB_DAP_JSONUTILS_H
+#include <cstdint>
+#include <optional>
+#include <unordered_map>
+
#include "BreakpointBase.h"
#include "DAPForward.h"
#include "lldb/API/SBModule.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/JSON.h"
-#include <cstdint>
-#include <optional>
-#include <unordered_map>
namespace lldb_dap {
@@ -260,13 +261,16 @@ CreateBreakpoint(BreakpointBase *bp,
/// Converts a LLDB module to a VS Code DAP module for use in "modules" events.
///
+/// \param[in] target
+/// A LLDB target object to convert into a JSON value.
+///
/// \param[in] module
-/// A LLDB module object to convert into a JSON value
+/// A LLDB module object to convert into a JSON value.
///
/// \return
/// A "Module" JSON object with that follows the formal JSON
/// definition outlined by Microsoft.
-llvm::json::Value CreateModule(lldb::SBModule &module);
+llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module);
/// Create a "Event" JSON object using \a event_name as the event name
///
@@ -356,10 +360,15 @@ llvm::json::Value CreateSource(llvm::StringRef source_path);
/// The LLDB stack frame to use when populating out the "StackFrame"
/// object.
///
+/// \param[in] format
+/// The LLDB format to use when populating out the "StackFrame"
+/// object.
+///
/// \return
/// A "StackFrame" JSON object with that follows the formal JSON
/// definition outlined by Microsoft.
-llvm::json::Value CreateStackFrame(lldb::SBFrame &frame);
+llvm::json::Value CreateStackFrame(lldb::SBFrame &frame,
+ lldb::SBFormat &format);
/// Create a "StackFrame" label object for a LLDB thread.
///
@@ -375,10 +384,14 @@ llvm::json::Value CreateStackFrame(lldb::SBFrame &frame);
/// The LLDB thread to use when populating out the "Thread"
/// object.
///
+/// \param[in] format
+/// The configured formatter for the DAP session.
+///
/// \return
/// A "StackFrame" JSON object with that follows the formal JSON
/// definition outlined by Microsoft.
-llvm::json::Value CreateExtendedStackFrameLabel(lldb::SBThread &thread);
+llvm::json::Value CreateExtendedStackFrameLabel(lldb::SBThread &thread,
+ lldb::SBFormat &format);
/// Create a "instruction" object for a LLDB disassemble object as described in
/// the Visual Studio Code debug adaptor definition.
@@ -403,11 +416,14 @@ llvm::json::Value CreateInstructionBreakpoint(BreakpointBase *ibp);
/// \param[in] thread
/// The LLDB thread to use when populating out the "Thread"
/// object.
+/// \param[in] format
+/// The LLDB format to use when populating out the "Thread"
+/// object.
///
/// \return
/// A "Thread" JSON object with that follows the formal JSON
/// definition outlined by Microsoft.
-llvm::json::Value CreateThread(lldb::SBThread &thread);
+llvm::json::Value CreateThread(lldb::SBThread &thread, lldb::SBFormat &format);
/// Create a "StoppedEvent" object for a LLDB thread object.
///
@@ -423,6 +439,9 @@ llvm::json::Value CreateThread(lldb::SBThread &thread);
/// "allThreadsStopped" - set to True to indicate that all threads
/// stop when any thread stops.
///
+/// \param[in] dap
+/// The DAP session associated with the stopped thread.
+///
/// \param[in] thread
/// The LLDB thread to use when populating out the "StoppedEvent"
/// object.
@@ -430,7 +449,8 @@ llvm::json::Value CreateThread(lldb::SBThread &thread);
/// \return
/// A "StoppedEvent" JSON object with that follows the formal JSON
/// definition outlined by Microsoft.
-llvm::json::Value CreateThreadStopped(lldb::SBThread &thread, uint32_t stop_id);
+llvm::json::Value CreateThreadStopped(DAP &dap, lldb::SBThread &thread,
+ uint32_t stop_id);
/// \return
/// The variable name of \a value or a default placeholder.
@@ -468,8 +488,8 @@ struct VariableDescription {
/// The SBValue for this variable.
lldb::SBValue v;
- VariableDescription(lldb::SBValue v, bool format_hex = false,
- bool is_name_duplicated = false,
+ VariableDescription(lldb::SBValue &v, bool auto_variable_summaries,
+ bool format_hex = false, bool is_name_duplicated = false,
std::optional<std::string> custom_name = {});
/// Create a JSON object that represents these extensions to the DAP variable
@@ -481,7 +501,7 @@ struct VariableDescription {
};
/// Does the given variable have an associated value location?
-bool ValuePointsToCode(lldb::SBValue v);
+bool ValuePointsToCode(lldb::SBValue &v);
/// Pack a location into a single integer which we can send via
/// the debug adapter protocol.
@@ -521,6 +541,13 @@ std::pair<int64_t, bool> UnpackLocation(int64_t location_id);
/// It set to true the variable will be formatted as hex in
/// the "value" key value pair for the value of the variable.
///
+/// \param[in] auto_variable_summaries
+/// Whether the variable should include an auto summary if the default
+/// summary is empty.
+///
+/// \param[in] synthetic_child_debugging
+/// Whether the variable should include synthetic children references.
+///
/// \param[in] is_name_duplicated
/// Whether the same variable name appears multiple times within the same
/// context (e.g. locals). This can happen due to shadowed variables in
@@ -536,14 +563,15 @@ std::pair<int64_t, bool> UnpackLocation(int64_t location_id);
/// \return
/// A "Variable" JSON object with that follows the formal JSON
/// definition outlined by Microsoft.
-llvm::json::Value CreateVariable(lldb::SBValue v, int64_t var_ref,
- bool format_hex,
+llvm::json::Value CreateVariable(lldb::SBValue &v, int64_t var_ref,
+ bool format_hex, bool auto_variable_summaries,
+ bool synthetic_child_debugging,
bool is_name_duplicated = false,
std::optional<std::string> custom_name = {});
llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit unit);
-/// Create a runInTerminal reverse request object
+/// Create a runInTerminal reverse request object.
///
/// \param[in] launch_request
/// The original launch_request object whose fields are used to construct
@@ -574,7 +602,7 @@ CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request,
///
/// \return
/// A body JSON object with debug info and breakpoint info
-llvm::json::Object CreateTerminatedEventObject();
+llvm::json::Object CreateTerminatedEventObject(lldb::SBTarget &target);
/// Convert a given JSON object to a string.
std::string JSONToString(const llvm::json::Value &json);
diff --git a/lldb/tools/lldb-dap/LLDBUtils.cpp b/lldb/tools/lldb-dap/LLDBUtils.cpp
index 2ffcba7dff4f24..4c4bac8db3dca9 100644
--- a/lldb/tools/lldb-dap/LLDBUtils.cpp
+++ b/lldb/tools/lldb-dap/LLDBUtils.cpp
@@ -7,15 +7,17 @@
//===----------------------------------------------------------------------===//
#include "LLDBUtils.h"
-#include "DAP.h"
-#include "JSONUtils.h"
-#include "lldb/API/SBStringList.h"
#include <mutex>
+#include "lldb/API/SBStringList.h"
+
+#include "DAP.h"
+#include "JSONUtils.h"
+
namespace lldb_dap {
-bool RunLLDBCommands(llvm::StringRef prefix,
+bool RunLLDBCommands(DAP &dap, llvm::StringRef prefix,
const llvm::ArrayRef<std::string> &commands,
llvm::raw_ostream &strm, bool parse_command_directives) {
if (commands.empty())
@@ -23,7 +25,7 @@ bool RunLLDBCommands(llvm::StringRef prefix,
bool did_print_prefix = false;
- lldb::SBCommandInterpreter interp = g_dap.debugger.GetCommandInterpreter();
+ lldb::SBCommandInterpreter interp = dap.debugger.GetCommandInterpreter();
for (llvm::StringRef command : commands) {
lldb::SBCommandReturnObject result;
bool quiet_on_success = false;
@@ -78,7 +80,7 @@ bool RunLLDBCommands(llvm::StringRef prefix,
return true;
}
-std::string RunLLDBCommands(llvm::StringRef prefix,
+std::string RunLLDBCommands(DAP &dap, llvm::StringRef prefix,
const llvm::ArrayRef<std::string> &commands,
bool &required_command_failed,
bool parse_command_directives) {
@@ -86,15 +88,15 @@ std::string RunLLDBCommands(llvm::StringRef prefix,
std::string s;
llvm::raw_string_ostream strm(s);
required_command_failed =
- !RunLLDBCommands(prefix, commands, strm, parse_command_directives);
+ !RunLLDBCommands(dap, prefix, commands, strm, parse_command_directives);
return s;
}
std::string
-RunLLDBCommandsVerbatim(llvm::StringRef prefix,
+RunLLDBCommandsVerbatim(DAP &dap, llvm::StringRef prefix,
const llvm::ArrayRef<std::string> &commands) {
bool required_command_failed = false;
- return RunLLDBCommands(prefix, commands, required_command_failed,
+ return RunLLDBCommands(dap, prefix, commands, required_command_failed,
/*parse_command_directives=*/false);
}
diff --git a/lldb/tools/lldb-dap/LLDBUtils.h b/lldb/tools/lldb-dap/LLDBUtils.h
index d5072d19029a1e..3a3d02f8867dfd 100644
--- a/lldb/tools/lldb-dap/LLDBUtils.h
+++ b/lldb/tools/lldb-dap/LLDBUtils.h
@@ -30,6 +30,9 @@ namespace lldb_dap {
/// emitted regardless, and \b false is returned without executing the
/// remaining commands.
///
+/// \param[in] dap
+/// The DAP session to execute the commands from.
+///
/// \param[in] prefix
/// A string that will be printed into \a strm prior to emitting
/// the prompt + command and command output. Can be NULL.
@@ -48,7 +51,7 @@ namespace lldb_dap {
/// \return
/// \b true, unless a command prefixed with \b ! fails and parsing of
/// command directives is enabled.
-bool RunLLDBCommands(llvm::StringRef prefix,
+bool RunLLDBCommands(DAP &dap, llvm::StringRef prefix,
const llvm::ArrayRef<std::string> &commands,
llvm::raw_ostream &strm, bool parse_command_directives);
@@ -57,6 +60,9 @@ bool RunLLDBCommands(llvm::StringRef prefix,
/// All output from every command, including the prompt + the command
/// is returned in the std::string return value.
///
+/// \param[in] dap
+/// The DAP session to execute the commands from.
+///
/// \param[in] prefix
/// A string that will be printed into \a strm prior to emitting
/// the prompt + command and command output. Can be NULL.
@@ -75,14 +81,14 @@ bool RunLLDBCommands(llvm::StringRef prefix,
/// \return
/// A std::string that contains the prefix and all commands and
/// command output.
-std::string RunLLDBCommands(llvm::StringRef prefix,
+std::string RunLLDBCommands(DAP &dap, llvm::StringRef prefix,
const llvm::ArrayRef<std::string> &commands,
bool &required_command_failed,
bool parse_command_directives = true);
/// Similar to the method above, but without parsing command directives.
std::string
-RunLLDBCommandsVerbatim(llvm::StringRef prefix,
+RunLLDBCommandsVerbatim(DAP &dap, llvm::StringRef prefix,
const llvm::ArrayRef<std::string> &commands);
/// Check if a thread has a stop reason.
diff --git a/lldb/tools/lldb-dap/OutputRedirector.cpp b/lldb/tools/lldb-dap/OutputRedirector.cpp
index 2c2f49569869b4..7fc9aaef20b911 100644
--- a/lldb/tools/lldb-dap/OutputRedirector.cpp
+++ b/lldb/tools/lldb-dap/OutputRedirector.cpp
@@ -21,7 +21,7 @@ using namespace llvm;
namespace lldb_dap {
-Error RedirectFd(int fd, std::function<void(llvm::StringRef)> callback) {
+Expected<int> RedirectFd(int fd, std::function<void(llvm::StringRef)> callback) {
int new_fd[2];
#if defined(_WIN32)
if (_pipe(new_fd, 4096, O_TEXT) == -1) {
@@ -34,7 +34,7 @@ Error RedirectFd(int fd, std::function<void(llvm::StringRef)> callback) {
strerror(error));
}
- if (dup2(new_fd[1], fd) == -1) {
+ if (fd != -1 && dup2(new_fd[1], fd) == -1) {
int error = errno;
return createStringError(inconvertibleErrorCode(),
"Couldn't override the fd %d. %s", fd,
@@ -57,7 +57,7 @@ Error RedirectFd(int fd, std::function<void(llvm::StringRef)> callback) {
}
});
t.detach();
- return Error::success();
+ return new_fd[1];
}
} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/OutputRedirector.h b/lldb/tools/lldb-dap/OutputRedirector.h
index e26d1648b104f9..418b8bac102c7f 100644
--- a/lldb/tools/lldb-dap/OutputRedirector.h
+++ b/lldb/tools/lldb-dap/OutputRedirector.h
@@ -16,10 +16,16 @@ namespace lldb_dap {
/// Redirects the output of a given file descriptor to a callback.
///
+/// \param[in] fd
+/// Either -1 or the fd duplicate into the new handle.
+///
+/// \param[in] callback
+/// A callback invoked each time the file is written.
+///
/// \return
-/// \a Error::success if the redirection was set up correctly, or an error
-/// otherwise.
-llvm::Error RedirectFd(int fd, std::function<void(llvm::StringRef)> callback);
+/// A new file handle for the output.
+llvm::Expected<int> RedirectFd(int fd,
+ std::function<void(llvm::StringRef)> callback);
} // namespace lldb_dap
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..ae5f0548707d85 100644
--- a/lldb/tools/lldb-dap/SourceBreakpoint.h
+++ b/lldb/tools/lldb-dap/SourceBreakpoint.h
@@ -31,8 +31,8 @@ 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 &dap) : Breakpoint(dap), line(0), column(0) {}
+ SourceBreakpoint(DAP &dap, 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..8cd297aa696168 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 &dap, const llvm::json::Object &obj)
+ : BreakpointBase(dap, 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..a789208f1c7ae6 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 &dap, const llvm::json::Object &obj);
+ Watchpoint(DAP &dap, lldb::SBWatchpoint wp) : BreakpointBase(dap), 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..65dfbbabdcdd12 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -120,62 +120,166 @@ constexpr int StackPageSize = 20;
/// Prints a welcome message on the editor if the preprocessor variable
/// LLDB_DAP_WELCOME_MESSAGE is defined.
-static void PrintWelcomeMessage() {
+static void PrintWelcomeMessage(DAP &dap) {
#ifdef LLDB_DAP_WELCOME_MESSAGE
- g_dap.SendOutput(OutputType::Console, LLDB_DAP_WELCOME_MESSAGE);
+ dap.SendOutput(OutputType::Console, LLDB_DAP_WELCOME_MESSAGE);
#endif
}
-lldb::SBValueList *GetTopLevelScope(int64_t variablesReference) {
+void RegisterRequestCallbacks(DAP &dap);
+
+lldb::SBValueList *GetTopLevelScope(DAP &dap, int64_t variablesReference) {
switch (variablesReference) {
case VARREF_LOCALS:
- return &g_dap.variables.locals;
+ return &dap.variables.locals;
case VARREF_GLOBALS:
- return &g_dap.variables.globals;
+ return &dap.variables.globals;
case VARREF_REGS:
- return &g_dap.variables.registers;
+ return &dap.variables.registers;
default:
return nullptr;
}
}
-SOCKET AcceptConnection(int portno) {
- // Accept a socket connection from any host on "portno".
- SOCKET newsockfd = -1;
- struct sockaddr_in serv_addr, cli_addr;
+/// used only by TestVSCode_redirection_to_console.py
+void redirection_test() {
+ printf("stdout message\n");
+ fprintf(stderr, "stderr message\n");
+ fflush(stdout);
+ fflush(stderr);
+}
+
+/// Redirect stdout and stderr fo the IDE's console output.
+///
+/// Errors in this operation will be printed to the log file and the IDE's
+/// console output as well.
+///
+/// \return
+/// A fd pointing to the original stdout.
+void SetupRedirection(DAP &dap, int stdoutfd = -1, int stderrfd = -1) {
+ auto output_callback_stderr = [&dap](llvm::StringRef data) {
+ dap.SendOutput(OutputType::Stderr, data);
+ };
+ auto output_callback_stdout = [&dap](llvm::StringRef data) {
+ dap.SendOutput(OutputType::Stdout, data);
+ };
+
+ llvm::Expected<int> new_stdout_fd =
+ RedirectFd(stdoutfd, output_callback_stdout);
+ if (auto err = new_stdout_fd.takeError()) {
+ std::string error_message = llvm::toString(std::move(err));
+ if (dap.log)
+ *dap.log << error_message << std::endl;
+ output_callback_stderr(error_message);
+ }
+ dap.out = lldb::SBFile(new_stdout_fd.get(), "w", false);
+
+ llvm::Expected<int> new_stderr_fd =
+ RedirectFd(stderrfd, output_callback_stderr);
+ if (auto err = new_stderr_fd.takeError()) {
+ std::string error_message = llvm::toString(std::move(err));
+ if (dap.log)
+ *dap.log << error_message << std::endl;
+ output_callback_stderr(error_message);
+ }
+ dap.err = lldb::SBFile(new_stderr_fd.get(), "w", false);
+
+ /// used only by TestVSCode_redirection_to_console.py
+ if (getenv("LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION") != nullptr)
+ redirection_test();
+}
+
+int AcceptConnection(llvm::StringRef program_path,
+ const std::vector<std::string> &pre_init_commands,
+ std::shared_ptr<std::ofstream> log,
+ ReplMode default_repl_mode, int portno) {
SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
- if (g_dap.log)
- *g_dap.log << "error: opening socket (" << strerror(errno) << ")"
- << std::endl;
- } else {
- memset((char *)&serv_addr, 0, sizeof(serv_addr));
- serv_addr.sin_family = AF_INET;
- // serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
- serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- serv_addr.sin_port = htons(portno);
- if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
- if (g_dap.log)
- *g_dap.log << "error: binding socket (" << strerror(errno) << ")"
- << std::endl;
- } else {
- listen(sockfd, 5);
- socklen_t clilen = sizeof(cli_addr);
- newsockfd =
- llvm::sys::RetryAfterSignal(static_cast<SOCKET>(-1), accept, sockfd,
- (struct sockaddr *)&cli_addr, &clilen);
- if (newsockfd < 0)
- if (g_dap.log)
- *g_dap.log << "error: accept (" << strerror(errno) << ")"
- << std::endl;
- }
+ if (log)
+ *log << "error: opening socket (" << strerror(errno) << ")" << std::endl;
+ perror("error: socket(int, int, int)");
+ return 1;
+ }
+
+ struct sockaddr_in serv_addr, cli_addr;
+ bzero(&serv_addr, sizeof(serv_addr));
+ bzero(&cli_addr, sizeof(cli_addr));
+ socklen_t clilen = sizeof(cli_addr);
+ serv_addr.sin_family = AF_INET;
+ // serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ serv_addr.sin_port = htons(portno);
+ if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
+ if (log)
+ *log << "error: binding socket (" << strerror(errno) << ")" << std::endl;
+ perror("error: bind(int, struct sockaddr *, socklen_t)");
+#if defined(_WIN32)
+ closesocket(sockfd);
+#else
+ close(sockfd);
+#endif
+ return 1;
+ }
+
+ if (listen(sockfd, 5) < 0) {
+ if (log)
+ *log << "error: listen() (" << strerror(errno) << ")" << std::endl;
+ perror("error: listen(int, int)");
#if defined(_WIN32)
closesocket(sockfd);
#else
close(sockfd);
#endif
+ return 1;
}
- return newsockfd;
+
+ while (true) {
+ SOCKET clientfd =
+ llvm::sys::RetryAfterSignal(static_cast<SOCKET>(-1), accept, sockfd,
+ (struct sockaddr *)&cli_addr, &clilen);
+ if (clientfd < 0) {
+ if (log)
+ *log << "error: accept (" << strerror(errno) << ")" << std::endl;
+ perror("error: accept(int, struct sockaddr *, socklen_t *)");
+ break;
+ }
+
+ std::thread t([program_path, pre_init_commands, log, default_repl_mode,
+ clientfd]() {
+ printf("accepted client fd %d\n", clientfd);
+ DAP dap = DAP(program_path, log, default_repl_mode);
+ dap.debug_adaptor_path = program_path;
+
+ SetupRedirection(dap);
+ RegisterRequestCallbacks(dap);
+
+ dap.input.descriptor = StreamDescriptor::from_socket(clientfd, false);
+ dap.output.descriptor = StreamDescriptor::from_socket(clientfd, false);
+
+ for (const std::string &arg : pre_init_commands) {
+ dap.pre_init_commands.push_back(arg);
+ }
+
+ bool CleanExit = true;
+ if (auto Err = dap.Loop()) {
+ llvm::errs() << "Transport Error: " << llvm::toString(std::move(Err))
+ << "\n";
+ CleanExit = false;
+ }
+
+ printf("closing client fd %d ? %s\n", clientfd,
+ CleanExit ? "true" : "false");
+ close(clientfd);
+ });
+ t.detach();
+ }
+
+#if defined(_WIN32)
+ closesocket(sockfd);
+#else
+ close(sockfd);
+#endif
+ return 0;
}
std::vector<const char *> MakeArgv(const llvm::ArrayRef<std::string> &strs) {
@@ -191,66 +295,65 @@ std::vector<const char *> MakeArgv(const llvm::ArrayRef<std::string> &strs) {
}
// Send a "exited" event to indicate the process has exited.
-void SendProcessExitedEvent(lldb::SBProcess &process) {
+void SendProcessExitedEvent(DAP &dap, lldb::SBProcess &process) {
llvm::json::Object event(CreateEventObject("exited"));
llvm::json::Object body;
body.try_emplace("exitCode", (int64_t)process.GetExitStatus());
event.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(event)));
+ dap.SendJSON(llvm::json::Value(std::move(event)));
}
-void SendThreadExitedEvent(lldb::tid_t tid) {
+void SendThreadExitedEvent(DAP &dap, lldb::tid_t tid) {
llvm::json::Object event(CreateEventObject("thread"));
llvm::json::Object body;
body.try_emplace("reason", "exited");
body.try_emplace("threadId", (int64_t)tid);
event.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(event)));
+ dap.SendJSON(llvm::json::Value(std::move(event)));
}
// Send a "continued" event to indicate the process is in the running state.
-void SendContinuedEvent() {
- lldb::SBProcess process = g_dap.target.GetProcess();
+void SendContinuedEvent(DAP &dap) {
+ lldb::SBProcess process = dap.target.GetProcess();
if (!process.IsValid()) {
return;
}
// If the focus thread is not set then we haven't reported any thread status
// to the client, so nothing to report.
- if (!g_dap.configuration_done_sent ||
- g_dap.focus_tid == LLDB_INVALID_THREAD_ID) {
+ if (!dap.configuration_done_sent || dap.focus_tid == LLDB_INVALID_THREAD_ID) {
return;
}
llvm::json::Object event(CreateEventObject("continued"));
llvm::json::Object body;
- body.try_emplace("threadId", (int64_t)g_dap.focus_tid);
+ body.try_emplace("threadId", (int64_t)dap.focus_tid);
body.try_emplace("allThreadsContinued", true);
event.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(event)));
+ dap.SendJSON(llvm::json::Value(std::move(event)));
}
// Send a "terminated" event to indicate the process is done being
// debugged.
-void SendTerminatedEvent() {
+void SendTerminatedEvent(DAP &dap) {
// Prevent races if the process exits while we're being asked to disconnect.
- llvm::call_once(g_dap.terminated_event_flag, [&] {
- g_dap.RunTerminateCommands();
+ llvm::call_once(dap.terminated_event_flag, [&] {
+ dap.RunTerminateCommands();
// Send a "terminated" event
- llvm::json::Object event(CreateTerminatedEventObject());
- g_dap.SendJSON(llvm::json::Value(std::move(event)));
+ llvm::json::Object event(CreateTerminatedEventObject(dap.target));
+ dap.SendJSON(llvm::json::Value(std::move(event)));
});
}
// Send a thread stopped event for all threads as long as the process
// is stopped.
-void SendThreadStoppedEvent() {
- lldb::SBProcess process = g_dap.target.GetProcess();
+void SendThreadStoppedEvent(DAP &dap) {
+ lldb::SBProcess process = dap.target.GetProcess();
if (process.IsValid()) {
auto state = process.GetState();
if (state == lldb::eStateStopped) {
llvm::DenseSet<lldb::tid_t> old_thread_ids;
- old_thread_ids.swap(g_dap.thread_ids);
+ old_thread_ids.swap(dap.thread_ids);
uint32_t stop_id = process.GetStopID();
const uint32_t num_threads = process.GetNumThreads();
@@ -266,10 +369,10 @@ void SendThreadStoppedEvent() {
const lldb::tid_t tid = thread.GetThreadID();
const bool has_reason = ThreadHasStopReason(thread);
// If the focus thread doesn't have a stop reason, clear the thread ID
- if (tid == g_dap.focus_tid) {
+ if (tid == dap.focus_tid) {
focus_thread_exists = true;
if (!has_reason)
- g_dap.focus_tid = LLDB_INVALID_THREAD_ID;
+ dap.focus_tid = LLDB_INVALID_THREAD_ID;
}
if (has_reason) {
++num_threads_with_reason;
@@ -278,47 +381,46 @@ void SendThreadStoppedEvent() {
}
}
- // We will have cleared g_dap.focus_tid if the focus thread doesn't have
+ // We will have cleared dap.focus_tid if the focus thread doesn't have
// a stop reason, so if it was cleared, or wasn't set, or doesn't exist,
// then set the focus thread to the first thread with a stop reason.
- if (!focus_thread_exists || g_dap.focus_tid == LLDB_INVALID_THREAD_ID)
- g_dap.focus_tid = first_tid_with_reason;
+ if (!focus_thread_exists || dap.focus_tid == LLDB_INVALID_THREAD_ID)
+ dap.focus_tid = first_tid_with_reason;
// If no threads stopped with a reason, then report the first one so
// we at least let the UI know we stopped.
if (num_threads_with_reason == 0) {
lldb::SBThread thread = process.GetThreadAtIndex(0);
- g_dap.focus_tid = thread.GetThreadID();
- g_dap.SendJSON(CreateThreadStopped(thread, stop_id));
+ dap.focus_tid = thread.GetThreadID();
+ dap.SendJSON(CreateThreadStopped(dap, thread, stop_id));
} else {
for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
- g_dap.thread_ids.insert(thread.GetThreadID());
+ dap.thread_ids.insert(thread.GetThreadID());
if (ThreadHasStopReason(thread)) {
- g_dap.SendJSON(CreateThreadStopped(thread, stop_id));
+ dap.SendJSON(CreateThreadStopped(dap, thread, stop_id));
}
}
}
for (auto tid : old_thread_ids) {
- auto end = g_dap.thread_ids.end();
- auto pos = g_dap.thread_ids.find(tid);
+ auto end = dap.thread_ids.end();
+ auto pos = dap.thread_ids.find(tid);
if (pos == end)
- SendThreadExitedEvent(tid);
+ SendThreadExitedEvent(dap, tid);
}
} else {
- if (g_dap.log)
- *g_dap.log << "error: SendThreadStoppedEvent() when process"
- " isn't stopped ("
- << lldb::SBDebugger::StateAsCString(state) << ')'
- << std::endl;
+ if (dap.log)
+ *dap.log << "error: SendThreadStoppedEvent() when process"
+ " isn't stopped ("
+ << lldb::SBDebugger::StateAsCString(state) << ')' << std::endl;
}
} else {
- if (g_dap.log)
- *g_dap.log << "error: SendThreadStoppedEvent() invalid process"
- << std::endl;
+ if (dap.log)
+ *dap.log << "error: SendThreadStoppedEvent() invalid process"
+ << std::endl;
}
- g_dap.RunStopCommands();
+ dap.RunStopCommands();
}
// "ProcessEvent": {
@@ -375,14 +477,14 @@ void SendThreadStoppedEvent() {
// }
// ]
// }
-void SendProcessEvent(LaunchMethod launch_method) {
- lldb::SBFileSpec exe_fspec = g_dap.target.GetExecutable();
+void SendProcessEvent(DAP &dap, LaunchMethod launch_method) {
+ lldb::SBFileSpec exe_fspec = dap.target.GetExecutable();
char exe_path[PATH_MAX];
exe_fspec.GetPath(exe_path, sizeof(exe_path));
llvm::json::Object event(CreateEventObject("process"));
llvm::json::Object body;
EmplaceSafeString(body, "name", std::string(exe_path));
- const auto pid = g_dap.target.GetProcess().GetProcessID();
+ const auto pid = dap.target.GetProcess().GetProcessID();
body.try_emplace("systemProcessId", (int64_t)pid);
body.try_emplace("isLocalProcess", true);
const char *startMethod = nullptr;
@@ -399,31 +501,31 @@ void SendProcessEvent(LaunchMethod launch_method) {
}
body.try_emplace("startMethod", startMethod);
event.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(event)));
+ dap.SendJSON(llvm::json::Value(std::move(event)));
}
// Grab any STDOUT and STDERR from the process and send it up to VS Code
// via an "output" event to the "stdout" and "stderr" categories.
-void SendStdOutStdErr(lldb::SBProcess &process) {
+void SendStdOutStdErr(DAP &dap, lldb::SBProcess &process) {
char buffer[OutputBufferSize];
size_t count;
while ((count = process.GetSTDOUT(buffer, sizeof(buffer))) > 0)
- g_dap.SendOutput(OutputType::Stdout, llvm::StringRef(buffer, count));
+ dap.SendOutput(OutputType::Stdout, llvm::StringRef(buffer, count));
while ((count = process.GetSTDERR(buffer, sizeof(buffer))) > 0)
- g_dap.SendOutput(OutputType::Stderr, llvm::StringRef(buffer, count));
+ dap.SendOutput(OutputType::Stderr, llvm::StringRef(buffer, count));
}
-void ProgressEventThreadFunction() {
+void ProgressEventThreadFunction(DAP &dap) {
lldb::SBListener listener("lldb-dap.progress.listener");
- g_dap.debugger.GetBroadcaster().AddListener(
+ dap.debugger.GetBroadcaster().AddListener(
listener, lldb::SBDebugger::eBroadcastBitProgress);
- g_dap.broadcaster.AddListener(listener, eBroadcastBitStopProgressThread);
+ dap.broadcaster.AddListener(listener, eBroadcastBitStopProgressThread);
lldb::SBEvent event;
bool done = false;
while (!done) {
if (listener.WaitForEvent(1, event)) {
const auto event_mask = event.GetType();
- if (event.BroadcasterMatchesRef(g_dap.broadcaster)) {
+ if (event.BroadcasterMatchesRef(dap.broadcaster)) {
if (event_mask & eBroadcastBitStopProgressThread) {
done = true;
}
@@ -435,7 +537,7 @@ void ProgressEventThreadFunction() {
const char *message = lldb::SBDebugger::GetProgressFromEvent(
event, progress_id, completed, total, is_debugger_specific);
if (message)
- g_dap.SendProgressEvent(progress_id, message, completed, total);
+ dap.SendProgressEvent(progress_id, message, completed, total);
}
}
}
@@ -446,9 +548,9 @@ void ProgressEventThreadFunction() {
// "FILE *" to output packets back to VS Code and they have mutexes in them
// them prevent multiple threads from writing simultaneously so no locking
// is required.
-void EventThreadFunction() {
+void EventThreadFunction(DAP &dap) {
lldb::SBEvent event;
- lldb::SBListener listener = g_dap.debugger.GetListener();
+ lldb::SBListener listener = dap.debugger.GetListener();
bool done = false;
while (!done) {
if (listener.WaitForEvent(1, event)) {
@@ -484,50 +586,50 @@ void EventThreadFunction() {
// stop events which we do not want to send an event for. We will
// manually send a stopped event in request_configurationDone(...)
// so don't send any before then.
- if (g_dap.configuration_done_sent) {
+ if (dap.configuration_done_sent) {
// Only report a stopped event if the process was not
// automatically restarted.
if (!lldb::SBProcess::GetRestartedFromEvent(event)) {
- SendStdOutStdErr(process);
- SendThreadStoppedEvent();
+ SendStdOutStdErr(dap, process);
+ SendThreadStoppedEvent(dap);
}
}
break;
case lldb::eStateRunning:
- g_dap.WillContinue();
- SendContinuedEvent();
+ dap.WillContinue();
+ SendContinuedEvent(dap);
break;
case lldb::eStateExited:
lldb::SBStream stream;
process.GetStatus(stream);
- g_dap.SendOutput(OutputType::Console, stream.GetData());
+ dap.SendOutput(OutputType::Console, stream.GetData());
// When restarting, we can get an "exited" event for the process we
// just killed with the old PID, or even with no PID. In that case
// we don't have to terminate the session.
if (process.GetProcessID() == LLDB_INVALID_PROCESS_ID ||
- process.GetProcessID() == g_dap.restarting_process_id) {
- g_dap.restarting_process_id = LLDB_INVALID_PROCESS_ID;
+ process.GetProcessID() == dap.restarting_process_id) {
+ dap.restarting_process_id = LLDB_INVALID_PROCESS_ID;
} else {
// Run any exit LLDB commands the user specified in the
// launch.json
- g_dap.RunExitCommands();
- SendProcessExitedEvent(process);
- SendTerminatedEvent();
+ dap.RunExitCommands();
+ SendProcessExitedEvent(dap, process);
+ SendTerminatedEvent(dap);
done = true;
}
break;
}
} else if ((event_mask & lldb::SBProcess::eBroadcastBitSTDOUT) ||
(event_mask & lldb::SBProcess::eBroadcastBitSTDERR)) {
- SendStdOutStdErr(process);
+ SendStdOutStdErr(dap, process);
}
} else if (lldb::SBBreakpoint::EventIsBreakpointEvent(event)) {
if (event_mask & lldb::SBTarget::eBroadcastBitBreakpointChanged) {
auto event_type =
lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event);
- auto bp =
- Breakpoint(lldb::SBBreakpoint::GetBreakpointFromEvent(event));
+ auto bp = Breakpoint(
+ 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
@@ -549,10 +651,10 @@ void EventThreadFunction() {
body.try_emplace("breakpoint", source_bp);
body.try_emplace("reason", "changed");
bp_event.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(bp_event)));
+ dap.SendJSON(llvm::json::Value(std::move(bp_event)));
}
}
- } else if (event.BroadcasterMatchesRef(g_dap.broadcaster)) {
+ } else if (event.BroadcasterMatchesRef(dap.broadcaster)) {
if (event_mask & eBroadcastBitStopEventThread) {
done = true;
}
@@ -561,9 +663,11 @@ void EventThreadFunction() {
}
}
-lldb::SBValue FindVariable(uint64_t variablesReference, llvm::StringRef name) {
+lldb::SBValue FindVariable(DAP &dap, uint64_t variablesReference,
+ llvm::StringRef name) {
lldb::SBValue variable;
- if (lldb::SBValueList *top_scope = GetTopLevelScope(variablesReference)) {
+ if (lldb::SBValueList *top_scope =
+ GetTopLevelScope(dap, variablesReference)) {
bool is_duplicated_variable_name = name.contains(" @");
// variablesReference is one of our scopes, not an actual variable it is
// asking for a variable in locals or globals or registers
@@ -585,7 +689,7 @@ lldb::SBValue FindVariable(uint64_t variablesReference, llvm::StringRef name) {
// We have a named item within an actual variable so we need to find it
// withing the container variable by name.
- lldb::SBValue container = g_dap.variables.GetVariable(variablesReference);
+ lldb::SBValue container = dap.variables.GetVariable(variablesReference);
variable = container.GetChildMemberWithName(name.data());
if (!variable.IsValid()) {
if (name.starts_with("[")) {
@@ -603,7 +707,7 @@ lldb::SBValue FindVariable(uint64_t variablesReference, llvm::StringRef name) {
// Both attach and launch take a either a sourcePath or sourceMap
// argument (or neither), from which we need to set the target.source-map.
-void SetSourceMapFromArguments(const llvm::json::Object &arguments) {
+void SetSourceMapFromArguments(DAP &dap, const llvm::json::Object &arguments) {
const char *sourceMapHelp =
"source must be be an array of two-element arrays, "
"each containing a source and replacement path string.\n";
@@ -622,7 +726,7 @@ void SetSourceMapFromArguments(const llvm::json::Object &arguments) {
if (mapping == nullptr || mapping->size() != 2 ||
(*mapping)[0].kind() != llvm::json::Value::String ||
(*mapping)[1].kind() != llvm::json::Value::String) {
- g_dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp));
+ dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp));
return;
}
const auto mapFrom = GetAsString((*mapping)[0]);
@@ -637,7 +741,7 @@ void SetSourceMapFromArguments(const llvm::json::Object &arguments) {
}
} else {
if (ObjectContainsKey(arguments, sourceMapKey)) {
- g_dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp));
+ dap.SendOutput(OutputType::Console, llvm::StringRef(sourceMapHelp));
return;
}
if (sourcePath.empty())
@@ -646,7 +750,7 @@ void SetSourceMapFromArguments(const llvm::json::Object &arguments) {
strm << "\".\" \"" << sourcePath << "\"";
}
if (!sourceMapCommand.empty()) {
- g_dap.RunLLDBCommands("Setting source map:", {sourceMapCommand});
+ dap.RunLLDBCommands("Setting source map:", {sourceMapCommand});
}
}
@@ -682,14 +786,15 @@ void SetSourceMapFromArguments(const llvm::json::Object &arguments) {
// 13. th3->s2
//
// s=3,l=3 = [th0->s3, label1, th1->s0]
-bool FillStackFrames(lldb::SBThread &thread, llvm::json::Array &stack_frames,
- int64_t &offset, const int64_t start_frame,
- const int64_t levels) {
+bool FillStackFrames(DAP &dap, lldb::SBThread &thread,
+ llvm::json::Array &stack_frames, int64_t &offset,
+ const int64_t start_frame, const int64_t levels) {
bool reached_end_of_stack = false;
for (int64_t i = start_frame;
static_cast<int64_t>(stack_frames.size()) < levels; i++) {
if (i == -1) {
- stack_frames.emplace_back(CreateExtendedStackFrameLabel(thread));
+ stack_frames.emplace_back(
+ CreateExtendedStackFrameLabel(thread, dap.thread_format));
continue;
}
@@ -700,10 +805,10 @@ bool FillStackFrames(lldb::SBThread &thread, llvm::json::Array &stack_frames,
break;
}
- stack_frames.emplace_back(CreateStackFrame(frame));
+ stack_frames.emplace_back(CreateStackFrame(frame, dap.frame_format));
}
- if (g_dap.display_extended_backtrace && reached_end_of_stack) {
+ if (dap.display_extended_backtrace && reached_end_of_stack) {
// Check for any extended backtraces.
for (uint32_t bt = 0;
bt < thread.GetProcess().GetNumExtendedBacktraceTypes(); bt++) {
@@ -713,7 +818,7 @@ bool FillStackFrames(lldb::SBThread &thread, llvm::json::Array &stack_frames,
continue;
reached_end_of_stack = FillStackFrames(
- backtrace, stack_frames, offset,
+ dap, backtrace, stack_frames, offset,
(start_frame - offset) > 0 ? start_frame - offset : -1, levels);
if (static_cast<int64_t>(stack_frames.size()) >= levels)
break;
@@ -751,15 +856,15 @@ bool FillStackFrames(lldb::SBThread &thread, llvm::json::Array &stack_frames,
// acknowledgement, so no body field is required."
// }]
// }
-void request_attach(const llvm::json::Object &request) {
- g_dap.is_attach = true;
- g_dap.last_launch_or_attach_request = request;
+void request_attach(DAP &dap, const llvm::json::Object &request) {
+ dap.is_attach = true;
+ dap.last_launch_or_attach_request = request;
llvm::json::Object response;
lldb::SBError error;
FillResponse(request, response);
lldb::SBAttachInfo attach_info;
const int invalid_port = 0;
- auto arguments = request.getObject("arguments");
+ const auto *arguments = request.getObject("arguments");
const lldb::pid_t pid =
GetUnsigned(arguments, "pid", LLDB_INVALID_PROCESS_ID);
const auto gdb_remote_port =
@@ -770,30 +875,29 @@ void request_attach(const llvm::json::Object &request) {
attach_info.SetProcessID(pid);
const auto wait_for = GetBoolean(arguments, "waitFor", false);
attach_info.SetWaitForLaunch(wait_for, false /*async*/);
- g_dap.init_commands = GetStrings(arguments, "initCommands");
- g_dap.pre_run_commands = GetStrings(arguments, "preRunCommands");
- g_dap.stop_commands = GetStrings(arguments, "stopCommands");
- g_dap.exit_commands = GetStrings(arguments, "exitCommands");
- g_dap.terminate_commands = GetStrings(arguments, "terminateCommands");
+ dap.init_commands = GetStrings(arguments, "initCommands");
+ dap.pre_run_commands = GetStrings(arguments, "preRunCommands");
+ dap.stop_commands = GetStrings(arguments, "stopCommands");
+ dap.exit_commands = GetStrings(arguments, "exitCommands");
+ dap.terminate_commands = GetStrings(arguments, "terminateCommands");
auto attachCommands = GetStrings(arguments, "attachCommands");
llvm::StringRef core_file = GetString(arguments, "coreFile");
const uint64_t timeout_seconds = GetUnsigned(arguments, "timeout", 30);
- g_dap.stop_at_entry =
+ dap.stop_at_entry =
core_file.empty() ? GetBoolean(arguments, "stopOnEntry", false) : true;
- g_dap.post_run_commands = GetStrings(arguments, "postRunCommands");
+ dap.post_run_commands = GetStrings(arguments, "postRunCommands");
const llvm::StringRef debuggerRoot = GetString(arguments, "debuggerRoot");
- g_dap.enable_auto_variable_summaries =
+ dap.enable_auto_variable_summaries =
GetBoolean(arguments, "enableAutoVariableSummaries", false);
- g_dap.enable_synthetic_child_debugging =
+ dap.enable_synthetic_child_debugging =
GetBoolean(arguments, "enableSyntheticChildDebugging", false);
- g_dap.display_extended_backtrace =
+ dap.display_extended_backtrace =
GetBoolean(arguments, "displayExtendedBacktrace", false);
- g_dap.command_escape_prefix =
- GetString(arguments, "commandEscapePrefix", "`");
- g_dap.SetFrameFormat(GetString(arguments, "customFrameFormat"));
- g_dap.SetThreadFormat(GetString(arguments, "customThreadFormat"));
+ dap.command_escape_prefix = GetString(arguments, "commandEscapePrefix", "`");
+ dap.SetFrameFormat(GetString(arguments, "customFrameFormat"));
+ dap.SetThreadFormat(GetString(arguments, "customThreadFormat"));
- PrintWelcomeMessage();
+ PrintWelcomeMessage(dap);
// This is a hack for loading DWARF in .o files on Mac where the .o files
// in the debug map of the main executable have relative paths which require
@@ -803,29 +907,29 @@ void request_attach(const llvm::json::Object &request) {
llvm::sys::fs::set_current_path(debuggerRoot);
// Run any initialize LLDB commands the user specified in the launch.json
- if (llvm::Error err = g_dap.RunInitCommands()) {
+ if (llvm::Error err = dap.RunInitCommands()) {
response["success"] = false;
EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
- SetSourceMapFromArguments(*arguments);
+ SetSourceMapFromArguments(dap, *arguments);
lldb::SBError status;
- g_dap.SetTarget(g_dap.CreateTargetFromArguments(*arguments, status));
+ dap.SetTarget(dap.CreateTargetFromArguments(*arguments, status));
if (status.Fail()) {
response["success"] = llvm::json::Value(false);
EmplaceSafeString(response, "message", status.GetCString());
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
// Run any pre run LLDB commands the user specified in the launch.json
- if (llvm::Error err = g_dap.RunPreRunCommands()) {
+ if (llvm::Error err = dap.RunPreRunCommands()) {
response["success"] = false;
EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
@@ -834,15 +938,15 @@ void request_attach(const llvm::json::Object &request) {
char attach_msg[256];
auto attach_msg_len = snprintf(attach_msg, sizeof(attach_msg),
"Waiting to attach to \"%s\"...",
- g_dap.target.GetExecutable().GetFilename());
- g_dap.SendOutput(OutputType::Console,
- llvm::StringRef(attach_msg, attach_msg_len));
+ dap.target.GetExecutable().GetFilename());
+ dap.SendOutput(OutputType::Console,
+ llvm::StringRef(attach_msg, attach_msg_len));
}
if (attachCommands.empty()) {
// No "attachCommands", just attach normally.
// Disable async events so the attach will be successful when we return from
// the launch call and the launch will happen synchronously
- g_dap.debugger.SetAsync(false);
+ dap.debugger.SetAsync(false);
if (core_file.empty()) {
if ((pid != LLDB_INVALID_PROCESS_ID) &&
(gdb_remote_port != invalid_port)) {
@@ -850,44 +954,44 @@ void request_attach(const llvm::json::Object &request) {
error.SetErrorString("The user can't specify both pid and port");
} else if (gdb_remote_port != invalid_port) {
// If port is specified and pid is not.
- lldb::SBListener listener = g_dap.debugger.GetListener();
+ lldb::SBListener listener = dap.debugger.GetListener();
// If the user hasn't provided the hostname property, default localhost
// being used.
std::string connect_url =
llvm::formatv("connect://{0}:", gdb_remote_hostname);
connect_url += std::to_string(gdb_remote_port);
- g_dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
- error);
+ dap.target.ConnectRemote(listener, connect_url.c_str(), "gdb-remote",
+ error);
} else {
// Attach by process name or id.
- g_dap.target.Attach(attach_info, error);
+ dap.target.Attach(attach_info, error);
}
} else
- g_dap.target.LoadCore(core_file.data(), error);
+ dap.target.LoadCore(core_file.data(), error);
// Reenable async events
- g_dap.debugger.SetAsync(true);
+ dap.debugger.SetAsync(true);
} else {
// We have "attachCommands" that are a set of commands that are expected
// to execute the commands after which a process should be created. If there
// is no valid process after running these commands, we have failed.
- if (llvm::Error err = g_dap.RunAttachCommands(attachCommands)) {
+ if (llvm::Error err = dap.RunAttachCommands(attachCommands)) {
response["success"] = false;
EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
// The custom commands might have created a new target so we should use the
// selected target after these commands are run.
- g_dap.target = g_dap.debugger.GetSelectedTarget();
+ dap.target = dap.debugger.GetSelectedTarget();
// Make sure the process is attached and stopped before proceeding as the
// the launch commands are not run using the synchronous mode.
- error = g_dap.WaitForProcessToStop(timeout_seconds);
+ error = dap.WaitForProcessToStop(timeout_seconds);
}
if (error.Success() && core_file.empty()) {
- auto attached_pid = g_dap.target.GetProcess().GetProcessID();
+ auto attached_pid = dap.target.GetProcess().GetProcessID();
if (attached_pid == LLDB_INVALID_PROCESS_ID) {
if (attachCommands.empty())
error.SetErrorString("failed to attach to a process");
@@ -900,13 +1004,13 @@ void request_attach(const llvm::json::Object &request) {
response["success"] = llvm::json::Value(false);
EmplaceSafeString(response, "message", std::string(error.GetCString()));
} else {
- g_dap.RunPostRunCommands();
+ dap.RunPostRunCommands();
}
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
if (error.Success()) {
- SendProcessEvent(Attach);
- g_dap.SendJSON(CreateEventObject("initialized"));
+ SendProcessEvent(dap, Attach);
+ dap.SendJSON(CreateEventObject("initialized"));
}
}
@@ -964,15 +1068,15 @@ void request_attach(const llvm::json::Object &request) {
// "required": [ "body" ]
// }]
// }
-void request_continue(const llvm::json::Object &request) {
+void request_continue(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
- lldb::SBProcess process = g_dap.target.GetProcess();
+ lldb::SBProcess process = dap.target.GetProcess();
lldb::SBError error = process.Continue();
llvm::json::Object body;
body.try_emplace("allThreadsContinued", true);
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "ConfigurationDoneRequest": {
@@ -1006,15 +1110,15 @@ void request_continue(const llvm::json::Object &request) {
// just an acknowledgement, so no body field is required."
// }]
// },
-void request_configurationDone(const llvm::json::Object &request) {
+void request_configurationDone(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
- g_dap.configuration_done_sent = true;
- if (g_dap.stop_at_entry)
- SendThreadStoppedEvent();
+ dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.configuration_done_sent = true;
+ if (dap.stop_at_entry)
+ SendThreadStoppedEvent(dap);
else
- g_dap.target.GetProcess().Continue();
+ dap.target.GetProcess().Continue();
}
// "DisconnectRequest": {
@@ -1061,15 +1165,15 @@ void request_configurationDone(const llvm::json::Object &request) {
// acknowledgement, so no body field is required."
// }]
// }
-void request_disconnect(const llvm::json::Object &request) {
+void request_disconnect(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
- auto arguments = request.getObject("arguments");
+ const auto *arguments = request.getObject("arguments");
- bool defaultTerminateDebuggee = g_dap.is_attach ? false : true;
+ bool defaultTerminateDebuggee = dap.is_attach ? false : true;
bool terminateDebuggee =
GetBoolean(arguments, "terminateDebuggee", defaultTerminateDebuggee);
- lldb::SBProcess process = g_dap.target.GetProcess();
+ lldb::SBProcess process = dap.target.GetProcess();
auto state = process.GetState();
switch (state) {
case lldb::eStateInvalid:
@@ -1085,24 +1189,24 @@ void request_disconnect(const llvm::json::Object &request) {
case lldb::eStateSuspended:
case lldb::eStateStopped:
case lldb::eStateRunning:
- g_dap.debugger.SetAsync(false);
+ dap.debugger.SetAsync(false);
lldb::SBError error = terminateDebuggee ? process.Kill() : process.Detach();
if (!error.Success())
EmplaceSafeString(response, "error", error.GetCString());
- g_dap.debugger.SetAsync(true);
+ dap.debugger.SetAsync(true);
break;
}
- SendTerminatedEvent();
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
- if (g_dap.event_thread.joinable()) {
- g_dap.broadcaster.BroadcastEventByType(eBroadcastBitStopEventThread);
- g_dap.event_thread.join();
+ SendTerminatedEvent(dap);
+ dap.SendJSON(llvm::json::Value(std::move(response)));
+ if (dap.event_thread.joinable()) {
+ dap.broadcaster.BroadcastEventByType(eBroadcastBitStopEventThread);
+ dap.event_thread.join();
}
- if (g_dap.progress_event_thread.joinable()) {
- g_dap.broadcaster.BroadcastEventByType(eBroadcastBitStopProgressThread);
- g_dap.progress_event_thread.join();
+ if (dap.progress_event_thread.joinable()) {
+ dap.broadcaster.BroadcastEventByType(eBroadcastBitStopProgressThread);
+ dap.progress_event_thread.join();
}
- g_dap.disconnecting = true;
+ dap.disconnecting = true;
}
// "ExceptionInfoRequest": {
@@ -1202,18 +1306,18 @@ void request_disconnect(const llvm::json::Object &request) {
// }
// }
// },
-void request_exceptionInfo(const llvm::json::Object &request) {
+void request_exceptionInfo(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
- auto arguments = request.getObject("arguments");
+ const auto *const arguments = request.getObject("arguments");
llvm::json::Object body;
- lldb::SBThread thread = g_dap.GetLLDBThread(*arguments);
+ lldb::SBThread thread = dap.GetLLDBThread(*arguments);
if (thread.IsValid()) {
auto stopReason = thread.GetStopReason();
if (stopReason == lldb::eStopReasonSignal)
body.try_emplace("exceptionId", "signal");
else if (stopReason == lldb::eStopReasonBreakpoint) {
- ExceptionBreakpoint *exc_bp = g_dap.GetExceptionBPFromStopReason(thread);
+ ExceptionBreakpoint *exc_bp = dap.GetExceptionBPFromStopReason(thread);
if (exc_bp) {
EmplaceSafeString(body, "exceptionId", exc_bp->filter);
EmplaceSafeString(body, "description", exc_bp->label);
@@ -1259,7 +1363,7 @@ void request_exceptionInfo(const llvm::json::Object &request) {
response["success"] = llvm::json::Value(false);
}
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "CompletionsRequest": {
@@ -1377,14 +1481,14 @@ void request_exceptionInfo(const llvm::json::Object &request) {
// "interface", "module", "property", "unit", "value", "enum", "keyword",
// "snippet", "text", "color", "file", "reference", "customcolor" ]
// }
-void request_completions(const llvm::json::Object &request) {
+void request_completions(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
llvm::json::Object body;
- auto arguments = request.getObject("arguments");
+ const auto *const arguments = request.getObject("arguments");
// If we have a frame, try to set the context for variable completions.
- lldb::SBFrame frame = g_dap.GetLLDBFrame(*arguments);
+ lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
if (frame.IsValid()) {
frame.GetThread().GetProcess().SetSelectedThread(frame.GetThread());
frame.GetThread().SetSelectedFrame(frame.GetFrameID());
@@ -1404,19 +1508,19 @@ void request_completions(const llvm::json::Object &request) {
llvm::json::Array targets;
bool had_escape_prefix =
- llvm::StringRef(text).starts_with(g_dap.command_escape_prefix);
- ReplMode completion_mode = g_dap.DetectReplMode(frame, text, true);
+ llvm::StringRef(text).starts_with(dap.command_escape_prefix);
+ ReplMode completion_mode = dap.DetectReplMode(frame, text, true);
// Handle the offset change introduced by stripping out the
// `command_escape_prefix`.
if (had_escape_prefix) {
- if (offset < static_cast<int64_t>(g_dap.command_escape_prefix.size())) {
+ if (offset < static_cast<int64_t>(dap.command_escape_prefix.size())) {
body.try_emplace("targets", std::move(targets));
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
- offset -= g_dap.command_escape_prefix.size();
+ offset -= dap.command_escape_prefix.size();
}
// While the user is typing then we likely have an incomplete input and cannot
@@ -1433,9 +1537,8 @@ void request_completions(const llvm::json::Object &request) {
lldb::SBStringList matches;
lldb::SBStringList descriptions;
- if (!g_dap.debugger.GetCommandInterpreter()
- .HandleCompletionWithDescriptions(line.c_str(), cursor, 0, 100,
- matches, descriptions))
+ if (!dap.debugger.GetCommandInterpreter().HandleCompletionWithDescriptions(
+ line.c_str(), cursor, 0, 100, matches, descriptions))
continue;
// The first element is the common substring after the cursor position for
@@ -1465,7 +1568,7 @@ void request_completions(const llvm::json::Object &request) {
body.try_emplace("targets", std::move(targets));
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "EvaluateRequest": {
@@ -1592,31 +1695,31 @@ void request_completions(const llvm::json::Object &request) {
// "required": [ "body" ]
// }]
// }
-void request_evaluate(const llvm::json::Object &request) {
+void request_evaluate(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
llvm::json::Object body;
- auto arguments = request.getObject("arguments");
- lldb::SBFrame frame = g_dap.GetLLDBFrame(*arguments);
+ const auto *const arguments = request.getObject("arguments");
+ lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
std::string expression = GetString(arguments, "expression").str();
llvm::StringRef context = GetString(arguments, "context");
bool repeat_last_command =
- expression.empty() && g_dap.last_nonempty_var_expression.empty();
+ expression.empty() && dap.last_nonempty_var_expression.empty();
if (context == "repl" &&
(repeat_last_command ||
(!expression.empty() &&
- g_dap.DetectReplMode(frame, expression, false) == ReplMode::Command))) {
+ dap.DetectReplMode(frame, expression, false) == ReplMode::Command))) {
// Since the current expression is not for a variable, clear the
// last_nonempty_var_expression field.
- g_dap.last_nonempty_var_expression.clear();
+ dap.last_nonempty_var_expression.clear();
// If we're evaluating a command relative to the current frame, set the
// focus_tid to the current frame for any thread related events.
if (frame.IsValid()) {
- g_dap.focus_tid = frame.GetThread().GetThreadID();
+ dap.focus_tid = frame.GetThread().GetThreadID();
}
- auto result =
- RunLLDBCommandsVerbatim(llvm::StringRef(), {std::string(expression)});
+ auto result = RunLLDBCommandsVerbatim(dap, llvm::StringRef(),
+ {std::string(expression)});
EmplaceSafeString(body, "result", result);
body.try_emplace("variablesReference", (int64_t)0);
} else {
@@ -1626,9 +1729,9 @@ void request_evaluate(const llvm::json::Object &request) {
// evaluation); otherwise save the current non-empty expression for the
// next (possibly empty) variable expression.
if (expression.empty())
- expression = g_dap.last_nonempty_var_expression;
+ expression = dap.last_nonempty_var_expression;
else
- g_dap.last_nonempty_var_expression = expression;
+ dap.last_nonempty_var_expression = expression;
}
// Always try to get the answer from the local variables if possible. If
// this fails, then if the context is not "hover", actually evaluate an
@@ -1657,12 +1760,12 @@ void request_evaluate(const llvm::json::Object &request) {
else
EmplaceSafeString(response, "message", "evaluate failed");
} else {
- VariableDescription desc(value);
+ VariableDescription desc(value, dap.enable_auto_variable_summaries);
EmplaceSafeString(body, "result", desc.GetResult(context));
EmplaceSafeString(body, "type", desc.display_type_name);
int64_t var_ref = 0;
if (value.MightHaveChildren() || ValuePointsToCode(value))
- var_ref = g_dap.variables.InsertVariable(
+ var_ref = dap.variables.InsertVariable(
value, /*is_permanent=*/context == "repl");
if (value.MightHaveChildren())
body.try_emplace("variablesReference", var_ref);
@@ -1676,7 +1779,7 @@ void request_evaluate(const llvm::json::Object &request) {
}
}
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "compileUnitsRequest": {
@@ -1719,16 +1822,16 @@ void request_evaluate(const llvm::json::Object &request) {
// }
// }]
// }
-void request_compileUnits(const llvm::json::Object &request) {
+void request_compileUnits(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
llvm::json::Object body;
llvm::json::Array units;
- auto arguments = request.getObject("arguments");
+ const auto *const arguments = request.getObject("arguments");
std::string module_id = std::string(GetString(arguments, "moduleId"));
- int num_modules = g_dap.target.GetNumModules();
+ int num_modules = dap.target.GetNumModules();
for (int i = 0; i < num_modules; i++) {
- auto curr_module = g_dap.target.GetModuleAtIndex(i);
+ auto curr_module = dap.target.GetModuleAtIndex(i);
if (module_id == curr_module.GetUUIDString()) {
int num_units = curr_module.GetNumCompileUnits();
for (int j = 0; j < num_units; j++) {
@@ -1740,7 +1843,7 @@ void request_compileUnits(const llvm::json::Object &request) {
}
}
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "modulesRequest": {
@@ -1769,20 +1872,20 @@ void request_compileUnits(const llvm::json::Object &request) {
// }
// }]
// }
-void request_modules(const llvm::json::Object &request) {
+void request_modules(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
llvm::json::Array modules;
- for (size_t i = 0; i < g_dap.target.GetNumModules(); i++) {
- lldb::SBModule module = g_dap.target.GetModuleAtIndex(i);
- modules.emplace_back(CreateModule(module));
+ for (size_t i = 0; i < dap.target.GetNumModules(); i++) {
+ lldb::SBModule module = dap.target.GetModuleAtIndex(i);
+ modules.emplace_back(CreateModule(dap.target, module));
}
llvm::json::Object body;
body.try_emplace("modules", std::move(modules));
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "InitializeRequest": {
@@ -1861,49 +1964,61 @@ void request_modules(const llvm::json::Object &request) {
// }
// }]
// }
-void request_initialize(const llvm::json::Object &request) {
+void request_initialize(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
llvm::json::Object body;
- auto log_cb = [](const char *buf, void *baton) -> void {
- g_dap.SendOutput(OutputType::Console, llvm::StringRef{buf});
- };
+ const auto *const arguments = request.getObject("arguments");
- auto arguments = request.getObject("arguments");
// sourceInitFile option is not from formal DAP specification. It is only
// used by unit tests to prevent sourcing .lldbinit files from environment
// which may affect the outcome of tests.
bool source_init_file = GetBoolean(arguments, "sourceInitFile", true);
- g_dap.debugger = lldb::SBDebugger::Create(source_init_file, log_cb, nullptr);
- if (llvm::Error err = g_dap.RunPreInitCommands()) {
+ lldb::SBDebugger debugger = lldb::SBDebugger::Create(false);
+
+ debugger.SetInputFile(dap.in);
+ debugger.SetOutputFile(dap.out);
+ debugger.SetErrorFile(dap.err);
+
+ lldb::SBCommandInterpreter interp = debugger.GetCommandInterpreter();
+
+ if (source_init_file) {
+ lldb::SBCommandReturnObject result;
+ interp.SourceInitFileInCurrentWorkingDirectory(result);
+ interp.SourceInitFileInHomeDirectory(result, false);
+ }
+
+ dap.debugger = debugger;
+ if (llvm::Error err = dap.RunPreInitCommands()) {
response["success"] = false;
EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
- g_dap.PopulateExceptionBreakpoints();
- auto cmd = g_dap.debugger.GetCommandInterpreter().AddMultiwordCommand(
+ dap.PopulateExceptionBreakpoints();
+ auto cmd = dap.debugger.GetCommandInterpreter().AddMultiwordCommand(
"lldb-dap", "Commands for managing lldb-dap.");
if (GetBoolean(arguments, "supportsStartDebuggingRequest", false)) {
cmd.AddCommand(
- "start-debugging", new StartDebuggingRequestHandler(),
+ "start-debugging", new StartDebuggingRequestHandler(dap),
"Sends a startDebugging request from the debug adapter to the client "
"to start a child debug session of the same type as the caller.");
}
cmd.AddCommand(
- "repl-mode", new ReplModeRequestHandler(),
+ "repl-mode", new ReplModeRequestHandler(dap),
"Get or set the repl behavior of lldb-dap evaluation requests.");
- cmd.AddCommand("send-event", new SendEventRequestHandler(),
+ cmd.AddCommand("send-event", new SendEventRequestHandler(dap),
"Sends an DAP event to the client.");
- g_dap.progress_event_thread = std::thread(ProgressEventThreadFunction);
+ dap.progress_event_thread =
+ std::thread(ProgressEventThreadFunction, std::ref(dap));
// Start our event thread so we can receive events from the debugger, target,
// process and more.
- g_dap.event_thread = std::thread(EventThreadFunction);
+ dap.event_thread = std::thread(EventThreadFunction, std::ref(dap));
// The debug adapter supports the configurationDoneRequest.
body.try_emplace("supportsConfigurationDoneRequest", true);
@@ -1919,7 +2034,7 @@ void request_initialize(const llvm::json::Object &request) {
body.try_emplace("supportsEvaluateForHovers", true);
// Available filters or options for the setExceptionBreakpoints request.
llvm::json::Array filters;
- for (const auto &exc_bp : *g_dap.exception_breakpoints) {
+ for (const auto &exc_bp : *dap.exception_breakpoints) {
filters.emplace_back(CreateExceptionBreakpointFilter(exc_bp));
}
body.try_emplace("exceptionBreakpointFilters", std::move(filters));
@@ -1992,16 +2107,17 @@ void request_initialize(const llvm::json::Object &request) {
// Put in non-DAP specification lldb specific information.
llvm::json::Object lldb_json;
- lldb_json.try_emplace("version", g_dap.debugger.GetVersionString());
+ lldb_json.try_emplace("version", dap.debugger.GetVersionString());
body.try_emplace("__lldb", std::move(lldb_json));
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
-llvm::Error request_runInTerminal(const llvm::json::Object &launch_request,
+llvm::Error request_runInTerminal(DAP &dap,
+ const llvm::json::Object &launch_request,
const uint64_t timeout_seconds) {
- g_dap.is_attach = true;
+ dap.is_attach = true;
lldb::SBAttachInfo attach_info;
llvm::Expected<std::shared_ptr<FifoFile>> comm_file_or_err =
@@ -2017,25 +2133,25 @@ llvm::Error request_runInTerminal(const llvm::json::Object &launch_request,
debugger_pid = getpid();
#endif
llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest(
- launch_request, g_dap.debug_adaptor_path, comm_file.m_path, debugger_pid);
- g_dap.SendReverseRequest("runInTerminal", std::move(reverse_request),
- [](llvm::Expected<llvm::json::Value> value) {
- if (!value) {
- llvm::Error err = value.takeError();
- llvm::errs()
- << "runInTerminal request failed: "
- << llvm::toString(std::move(err)) << "\n";
- }
- });
+ launch_request, dap.debug_adaptor_path, comm_file.m_path, debugger_pid);
+ dap.SendReverseRequest("runInTerminal", std::move(reverse_request),
+ [](llvm::Expected<llvm::json::Value> value) {
+ if (!value) {
+ llvm::Error err = value.takeError();
+ llvm::errs()
+ << "runInTerminal request failed: "
+ << llvm::toString(std::move(err)) << "\n";
+ }
+ });
if (llvm::Expected<lldb::pid_t> pid = comm_channel.GetLauncherPid())
attach_info.SetProcessID(*pid);
else
return pid.takeError();
- g_dap.debugger.SetAsync(false);
+ dap.debugger.SetAsync(false);
lldb::SBError error;
- g_dap.target.Attach(attach_info, error);
+ dap.target.Attach(attach_info, error);
if (error.Fail())
return llvm::createStringError(llvm::inconvertibleErrorCode(),
@@ -2053,11 +2169,11 @@ llvm::Error request_runInTerminal(const llvm::json::Object &launch_request,
// process right in the middle of the exec. To the user, what we are doing is
// transparent, as they will only be able to see the process since the exec,
// completely unaware of the preparatory work.
- g_dap.target.GetProcess().Continue();
+ dap.target.GetProcess().Continue();
// Now that the actual target is just starting (i.e. exec was just invoked),
// we return the debugger to its async state.
- g_dap.debugger.SetAsync(true);
+ dap.debugger.SetAsync(true);
// If sending the notification failed, the launcher should be dead by now and
// the async didAttach notification should have an error message, so we
@@ -2074,13 +2190,13 @@ llvm::Error request_runInTerminal(const llvm::json::Object &launch_request,
// runInTerminal if applicable. It doesn't do any of the additional
// initialization and bookkeeping stuff that is needed for `request_launch`.
// This way we can reuse the process launching logic for RestartRequest too.
-lldb::SBError LaunchProcess(const llvm::json::Object &request) {
+lldb::SBError LaunchProcess(DAP &dap, const llvm::json::Object &request) {
lldb::SBError error;
- auto arguments = request.getObject("arguments");
+ const auto *const arguments = request.getObject("arguments");
auto launchCommands = GetStrings(arguments, "launchCommands");
// Instantiate a launch info instance for the target.
- auto launch_info = g_dap.target.GetLaunchInfo();
+ auto launch_info = dap.target.GetLaunchInfo();
// Grab the current working directory if there is one and set it in the
// launch info.
@@ -2113,29 +2229,29 @@ lldb::SBError LaunchProcess(const llvm::json::Object &request) {
const uint64_t timeout_seconds = GetUnsigned(arguments, "timeout", 30);
if (GetBoolean(arguments, "runInTerminal", false)) {
- if (llvm::Error err = request_runInTerminal(request, timeout_seconds))
+ if (llvm::Error err = request_runInTerminal(dap, request, timeout_seconds))
error.SetErrorString(llvm::toString(std::move(err)).c_str());
} else if (launchCommands.empty()) {
// Disable async events so the launch will be successful when we return from
// the launch call and the launch will happen synchronously
- g_dap.debugger.SetAsync(false);
- g_dap.target.Launch(launch_info, error);
- g_dap.debugger.SetAsync(true);
+ dap.debugger.SetAsync(false);
+ dap.target.Launch(launch_info, error);
+ dap.debugger.SetAsync(true);
} else {
// Set the launch info so that run commands can access the configured
// launch details.
- g_dap.target.SetLaunchInfo(launch_info);
- if (llvm::Error err = g_dap.RunLaunchCommands(launchCommands)) {
+ dap.target.SetLaunchInfo(launch_info);
+ if (llvm::Error err = dap.RunLaunchCommands(launchCommands)) {
error.SetErrorString(llvm::toString(std::move(err)).c_str());
return error;
}
// The custom commands might have created a new target so we should use the
// selected target after these commands are run.
- g_dap.target = g_dap.debugger.GetSelectedTarget();
+ dap.target = dap.debugger.GetSelectedTarget();
// Make sure the process is launched and stopped at the entry point before
// proceeding as the launch commands are not run using the synchronous
// mode.
- error = g_dap.WaitForProcessToStop(timeout_seconds);
+ error = dap.WaitForProcessToStop(timeout_seconds);
}
return error;
}
@@ -2174,32 +2290,31 @@ lldb::SBError LaunchProcess(const llvm::json::Object &request) {
// acknowledgement, so no body field is required."
// }]
// }
-void request_launch(const llvm::json::Object &request) {
- g_dap.is_attach = false;
- g_dap.last_launch_or_attach_request = request;
+void request_launch(DAP &dap, const llvm::json::Object &request) {
+ dap.is_attach = false;
+ dap.last_launch_or_attach_request = request;
llvm::json::Object response;
FillResponse(request, response);
- auto arguments = request.getObject("arguments");
- g_dap.init_commands = GetStrings(arguments, "initCommands");
- g_dap.pre_run_commands = GetStrings(arguments, "preRunCommands");
- g_dap.stop_commands = GetStrings(arguments, "stopCommands");
- g_dap.exit_commands = GetStrings(arguments, "exitCommands");
- g_dap.terminate_commands = GetStrings(arguments, "terminateCommands");
- g_dap.post_run_commands = GetStrings(arguments, "postRunCommands");
- g_dap.stop_at_entry = GetBoolean(arguments, "stopOnEntry", false);
+ const auto *const arguments = request.getObject("arguments");
+ dap.init_commands = GetStrings(arguments, "initCommands");
+ dap.pre_run_commands = GetStrings(arguments, "preRunCommands");
+ dap.stop_commands = GetStrings(arguments, "stopCommands");
+ dap.exit_commands = GetStrings(arguments, "exitCommands");
+ dap.terminate_commands = GetStrings(arguments, "terminateCommands");
+ dap.post_run_commands = GetStrings(arguments, "postRunCommands");
+ dap.stop_at_entry = GetBoolean(arguments, "stopOnEntry", false);
const llvm::StringRef debuggerRoot = GetString(arguments, "debuggerRoot");
- g_dap.enable_auto_variable_summaries =
+ dap.enable_auto_variable_summaries =
GetBoolean(arguments, "enableAutoVariableSummaries", false);
- g_dap.enable_synthetic_child_debugging =
+ dap.enable_synthetic_child_debugging =
GetBoolean(arguments, "enableSyntheticChildDebugging", false);
- g_dap.display_extended_backtrace =
+ dap.display_extended_backtrace =
GetBoolean(arguments, "displayExtendedBacktrace", false);
- g_dap.command_escape_prefix =
- GetString(arguments, "commandEscapePrefix", "`");
- g_dap.SetFrameFormat(GetString(arguments, "customFrameFormat"));
- g_dap.SetThreadFormat(GetString(arguments, "customThreadFormat"));
+ dap.command_escape_prefix = GetString(arguments, "commandEscapePrefix", "`");
+ dap.SetFrameFormat(GetString(arguments, "customFrameFormat"));
+ dap.SetThreadFormat(GetString(arguments, "customThreadFormat"));
- PrintWelcomeMessage();
+ PrintWelcomeMessage(dap);
// This is a hack for loading DWARF in .o files on Mac where the .o files
// in the debug map of the main executable have relative paths which
@@ -2211,50 +2326,50 @@ void request_launch(const llvm::json::Object &request) {
// Run any initialize LLDB commands the user specified in the launch.json.
// This is run before target is created, so commands can't do anything with
// the targets - preRunCommands are run with the target.
- if (llvm::Error err = g_dap.RunInitCommands()) {
+ if (llvm::Error err = dap.RunInitCommands()) {
response["success"] = false;
EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
- SetSourceMapFromArguments(*arguments);
+ SetSourceMapFromArguments(dap, *arguments);
lldb::SBError status;
- g_dap.SetTarget(g_dap.CreateTargetFromArguments(*arguments, status));
+ dap.SetTarget(dap.CreateTargetFromArguments(*arguments, status));
if (status.Fail()) {
response["success"] = llvm::json::Value(false);
EmplaceSafeString(response, "message", status.GetCString());
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
// Run any pre run LLDB commands the user specified in the launch.json
- if (llvm::Error err = g_dap.RunPreRunCommands()) {
+ if (llvm::Error err = dap.RunPreRunCommands()) {
response["success"] = false;
EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
- status = LaunchProcess(request);
+ status = LaunchProcess(dap, request);
if (status.Fail()) {
response["success"] = llvm::json::Value(false);
EmplaceSafeString(response, "message", std::string(status.GetCString()));
} else {
- g_dap.RunPostRunCommands();
+ dap.RunPostRunCommands();
}
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
if (!status.Fail()) {
- if (g_dap.is_attach)
- SendProcessEvent(Attach); // this happens when doing runInTerminal
+ if (dap.is_attach)
+ SendProcessEvent(dap, Attach); // this happens when doing runInTerminal
else
- SendProcessEvent(Launch);
+ SendProcessEvent(dap, Launch);
}
- g_dap.SendJSON(CreateEventObject("initialized"));
+ dap.SendJSON(CreateEventObject("initialized"));
}
// Check if the step-granularity is `instruction`
@@ -2308,15 +2423,15 @@ static bool hasInstructionGranularity(const llvm::json::Object &requestArgs) {
// acknowledgement, so no body field is required."
// }]
// }
-void request_next(const llvm::json::Object &request) {
+void request_next(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
- auto arguments = request.getObject("arguments");
- lldb::SBThread thread = g_dap.GetLLDBThread(*arguments);
+ const auto *const arguments = request.getObject("arguments");
+ lldb::SBThread thread = dap.GetLLDBThread(*arguments);
if (thread.IsValid()) {
// Remember the thread ID that caused the resume so we can set the
// "threadCausedFocus" boolean value in the "stopped" events.
- g_dap.focus_tid = thread.GetThreadID();
+ dap.focus_tid = thread.GetThreadID();
if (hasInstructionGranularity(*arguments)) {
thread.StepInstruction(/*step_over=*/true);
} else {
@@ -2325,7 +2440,7 @@ void request_next(const llvm::json::Object &request) {
} else {
response["success"] = llvm::json::Value(false);
}
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "PauseRequest": {
@@ -2364,12 +2479,12 @@ void request_next(const llvm::json::Object &request) {
// acknowledgement, so no body field is required."
// }]
// }
-void request_pause(const llvm::json::Object &request) {
+void request_pause(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
- lldb::SBProcess process = g_dap.target.GetProcess();
+ lldb::SBProcess process = dap.target.GetProcess();
lldb::SBError error = process.Stop();
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "RestartRequest": {
@@ -2413,14 +2528,14 @@ void request_pause(const llvm::json::Object &request) {
// acknowledgement, so no body field is required."
// }]
// },
-void request_restart(const llvm::json::Object &request) {
+void request_restart(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
- if (!g_dap.last_launch_or_attach_request) {
+ if (!dap.last_launch_or_attach_request) {
response["success"] = llvm::json::Value(false);
EmplaceSafeString(response, "message",
"Restart request received but no process was launched.");
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
// Check if we were in a "launch" session or an "attach" session.
@@ -2432,35 +2547,36 @@ void request_restart(const llvm::json::Object &request) {
// Note that when using runInTerminal we're technically attached, but it's an
// implementation detail. The adapter *did* launch the process in response to
// a "launch" command, so we can still stop it and re-run it. This is why we
- // don't just check `g_dap.is_attach`.
- if (GetString(*g_dap.last_launch_or_attach_request, "command") == "attach") {
+ // don't just check `dap.is_attach`.
+ if (GetString(*dap.last_launch_or_attach_request, "command") == "attach") {
response["success"] = llvm::json::Value(false);
EmplaceSafeString(response, "message",
"Restarting an \"attach\" session is not supported.");
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
// The optional `arguments` field in RestartRequest can contain an updated
// version of the launch arguments. If there's one, use it.
- auto restart_arguments = request.getObject("arguments");
+ const auto *restart_arguments = request.getObject("arguments");
if (restart_arguments) {
- auto launch_request_arguments = restart_arguments->getObject("arguments");
+ const auto *launch_request_arguments =
+ restart_arguments->getObject("arguments");
if (launch_request_arguments) {
- (*g_dap.last_launch_or_attach_request)["arguments"] =
+ (*dap.last_launch_or_attach_request)["arguments"] =
llvm::json::Value(llvm::json::Object(*launch_request_arguments));
}
}
// Keep track of the old PID so when we get a "process exited" event from the
// killed process we can detect it and not shut down the whole session.
- lldb::SBProcess process = g_dap.target.GetProcess();
- g_dap.restarting_process_id = process.GetProcessID();
+ lldb::SBProcess process = dap.target.GetProcess();
+ dap.restarting_process_id = process.GetProcessID();
// Stop the current process if necessary. The logic here is similar to
// CommandObjectProcessLaunchOrAttach::StopProcessIfNecessary, except that
// we don't ask the user for confirmation.
- g_dap.debugger.SetAsync(false);
+ dap.debugger.SetAsync(false);
if (process.IsValid()) {
lldb::StateType state = process.GetState();
if (state != lldb::eStateConnected) {
@@ -2468,21 +2584,21 @@ void request_restart(const llvm::json::Object &request) {
}
// Clear the list of thread ids to avoid sending "thread exited" events
// for threads of the process we are terminating.
- g_dap.thread_ids.clear();
+ dap.thread_ids.clear();
}
- g_dap.debugger.SetAsync(true);
- LaunchProcess(*g_dap.last_launch_or_attach_request);
+ dap.debugger.SetAsync(true);
+ LaunchProcess(dap, *dap.last_launch_or_attach_request);
// This is normally done after receiving a "configuration done" request.
// Because we're restarting, configuration has already happened so we can
// continue the process right away.
- if (g_dap.stop_at_entry) {
- SendThreadStoppedEvent();
+ if (dap.stop_at_entry) {
+ SendThreadStoppedEvent(dap);
} else {
- g_dap.target.GetProcess().Continue();
+ dap.target.GetProcess().Continue();
}
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "ScopesRequest": {
@@ -2536,12 +2652,12 @@ void request_restart(const llvm::json::Object &request) {
// "required": [ "body" ]
// }]
// }
-void request_scopes(const llvm::json::Object &request) {
+void request_scopes(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
llvm::json::Object body;
- auto arguments = request.getObject("arguments");
- lldb::SBFrame frame = g_dap.GetLLDBFrame(*arguments);
+ const auto *const arguments = request.getObject("arguments");
+ lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
// As the user selects different stack frames in the GUI, a "scopes" request
// will be sent to the DAP. This is the only way we know that the user has
// selected a frame in a thread. There are no other notifications that are
@@ -2561,18 +2677,18 @@ void request_scopes(const llvm::json::Object &request) {
frame.GetThread().SetSelectedFrame(frame.GetFrameID());
}
- g_dap.variables.locals = frame.GetVariables(/*arguments=*/true,
- /*locals=*/true,
- /*statics=*/false,
- /*in_scope_only=*/true);
- g_dap.variables.globals = frame.GetVariables(/*arguments=*/false,
- /*locals=*/false,
- /*statics=*/true,
- /*in_scope_only=*/true);
- g_dap.variables.registers = frame.GetRegisters();
- body.try_emplace("scopes", g_dap.CreateTopLevelScopes());
+ dap.variables.locals = frame.GetVariables(/*arguments=*/true,
+ /*locals=*/true,
+ /*statics=*/false,
+ /*in_scope_only=*/true);
+ dap.variables.globals = frame.GetVariables(/*arguments=*/false,
+ /*locals=*/false,
+ /*statics=*/true,
+ /*in_scope_only=*/true);
+ dap.variables.registers = frame.GetRegisters();
+ body.try_emplace("scopes", dap.CreateTopLevelScopes());
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "SetBreakpointsRequest": {
@@ -2685,14 +2801,14 @@ void request_scopes(const llvm::json::Object &request) {
// },
// "required": [ "line" ]
// }
-void request_setBreakpoints(const llvm::json::Object &request) {
+void request_setBreakpoints(DAP &dap, 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 *const 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,14 +2817,14 @@ 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(dap, *bp_obj);
+ request_bps.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()) {
+ auto existing_source_bps = dap.source_breakpoints.find(path);
+ if (existing_source_bps != dap.source_breakpoints.end()) {
const auto &existing_bp =
existing_source_bps->second.find(src_bp.line);
if (existing_bp != existing_source_bps->second.end()) {
@@ -2719,10 +2835,9 @@ void request_setBreakpoints(const llvm::json::Object &request) {
}
}
// 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);
+ dap.source_breakpoints[path].try_emplace(src_bp.line, src_bp);
+ src_bp.SetBreakpoint(path.data());
+ AppendBreakpoint(&src_bp, response_breakpoints, path, src_bp.line);
}
}
}
@@ -2730,13 +2845,13 @@ void request_setBreakpoints(const llvm::json::Object &request) {
// Delete any breakpoints in this source file that aren't in the
// request_bps set. There is no call to remove breakpoints other than
// calling this function with a smaller or empty "breakpoints" list.
- auto old_src_bp_pos = g_dap.source_breakpoints.find(path);
- if (old_src_bp_pos != g_dap.source_breakpoints.end()) {
+ auto old_src_bp_pos = dap.source_breakpoints.find(path);
+ if (old_src_bp_pos != dap.source_breakpoints.end()) {
for (auto &old_bp : old_src_bp_pos->second) {
auto request_pos = request_bps.find(old_bp.first);
if (request_pos == request_bps.end()) {
// This breakpoint no longer exists in this source file, delete it
- g_dap.target.BreakpointDelete(old_bp.second.bp.GetID());
+ dap.target.BreakpointDelete(old_bp.second.bp.GetID());
old_src_bp_pos->second.erase(old_bp.first);
}
}
@@ -2745,7 +2860,7 @@ void request_setBreakpoints(const llvm::json::Object &request) {
llvm::json::Object body;
body.try_emplace("breakpoints", std::move(response_breakpoints));
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "SetExceptionBreakpointsRequest": {
@@ -2795,32 +2910,33 @@ void request_setBreakpoints(const llvm::json::Object &request) {
// just an acknowledgement, so no body field is required."
// }]
// }
-void request_setExceptionBreakpoints(const llvm::json::Object &request) {
+void request_setExceptionBreakpoints(DAP &dap,
+ 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 *const 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;
- for (const auto &bp : *g_dap.exception_breakpoints)
+ for (const auto &bp : *dap.exception_breakpoints)
unset_filters.insert(bp.filter);
for (const auto &value : *filters) {
const auto filter = GetAsString(value);
- auto exc_bp = g_dap.GetExceptionBreakpoint(std::string(filter));
+ auto *exc_bp = dap.GetExceptionBreakpoint(std::string(filter));
if (exc_bp) {
exc_bp->SetBreakpoint();
unset_filters.erase(std::string(filter));
}
}
for (const auto &filter : unset_filters) {
- auto exc_bp = g_dap.GetExceptionBreakpoint(filter);
+ auto *exc_bp = dap.GetExceptionBreakpoint(filter);
if (exc_bp)
exc_bp->ClearBreakpoint();
}
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "SetFunctionBreakpointsRequest": {
@@ -2901,31 +3017,32 @@ void request_setExceptionBreakpoints(const llvm::json::Object &request) {
// "required": [ "body" ]
// }]
// }
-void request_setFunctionBreakpoints(const llvm::json::Object &request) {
+void request_setFunctionBreakpoints(DAP &dap,
+ 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");
+ const auto *const arguments = request.getObject("arguments");
+ const auto *breakpoints = arguments->getArray("breakpoints");
FunctionBreakpointMap request_bps;
llvm::json::Array response_breakpoints;
for (const auto &value : *breakpoints) {
- auto bp_obj = value.getAsObject();
+ const auto *bp_obj = value.getAsObject();
if (bp_obj == nullptr)
continue;
- FunctionBreakpoint func_bp(*bp_obj);
- request_bps[func_bp.functionName] = std::move(func_bp);
+ FunctionBreakpoint func_bp(dap, *bp_obj);
+ request_bps.try_emplace(func_bp.functionName, std::move(func_bp));
}
std::vector<llvm::StringRef> remove_names;
// Disable any function breakpoints that aren't in the request_bps.
// 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) {
+ for (auto &pair : 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());
+ dap.target.BreakpointDelete(pair.second.bp.GetID());
remove_names.push_back(pair.first());
} else {
// Update the existing breakpoint as any setting withing the function
@@ -2940,22 +3057,21 @@ void request_setFunctionBreakpoints(const llvm::json::Object &request) {
}
// Remove any breakpoints that are no longer in our list
for (const auto &name : remove_names)
- g_dap.function_breakpoints.erase(name);
+ 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) {
+ pair.second.SetBreakpoint();
// 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);
+ AppendBreakpoint(&pair.second, response_breakpoints);
+ dap.function_breakpoints.try_emplace(pair.first(), std::move(pair.second));
}
llvm::json::Object body;
body.try_emplace("breakpoints", std::move(response_breakpoints));
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "DataBreakpointInfoRequest": {
@@ -3050,7 +3166,7 @@ void request_setFunctionBreakpoints(const llvm::json::Object &request) {
// "required": [ "body" ]
// }]
// }
-void request_dataBreakpointInfo(const llvm::json::Object &request) {
+void request_dataBreakpointInfo(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
llvm::json::Object body;
@@ -3060,8 +3176,8 @@ void request_dataBreakpointInfo(const llvm::json::Object &request) {
const auto variablesReference =
GetUnsigned(arguments, "variablesReference", 0);
llvm::StringRef name = GetString(arguments, "name");
- lldb::SBFrame frame = g_dap.GetLLDBFrame(*arguments);
- lldb::SBValue variable = FindVariable(variablesReference, name);
+ lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
+ lldb::SBValue variable = FindVariable(dap, variablesReference, name);
std::string addr, size;
if (variable.IsValid()) {
@@ -3096,7 +3212,7 @@ void request_dataBreakpointInfo(const llvm::json::Object &request) {
addr = llvm::utohexstr(load_addr);
lldb::SBMemoryRegionInfo region;
lldb::SBError err =
- g_dap.target.GetProcess().GetMemoryRegionInfo(load_addr, region);
+ dap.target.GetProcess().GetMemoryRegionInfo(load_addr, region);
// Only lldb-server supports "qMemoryRegionInfo". So, don't fail this
// request if SBProcess::GetMemoryRegionInfo returns error.
if (err.Success()) {
@@ -3126,7 +3242,7 @@ void request_dataBreakpointInfo(const llvm::json::Object &request) {
size + " bytes at " + addr + " " + name.str());
}
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "SetDataBreakpointsRequest": {
@@ -3189,20 +3305,20 @@ void request_dataBreakpointInfo(const llvm::json::Object &request) {
// "required": [ "body" ]
// }]
// }
-void request_setDataBreakpoints(const llvm::json::Object &request) {
+void request_setDataBreakpoints(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
lldb::SBError error;
FillResponse(request, response);
const auto *arguments = request.getObject("arguments");
const auto *breakpoints = arguments->getArray("breakpoints");
llvm::json::Array response_breakpoints;
- g_dap.target.DeleteAllWatchpoints();
+ dap.target.DeleteAllWatchpoints();
std::vector<Watchpoint> watchpoints;
if (breakpoints) {
for (const auto &bp : *breakpoints) {
const auto *bp_obj = bp.getAsObject();
if (bp_obj) {
- Watchpoint wp(*bp_obj);
+ Watchpoint wp(dap, *bp_obj);
watchpoints.push_back(wp);
}
}
@@ -3223,7 +3339,7 @@ void request_setDataBreakpoints(const llvm::json::Object &request) {
llvm::json::Object body;
body.try_emplace("breakpoints", std::move(response_breakpoints));
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "SourceRequest": {
@@ -3284,12 +3400,12 @@ void request_setDataBreakpoints(const llvm::json::Object &request) {
// "required": [ "body" ]
// }]
// }
-void request_source(const llvm::json::Object &request) {
+void request_source(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
llvm::json::Object body{{"content", ""}};
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "StackTraceRequest": {
@@ -3370,12 +3486,12 @@ void request_source(const llvm::json::Object &request) {
// "required": [ "body" ]
// }]
// }
-void request_stackTrace(const llvm::json::Object &request) {
+void request_stackTrace(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
lldb::SBError error;
- auto arguments = request.getObject("arguments");
- lldb::SBThread thread = g_dap.GetLLDBThread(*arguments);
+ const auto *const arguments = request.getObject("arguments");
+ lldb::SBThread thread = dap.GetLLDBThread(*arguments);
llvm::json::Array stack_frames;
llvm::json::Object body;
@@ -3384,7 +3500,7 @@ void request_stackTrace(const llvm::json::Object &request) {
const auto levels = GetUnsigned(arguments, "levels", 0);
int64_t offset = 0;
bool reached_end_of_stack =
- FillStackFrames(thread, stack_frames, offset, start_frame,
+ FillStackFrames(dap, thread, stack_frames, offset, start_frame,
levels == 0 ? INT64_MAX : levels);
body.try_emplace("totalFrames",
start_frame + stack_frames.size() +
@@ -3393,7 +3509,7 @@ void request_stackTrace(const llvm::json::Object &request) {
body.try_emplace("stackFrames", std::move(stack_frames));
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "StepInRequest": {
@@ -3446,25 +3562,25 @@ void request_stackTrace(const llvm::json::Object &request) {
// acknowledgement, so no body field is required."
// }]
// }
-void request_stepIn(const llvm::json::Object &request) {
+void request_stepIn(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
- auto arguments = request.getObject("arguments");
+ const auto *const arguments = request.getObject("arguments");
std::string step_in_target;
uint64_t target_id = GetUnsigned(arguments, "targetId", 0);
- auto it = g_dap.step_in_targets.find(target_id);
- if (it != g_dap.step_in_targets.end())
+ auto it = dap.step_in_targets.find(target_id);
+ if (it != dap.step_in_targets.end())
step_in_target = it->second;
const bool single_thread = GetBoolean(arguments, "singleThread", false);
lldb::RunMode run_mode =
single_thread ? lldb::eOnlyThisThread : lldb::eOnlyDuringStepping;
- lldb::SBThread thread = g_dap.GetLLDBThread(*arguments);
+ lldb::SBThread thread = dap.GetLLDBThread(*arguments);
if (thread.IsValid()) {
// Remember the thread ID that caused the resume so we can set the
// "threadCausedFocus" boolean value in the "stopped" events.
- g_dap.focus_tid = thread.GetThreadID();
+ dap.focus_tid = thread.GetThreadID();
if (hasInstructionGranularity(*arguments)) {
thread.StepInstruction(/*step_over=*/false);
} else {
@@ -3473,7 +3589,7 @@ void request_stepIn(const llvm::json::Object &request) {
} else {
response["success"] = llvm::json::Value(false);
}
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "StepInTargetsRequest": {
@@ -3529,24 +3645,24 @@ void request_stepIn(const llvm::json::Object &request) {
// "required": [ "body" ]
// }]
// }
-void request_stepInTargets(const llvm::json::Object &request) {
+void request_stepInTargets(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
- auto arguments = request.getObject("arguments");
+ const auto *const arguments = request.getObject("arguments");
- g_dap.step_in_targets.clear();
- lldb::SBFrame frame = g_dap.GetLLDBFrame(*arguments);
+ dap.step_in_targets.clear();
+ lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
if (frame.IsValid()) {
lldb::SBAddress pc_addr = frame.GetPCAddress();
lldb::SBAddress line_end_addr =
pc_addr.GetLineEntry().GetSameLineContiguousAddressRangeEnd(true);
- lldb::SBInstructionList insts = g_dap.target.ReadInstructions(
+ lldb::SBInstructionList insts = dap.target.ReadInstructions(
pc_addr, line_end_addr, /*flavor_string=*/nullptr);
if (!insts.IsValid()) {
response["success"] = false;
response["message"] = "Failed to get instructions for frame.";
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
@@ -3557,29 +3673,29 @@ void request_stepInTargets(const llvm::json::Object &request) {
if (!inst.IsValid())
break;
- lldb::addr_t inst_addr = inst.GetAddress().GetLoadAddress(g_dap.target);
+ lldb::addr_t inst_addr = inst.GetAddress().GetLoadAddress(dap.target);
// Note: currently only x86/x64 supports flow kind.
lldb::InstructionControlFlowKind flow_kind =
- inst.GetControlFlowKind(g_dap.target);
+ inst.GetControlFlowKind(dap.target);
if (flow_kind == lldb::eInstructionControlFlowKindCall) {
// Use call site instruction address as id which is easy to debug.
llvm::json::Object step_in_target;
step_in_target["id"] = inst_addr;
- llvm::StringRef call_operand_name = inst.GetOperands(g_dap.target);
+ llvm::StringRef call_operand_name = inst.GetOperands(dap.target);
lldb::addr_t call_target_addr;
if (call_operand_name.getAsInteger(0, call_target_addr))
continue;
lldb::SBAddress call_target_load_addr =
- g_dap.target.ResolveLoadAddress(call_target_addr);
+ dap.target.ResolveLoadAddress(call_target_addr);
if (!call_target_load_addr.IsValid())
continue;
// The existing ThreadPlanStepInRange only accept step in target
// function with debug info.
- lldb::SBSymbolContext sc = g_dap.target.ResolveSymbolContextForAddress(
+ lldb::SBSymbolContext sc = dap.target.ResolveSymbolContextForAddress(
call_target_load_addr, lldb::eSymbolContextFunction);
// The existing ThreadPlanStepInRange only accept step in target
@@ -3592,7 +3708,7 @@ void request_stepInTargets(const llvm::json::Object &request) {
if (step_in_target_name.empty())
continue;
- g_dap.step_in_targets.try_emplace(inst_addr, step_in_target_name);
+ dap.step_in_targets.try_emplace(inst_addr, step_in_target_name);
step_in_target.try_emplace("label", step_in_target_name);
step_in_targets.emplace_back(std::move(step_in_target));
}
@@ -3604,7 +3720,7 @@ void request_stepInTargets(const llvm::json::Object &request) {
response["success"] = llvm::json::Value(false);
response["message"] = "Failed to get frame for input frameId.";
}
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "StepOutRequest": {
@@ -3643,20 +3759,20 @@ void request_stepInTargets(const llvm::json::Object &request) {
// acknowledgement, so no body field is required."
// }]
// }
-void request_stepOut(const llvm::json::Object &request) {
+void request_stepOut(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
- auto arguments = request.getObject("arguments");
- lldb::SBThread thread = g_dap.GetLLDBThread(*arguments);
+ const auto *const arguments = request.getObject("arguments");
+ lldb::SBThread thread = dap.GetLLDBThread(*arguments);
if (thread.IsValid()) {
// Remember the thread ID that caused the resume so we can set the
// "threadCausedFocus" boolean value in the "stopped" events.
- g_dap.focus_tid = thread.GetThreadID();
+ dap.focus_tid = thread.GetThreadID();
thread.StepOut();
} else {
response["success"] = llvm::json::Value(false);
}
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "ThreadsRequest": {
@@ -3694,8 +3810,8 @@ void request_stepOut(const llvm::json::Object &request) {
// "required": [ "body" ]
// }]
// }
-void request_threads(const llvm::json::Object &request) {
- lldb::SBProcess process = g_dap.target.GetProcess();
+void request_threads(DAP &dap, const llvm::json::Object &request) {
+ lldb::SBProcess process = dap.target.GetProcess();
llvm::json::Object response;
FillResponse(request, response);
@@ -3703,7 +3819,7 @@ void request_threads(const llvm::json::Object &request) {
llvm::json::Array threads;
for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
- threads.emplace_back(CreateThread(thread));
+ threads.emplace_back(CreateThread(thread, dap.thread_format));
}
if (threads.size() == 0) {
response["success"] = llvm::json::Value(false);
@@ -3711,7 +3827,7 @@ void request_threads(const llvm::json::Object &request) {
llvm::json::Object body;
body.try_emplace("threads", std::move(threads));
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "SetVariableRequest": {
@@ -3807,12 +3923,12 @@ void request_threads(const llvm::json::Object &request) {
// "required": [ "body" ]
// }]
// }
-void request_setVariable(const llvm::json::Object &request) {
+void request_setVariable(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
llvm::json::Array variables;
llvm::json::Object body;
- auto arguments = request.getObject("arguments");
+ const auto *const arguments = request.getObject("arguments");
// This is a reference to the containing variable/scope
const auto variablesReference =
GetUnsigned(arguments, "variablesReference", 0);
@@ -3832,28 +3948,28 @@ void request_setVariable(const llvm::json::Object &request) {
// only specifies the variable reference of the enclosing scope/variable, and
// the name of the variable. We could have two shadowed variables with the
// same name in "Locals" or "Globals". In our case the "id" absolute index
- // of the variable within the g_dap.variables list.
+ // of the variable within the dap.variables list.
const auto id_value = GetUnsigned(arguments, "id", UINT64_MAX);
if (id_value != UINT64_MAX) {
- variable = g_dap.variables.GetVariable(id_value);
+ variable = dap.variables.GetVariable(id_value);
} else {
- variable = FindVariable(variablesReference, name);
+ variable = FindVariable(dap, variablesReference, name);
}
if (variable.IsValid()) {
lldb::SBError error;
bool success = variable.SetValueFromCString(value.data(), error);
if (success) {
- VariableDescription desc(variable);
+ VariableDescription desc(variable, dap.enable_auto_variable_summaries);
EmplaceSafeString(body, "result", desc.display_value);
EmplaceSafeString(body, "type", desc.display_type_name);
- // We don't know the index of the variable in our g_dap.variables
+ // We don't know the index of the variable in our dap.variables
// so always insert a new one to get its variablesReference.
// is_permanent is false because debug console does not support
// setVariable request.
int64_t new_var_ref =
- g_dap.variables.InsertVariable(variable, /*is_permanent=*/false);
+ dap.variables.InsertVariable(variable, /*is_permanent=*/false);
if (variable.MightHaveChildren())
body.try_emplace("variablesReference", new_var_ref);
else
@@ -3872,7 +3988,7 @@ void request_setVariable(const llvm::json::Object &request) {
}
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "VariablesRequest": {
@@ -3948,21 +4064,22 @@ void request_setVariable(const llvm::json::Object &request) {
// "required": [ "body" ]
// }]
// }
-void request_variables(const llvm::json::Object &request) {
+void request_variables(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
llvm::json::Array variables;
- auto arguments = request.getObject("arguments");
+ const auto *const arguments = request.getObject("arguments");
const auto variablesReference =
GetUnsigned(arguments, "variablesReference", 0);
const int64_t start = GetSigned(arguments, "start", 0);
const int64_t count = GetSigned(arguments, "count", 0);
bool hex = false;
- auto format = arguments->getObject("format");
+ const auto *format = arguments->getObject("format");
if (format)
hex = GetBoolean(format, "hex", false);
- if (lldb::SBValueList *top_scope = GetTopLevelScope(variablesReference)) {
+ if (lldb::SBValueList *top_scope =
+ GetTopLevelScope(dap, variablesReference)) {
// variablesReference is one of our scopes, not an actual variable it is
// asking for the list of args, locals or globals.
int64_t start_idx = 0;
@@ -3974,8 +4091,8 @@ void request_variables(const llvm::json::Object &request) {
// and resolve what the pointer resolves to. Only change the format if the
// format was set to the default format or if it was hex as some registers
// have formats set for them.
- const uint32_t addr_size = g_dap.target.GetProcess().GetAddressByteSize();
- lldb::SBValue reg_set = g_dap.variables.registers.GetValueAtIndex(0);
+ const uint32_t addr_size = dap.target.GetProcess().GetAddressByteSize();
+ lldb::SBValue reg_set = dap.variables.registers.GetValueAtIndex(0);
const uint32_t num_regs = reg_set.GetNumChildren();
for (uint32_t reg_idx = 0; reg_idx < num_regs; ++reg_idx) {
lldb::SBValue reg = reg_set.GetChildAtIndex(reg_idx);
@@ -4032,25 +4149,27 @@ void request_variables(const llvm::json::Object &request) {
break;
int64_t var_ref =
- g_dap.variables.InsertVariable(variable, /*is_permanent=*/false);
+ dap.variables.InsertVariable(variable, /*is_permanent=*/false);
variables.emplace_back(CreateVariable(
- variable, var_ref, hex,
+ variable, var_ref, hex, dap.enable_auto_variable_summaries,
variable_name_counts[GetNonNullVariableName(variable)] > 1));
}
} else {
// We are expanding a variable that has children, so we will return its
// children.
- lldb::SBValue variable = g_dap.variables.GetVariable(variablesReference);
+ lldb::SBValue variable = dap.variables.GetVariable(variablesReference);
if (variable.IsValid()) {
auto addChild = [&](lldb::SBValue child,
std::optional<std::string> custom_name = {}) {
if (!child.IsValid())
return;
bool is_permanent =
- g_dap.variables.IsPermanentVariableReference(variablesReference);
- int64_t var_ref = g_dap.variables.InsertVariable(child, is_permanent);
+ dap.variables.IsPermanentVariableReference(variablesReference);
+ int64_t var_ref = dap.variables.InsertVariable(child, is_permanent);
variables.emplace_back(CreateVariable(
- child, var_ref, hex, /*is_name_duplicated=*/false, custom_name));
+ child, var_ref, hex, dap.enable_auto_variable_summaries,
+ dap.enable_synthetic_child_debugging,
+ /*is_name_duplicated=*/false, custom_name));
};
const int64_t num_children = variable.GetNumChildren();
int64_t end_idx = start + ((count == 0) ? num_children : count);
@@ -4062,7 +4181,7 @@ void request_variables(const llvm::json::Object &request) {
// "[raw]" child that can be used to inspect the raw version of a
// synthetic member. That eliminates the need for the user to go to the
// debug console and type `frame var <variable> to get these values.
- if (g_dap.enable_synthetic_child_debugging && variable.IsSynthetic() &&
+ if (dap.enable_synthetic_child_debugging && variable.IsSynthetic() &&
i == num_children)
addChild(variable.GetNonSyntheticValue(), "[raw]");
}
@@ -4070,7 +4189,7 @@ void request_variables(const llvm::json::Object &request) {
llvm::json::Object body;
body.try_emplace("variables", std::move(variables));
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "LocationsRequest": {
@@ -4150,7 +4269,7 @@ void request_variables(const llvm::json::Object &request) {
// }
// }]
// },
-void request_locations(const llvm::json::Object &request) {
+void request_locations(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
auto *arguments = request.getObject("arguments");
@@ -4159,11 +4278,11 @@ void request_locations(const llvm::json::Object &request) {
// We use the lowest bit to distinguish between value location and declaration
// location
auto [var_ref, is_value_location] = UnpackLocation(location_id);
- lldb::SBValue variable = g_dap.variables.GetVariable(var_ref);
+ lldb::SBValue variable = dap.variables.GetVariable(var_ref);
if (!variable.IsValid()) {
response["success"] = false;
response["message"] = "Invalid variable reference";
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
@@ -4175,18 +4294,18 @@ void request_locations(const llvm::json::Object &request) {
response["success"] = false;
response["message"] =
"Value locations are only available for pointers and references";
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
lldb::addr_t addr = variable.GetValueAsAddress();
lldb::SBLineEntry line_entry =
- g_dap.target.ResolveLoadAddress(addr).GetLineEntry();
+ dap.target.ResolveLoadAddress(addr).GetLineEntry();
if (!line_entry.IsValid()) {
response["success"] = false;
response["message"] = "Failed to resolve line entry for location";
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
@@ -4201,7 +4320,7 @@ void request_locations(const llvm::json::Object &request) {
if (!decl.IsValid()) {
response["success"] = false;
response["message"] = "No declaration location available";
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
@@ -4213,7 +4332,7 @@ void request_locations(const llvm::json::Object &request) {
}
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "DisassembleRequest": {
@@ -4288,7 +4407,7 @@ void request_locations(const llvm::json::Object &request) {
// }
// }]
// }
-void request_disassemble(const llvm::json::Object &request) {
+void request_disassemble(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
auto *arguments = request.getObject("arguments");
@@ -4299,28 +4418,27 @@ void request_disassemble(const llvm::json::Object &request) {
response["success"] = false;
response["message"] =
"Malformed memory reference: " + memoryReference.str();
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
lldb::addr_t addr_ptr = *addr_opt;
addr_ptr += GetSigned(arguments, "instructionOffset", 0);
- lldb::SBAddress addr(addr_ptr, g_dap.target);
+ lldb::SBAddress addr(addr_ptr, dap.target);
if (!addr.IsValid()) {
response["success"] = false;
response["message"] = "Memory reference not found in the current binary.";
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
const auto inst_count = GetUnsigned(arguments, "instructionCount", 0);
- lldb::SBInstructionList insts =
- g_dap.target.ReadInstructions(addr, inst_count);
+ lldb::SBInstructionList insts = dap.target.ReadInstructions(addr, inst_count);
if (!insts.IsValid()) {
response["success"] = false;
response["message"] = "Failed to find instructions for memory address.";
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
@@ -4330,11 +4448,11 @@ void request_disassemble(const llvm::json::Object &request) {
for (size_t i = 0; i < num_insts; ++i) {
lldb::SBInstruction inst = insts.GetInstructionAtIndex(i);
auto addr = inst.GetAddress();
- const auto inst_addr = addr.GetLoadAddress(g_dap.target);
- const char *m = inst.GetMnemonic(g_dap.target);
- const char *o = inst.GetOperands(g_dap.target);
- const char *c = inst.GetComment(g_dap.target);
- auto d = inst.GetData(g_dap.target);
+ const auto inst_addr = addr.GetLoadAddress(dap.target);
+ const char *m = inst.GetMnemonic(dap.target);
+ const char *o = inst.GetOperands(dap.target);
+ const char *c = inst.GetComment(dap.target);
+ auto d = inst.GetData(dap.target);
std::string bytes;
llvm::raw_string_ostream sb(bytes);
@@ -4418,7 +4536,7 @@ void request_disassemble(const llvm::json::Object &request) {
llvm::json::Object body;
body.try_emplace("instructions", std::move(instructions));
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "ReadMemoryRequest": {
@@ -4498,7 +4616,7 @@ void request_disassemble(const llvm::json::Object &request) {
// }
// }]
// },
-void request_readMemory(const llvm::json::Object &request) {
+void request_readMemory(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
FillResponse(request, response);
auto *arguments = request.getObject("arguments");
@@ -4509,7 +4627,7 @@ void request_readMemory(const llvm::json::Object &request) {
response["success"] = false;
response["message"] =
"Malformed memory reference: " + memoryReference.str();
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
lldb::addr_t addr_int = *addr_opt;
@@ -4523,13 +4641,13 @@ void request_readMemory(const llvm::json::Object &request) {
std::vector<uint8_t> buf;
buf.resize(count_read);
lldb::SBError error;
- lldb::SBAddress addr{addr_int, g_dap.target};
+ lldb::SBAddress addr{addr_int, dap.target};
size_t count_result =
- g_dap.target.ReadMemory(addr, buf.data(), count_read, error);
+ dap.target.ReadMemory(addr, buf.data(), count_read, error);
if (count_result == 0) {
response["success"] = false;
EmplaceSafeString(response, "message", error.GetCString());
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
return;
}
buf.resize(std::min<size_t>(count_result, count_requested));
@@ -4539,25 +4657,26 @@ void request_readMemory(const llvm::json::Object &request) {
body.try_emplace("address", formatted_addr);
body.try_emplace("data", llvm::encodeBase64(buf));
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// A request used in testing to get the details on all breakpoints that are
// currently set in the target. This helps us to test "setBreakpoints" and
// "setFunctionBreakpoints" requests to verify we have the correct set of
// breakpoints currently set in LLDB.
-void request__testGetTargetBreakpoints(const llvm::json::Object &request) {
+void request__testGetTargetBreakpoints(DAP &dap,
+ const llvm::json::Object &request) {
llvm::json::Object response;
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));
+ for (uint32_t i = 0; dap.target.GetBreakpointAtIndex(i).IsValid(); ++i) {
+ auto bp = Breakpoint(dap, dap.target.GetBreakpointAtIndex(i));
AppendBreakpoint(&bp, response_breakpoints);
}
llvm::json::Object body;
body.try_emplace("breakpoints", std::move(response_breakpoints));
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
// "SetInstructionBreakpointsRequest" : {
@@ -4748,31 +4867,32 @@ void request__testGetTargetBreakpoints(const llvm::json::Object &request) {
// },
// "required" : ["verified"]
// },
-
-void request_setInstructionBreakpoints(const llvm::json::Object &request) {
+void request_setInstructionBreakpoints(DAP &dap,
+ 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 *const 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();
+ const auto *bp_obj = bp.getAsObject();
if (bp_obj) {
// Read instruction breakpoint request.
- InstructionBreakpoint inst_bp(*bp_obj);
+ InstructionBreakpoint inst_bp(dap, *bp_obj);
// Store them into map for reference.
- request_ibp[inst_bp.instructionAddressReference] = std::move(inst_bp);
+ request_ibp.try_emplace(inst_bp.instructionAddressReference,
+ std::move(inst_bp));
}
}
// Iterate previous active instruction breakpoint list.
- for (auto &prev_ibp : g_dap.instruction_breakpoints) {
+ for (auto &prev_ibp : dap.instruction_breakpoints) {
// Find previous instruction breakpoint reference address in newly
// received instruction breakpoint list.
auto inst_reference = request_ibp.find(prev_ibp.first);
@@ -4780,9 +4900,9 @@ void request_setInstructionBreakpoints(const llvm::json::Object &request) {
// 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);
+ dap.target.BreakpointDelete(prev_ibp.second.id);
// Update Prev instruction breakpoint list.
- g_dap.instruction_breakpoints.erase(prev_ibp.first);
+ dap.instruction_breakpoints.erase(prev_ibp.first);
} else {
// Instead of recreating breakpoint instance, update the breakpoint if
// there are any conditional changes.
@@ -4794,62 +4914,60 @@ void request_setInstructionBreakpoints(const llvm::json::Object &request) {
}
for (auto &req_bpi : request_ibp) {
+ req_bpi.second.SetInstructionBreakpoint();
// 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));
+ response_breakpoints.emplace_back(
+ CreateInstructionBreakpoint(&req_bpi.second));
+ dap.instruction_breakpoints.try_emplace(req_bpi.first,
+ std::move(req_bpi.second));
}
}
body.try_emplace("breakpoints", std::move(response_breakpoints));
response.try_emplace("body", std::move(body));
- g_dap.SendJSON(llvm::json::Value(std::move(response)));
+ dap.SendJSON(llvm::json::Value(std::move(response)));
}
-void RegisterRequestCallbacks() {
- g_dap.RegisterRequestCallback("attach", request_attach);
- g_dap.RegisterRequestCallback("completions", request_completions);
- g_dap.RegisterRequestCallback("continue", request_continue);
- g_dap.RegisterRequestCallback("configurationDone", request_configurationDone);
- g_dap.RegisterRequestCallback("disconnect", request_disconnect);
- g_dap.RegisterRequestCallback("evaluate", request_evaluate);
- g_dap.RegisterRequestCallback("exceptionInfo", request_exceptionInfo);
- g_dap.RegisterRequestCallback("initialize", request_initialize);
- g_dap.RegisterRequestCallback("launch", request_launch);
- g_dap.RegisterRequestCallback("next", request_next);
- g_dap.RegisterRequestCallback("pause", request_pause);
- g_dap.RegisterRequestCallback("restart", request_restart);
- g_dap.RegisterRequestCallback("scopes", request_scopes);
- g_dap.RegisterRequestCallback("setBreakpoints", request_setBreakpoints);
- g_dap.RegisterRequestCallback("setExceptionBreakpoints",
- request_setExceptionBreakpoints);
- g_dap.RegisterRequestCallback("setFunctionBreakpoints",
- request_setFunctionBreakpoints);
- g_dap.RegisterRequestCallback("dataBreakpointInfo",
- request_dataBreakpointInfo);
- g_dap.RegisterRequestCallback("setDataBreakpoints",
- request_setDataBreakpoints);
- g_dap.RegisterRequestCallback("setVariable", request_setVariable);
- g_dap.RegisterRequestCallback("source", request_source);
- g_dap.RegisterRequestCallback("stackTrace", request_stackTrace);
- g_dap.RegisterRequestCallback("stepIn", request_stepIn);
- g_dap.RegisterRequestCallback("stepInTargets", request_stepInTargets);
- g_dap.RegisterRequestCallback("stepOut", request_stepOut);
- g_dap.RegisterRequestCallback("threads", request_threads);
- g_dap.RegisterRequestCallback("variables", request_variables);
- g_dap.RegisterRequestCallback("locations", request_locations);
- g_dap.RegisterRequestCallback("disassemble", request_disassemble);
- g_dap.RegisterRequestCallback("readMemory", request_readMemory);
- g_dap.RegisterRequestCallback("setInstructionBreakpoints",
- request_setInstructionBreakpoints);
+void RegisterRequestCallbacks(DAP &dap) {
+ dap.RegisterRequestCallback("attach", request_attach);
+ dap.RegisterRequestCallback("completions", request_completions);
+ dap.RegisterRequestCallback("continue", request_continue);
+ dap.RegisterRequestCallback("configurationDone", request_configurationDone);
+ dap.RegisterRequestCallback("disconnect", request_disconnect);
+ dap.RegisterRequestCallback("evaluate", request_evaluate);
+ dap.RegisterRequestCallback("exceptionInfo", request_exceptionInfo);
+ dap.RegisterRequestCallback("initialize", request_initialize);
+ dap.RegisterRequestCallback("launch", request_launch);
+ dap.RegisterRequestCallback("next", request_next);
+ dap.RegisterRequestCallback("pause", request_pause);
+ dap.RegisterRequestCallback("restart", request_restart);
+ dap.RegisterRequestCallback("scopes", request_scopes);
+ dap.RegisterRequestCallback("setBreakpoints", request_setBreakpoints);
+ dap.RegisterRequestCallback("setExceptionBreakpoints",
+ request_setExceptionBreakpoints);
+ dap.RegisterRequestCallback("setFunctionBreakpoints",
+ request_setFunctionBreakpoints);
+ dap.RegisterRequestCallback("dataBreakpointInfo", request_dataBreakpointInfo);
+ dap.RegisterRequestCallback("setDataBreakpoints", request_setDataBreakpoints);
+ dap.RegisterRequestCallback("setVariable", request_setVariable);
+ dap.RegisterRequestCallback("source", request_source);
+ dap.RegisterRequestCallback("stackTrace", request_stackTrace);
+ dap.RegisterRequestCallback("stepIn", request_stepIn);
+ dap.RegisterRequestCallback("stepInTargets", request_stepInTargets);
+ dap.RegisterRequestCallback("stepOut", request_stepOut);
+ dap.RegisterRequestCallback("threads", request_threads);
+ dap.RegisterRequestCallback("variables", request_variables);
+ dap.RegisterRequestCallback("locations", request_locations);
+ dap.RegisterRequestCallback("disassemble", request_disassemble);
+ dap.RegisterRequestCallback("readMemory", request_readMemory);
+ dap.RegisterRequestCallback("setInstructionBreakpoints",
+ request_setInstructionBreakpoints);
// Custom requests
- g_dap.RegisterRequestCallback("compileUnits", request_compileUnits);
- g_dap.RegisterRequestCallback("modules", request_modules);
+ dap.RegisterRequestCallback("compileUnits", request_compileUnits);
+ dap.RegisterRequestCallback("modules", request_modules);
// Testing requests
- g_dap.RegisterRequestCallback("_testGetTargetBreakpoints",
- request__testGetTargetBreakpoints);
+ dap.RegisterRequestCallback("_testGetTargetBreakpoints",
+ request__testGetTargetBreakpoints);
}
} // anonymous namespace
@@ -4940,49 +5058,6 @@ void LaunchRunInTerminalTarget(llvm::opt::Arg &target_arg,
#endif
}
-/// used only by TestVSCode_redirection_to_console.py
-void redirection_test() {
- printf("stdout message\n");
- fprintf(stderr, "stderr message\n");
- fflush(stdout);
- fflush(stderr);
-}
-
-/// Redirect stdout and stderr fo the IDE's console output.
-///
-/// Errors in this operation will be printed to the log file and the IDE's
-/// console output as well.
-///
-/// \return
-/// A fd pointing to the original stdout.
-int SetupStdoutStderrRedirection() {
- int stdoutfd = fileno(stdout);
- int new_stdout_fd = dup(stdoutfd);
- auto output_callback_stderr = [](llvm::StringRef data) {
- g_dap.SendOutput(OutputType::Stderr, data);
- };
- auto output_callback_stdout = [](llvm::StringRef data) {
- g_dap.SendOutput(OutputType::Stdout, data);
- };
- if (llvm::Error err = RedirectFd(stdoutfd, output_callback_stdout)) {
- std::string error_message = llvm::toString(std::move(err));
- if (g_dap.log)
- *g_dap.log << error_message << std::endl;
- output_callback_stderr(error_message);
- }
- if (llvm::Error err = RedirectFd(fileno(stderr), output_callback_stderr)) {
- std::string error_message = llvm::toString(std::move(err));
- if (g_dap.log)
- *g_dap.log << error_message << std::endl;
- output_callback_stderr(error_message);
- }
-
- /// used only by TestVSCode_redirection_to_console.py
- if (getenv("LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION") != nullptr)
- redirection_test();
- return new_stdout_fd;
-}
-
int main(int argc, char *argv[]) {
llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);
#if !defined(__APPLE__)
@@ -4996,7 +5071,6 @@ int main(int argc, char *argv[]) {
llvm::SmallString<256> program_path(argv[0]);
llvm::sys::fs::make_absolute(program_path);
- g_dap.debug_adaptor_path = program_path.str().str();
LLDBDAPOptTable T;
unsigned MAI, MAC;
@@ -5008,15 +5082,16 @@ int main(int argc, char *argv[]) {
return EXIT_SUCCESS;
}
+ auto default_repl_mode = ReplMode::Auto;
if (input_args.hasArg(OPT_repl_mode)) {
llvm::opt::Arg *repl_mode = input_args.getLastArg(OPT_repl_mode);
llvm::StringRef repl_mode_value = repl_mode->getValue();
if (repl_mode_value == "auto") {
- g_dap.repl_mode = ReplMode::Auto;
+ default_repl_mode = ReplMode::Auto;
} else if (repl_mode_value == "variable") {
- g_dap.repl_mode = ReplMode::Variable;
+ default_repl_mode = ReplMode::Variable;
} else if (repl_mode_value == "command") {
- g_dap.repl_mode = ReplMode::Command;
+ default_repl_mode = ReplMode::Command;
} else {
llvm::errs()
<< "'" << repl_mode_value
@@ -5053,22 +5128,21 @@ int main(int argc, char *argv[]) {
}
}
- // stdout/stderr redirection to the IDE's console
- int new_stdout_fd = SetupStdoutStderrRedirection();
-
// Initialize LLDB first before we do anything.
- lldb::SBDebugger::Initialize();
+ lldb::SBError error = lldb::SBDebugger::InitializeWithErrorHandling();
+ if (error.Fail()) {
+ llvm::errs() << "Failed to initialize LLDB: " << error.GetCString() << "\n";
+ return EXIT_FAILURE;
+ }
// Terminate the debugger before the C++ destructor chain kicks in.
auto terminate_debugger =
llvm::make_scope_exit([] { lldb::SBDebugger::Terminate(); });
- RegisterRequestCallbacks();
-
int portno = -1;
if (auto *arg = input_args.getLastArg(OPT_port)) {
- auto optarg = arg->getValue();
+ const auto *optarg = arg->getValue();
char *remainder;
portno = strtol(optarg, &remainder, 0);
if (remainder == optarg || *remainder != '\0') {
@@ -5077,36 +5151,46 @@ int main(int argc, char *argv[]) {
}
}
+ const char *log_file_path = getenv("LLDBDAP_LOG");
+ std::shared_ptr<std::ofstream> log;
+ if (log_file_path)
+ log = std::make_shared<std::ofstream>(log_file_path);
+
#if !defined(_WIN32)
if (input_args.hasArg(OPT_wait_for_debugger)) {
printf("Paused waiting for debugger to attach (pid = %i)...\n", getpid());
pause();
}
#endif
+
if (portno != -1) {
printf("Listening on port %i...\n", portno);
- SOCKET socket_fd = AcceptConnection(portno);
- if (socket_fd >= 0) {
- g_dap.input.descriptor = StreamDescriptor::from_socket(socket_fd, true);
- g_dap.output.descriptor = StreamDescriptor::from_socket(socket_fd, false);
- } else {
- return EXIT_FAILURE;
- }
- } else {
- g_dap.input.descriptor = StreamDescriptor::from_file(fileno(stdin), false);
- g_dap.output.descriptor = StreamDescriptor::from_file(new_stdout_fd, false);
+ auto pre_init_commands = input_args.getAllArgValues(OPT_pre_init_command);
+ return AcceptConnection(program_path.str(), pre_init_commands,
+ std::move(log), default_repl_mode, portno);
}
+ DAP dap = DAP(program_path.str(), std::move(log), default_repl_mode);
+
+ // stdout/stderr redirection to the IDE's console
+ int new_stdout_fd = dup(fileno(stdout));
+ SetupRedirection(dap, new_stdout_fd, fileno(stderr));
+
+ // Register request callbacks.
+ RegisterRequestCallbacks(dap);
+
+ dap.input.descriptor = StreamDescriptor::from_file(fileno(stdin), false);
+ dap.output.descriptor = StreamDescriptor::from_file(new_stdout_fd, false);
+
for (const std::string &arg :
input_args.getAllArgValues(OPT_pre_init_command)) {
- g_dap.pre_init_commands.push_back(arg);
+ dap.pre_init_commands.push_back(arg);
}
bool CleanExit = true;
- if (auto Err = g_dap.Loop()) {
- if (g_dap.log)
- *g_dap.log << "Transport Error: " << llvm::toString(std::move(Err))
- << "\n";
+ if (auto Err = dap.Loop()) {
+ if (dap.log)
+ *dap.log << "Transport Error: " << llvm::toString(std::move(Err)) << "\n";
CleanExit = false;
}
>From 4c8a1b73e591686123f92aa2d02920afebbb1b0d Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Fri, 1 Nov 2024 14:44:12 -0700
Subject: [PATCH 2/5] Fix dap_server.py to work with python3, we need to
explicitly open file handles for sockets with "b" to write `bytes` instead of
`str`.
Fix lldb-dap.cpp handling of stdout.
---
.../Python/lldbsuite/test/lldbtest.py | 8 +
.../test/tools/lldb-dap/dap_server.py | 93 +++--
.../test/tools/lldb-dap/lldbdap_testcase.py | 18 +-
lldb/test/API/tools/lldb-dap/server/Makefile | 3 +
.../tools/lldb-dap/server/TestDAP_server.py | 67 ++++
lldb/test/API/tools/lldb-dap/server/main.c | 6 +
lldb/tools/lldb-dap/Breakpoint.h | 6 +-
lldb/tools/lldb-dap/BreakpointBase.cpp | 4 +-
lldb/tools/lldb-dap/BreakpointBase.h | 8 +-
lldb/tools/lldb-dap/FunctionBreakpoint.cpp | 6 +-
lldb/tools/lldb-dap/FunctionBreakpoint.h | 2 +-
lldb/tools/lldb-dap/InstructionBreakpoint.cpp | 6 +-
lldb/tools/lldb-dap/InstructionBreakpoint.h | 4 +-
lldb/tools/lldb-dap/JSONUtils.cpp | 5 +-
lldb/tools/lldb-dap/JSONUtils.h | 2 +-
lldb/tools/lldb-dap/Options.td | 9 +
lldb/tools/lldb-dap/SourceBreakpoint.cpp | 20 +-
lldb/tools/lldb-dap/SourceBreakpoint.h | 3 +-
lldb/tools/lldb-dap/Watchpoint.cpp | 7 +-
lldb/tools/lldb-dap/Watchpoint.h | 4 +-
lldb/tools/lldb-dap/lldb-dap.cpp | 354 ++++++++++++------
21 files changed, 453 insertions(+), 182 deletions(-)
create mode 100644 lldb/test/API/tools/lldb-dap/server/Makefile
create mode 100644 lldb/test/API/tools/lldb-dap/server/TestDAP_server.py
create mode 100644 lldb/test/API/tools/lldb-dap/server/main.c
diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py
index 8884ef5933ada8..c143b7ba37af22 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -37,6 +37,7 @@
import re
import shutil
import signal
+import socket
from subprocess import *
import sys
import time
@@ -250,6 +251,13 @@ def which(program):
return None
+def pickrandomport():
+ """Returns a random open port."""
+ with socket.socket() as sock:
+ sock.bind(("", 0))
+ return sock.getsockname()[1]
+
+
class ValueCheck:
def __init__(
self,
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
index c29992ce9c7848..31819328040aca 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
@@ -903,7 +903,7 @@ def request_setBreakpoints(self, file_path, line_array, data=None):
"sourceModified": False,
}
if line_array is not None:
- args_dict["lines"] = "%s" % line_array
+ args_dict["lines"] = line_array
breakpoints = []
for i, line in enumerate(line_array):
breakpoint_data = None
@@ -1154,34 +1154,32 @@ class DebugAdaptorServer(DebugCommunication):
def __init__(
self,
executable=None,
- port=None,
+ launch=True,
+ connect=None,
init_commands=[],
log_file=None,
env=None,
):
self.process = None
- if executable is not None:
- adaptor_env = os.environ.copy()
- if env is not None:
- adaptor_env.update(env)
-
- if log_file:
- adaptor_env["LLDBDAP_LOG"] = log_file
- self.process = subprocess.Popen(
- [executable],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- env=adaptor_env,
+ if launch:
+ self.process = DebugAdaptorServer.launch(
+ executable, connect=connect, log_file=log_file, env=env
)
+
+ if connect:
+ if isinstance(connect, str) and connect.startswith("/"):
+ s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ s.connect(connect)
+ else:
+ port = int(connect)
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.connect(("127.0.0.1", port))
DebugCommunication.__init__(
- self, self.process.stdout, self.process.stdin, init_commands, log_file
+ self, s.makefile("rb"), s.makefile("wb"), init_commands, log_file
)
- elif port is not None:
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.connect(("127.0.0.1", port))
+ else:
DebugCommunication.__init__(
- self, s.makefile("r"), s.makefile("w"), init_commands
+ self, self.process.stdout, self.process.stdin, init_commands, log_file
)
def get_pid(self):
@@ -1191,11 +1189,45 @@ def get_pid(self):
def terminate(self):
super(DebugAdaptorServer, self).terminate()
- if self.process is not None:
+ if self.process:
self.process.terminate()
self.process.wait()
self.process = None
+ @classmethod
+ def launch(
+ cls, executable: str, /, connect=None, log_file=None, env=None
+ ) -> subprocess.Popen:
+ adaptor_env = os.environ.copy()
+ if env:
+ adaptor_env.update(env)
+
+ if log_file:
+ adaptor_env["LLDBDAP_LOG"] = log_file
+
+ args = [executable]
+ if connect:
+ if isinstance(connect, str) and connect.startswith("/"):
+ args.append("--unix-socket")
+ args.append(connect)
+ else:
+ args.append("--port")
+ args.append(str(connect))
+
+ proc = subprocess.Popen(
+ args,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=sys.stdout,
+ env=adaptor_env,
+ )
+
+ if connect:
+ # Wait for the server to startup.
+ time.sleep(0.1)
+
+ return proc
+
def attach_options_specified(options):
if options.pid is not None:
@@ -1271,9 +1303,9 @@ def main():
)
parser.add_option(
- "--vscode",
+ "--dap",
type="string",
- dest="vscode_path",
+ dest="dap",
help=(
"The path to the command line program that implements the "
"Visual Studio Code Debug Adaptor protocol."
@@ -1349,10 +1381,9 @@ def main():
)
parser.add_option(
- "--port",
- type="int",
- dest="port",
- help="Attach a socket to a port instead of using STDIN for VSCode",
+ "--connect",
+ dest="connect",
+ help="Attach a socket to the connection ('<port>' or '<path>') instead of using STDIN for VSCode",
default=None,
)
@@ -1498,15 +1529,15 @@ def main():
(options, args) = parser.parse_args(sys.argv[1:])
- if options.vscode_path is None and options.port is None:
+ if options.dap is None and options.connect is None:
print(
"error: must either specify a path to a Visual Studio Code "
- "Debug Adaptor vscode executable path using the --vscode "
+ "Debug Adaptor vscode executable path using the --dap "
"option, or a port to attach to for an existing lldb-dap "
- "using the --port option"
+ "using the --connect option"
)
return
- dbg = DebugAdaptorServer(executable=options.vscode_path, port=options.port)
+ dbg = DebugAdaptorServer(executable=options.dap, port=options.connect)
if options.debug:
raw_input('Waiting for debugger to attach pid "%i"' % (dbg.get_pid()))
if options.replay:
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 a25466f07fa557..39d200a2ac566e 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
@@ -13,7 +13,7 @@ class DAPTestCaseBase(TestBase):
timeoutval = 10 * (10 if ('ASAN_OPTIONS' in os.environ) else 1)
NO_DEBUG_INFO_TESTCASE = True
- def create_debug_adaptor(self, lldbDAPEnv=None):
+ def create_debug_adaptor(self, env=None, launch=True, connect=None):
"""Create the Visual Studio Code debug adaptor"""
self.assertTrue(
is_exe(self.lldbDAPExec), "lldb-dap must exist and be executable"
@@ -21,14 +21,20 @@ def create_debug_adaptor(self, lldbDAPEnv=None):
log_file_path = self.getBuildArtifact("dap.txt")
self.dap_server = dap_server.DebugAdaptorServer(
executable=self.lldbDAPExec,
+ launch=launch,
+ connect=connect,
init_commands=self.setUpCommands(),
log_file=log_file_path,
- env=lldbDAPEnv,
+ env=env,
)
- def build_and_create_debug_adaptor(self, lldbDAPEnv=None):
+ def build_and_create_debug_adaptor(
+ self, lldbDAPEnv=None, lldbDAPLaunch=True, lldbDAPConnect=None
+ ):
self.build()
- self.create_debug_adaptor(lldbDAPEnv)
+ self.create_debug_adaptor(
+ lldbDAPEnv, launch=lldbDAPLaunch, connect=lldbDAPConnect
+ )
def set_source_breakpoints(self, source_path, lines, data=None):
"""Sets source breakpoints and returns an array of strings containing
@@ -475,11 +481,13 @@ def build_and_launch(
customThreadFormat=None,
launchCommands=None,
expectFailure=False,
+ lldbDAPConnect=None,
+ lldbDAPLaunch=True,
):
"""Build the default Makefile target, create the DAP debug adaptor,
and launch the process.
"""
- self.build_and_create_debug_adaptor(lldbDAPEnv)
+ self.build_and_create_debug_adaptor(lldbDAPEnv, lldbDAPLaunch, lldbDAPConnect)
self.assertTrue(os.path.exists(program), "executable must exist")
return self.launch(
diff --git a/lldb/test/API/tools/lldb-dap/server/Makefile b/lldb/test/API/tools/lldb-dap/server/Makefile
new file mode 100644
index 00000000000000..10495940055b63
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/server/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py b/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py
new file mode 100644
index 00000000000000..ae7094b096c4d7
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py
@@ -0,0 +1,67 @@
+"""
+Test lldb-dap server integration.
+"""
+
+import os
+import tempfile
+
+import dap_server
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+import lldbdap_testcase
+
+
+class TestDAP_server(lldbdap_testcase.DAPTestCaseBase):
+ def do_test_server(self, connect):
+ log_file_path = self.getBuildArtifact("dap.txt")
+ print("connect", connect)
+ server = dap_server.DebugAdaptorServer.launch(
+ self.lldbDAPExec, connect=connect, log_file=log_file_path
+ )
+
+ def cleanup():
+ server.terminate()
+ server.wait()
+
+ self.addTearDownHook(cleanup)
+
+ self.build()
+ program = self.getBuildArtifact("a.out")
+ source = "main.c"
+ breakpoint_line = line_number(source, "// breakpoint")
+
+ # Initial connection over the port.
+ self.create_debug_adaptor(launch=False, connect=connect)
+ self.launch(
+ program,
+ disconnectAutomatically=False,
+ )
+ self.set_source_breakpoints(source, [breakpoint_line])
+ self.continue_to_next_stop()
+ self.continue_to_exit()
+ output = self.get_stdout()
+ self.assertEquals(output, "hello world!\r\n")
+ self.dap_server.request_disconnect()
+
+ # Second connection over the port.
+ self.create_debug_adaptor(launch=False, connect=connect)
+ self.launch(program)
+ self.set_source_breakpoints(source, [breakpoint_line])
+ self.continue_to_next_stop()
+ self.continue_to_exit()
+ output = self.get_stdout()
+ self.assertEquals(output, "hello world!\r\n")
+
+ def test_server_port(self):
+ """
+ Test launching a binary with a lldb-dap in server mode on a specific port.
+ """
+ port = pickrandomport()
+ self.do_test_server(port)
+
+ def test_server_unix_socket(self):
+ """
+ Test launching a binary with a lldb-dap in server mode on a unix socket.
+ """
+ dir = tempfile.gettempdir()
+ self.do_test_server(dir + "/dap-connection-" + str(os.getpid()))
diff --git a/lldb/test/API/tools/lldb-dap/server/main.c b/lldb/test/API/tools/lldb-dap/server/main.c
new file mode 100644
index 00000000000000..c3599057621276
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/server/main.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int main(int argc, char const *argv[]) {
+ printf("hello world!\n"); // breakpoint 1
+ return 0;
+}
diff --git a/lldb/tools/lldb-dap/Breakpoint.h b/lldb/tools/lldb-dap/Breakpoint.h
index cffeb2fab1f0ef..add4ff4da2a9bb 100644
--- a/lldb/tools/lldb-dap/Breakpoint.h
+++ b/lldb/tools/lldb-dap/Breakpoint.h
@@ -19,9 +19,9 @@ struct Breakpoint : public BreakpointBase {
// The LLDB breakpoint associated wit this source breakpoint
lldb::SBBreakpoint 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) {}
+ 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 f07921783aa985..27ae000e88c37a 100644
--- a/lldb/tools/lldb-dap/BreakpointBase.cpp
+++ b/lldb/tools/lldb-dap/BreakpointBase.cpp
@@ -11,9 +11,7 @@
using namespace lldb_dap;
-BreakpointBase::BreakpointBase(DAP &d) : dap(d) {}
-
-BreakpointBase::BreakpointBase(DAP &d, const llvm::json::Object &obj)
+BreakpointBase::BreakpointBase(DAP *d, const llvm::json::Object &obj)
: dap(d), condition(std::string(GetString(obj, "condition"))),
hitCondition(std::string(GetString(obj, "hitCondition"))) {}
diff --git a/lldb/tools/lldb-dap/BreakpointBase.h b/lldb/tools/lldb-dap/BreakpointBase.h
index 121688d08f1524..cc808f396e99e2 100644
--- a/lldb/tools/lldb-dap/BreakpointBase.h
+++ b/lldb/tools/lldb-dap/BreakpointBase.h
@@ -19,7 +19,7 @@ namespace lldb_dap {
struct BreakpointBase {
// Associated DAP session.
- DAP &dap;
+ DAP *dap;
// An optional expression for conditional breakpoints.
std::string condition;
@@ -27,8 +27,10 @@ struct BreakpointBase {
// ignored. The backend is expected to interpret the expression as needed
std::string hitCondition;
- BreakpointBase(DAP &d);
- BreakpointBase(DAP &d, const llvm::json::Object &obj);
+ BreakpointBase(DAP *d) : dap(d) {}
+ BreakpointBase(DAP *d, const llvm::json::Object &obj);
+ BreakpointBase() = default;
+ BreakpointBase(const BreakpointBase &) = default;
virtual ~BreakpointBase() = default;
virtual void SetCondition() = 0;
diff --git a/lldb/tools/lldb-dap/FunctionBreakpoint.cpp b/lldb/tools/lldb-dap/FunctionBreakpoint.cpp
index ef6df6c0dc91cc..e35dc2dc2ca4b8 100644
--- a/lldb/tools/lldb-dap/FunctionBreakpoint.cpp
+++ b/lldb/tools/lldb-dap/FunctionBreakpoint.cpp
@@ -13,13 +13,13 @@
namespace lldb_dap {
-FunctionBreakpoint::FunctionBreakpoint(DAP &d, const llvm::json::Object &obj)
+FunctionBreakpoint::FunctionBreakpoint(DAP *d, const llvm::json::Object &obj)
: Breakpoint(d, obj), functionName(std::string(GetString(obj, "name"))) {}
void FunctionBreakpoint::SetBreakpoint() {
- if (functionName.empty())
+ if (functionName.empty() || !dap)
return;
- bp = 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 93f0b93b35291d..a66acf8b1945e9 100644
--- a/lldb/tools/lldb-dap/FunctionBreakpoint.h
+++ b/lldb/tools/lldb-dap/FunctionBreakpoint.h
@@ -17,7 +17,7 @@ namespace lldb_dap {
struct FunctionBreakpoint : public Breakpoint {
std::string functionName;
- FunctionBreakpoint(DAP &dap, 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 d6ccb2dfc29111..cd4f719ff13517 100644
--- a/lldb/tools/lldb-dap/InstructionBreakpoint.cpp
+++ b/lldb/tools/lldb-dap/InstructionBreakpoint.cpp
@@ -15,7 +15,7 @@
namespace lldb_dap {
// Instruction Breakpoint
-InstructionBreakpoint::InstructionBreakpoint(DAP &d,
+InstructionBreakpoint::InstructionBreakpoint(DAP *d,
const llvm::json::Object &obj)
: Breakpoint(d, obj), instructionAddressReference(LLDB_INVALID_ADDRESS),
id(0), offset(GetSigned(obj, "offset", 0)) {
@@ -25,7 +25,9 @@ InstructionBreakpoint::InstructionBreakpoint(DAP &d,
}
void InstructionBreakpoint::SetInstructionBreakpoint() {
- bp = dap.target.BreakpointCreateByAddress(instructionAddressReference);
+ if (!dap)
+ return;
+ bp = dap->target.BreakpointCreateByAddress(instructionAddressReference);
id = bp.GetID();
}
diff --git a/lldb/tools/lldb-dap/InstructionBreakpoint.h b/lldb/tools/lldb-dap/InstructionBreakpoint.h
index d7f3fb8ad3a4eb..8c5249f9e0825f 100644
--- a/lldb/tools/lldb-dap/InstructionBreakpoint.h
+++ b/lldb/tools/lldb-dap/InstructionBreakpoint.h
@@ -26,10 +26,10 @@ struct InstructionBreakpoint : public Breakpoint {
int32_t id;
int32_t offset;
- InstructionBreakpoint(DAP &d)
+ InstructionBreakpoint(DAP *d)
: Breakpoint(d), instructionAddressReference(LLDB_INVALID_ADDRESS), id(0),
offset(0) {}
- InstructionBreakpoint(DAP &d, const llvm::json::Object &obj);
+ InstructionBreakpoint(DAP *d, const llvm::json::Object &obj);
// Set instruction breakpoint in LLDB as a new breakpoint
void SetInstructionBreakpoint();
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index c6d9e86f0d8bf1..0d56b235c52c1c 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -27,6 +27,7 @@
#include "ExceptionBreakpoint.h"
#include "JSONUtils.h"
#include "LLDBUtils.h"
+#include "lldb/lldb-defines.h"
namespace lldb_dap {
@@ -496,10 +497,10 @@ llvm::json::Value CreateModule(lldb::SBTarget &target, lldb::SBModule &module) {
return llvm::json::Value(std::move(object));
}
-void AppendBreakpoint(BreakpointBase *bp, llvm::json::Array &breakpoints,
+void AppendBreakpoint(BreakpointBase &bp, llvm::json::Array &breakpoints,
std::optional<llvm::StringRef> request_path,
std::optional<uint32_t> request_line) {
- breakpoints.emplace_back(CreateBreakpoint(bp, request_path, request_line));
+ breakpoints.emplace_back(CreateBreakpoint(&bp, request_path, request_line));
}
// "Event": {
diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h
index d06c0ddb9e638e..6212d2e0d0a554 100644
--- a/lldb/tools/lldb-dap/JSONUtils.h
+++ b/lldb/tools/lldb-dap/JSONUtils.h
@@ -222,7 +222,7 @@ void FillResponse(const llvm::json::Object &request,
/// provided by the setBreakpoints request are returned to the IDE as a
/// fallback.
void AppendBreakpoint(
- BreakpointBase *bp, llvm::json::Array &breakpoints,
+ BreakpointBase &bp, llvm::json::Array &breakpoints,
std::optional<llvm::StringRef> request_path = std::nullopt,
std::optional<uint32_t> request_line = std::nullopt);
diff --git a/lldb/tools/lldb-dap/Options.td b/lldb/tools/lldb-dap/Options.td
index d7b4a065abec01..dfca79b2884ac8 100644
--- a/lldb/tools/lldb-dap/Options.td
+++ b/lldb/tools/lldb-dap/Options.td
@@ -22,8 +22,17 @@ def port: S<"port">,
HelpText<"Communicate with the lldb-dap tool over the defined port.">;
def: Separate<["-"], "p">,
Alias<port>,
+ MetaVarName<"<port>">,
HelpText<"Alias for --port">;
+def unix_socket: S<"unix-socket">,
+ MetaVarName<"<path>">,
+ HelpText<"Communicate with the lldb-dap tool over the unix socket or named pipe.">;
+def: Separate<["-"], "u">,
+ Alias<unix_socket>,
+ MetaVarName<"<path>">,
+ HelpText<"Alias for --unix_socket">;
+
def launch_target: S<"launch-target">,
MetaVarName<"<target>">,
HelpText<"Launch a target for the launchInTerminal request. Any argument "
diff --git a/lldb/tools/lldb-dap/SourceBreakpoint.cpp b/lldb/tools/lldb-dap/SourceBreakpoint.cpp
index 7415a0914dad43..f4803c82cb34b1 100644
--- a/lldb/tools/lldb-dap/SourceBreakpoint.cpp
+++ b/lldb/tools/lldb-dap/SourceBreakpoint.cpp
@@ -12,16 +12,19 @@
namespace lldb_dap {
-SourceBreakpoint::SourceBreakpoint(DAP &dap, const llvm::json::Object &obj)
+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) {
+ if (!dap)
+ return;
+
lldb::SBFileSpecList module_list;
- bp = 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();
@@ -278,16 +281,19 @@ void SourceBreakpoint::SetLogMessage() {
}
void SourceBreakpoint::NotifyLogMessageError(llvm::StringRef error) {
+ if (!dap)
+ return;
+
std::string message = "Log message has error: ";
message += error;
- dap.SendOutput(OutputType::Console, message);
+ dap->SendOutput(OutputType::Console, message);
}
/*static*/
bool SourceBreakpoint::BreakpointHitCallback(
void *baton, lldb::SBProcess &process, lldb::SBThread &thread,
lldb::SBBreakpointLocation &location) {
- if (!baton)
+ if (!baton || !(static_cast<SourceBreakpoint *>(baton)->dap))
return true;
SourceBreakpoint *bp = (SourceBreakpoint *)baton;
@@ -306,7 +312,7 @@ bool SourceBreakpoint::BreakpointHitCallback(
if (value.GetError().Fail())
value = frame.EvaluateExpression(expr);
output +=
- VariableDescription(value, bp->dap.enable_auto_variable_summaries)
+ VariableDescription(value, bp->dap->enable_auto_variable_summaries)
.display_value;
} else {
output += messagePart.text;
@@ -314,7 +320,7 @@ bool SourceBreakpoint::BreakpointHitCallback(
}
if (!output.empty() && output.back() != '\n')
output.push_back('\n'); // Ensure log message has line break.
- bp->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 ae5f0548707d85..e7e661803156dd 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(DAP &dap) : Breakpoint(dap), line(0), column(0) {}
- SourceBreakpoint(DAP &dap, 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 8cd297aa696168..29f7f91569155a 100644
--- a/lldb/tools/lldb-dap/Watchpoint.cpp
+++ b/lldb/tools/lldb-dap/Watchpoint.cpp
@@ -14,8 +14,8 @@
#include "JSONUtils.h"
namespace lldb_dap {
-Watchpoint::Watchpoint(DAP &dap, const llvm::json::Object &obj)
- : BreakpointBase(dap, 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('/');
@@ -45,7 +45,8 @@ void Watchpoint::CreateJsonObject(llvm::json::Object &object) {
}
void Watchpoint::SetWatchpoint() {
- wp = dap.target.WatchpointCreateByAddress(addr, size, options, error);
+ if (!dap) return;
+ 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 a789208f1c7ae6..f32034bba24997 100644
--- a/lldb/tools/lldb-dap/Watchpoint.h
+++ b/lldb/tools/lldb-dap/Watchpoint.h
@@ -25,8 +25,8 @@ struct Watchpoint : public BreakpointBase {
lldb::SBWatchpoint wp;
lldb::SBError error;
- Watchpoint(DAP &dap, const llvm::json::Object &obj);
- Watchpoint(DAP &dap, lldb::SBWatchpoint wp) : BreakpointBase(dap), wp(wp) {}
+ Watchpoint(DAP *dap, 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 65dfbbabdcdd12..d7fa657cd7e1fe 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -15,7 +15,9 @@
#include "lldb/API/SBListener.h"
#include "lldb/API/SBMemoryRegionInfo.h"
#include "lldb/API/SBStringList.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Base64.h"
+#include "llvm/Support/raw_socket_stream.h"
#include <cassert>
#include <climits>
@@ -26,6 +28,7 @@
#include <optional>
#include <sys/stat.h>
#include <sys/types.h>
+#include <system_error>
#if defined(_WIN32)
// We need to #define NOMINMAX in order to skip `min()` and `max()` macro
// definitions that conflict with other system headers.
@@ -33,12 +36,15 @@
// the JSON code we use also has methods named `GetObject()` and we conflict
// against these.
#define NOMINMAX
+#include <afunix.h>
#include <windows.h>
+#include <winsock2.h>
#undef GetObject
#include <io.h>
#else
#include <netinet/in.h>
#include <sys/socket.h>
+#include <sys/un.h>
#include <unistd.h>
#endif
@@ -189,95 +195,194 @@ void SetupRedirection(DAP &dap, int stdoutfd = -1, int stderrfd = -1) {
redirection_test();
}
-int AcceptConnection(llvm::StringRef program_path,
- const std::vector<std::string> &pre_init_commands,
- std::shared_ptr<std::ofstream> log,
- ReplMode default_repl_mode, int portno) {
+void HandleClient(int clientfd, llvm::StringRef program_path,
+ const std::vector<std::string> &pre_init_commands,
+ std::shared_ptr<std::ofstream> log,
+ ReplMode default_repl_mode) {
+ if (log)
+ *log << "client[" << clientfd << "] connected\n";
+ DAP dap = DAP(program_path, log, default_repl_mode);
+ dap.debug_adaptor_path = program_path;
+
+ SetupRedirection(dap);
+ RegisterRequestCallbacks(dap);
+
+ dap.input.descriptor = StreamDescriptor::from_socket(clientfd, false);
+ dap.output.descriptor = StreamDescriptor::from_socket(clientfd, false);
+
+ for (const std::string &arg : pre_init_commands) {
+ dap.pre_init_commands.push_back(arg);
+ }
+
+ if (auto Err = dap.Loop()) {
+ if (log)
+ *log << "Transport Error: " << llvm::toString(std::move(Err)) << "\n";
+ }
+
+ if (log)
+ *log << "client[" << clientfd << "] connection closed\n";
+#if defined(_WIN32)
+ closesocket(clientfd);
+#else
+ close(clientfd);
+#endif
+}
+
+std::error_code getLastSocketErrorCode() {
+#ifdef _WIN32
+ return std::error_code(::WSAGetLastError(), std::system_category());
+#else
+ return llvm::errnoAsErrorCode();
+#endif
+}
+
+llvm::Expected<int> getSocketFD(llvm::StringRef path) {
+ if (llvm::sys::fs::exists(path) && (::remove(path.str().c_str()) == -1)) {
+ return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
+ "Remove existing socket failed");
+ }
+
+ SOCKET sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sockfd == -1) {
+ return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
+ "Create socket failed");
+ }
+
+ struct sockaddr_un addr;
+ bzero(&addr, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, path.str().c_str(), sizeof(addr.sun_path) - 1);
+
+ if (::bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+#if defined(_WIN32)
+ closesocket(sockfd);
+#else
+ close(sockfd);
+#endif
+ return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
+ "Socket bind() failed");
+ }
+
+ if (listen(sockfd, llvm::hardware_concurrency().compute_thread_count()) < 0) {
+#if defined(_WIN32)
+ closesocket(sockfd);
+#else
+ close(sockfd);
+#endif
+ return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
+ "Socket listen() failed");
+ }
+
+ return sockfd;
+}
+
+llvm::Expected<int> getSocketFD(int portno) {
SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
- if (log)
- *log << "error: opening socket (" << strerror(errno) << ")" << std::endl;
- perror("error: socket(int, int, int)");
- return 1;
+ return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
+ "Create socket failed");
}
- struct sockaddr_in serv_addr, cli_addr;
+ struct sockaddr_in serv_addr;
bzero(&serv_addr, sizeof(serv_addr));
- bzero(&cli_addr, sizeof(cli_addr));
- socklen_t clilen = sizeof(cli_addr);
serv_addr.sin_family = AF_INET;
// serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
- if (log)
- *log << "error: binding socket (" << strerror(errno) << ")" << std::endl;
- perror("error: bind(int, struct sockaddr *, socklen_t)");
#if defined(_WIN32)
closesocket(sockfd);
#else
close(sockfd);
#endif
- return 1;
+ return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
+ "Socket bind() failed");
}
- if (listen(sockfd, 5) < 0) {
- if (log)
- *log << "error: listen() (" << strerror(errno) << ")" << std::endl;
- perror("error: listen(int, int)");
+ if (listen(sockfd, llvm::hardware_concurrency().compute_thread_count()) < 0) {
#if defined(_WIN32)
closesocket(sockfd);
#else
close(sockfd);
#endif
- return 1;
+ return llvm::make_error<llvm::StringError>(getLastSocketErrorCode(),
+ "Socket listen() failed");
+ }
+
+ return sockfd;
+}
+
+int AcceptConnection(llvm::StringRef program_path,
+ const std::vector<std::string> &pre_init_commands,
+ std::shared_ptr<std::ofstream> log,
+ ReplMode default_repl_mode,
+ llvm::StringRef unix_socket_path) {
+ auto listening = getSocketFD(unix_socket_path);
+ if (auto E = listening.takeError()) {
+ llvm::errs() << "Listening on " << unix_socket_path
+ << " failed: " << llvm::toString(std::move(E)) << "\n";
+ return EXIT_FAILURE;
}
while (true) {
+ struct sockaddr_un cli_addr;
+ bzero(&cli_addr, sizeof(struct sockaddr_un));
+ socklen_t clilen = sizeof(cli_addr);
SOCKET clientfd =
- llvm::sys::RetryAfterSignal(static_cast<SOCKET>(-1), accept, sockfd,
+ llvm::sys::RetryAfterSignal(static_cast<SOCKET>(-1), accept, *listening,
(struct sockaddr *)&cli_addr, &clilen);
if (clientfd < 0) {
- if (log)
- *log << "error: accept (" << strerror(errno) << ")" << std::endl;
- perror("error: accept(int, struct sockaddr *, socklen_t *)");
- break;
+ llvm::errs() << "Client accept failed: "
+ << getLastSocketErrorCode().message() << "\n";
+ return EXIT_FAILURE;
}
- std::thread t([program_path, pre_init_commands, log, default_repl_mode,
- clientfd]() {
- printf("accepted client fd %d\n", clientfd);
- DAP dap = DAP(program_path, log, default_repl_mode);
- dap.debug_adaptor_path = program_path;
-
- SetupRedirection(dap);
- RegisterRequestCallbacks(dap);
+ std::thread t(HandleClient, clientfd, program_path, pre_init_commands, log,
+ default_repl_mode);
+ t.detach();
+ }
- dap.input.descriptor = StreamDescriptor::from_socket(clientfd, false);
- dap.output.descriptor = StreamDescriptor::from_socket(clientfd, false);
+#if defined(_WIN32)
+ closesocket(*listening);
+#else
+ close(*listening);
+#endif
+ return 0;
+}
- for (const std::string &arg : pre_init_commands) {
- dap.pre_init_commands.push_back(arg);
- }
+int AcceptConnection(llvm::StringRef program_path,
+ const std::vector<std::string> &pre_init_commands,
+ std::shared_ptr<std::ofstream> log,
+ ReplMode default_repl_mode, int portno) {
+ auto listening = getSocketFD(portno);
+ if (auto E = listening.takeError()) {
+ llvm::errs() << "Listening on " << portno
+ << " failed: " << llvm::toString(std::move(E)) << "\n";
+ return EXIT_FAILURE;
+ }
- bool CleanExit = true;
- if (auto Err = dap.Loop()) {
- llvm::errs() << "Transport Error: " << llvm::toString(std::move(Err))
- << "\n";
- CleanExit = false;
- }
+ while (true) {
+ struct sockaddr_in cli_addr;
+ bzero(&cli_addr, sizeof(struct sockaddr_in));
+ socklen_t clilen = sizeof(cli_addr);
+ SOCKET clientfd =
+ llvm::sys::RetryAfterSignal(static_cast<SOCKET>(-1), accept, *listening,
+ (struct sockaddr *)&cli_addr, &clilen);
+ if (clientfd < 0) {
+ llvm::errs() << "Client accept failed: "
+ << getLastSocketErrorCode().message() << "\n";
+ return EXIT_FAILURE;
+ }
- printf("closing client fd %d ? %s\n", clientfd,
- CleanExit ? "true" : "false");
- close(clientfd);
- });
+ std::thread t(HandleClient, clientfd, program_path, pre_init_commands, log,
+ default_repl_mode);
t.detach();
}
#if defined(_WIN32)
- closesocket(sockfd);
+ closesocket(*listening);
#else
- close(sockfd);
+ close(*listening);
#endif
return 0;
}
@@ -629,7 +734,7 @@ void EventThreadFunction(DAP &dap) {
auto event_type =
lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event);
auto bp = Breakpoint(
- dap, lldb::SBBreakpoint::GetBreakpointFromEvent(event));
+ &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
@@ -2712,12 +2817,12 @@ void request_scopes(DAP &dap, const llvm::json::Object &request) {
// },
// "SetBreakpointsArguments": {
// "type": "object",
-// "description": "Arguments for 'setBreakpoints' request.",
+// "description": "Arguments for `setBreakpoints` request.",
// "properties": {
// "source": {
// "$ref": "#/definitions/Source",
// "description": "The source location of the breakpoints; either
-// source.path or source.reference must be specified."
+// `source.path` or `source.sourceReference` must be specified."
// },
// "breakpoints": {
// "type": "array",
@@ -2731,15 +2836,16 @@ void request_scopes(DAP &dap, const llvm::json::Object &request) {
// "items": {
// "type": "integer"
// },
-// "description": "Deprecated: The code locations of the breakpoints."
+// "description": "Deprecated: The code locations of the
+// breakpoints."
// },
// "sourceModified": {
// "type": "boolean",
-// "description": "A value of true indicates that the underlying source
-// has been modified which results in new breakpoint locations."
+// "description": "A value of true indicates that the underlying
+// source has been modified which results in new breakpoint locations."
// }
-// },
-// "required": [ "source" ]
+// },
+// "required": [ "source" ]
// },
// "SetBreakpointsResponse": {
// "allOf": [ { "$ref": "#/definitions/Response" }, {
@@ -2771,36 +2877,55 @@ void request_scopes(DAP &dap, const llvm::json::Object &request) {
// }]
// },
// "SourceBreakpoint": {
-// "type": "object",
-// "description": "Properties of a breakpoint or logpoint passed to the
-// setBreakpoints request.", "properties": {
+// "type": "object",
+// "description": "Properties of a breakpoint or logpoint passed to the
+// `setBreakpoints` request.",
+// "properties": {
// "line": {
-// "type": "integer",
-// "description": "The source line of the breakpoint or logpoint."
+// "type": "integer",
+// "description": "The source line of the breakpoint or logpoint."
// },
// "column": {
-// "type": "integer",
-// "description": "An optional source column of the breakpoint."
+// "type": "integer",
+// "description": "Start position within source line of the breakpoint or
+// logpoint. It is measured in UTF-16 code units and the client capability
+// `columnsStartAt1` determines whether it is 0- or 1-based."
// },
// "condition": {
-// "type": "string",
-// "description": "An optional expression for conditional breakpoints."
+// "type": "string",
+// "description": "The expression for conditional breakpoints.\nIt is
+// only honored by a debug adapter if the corresponding capability
+// `supportsConditionalBreakpoints` is true."
// },
// "hitCondition": {
-// "type": "string",
-// "description": "An optional expression that controls how many hits of
-// the breakpoint are ignored. The backend is expected to interpret the
-// expression as needed."
+// "type": "string",
+// "description": "The 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.\nIf both this property and `condition` are specified,
+// `hitCondition` should be evaluated only if the `condition` is met, and
+// the debug adapter should stop only if both conditions are met."
// },
// "logMessage": {
-// "type": "string",
-// "description": "If this attribute exists and is non-empty, the backend
-// must not 'break' (stop) but log the message instead. Expressions within
-// {} are interpolated."
+// "type": "string",
+// "description": "If this attribute exists and is non-empty, the debug
+// adapter must not 'break' (stop)\nbut log the message instead.
+// Expressions within `{}` are interpolated.\nThe attribute is only
+// honored by a debug adapter if the corresponding capability
+// `supportsLogPoints` is true.\nIf either `hitCondition` or `condition`
+// is specified, then the message should only be logged if those
+// conditions are met."
+// },
+// "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": [ "line" ]
-// }
+// },
+// "required": [ "line" ]
+// },
void request_setBreakpoints(DAP &dap, const llvm::json::Object &request) {
llvm::json::Object response;
lldb::SBError error;
@@ -2819,25 +2944,19 @@ void request_setBreakpoints(DAP &dap, const llvm::json::Object &request) {
for (const auto &bp : *breakpoints) {
const auto *bp_obj = bp.getAsObject();
if (bp_obj) {
- SourceBreakpoint src_bp(dap, *bp_obj);
+ SourceBreakpoint src_bp(&dap, *bp_obj);
request_bps.try_emplace(src_bp.line, src_bp);
-
+ const auto [kv, inserted] =
+ 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 = dap.source_breakpoints.find(path);
- if (existing_source_bps != 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
- dap.source_breakpoints[path].try_emplace(src_bp.line, src_bp);
- src_bp.SetBreakpoint(path.data());
- AppendBreakpoint(&src_bp, response_breakpoints, path, src_bp.line);
+
+ AppendBreakpoint(kv->getSecond(), response_breakpoints, path,
+ src_bp.line);
}
}
}
@@ -3030,7 +3149,7 @@ void request_setFunctionBreakpoints(DAP &dap,
const auto *bp_obj = value.getAsObject();
if (bp_obj == nullptr)
continue;
- FunctionBreakpoint func_bp(dap, *bp_obj);
+ FunctionBreakpoint func_bp(&dap, *bp_obj);
request_bps.try_emplace(func_bp.functionName, std::move(func_bp));
}
@@ -3052,7 +3171,7 @@ void request_setFunctionBreakpoints(DAP &dap,
// 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);
+ AppendBreakpoint(pair.second, response_breakpoints);
}
}
// Remove any breakpoints that are no longer in our list
@@ -3064,7 +3183,7 @@ void request_setFunctionBreakpoints(DAP &dap,
for (auto &pair : request_bps) {
pair.second.SetBreakpoint();
// Add this breakpoint info to the response
- AppendBreakpoint(&pair.second, response_breakpoints);
+ AppendBreakpoint(pair.second, response_breakpoints);
dap.function_breakpoints.try_emplace(pair.first(), std::move(pair.second));
}
@@ -3318,7 +3437,7 @@ void request_setDataBreakpoints(DAP &dap, const llvm::json::Object &request) {
for (const auto &bp : *breakpoints) {
const auto *bp_obj = bp.getAsObject();
if (bp_obj) {
- Watchpoint wp(dap, *bp_obj);
+ Watchpoint wp(&dap, *bp_obj);
watchpoints.push_back(wp);
}
}
@@ -3334,7 +3453,7 @@ void request_setDataBreakpoints(DAP &dap, const llvm::json::Object &request) {
}
}
for (auto wp : watchpoints)
- AppendBreakpoint(&wp, response_breakpoints);
+ AppendBreakpoint(wp, response_breakpoints);
llvm::json::Object body;
body.try_emplace("breakpoints", std::move(response_breakpoints));
@@ -4152,6 +4271,7 @@ void request_variables(DAP &dap, const llvm::json::Object &request) {
dap.variables.InsertVariable(variable, /*is_permanent=*/false);
variables.emplace_back(CreateVariable(
variable, var_ref, hex, dap.enable_auto_variable_summaries,
+ dap.enable_synthetic_child_debugging,
variable_name_counts[GetNonNullVariableName(variable)] > 1));
}
} else {
@@ -4670,8 +4790,8 @@ void request__testGetTargetBreakpoints(DAP &dap,
FillResponse(request, response);
llvm::json::Array response_breakpoints;
for (uint32_t i = 0; dap.target.GetBreakpointAtIndex(i).IsValid(); ++i) {
- auto bp = Breakpoint(dap, dap.target.GetBreakpointAtIndex(i));
- AppendBreakpoint(&bp, response_breakpoints);
+ auto bp = Breakpoint(&dap, dap.target.GetBreakpointAtIndex(i));
+ AppendBreakpoint(bp, response_breakpoints);
}
llvm::json::Object body;
body.try_emplace("breakpoints", std::move(response_breakpoints));
@@ -4884,7 +5004,7 @@ void request_setInstructionBreakpoints(DAP &dap,
const auto *bp_obj = bp.getAsObject();
if (bp_obj) {
// Read instruction breakpoint request.
- InstructionBreakpoint inst_bp(dap, *bp_obj);
+ InstructionBreakpoint inst_bp(&dap, *bp_obj);
// Store them into map for reference.
request_ibp.try_emplace(inst_bp.instructionAddressReference,
std::move(inst_bp));
@@ -5139,6 +5259,9 @@ int main(int argc, char *argv[]) {
auto terminate_debugger =
llvm::make_scope_exit([] { lldb::SBDebugger::Terminate(); });
+ const auto pre_init_commands =
+ input_args.getAllArgValues(OPT_pre_init_command);
+
int portno = -1;
if (auto *arg = input_args.getLastArg(OPT_port)) {
@@ -5151,6 +5274,12 @@ int main(int argc, char *argv[]) {
}
}
+ std::string unix_socket_path;
+ if (auto *arg = input_args.getLastArg(OPT_unix_socket)) {
+ const auto *path = arg->getValue();
+ unix_socket_path.assign(path);
+ }
+
const char *log_file_path = getenv("LLDBDAP_LOG");
std::shared_ptr<std::ofstream> log;
if (log_file_path)
@@ -5164,33 +5293,34 @@ int main(int argc, char *argv[]) {
#endif
if (portno != -1) {
- printf("Listening on port %i...\n", portno);
- auto pre_init_commands = input_args.getAllArgValues(OPT_pre_init_command);
+ llvm::errs() << llvm::format("Listening on port %i...\n", portno);
return AcceptConnection(program_path.str(), pre_init_commands,
std::move(log), default_repl_mode, portno);
}
+ if (!unix_socket_path.empty()) {
+ return AcceptConnection(program_path.str(), pre_init_commands,
+ std::move(log), default_repl_mode,
+ unix_socket_path);
+ }
+
DAP dap = DAP(program_path.str(), std::move(log), default_repl_mode);
// stdout/stderr redirection to the IDE's console
int new_stdout_fd = dup(fileno(stdout));
- SetupRedirection(dap, new_stdout_fd, fileno(stderr));
+ SetupRedirection(dap, fileno(stdout), fileno(stderr));
// Register request callbacks.
RegisterRequestCallbacks(dap);
dap.input.descriptor = StreamDescriptor::from_file(fileno(stdin), false);
dap.output.descriptor = StreamDescriptor::from_file(new_stdout_fd, false);
-
- for (const std::string &arg :
- input_args.getAllArgValues(OPT_pre_init_command)) {
- dap.pre_init_commands.push_back(arg);
- }
+ dap.pre_init_commands = pre_init_commands;
bool CleanExit = true;
if (auto Err = dap.Loop()) {
- if (dap.log)
- *dap.log << "Transport Error: " << llvm::toString(std::move(Err)) << "\n";
+ if (log)
+ *log << "Transport Error: " << llvm::toString(std::move(Err)) << "\n";
CleanExit = false;
}
>From 0132c0dc3a1092fe6a4eb2a9f21265d39e0252fe Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Mon, 4 Nov 2024 14:28:20 -0800
Subject: [PATCH 3/5] Apply clang-format
---
lldb/tools/lldb-dap/OutputRedirector.cpp | 3 ++-
lldb/tools/lldb-dap/Watchpoint.cpp | 4 +++-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/lldb/tools/lldb-dap/OutputRedirector.cpp b/lldb/tools/lldb-dap/OutputRedirector.cpp
index 7fc9aaef20b911..0b725e1901b9fd 100644
--- a/lldb/tools/lldb-dap/OutputRedirector.cpp
+++ b/lldb/tools/lldb-dap/OutputRedirector.cpp
@@ -21,7 +21,8 @@ using namespace llvm;
namespace lldb_dap {
-Expected<int> RedirectFd(int fd, std::function<void(llvm::StringRef)> callback) {
+Expected<int> RedirectFd(int fd,
+ std::function<void(llvm::StringRef)> callback) {
int new_fd[2];
#if defined(_WIN32)
if (_pipe(new_fd, 4096, O_TEXT) == -1) {
diff --git a/lldb/tools/lldb-dap/Watchpoint.cpp b/lldb/tools/lldb-dap/Watchpoint.cpp
index 29f7f91569155a..c11cfa0375cc8c 100644
--- a/lldb/tools/lldb-dap/Watchpoint.cpp
+++ b/lldb/tools/lldb-dap/Watchpoint.cpp
@@ -45,7 +45,9 @@ void Watchpoint::CreateJsonObject(llvm::json::Object &object) {
}
void Watchpoint::SetWatchpoint() {
- if (!dap) return;
+ if (!dap)
+ return;
+
wp = dap->target.WatchpointCreateByAddress(addr, size, options, error);
if (!condition.empty())
SetCondition();
>From d710b9bd77859ecc5179aa745133c64874001a7f Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Tue, 5 Nov 2024 16:27:31 -0800
Subject: [PATCH 4/5] Use a DAP& instead of a DAP* in breakpoint helpers.
---
lldb/tools/lldb-dap/Breakpoint.h | 6 +++---
lldb/tools/lldb-dap/BreakpointBase.cpp | 2 +-
lldb/tools/lldb-dap/BreakpointBase.h | 7 +++----
lldb/tools/lldb-dap/FunctionBreakpoint.cpp | 6 +++---
lldb/tools/lldb-dap/FunctionBreakpoint.h | 2 +-
lldb/tools/lldb-dap/InstructionBreakpoint.cpp | 6 ++----
lldb/tools/lldb-dap/InstructionBreakpoint.h | 4 ++--
lldb/tools/lldb-dap/SourceBreakpoint.cpp | 18 ++++++------------
lldb/tools/lldb-dap/SourceBreakpoint.h | 2 +-
lldb/tools/lldb-dap/Watchpoint.cpp | 7 ++-----
lldb/tools/lldb-dap/Watchpoint.h | 4 ++--
lldb/tools/lldb-dap/lldb-dap.cpp | 13 ++++++-------
12 files changed, 32 insertions(+), 45 deletions(-)
diff --git a/lldb/tools/lldb-dap/Breakpoint.h b/lldb/tools/lldb-dap/Breakpoint.h
index add4ff4da2a9bb..cffeb2fab1f0ef 100644
--- a/lldb/tools/lldb-dap/Breakpoint.h
+++ b/lldb/tools/lldb-dap/Breakpoint.h
@@ -19,9 +19,9 @@ struct Breakpoint : public BreakpointBase {
// The LLDB breakpoint associated wit this source breakpoint
lldb::SBBreakpoint 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) {}
+ 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 27ae000e88c37a..c5d7a9778df018 100644
--- a/lldb/tools/lldb-dap/BreakpointBase.cpp
+++ b/lldb/tools/lldb-dap/BreakpointBase.cpp
@@ -11,7 +11,7 @@
using namespace lldb_dap;
-BreakpointBase::BreakpointBase(DAP *d, const llvm::json::Object &obj)
+BreakpointBase::BreakpointBase(DAP &d, const llvm::json::Object &obj)
: dap(d), condition(std::string(GetString(obj, "condition"))),
hitCondition(std::string(GetString(obj, "hitCondition"))) {}
diff --git a/lldb/tools/lldb-dap/BreakpointBase.h b/lldb/tools/lldb-dap/BreakpointBase.h
index cc808f396e99e2..bb660ddc451bd5 100644
--- a/lldb/tools/lldb-dap/BreakpointBase.h
+++ b/lldb/tools/lldb-dap/BreakpointBase.h
@@ -19,7 +19,7 @@ namespace lldb_dap {
struct BreakpointBase {
// Associated DAP session.
- DAP *dap;
+ DAP &dap;
// An optional expression for conditional breakpoints.
std::string condition;
@@ -27,9 +27,8 @@ struct BreakpointBase {
// ignored. The backend is expected to interpret the expression as needed
std::string hitCondition;
- BreakpointBase(DAP *d) : dap(d) {}
- BreakpointBase(DAP *d, const llvm::json::Object &obj);
- BreakpointBase() = default;
+ BreakpointBase(DAP &d) : dap(d) {}
+ BreakpointBase(DAP &d, const llvm::json::Object &obj);
BreakpointBase(const BreakpointBase &) = default;
virtual ~BreakpointBase() = default;
diff --git a/lldb/tools/lldb-dap/FunctionBreakpoint.cpp b/lldb/tools/lldb-dap/FunctionBreakpoint.cpp
index e35dc2dc2ca4b8..ef6df6c0dc91cc 100644
--- a/lldb/tools/lldb-dap/FunctionBreakpoint.cpp
+++ b/lldb/tools/lldb-dap/FunctionBreakpoint.cpp
@@ -13,13 +13,13 @@
namespace lldb_dap {
-FunctionBreakpoint::FunctionBreakpoint(DAP *d, const llvm::json::Object &obj)
+FunctionBreakpoint::FunctionBreakpoint(DAP &d, const llvm::json::Object &obj)
: Breakpoint(d, obj), functionName(std::string(GetString(obj, "name"))) {}
void FunctionBreakpoint::SetBreakpoint() {
- if (functionName.empty() || !dap)
+ if (functionName.empty())
return;
- bp = 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 a66acf8b1945e9..93f0b93b35291d 100644
--- a/lldb/tools/lldb-dap/FunctionBreakpoint.h
+++ b/lldb/tools/lldb-dap/FunctionBreakpoint.h
@@ -17,7 +17,7 @@ namespace lldb_dap {
struct FunctionBreakpoint : public Breakpoint {
std::string functionName;
- FunctionBreakpoint(DAP *dap, 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 cd4f719ff13517..d6ccb2dfc29111 100644
--- a/lldb/tools/lldb-dap/InstructionBreakpoint.cpp
+++ b/lldb/tools/lldb-dap/InstructionBreakpoint.cpp
@@ -15,7 +15,7 @@
namespace lldb_dap {
// Instruction Breakpoint
-InstructionBreakpoint::InstructionBreakpoint(DAP *d,
+InstructionBreakpoint::InstructionBreakpoint(DAP &d,
const llvm::json::Object &obj)
: Breakpoint(d, obj), instructionAddressReference(LLDB_INVALID_ADDRESS),
id(0), offset(GetSigned(obj, "offset", 0)) {
@@ -25,9 +25,7 @@ InstructionBreakpoint::InstructionBreakpoint(DAP *d,
}
void InstructionBreakpoint::SetInstructionBreakpoint() {
- if (!dap)
- return;
- bp = dap->target.BreakpointCreateByAddress(instructionAddressReference);
+ bp = dap.target.BreakpointCreateByAddress(instructionAddressReference);
id = bp.GetID();
}
diff --git a/lldb/tools/lldb-dap/InstructionBreakpoint.h b/lldb/tools/lldb-dap/InstructionBreakpoint.h
index 8c5249f9e0825f..d7f3fb8ad3a4eb 100644
--- a/lldb/tools/lldb-dap/InstructionBreakpoint.h
+++ b/lldb/tools/lldb-dap/InstructionBreakpoint.h
@@ -26,10 +26,10 @@ struct InstructionBreakpoint : public Breakpoint {
int32_t id;
int32_t offset;
- InstructionBreakpoint(DAP *d)
+ InstructionBreakpoint(DAP &d)
: Breakpoint(d), instructionAddressReference(LLDB_INVALID_ADDRESS), id(0),
offset(0) {}
- InstructionBreakpoint(DAP *d, const llvm::json::Object &obj);
+ InstructionBreakpoint(DAP &d, const llvm::json::Object &obj);
// Set instruction breakpoint in LLDB as a new breakpoint
void SetInstructionBreakpoint();
diff --git a/lldb/tools/lldb-dap/SourceBreakpoint.cpp b/lldb/tools/lldb-dap/SourceBreakpoint.cpp
index f4803c82cb34b1..364fe3cefb2a6a 100644
--- a/lldb/tools/lldb-dap/SourceBreakpoint.cpp
+++ b/lldb/tools/lldb-dap/SourceBreakpoint.cpp
@@ -12,18 +12,15 @@
namespace lldb_dap {
-SourceBreakpoint::SourceBreakpoint(DAP *dap, const llvm::json::Object &obj)
+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) {
- if (!dap)
- return;
-
lldb::SBFileSpecList module_list;
- bp = dap->target.BreakpointCreateByLocation(source_path.str().c_str(), line,
+ bp = dap.target.BreakpointCreateByLocation(source_path.str().c_str(), line,
column, 0, module_list);
if (!logMessage.empty())
SetLogMessage();
@@ -281,19 +278,16 @@ void SourceBreakpoint::SetLogMessage() {
}
void SourceBreakpoint::NotifyLogMessageError(llvm::StringRef error) {
- if (!dap)
- return;
-
std::string message = "Log message has error: ";
message += error;
- dap->SendOutput(OutputType::Console, message);
+ dap.SendOutput(OutputType::Console, message);
}
/*static*/
bool SourceBreakpoint::BreakpointHitCallback(
void *baton, lldb::SBProcess &process, lldb::SBThread &thread,
lldb::SBBreakpointLocation &location) {
- if (!baton || !(static_cast<SourceBreakpoint *>(baton)->dap))
+ if (!baton)
return true;
SourceBreakpoint *bp = (SourceBreakpoint *)baton;
@@ -312,7 +306,7 @@ bool SourceBreakpoint::BreakpointHitCallback(
if (value.GetError().Fail())
value = frame.EvaluateExpression(expr);
output +=
- VariableDescription(value, bp->dap->enable_auto_variable_summaries)
+ VariableDescription(value, bp->dap.enable_auto_variable_summaries)
.display_value;
} else {
output += messagePart.text;
@@ -320,7 +314,7 @@ bool SourceBreakpoint::BreakpointHitCallback(
}
if (!output.empty() && output.back() != '\n')
output.push_back('\n'); // Ensure log message has line break.
- bp->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 e7e661803156dd..113c0efbddcc5c 100644
--- a/lldb/tools/lldb-dap/SourceBreakpoint.h
+++ b/lldb/tools/lldb-dap/SourceBreakpoint.h
@@ -31,7 +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(DAP *d, 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 c11cfa0375cc8c..56dd5434269151 100644
--- a/lldb/tools/lldb-dap/Watchpoint.cpp
+++ b/lldb/tools/lldb-dap/Watchpoint.cpp
@@ -14,7 +14,7 @@
#include "JSONUtils.h"
namespace lldb_dap {
-Watchpoint::Watchpoint(DAP *d, const llvm::json::Object &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();
@@ -45,10 +45,7 @@ void Watchpoint::CreateJsonObject(llvm::json::Object &object) {
}
void Watchpoint::SetWatchpoint() {
- if (!dap)
- return;
-
- wp = 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 f32034bba24997..b84b4b1c9ff3d3 100644
--- a/lldb/tools/lldb-dap/Watchpoint.h
+++ b/lldb/tools/lldb-dap/Watchpoint.h
@@ -25,8 +25,8 @@ struct Watchpoint : public BreakpointBase {
lldb::SBWatchpoint wp;
lldb::SBError error;
- Watchpoint(DAP *dap, const llvm::json::Object &obj);
- Watchpoint(DAP *d, lldb::SBWatchpoint wp) : BreakpointBase(d), 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 d7fa657cd7e1fe..4c577546f52ff3 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -734,7 +734,7 @@ void EventThreadFunction(DAP &dap) {
auto event_type =
lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(event);
auto bp = Breakpoint(
- &dap, lldb::SBBreakpoint::GetBreakpointFromEvent(event));
+ 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
@@ -2944,7 +2944,7 @@ void request_setBreakpoints(DAP &dap, const llvm::json::Object &request) {
for (const auto &bp : *breakpoints) {
const auto *bp_obj = bp.getAsObject();
if (bp_obj) {
- SourceBreakpoint src_bp(&dap, *bp_obj);
+ SourceBreakpoint src_bp(dap, *bp_obj);
request_bps.try_emplace(src_bp.line, src_bp);
const auto [kv, inserted] =
dap.source_breakpoints[path].try_emplace(src_bp.line, src_bp);
@@ -3149,7 +3149,7 @@ void request_setFunctionBreakpoints(DAP &dap,
const auto *bp_obj = value.getAsObject();
if (bp_obj == nullptr)
continue;
- FunctionBreakpoint func_bp(&dap, *bp_obj);
+ FunctionBreakpoint func_bp(dap, *bp_obj);
request_bps.try_emplace(func_bp.functionName, std::move(func_bp));
}
@@ -3437,8 +3437,7 @@ void request_setDataBreakpoints(DAP &dap, const llvm::json::Object &request) {
for (const auto &bp : *breakpoints) {
const auto *bp_obj = bp.getAsObject();
if (bp_obj) {
- Watchpoint wp(&dap, *bp_obj);
- watchpoints.push_back(wp);
+ watchpoints.emplace_back(dap, *bp_obj);
}
}
}
@@ -4790,7 +4789,7 @@ void request__testGetTargetBreakpoints(DAP &dap,
FillResponse(request, response);
llvm::json::Array response_breakpoints;
for (uint32_t i = 0; dap.target.GetBreakpointAtIndex(i).IsValid(); ++i) {
- auto bp = Breakpoint(&dap, dap.target.GetBreakpointAtIndex(i));
+ auto bp = Breakpoint(dap, dap.target.GetBreakpointAtIndex(i));
AppendBreakpoint(bp, response_breakpoints);
}
llvm::json::Object body;
@@ -5004,7 +5003,7 @@ void request_setInstructionBreakpoints(DAP &dap,
const auto *bp_obj = bp.getAsObject();
if (bp_obj) {
// Read instruction breakpoint request.
- InstructionBreakpoint inst_bp(&dap, *bp_obj);
+ InstructionBreakpoint inst_bp(dap, *bp_obj);
// Store them into map for reference.
request_ibp.try_emplace(inst_bp.instructionAddressReference,
std::move(inst_bp));
>From ec85f0f7397c4790442cf99cd51ce61510da498f Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Tue, 5 Nov 2024 17:56:19 -0800
Subject: [PATCH 5/5] Adjust the naming of the test
dap_server.DebugAdaptorServer to be more consistent.
---
.../test/tools/lldb-dap/dap_server.py | 39 ++++++++++---------
.../test/tools/lldb-dap/lldbdap_testcase.py | 14 ++++---
.../tools/lldb-dap/server/TestDAP_server.py | 13 +++----
3 files changed, 34 insertions(+), 32 deletions(-)
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
index 31819328040aca..72287ab46a7797 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
@@ -1155,7 +1155,8 @@ def __init__(
self,
executable=None,
launch=True,
- connect=None,
+ port=None,
+ unix_socket=None,
init_commands=[],
log_file=None,
env=None,
@@ -1163,17 +1164,18 @@ def __init__(
self.process = None
if launch:
self.process = DebugAdaptorServer.launch(
- executable, connect=connect, log_file=log_file, env=env
+ executable, port=port, unix_socket=unix_socket, log_file=log_file, env=env
)
- if connect:
- if isinstance(connect, str) and connect.startswith("/"):
- s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- s.connect(connect)
- else:
- port = int(connect)
- s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- s.connect(("127.0.0.1", port))
+ if port:
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.connect(("127.0.0.1", port))
+ DebugCommunication.__init__(
+ self, s.makefile("rb"), s.makefile("wb"), init_commands, log_file
+ )
+ elif unix_socket:
+ s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ s.connect(unix_socket)
DebugCommunication.__init__(
self, s.makefile("rb"), s.makefile("wb"), init_commands, log_file
)
@@ -1196,7 +1198,7 @@ def terminate(self):
@classmethod
def launch(
- cls, executable: str, /, connect=None, log_file=None, env=None
+ cls, executable: str, /, port=None, unix_socket=None, log_file=None, env=None
) -> subprocess.Popen:
adaptor_env = os.environ.copy()
if env:
@@ -1206,13 +1208,12 @@ def launch(
adaptor_env["LLDBDAP_LOG"] = log_file
args = [executable]
- if connect:
- if isinstance(connect, str) and connect.startswith("/"):
- args.append("--unix-socket")
- args.append(connect)
- else:
- args.append("--port")
- args.append(str(connect))
+ if port:
+ args.append("--port")
+ args.append(str(port))
+ elif unix_socket:
+ args.append("--unix-socket")
+ args.append(unix_socket)
proc = subprocess.Popen(
args,
@@ -1222,7 +1223,7 @@ def launch(
env=adaptor_env,
)
- if connect:
+ if port or unix_socket:
# Wait for the server to startup.
time.sleep(0.1)
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 39d200a2ac566e..1f7d6e4e21e310 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
@@ -13,7 +13,7 @@ class DAPTestCaseBase(TestBase):
timeoutval = 10 * (10 if ('ASAN_OPTIONS' in os.environ) else 1)
NO_DEBUG_INFO_TESTCASE = True
- def create_debug_adaptor(self, env=None, launch=True, connect=None):
+ def create_debug_adaptor(self, env=None, launch=True, port=None, unix_socket=None):
"""Create the Visual Studio Code debug adaptor"""
self.assertTrue(
is_exe(self.lldbDAPExec), "lldb-dap must exist and be executable"
@@ -22,18 +22,19 @@ def create_debug_adaptor(self, env=None, launch=True, connect=None):
self.dap_server = dap_server.DebugAdaptorServer(
executable=self.lldbDAPExec,
launch=launch,
- connect=connect,
+ port=port,
+ unix_socket=unix_socket,
init_commands=self.setUpCommands(),
log_file=log_file_path,
env=env,
)
def build_and_create_debug_adaptor(
- self, lldbDAPEnv=None, lldbDAPLaunch=True, lldbDAPConnect=None
+ self, lldbDAPEnv=None, lldbDAPLaunch=True, lldbDAPPort=None, lldbDAPUnixSocket=None
):
self.build()
self.create_debug_adaptor(
- lldbDAPEnv, launch=lldbDAPLaunch, connect=lldbDAPConnect
+ lldbDAPEnv, launch=lldbDAPLaunch, port=lldbDAPPort, unix_socket=lldbDAPUnixSocket
)
def set_source_breakpoints(self, source_path, lines, data=None):
@@ -481,13 +482,14 @@ def build_and_launch(
customThreadFormat=None,
launchCommands=None,
expectFailure=False,
- lldbDAPConnect=None,
+ lldbDAPPort=None,
+ lldbDAPUnixSocket=None,
lldbDAPLaunch=True,
):
"""Build the default Makefile target, create the DAP debug adaptor,
and launch the process.
"""
- self.build_and_create_debug_adaptor(lldbDAPEnv, lldbDAPLaunch, lldbDAPConnect)
+ self.build_and_create_debug_adaptor(lldbDAPEnv=lldbDAPEnv, lldbDAPLaunch=lldbDAPLaunch, lldbDAPPort=lldbDAPPort, lldbDAPUnixSocket=lldbDAPUnixSocket)
self.assertTrue(os.path.exists(program), "executable must exist")
return self.launch(
diff --git a/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py b/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py
index ae7094b096c4d7..46b992a77a4815 100644
--- a/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py
+++ b/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py
@@ -12,11 +12,10 @@
class TestDAP_server(lldbdap_testcase.DAPTestCaseBase):
- def do_test_server(self, connect):
+ def do_test_server(self, port=None, unix_socket=None):
log_file_path = self.getBuildArtifact("dap.txt")
- print("connect", connect)
server = dap_server.DebugAdaptorServer.launch(
- self.lldbDAPExec, connect=connect, log_file=log_file_path
+ self.lldbDAPExec, port=port, unix_socket=unix_socket, log_file=log_file_path
)
def cleanup():
@@ -31,7 +30,7 @@ def cleanup():
breakpoint_line = line_number(source, "// breakpoint")
# Initial connection over the port.
- self.create_debug_adaptor(launch=False, connect=connect)
+ self.create_debug_adaptor(launch=False, port=port, unix_socket=unix_socket)
self.launch(
program,
disconnectAutomatically=False,
@@ -44,7 +43,7 @@ def cleanup():
self.dap_server.request_disconnect()
# Second connection over the port.
- self.create_debug_adaptor(launch=False, connect=connect)
+ self.create_debug_adaptor(launch=False, port=port, unix_socket=unix_socket)
self.launch(program)
self.set_source_breakpoints(source, [breakpoint_line])
self.continue_to_next_stop()
@@ -57,11 +56,11 @@ def test_server_port(self):
Test launching a binary with a lldb-dap in server mode on a specific port.
"""
port = pickrandomport()
- self.do_test_server(port)
+ self.do_test_server(port=port)
def test_server_unix_socket(self):
"""
Test launching a binary with a lldb-dap in server mode on a unix socket.
"""
dir = tempfile.gettempdir()
- self.do_test_server(dir + "/dap-connection-" + str(os.getpid()))
+ self.do_test_server(unix_socket=dir + "/dap-connection-" + str(os.getpid()))
More information about the lldb-commits
mailing list