[lldb-dap] Refactoring lldb-dap 'launch' request to use typed RequestHandler<>. (#133624)
This converts a number of json::Value's into well defined types that are used throughout lldb-dap and updates the 'launch' command to use the new well defined types. --------- Co-authored-by: Jonas Devlieghere <jonas@devlieghere.com>
This commit is contained in:
parent
785ab45c2b
commit
a0aa5f8933
@ -860,7 +860,8 @@ class DebugCommunication(object):
|
|||||||
args_dict["enableAutoVariableSummaries"] = enableAutoVariableSummaries
|
args_dict["enableAutoVariableSummaries"] = enableAutoVariableSummaries
|
||||||
args_dict["enableSyntheticChildDebugging"] = enableSyntheticChildDebugging
|
args_dict["enableSyntheticChildDebugging"] = enableSyntheticChildDebugging
|
||||||
args_dict["displayExtendedBacktrace"] = displayExtendedBacktrace
|
args_dict["displayExtendedBacktrace"] = displayExtendedBacktrace
|
||||||
args_dict["commandEscapePrefix"] = commandEscapePrefix
|
if commandEscapePrefix:
|
||||||
|
args_dict["commandEscapePrefix"] = commandEscapePrefix
|
||||||
command_dict = {"command": "launch", "type": "request", "arguments": args_dict}
|
command_dict = {"command": "launch", "type": "request", "arguments": args_dict}
|
||||||
response = self.send_recv(command_dict)
|
response = self.send_recv(command_dict)
|
||||||
|
|
||||||
|
@ -453,7 +453,8 @@ class DAPTestCaseBase(TestBase):
|
|||||||
|
|
||||||
if not (response and response["success"]):
|
if not (response and response["success"]):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
response["success"], "launch failed (%s)" % (response["message"])
|
response["success"],
|
||||||
|
"launch failed (%s)" % (response["body"]["error"]["format"]),
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -27,6 +27,34 @@ class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase):
|
|||||||
lines = output.splitlines()
|
lines = output.splitlines()
|
||||||
self.assertIn(program, lines[0], "make sure program path is in first argument")
|
self.assertIn(program, lines[0], "make sure program path is in first argument")
|
||||||
|
|
||||||
|
def test_failing_launch_program(self):
|
||||||
|
"""
|
||||||
|
Tests launching with an invalid program.
|
||||||
|
"""
|
||||||
|
program = self.getBuildArtifact("a.out")
|
||||||
|
self.create_debug_adapter()
|
||||||
|
response = self.launch(program, expectFailure=True)
|
||||||
|
self.assertFalse(response["success"])
|
||||||
|
self.assertEqual(
|
||||||
|
"'{0}' does not exist".format(program), response["body"]["error"]["format"]
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_failing_launch_commands_and_run_in_terminal(self):
|
||||||
|
"""
|
||||||
|
Tests launching with an invalid program.
|
||||||
|
"""
|
||||||
|
program = self.getBuildArtifact("a.out")
|
||||||
|
self.create_debug_adapter()
|
||||||
|
response = self.launch(
|
||||||
|
program, launchCommands=["a b c"], runInTerminal=True, expectFailure=True
|
||||||
|
)
|
||||||
|
self.assertFalse(response["success"])
|
||||||
|
self.assertTrue(self.get_dict_value(response, ["body", "error", "showUser"]))
|
||||||
|
self.assertEqual(
|
||||||
|
"launchCommands and runInTerminal are mutually exclusive",
|
||||||
|
self.get_dict_value(response, ["body", "error", "format"]),
|
||||||
|
)
|
||||||
|
|
||||||
@skipIfWindows
|
@skipIfWindows
|
||||||
def test_termination(self):
|
def test_termination(self):
|
||||||
"""
|
"""
|
||||||
@ -42,7 +70,9 @@ class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase):
|
|||||||
self.dap_server.request_disconnect()
|
self.dap_server.request_disconnect()
|
||||||
|
|
||||||
# Wait until the underlying lldb-dap process dies.
|
# Wait until the underlying lldb-dap process dies.
|
||||||
self.dap_server.process.wait(timeout=lldbdap_testcase.DAPTestCaseBase.timeoutval)
|
self.dap_server.process.wait(
|
||||||
|
timeout=lldbdap_testcase.DAPTestCaseBase.timeoutval
|
||||||
|
)
|
||||||
|
|
||||||
# Check the return code
|
# Check the return code
|
||||||
self.assertEqual(self.dap_server.process.poll(), 0)
|
self.assertEqual(self.dap_server.process.poll(), 0)
|
||||||
@ -460,7 +490,7 @@ class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase):
|
|||||||
|
|
||||||
self.assertFalse(response["success"])
|
self.assertFalse(response["success"])
|
||||||
self.assertRegex(
|
self.assertRegex(
|
||||||
response["message"],
|
response["body"]["error"]["format"],
|
||||||
r"Failed to run launch commands\. See the Debug Console for more details",
|
r"Failed to run launch commands\. See the Debug Console for more details",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -660,9 +660,7 @@ void DAP::RunTerminateCommands() {
|
|||||||
configuration.terminateCommands);
|
configuration.terminateCommands);
|
||||||
}
|
}
|
||||||
|
|
||||||
lldb::SBTarget
|
lldb::SBTarget DAP::CreateTarget(lldb::SBError &error) {
|
||||||
DAP::CreateTargetFromArguments(const llvm::json::Object &arguments,
|
|
||||||
lldb::SBError &error) {
|
|
||||||
// Grab the name of the program we need to debug and create a target using
|
// Grab the name of the program we need to debug and create a target using
|
||||||
// the given program as an argument. Executable file can be a source of target
|
// the given program as an argument. Executable file can be a source of target
|
||||||
// architecture and platform, if they differ from the host. Setting exe path
|
// architecture and platform, if they differ from the host. Setting exe path
|
||||||
@ -671,25 +669,15 @@ DAP::CreateTargetFromArguments(const llvm::json::Object &arguments,
|
|||||||
// creation. We also use target triple and platform from the launch
|
// creation. We also use target triple and platform from the launch
|
||||||
// configuration, if given, since in some cases ELF file doesn't contain
|
// configuration, if given, since in some cases ELF file doesn't contain
|
||||||
// enough information to determine correct arch and platform (or ELF can be
|
// enough information to determine correct arch and platform (or ELF can be
|
||||||
// omitted at all), so it is good to leave the user an apportunity to specify
|
// omitted at all), so it is good to leave the user an opportunity to specify
|
||||||
// those. Any of those three can be left empty.
|
// those. Any of those three can be left empty.
|
||||||
const llvm::StringRef target_triple =
|
|
||||||
GetString(arguments, "targetTriple").value_or("");
|
|
||||||
const llvm::StringRef platform_name =
|
|
||||||
GetString(arguments, "platformName").value_or("");
|
|
||||||
const llvm::StringRef program = GetString(arguments, "program").value_or("");
|
|
||||||
auto target = this->debugger.CreateTarget(
|
auto target = this->debugger.CreateTarget(
|
||||||
program.data(), target_triple.data(), platform_name.data(),
|
configuration.program.value_or("").data(),
|
||||||
|
configuration.targetTriple.value_or("").data(),
|
||||||
|
configuration.platformName.value_or("").data(),
|
||||||
true, // Add dependent modules.
|
true, // Add dependent modules.
|
||||||
error);
|
error);
|
||||||
|
|
||||||
if (error.Fail()) {
|
|
||||||
// Update message if there was an error.
|
|
||||||
error.SetErrorStringWithFormat(
|
|
||||||
"Could not create a target for a program '%s': %s.", program.data(),
|
|
||||||
error.GetCString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -945,7 +933,7 @@ llvm::Error DAP::Loop() {
|
|||||||
return queue_reader.get();
|
return queue_reader.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
lldb::SBError DAP::WaitForProcessToStop(uint32_t seconds) {
|
lldb::SBError DAP::WaitForProcessToStop(std::chrono::seconds seconds) {
|
||||||
lldb::SBError error;
|
lldb::SBError error;
|
||||||
lldb::SBProcess process = target.GetProcess();
|
lldb::SBProcess process = target.GetProcess();
|
||||||
if (!process.IsValid()) {
|
if (!process.IsValid()) {
|
||||||
@ -980,8 +968,8 @@ lldb::SBError DAP::WaitForProcessToStop(uint32_t seconds) {
|
|||||||
}
|
}
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds(250));
|
std::this_thread::sleep_for(std::chrono::microseconds(250));
|
||||||
}
|
}
|
||||||
error.SetErrorStringWithFormat("process failed to stop within %u seconds",
|
error.SetErrorStringWithFormat("process failed to stop within %lld seconds",
|
||||||
seconds);
|
seconds.count());
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1178,6 +1166,36 @@ bool SendEventRequestHandler::DoExecute(lldb::SBDebugger debugger,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DAP::ConfigureSourceMaps() {
|
||||||
|
if (configuration.sourceMap.empty() && !configuration.sourcePath)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string sourceMapCommand;
|
||||||
|
llvm::raw_string_ostream strm(sourceMapCommand);
|
||||||
|
strm << "settings set target.source-map ";
|
||||||
|
|
||||||
|
if (!configuration.sourceMap.empty()) {
|
||||||
|
for (const auto &kv : configuration.sourceMap) {
|
||||||
|
strm << "\"" << kv.first << "\" \"" << kv.second << "\" ";
|
||||||
|
}
|
||||||
|
} else if (configuration.sourcePath) {
|
||||||
|
strm << "\".\" \"" << *configuration.sourcePath << "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
RunLLDBCommands("Setting source map:", {sourceMapCommand});
|
||||||
|
}
|
||||||
|
|
||||||
|
void DAP::SetConfiguration(const protocol::Configuration &config,
|
||||||
|
bool is_attach) {
|
||||||
|
configuration = config;
|
||||||
|
this->is_attach = is_attach;
|
||||||
|
|
||||||
|
if (configuration.customFrameFormat)
|
||||||
|
SetFrameFormat(*configuration.customFrameFormat);
|
||||||
|
if (configuration.customThreadFormat)
|
||||||
|
SetThreadFormat(*configuration.customThreadFormat);
|
||||||
|
}
|
||||||
|
|
||||||
void DAP::SetFrameFormat(llvm::StringRef format) {
|
void DAP::SetFrameFormat(llvm::StringRef format) {
|
||||||
if (format.empty())
|
if (format.empty())
|
||||||
return;
|
return;
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
#include "lldb/lldb-types.h"
|
#include "lldb/lldb-types.h"
|
||||||
#include "llvm/ADT/DenseMap.h"
|
#include "llvm/ADT/DenseMap.h"
|
||||||
#include "llvm/ADT/DenseSet.h"
|
#include "llvm/ADT/DenseSet.h"
|
||||||
|
#include "llvm/ADT/FunctionExtras.h"
|
||||||
#include "llvm/ADT/SmallSet.h"
|
#include "llvm/ADT/SmallSet.h"
|
||||||
#include "llvm/ADT/StringMap.h"
|
#include "llvm/ADT/StringMap.h"
|
||||||
#include "llvm/ADT/StringRef.h"
|
#include "llvm/ADT/StringRef.h"
|
||||||
@ -175,9 +176,9 @@ struct DAP {
|
|||||||
llvm::once_flag init_exception_breakpoints_flag;
|
llvm::once_flag init_exception_breakpoints_flag;
|
||||||
// Map step in target id to list of function targets that user can choose.
|
// Map step in target id to list of function targets that user can choose.
|
||||||
llvm::DenseMap<lldb::addr_t, std::string> step_in_targets;
|
llvm::DenseMap<lldb::addr_t, std::string> step_in_targets;
|
||||||
// A copy of the last LaunchRequest or AttachRequest so we can reuse its
|
// A copy of the last LaunchRequest so we can reuse its arguments if we get a
|
||||||
// arguments if we get a RestartRequest.
|
// RestartRequest. Restarting an AttachRequest is not supported.
|
||||||
std::optional<llvm::json::Object> last_launch_or_attach_request;
|
std::optional<protocol::LaunchRequestArguments> last_launch_request;
|
||||||
lldb::tid_t focus_tid;
|
lldb::tid_t focus_tid;
|
||||||
bool disconnecting = false;
|
bool disconnecting = false;
|
||||||
llvm::once_flag terminated_event_flag;
|
llvm::once_flag terminated_event_flag;
|
||||||
@ -199,7 +200,6 @@ struct DAP {
|
|||||||
llvm::SmallDenseMap<int64_t, std::unique_ptr<ResponseHandler>>
|
llvm::SmallDenseMap<int64_t, std::unique_ptr<ResponseHandler>>
|
||||||
inflight_reverse_requests;
|
inflight_reverse_requests;
|
||||||
ReplMode repl_mode;
|
ReplMode repl_mode;
|
||||||
|
|
||||||
lldb::SBFormat frame_format;
|
lldb::SBFormat frame_format;
|
||||||
lldb::SBFormat thread_format;
|
lldb::SBFormat thread_format;
|
||||||
// This is used to allow request_evaluate to handle empty expressions
|
// This is used to allow request_evaluate to handle empty expressions
|
||||||
@ -248,6 +248,12 @@ struct DAP {
|
|||||||
/// Stop event handler threads.
|
/// Stop event handler threads.
|
||||||
void StopEventHandlers();
|
void StopEventHandlers();
|
||||||
|
|
||||||
|
/// Configures the debug adapter for launching/attaching.
|
||||||
|
void SetConfiguration(const protocol::Configuration &confing, bool is_attach);
|
||||||
|
|
||||||
|
/// Configure source maps based on the current `DAPConfiguration`.
|
||||||
|
void ConfigureSourceMaps();
|
||||||
|
|
||||||
/// Serialize the JSON value into a string and send the JSON packet to the
|
/// Serialize the JSON value into a string and send the JSON packet to the
|
||||||
/// "out" stream.
|
/// "out" stream.
|
||||||
void SendJSON(const llvm::json::Value &json);
|
void SendJSON(const llvm::json::Value &json);
|
||||||
@ -311,8 +317,6 @@ struct DAP {
|
|||||||
void RunTerminateCommands();
|
void RunTerminateCommands();
|
||||||
|
|
||||||
/// Create a new SBTarget object from the given request arguments.
|
/// Create a new SBTarget object from the given request arguments.
|
||||||
/// \param[in] arguments
|
|
||||||
/// Launch configuration arguments.
|
|
||||||
///
|
///
|
||||||
/// \param[out] error
|
/// \param[out] error
|
||||||
/// An SBError object that will contain an error description if
|
/// An SBError object that will contain an error description if
|
||||||
@ -320,8 +324,7 @@ struct DAP {
|
|||||||
///
|
///
|
||||||
/// \return
|
/// \return
|
||||||
/// An SBTarget object.
|
/// An SBTarget object.
|
||||||
lldb::SBTarget CreateTargetFromArguments(const llvm::json::Object &arguments,
|
lldb::SBTarget CreateTarget(lldb::SBError &error);
|
||||||
lldb::SBError &error);
|
|
||||||
|
|
||||||
/// Set given target object as a current target for lldb-dap and start
|
/// Set given target object as a current target for lldb-dap and start
|
||||||
/// listeing for its breakpoint events.
|
/// listeing for its breakpoint events.
|
||||||
@ -395,7 +398,7 @@ struct DAP {
|
|||||||
/// The number of seconds to poll the process to wait until it is stopped.
|
/// The number of seconds to poll the process to wait until it is stopped.
|
||||||
///
|
///
|
||||||
/// \return Error if waiting for the process fails, no error if succeeds.
|
/// \return Error if waiting for the process fails, no error if succeeds.
|
||||||
lldb::SBError WaitForProcessToStop(uint32_t seconds);
|
lldb::SBError WaitForProcessToStop(std::chrono::seconds seconds);
|
||||||
|
|
||||||
void SetFrameFormat(llvm::StringRef format);
|
void SetFrameFormat(llvm::StringRef format);
|
||||||
|
|
||||||
|
@ -43,10 +43,8 @@ namespace lldb_dap {
|
|||||||
// acknowledgement, so no body field is required."
|
// acknowledgement, so no body field is required."
|
||||||
// }]
|
// }]
|
||||||
// }
|
// }
|
||||||
|
|
||||||
void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
|
void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
|
||||||
dap.is_attach = true;
|
dap.is_attach = true;
|
||||||
dap.last_launch_or_attach_request = request;
|
|
||||||
llvm::json::Object response;
|
llvm::json::Object response;
|
||||||
lldb::SBError error;
|
lldb::SBError error;
|
||||||
FillResponse(request, response);
|
FillResponse(request, response);
|
||||||
@ -65,6 +63,7 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
|
|||||||
attach_info.SetWaitForLaunch(wait_for, false /*async*/);
|
attach_info.SetWaitForLaunch(wait_for, false /*async*/);
|
||||||
dap.configuration.initCommands = GetStrings(arguments, "initCommands");
|
dap.configuration.initCommands = GetStrings(arguments, "initCommands");
|
||||||
dap.configuration.preRunCommands = GetStrings(arguments, "preRunCommands");
|
dap.configuration.preRunCommands = GetStrings(arguments, "preRunCommands");
|
||||||
|
dap.configuration.postRunCommands = GetStrings(arguments, "postRunCommands");
|
||||||
dap.configuration.stopCommands = GetStrings(arguments, "stopCommands");
|
dap.configuration.stopCommands = GetStrings(arguments, "stopCommands");
|
||||||
dap.configuration.exitCommands = GetStrings(arguments, "exitCommands");
|
dap.configuration.exitCommands = GetStrings(arguments, "exitCommands");
|
||||||
dap.configuration.terminateCommands =
|
dap.configuration.terminateCommands =
|
||||||
@ -76,7 +75,6 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
|
|||||||
dap.stop_at_entry = core_file.empty()
|
dap.stop_at_entry = core_file.empty()
|
||||||
? GetBoolean(arguments, "stopOnEntry").value_or(false)
|
? GetBoolean(arguments, "stopOnEntry").value_or(false)
|
||||||
: true;
|
: true;
|
||||||
dap.configuration.postRunCommands = GetStrings(arguments, "postRunCommands");
|
|
||||||
const llvm::StringRef debuggerRoot =
|
const llvm::StringRef debuggerRoot =
|
||||||
GetString(arguments, "debuggerRoot").value_or("");
|
GetString(arguments, "debuggerRoot").value_or("");
|
||||||
dap.configuration.enableAutoVariableSummaries =
|
dap.configuration.enableAutoVariableSummaries =
|
||||||
@ -87,6 +85,9 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
|
|||||||
GetBoolean(arguments, "displayExtendedBacktrace").value_or(false);
|
GetBoolean(arguments, "displayExtendedBacktrace").value_or(false);
|
||||||
dap.configuration.commandEscapePrefix =
|
dap.configuration.commandEscapePrefix =
|
||||||
GetString(arguments, "commandEscapePrefix").value_or("`");
|
GetString(arguments, "commandEscapePrefix").value_or("`");
|
||||||
|
dap.configuration.program = GetString(arguments, "program");
|
||||||
|
dap.configuration.targetTriple = GetString(arguments, "targetTriple");
|
||||||
|
dap.configuration.platformName = GetString(arguments, "platformName");
|
||||||
dap.SetFrameFormat(GetString(arguments, "customFrameFormat").value_or(""));
|
dap.SetFrameFormat(GetString(arguments, "customFrameFormat").value_or(""));
|
||||||
dap.SetThreadFormat(GetString(arguments, "customThreadFormat").value_or(""));
|
dap.SetThreadFormat(GetString(arguments, "customThreadFormat").value_or(""));
|
||||||
|
|
||||||
@ -110,7 +111,7 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
|
|||||||
SetSourceMapFromArguments(*arguments);
|
SetSourceMapFromArguments(*arguments);
|
||||||
|
|
||||||
lldb::SBError status;
|
lldb::SBError status;
|
||||||
dap.SetTarget(dap.CreateTargetFromArguments(*arguments, status));
|
dap.SetTarget(dap.CreateTarget(status));
|
||||||
if (status.Fail()) {
|
if (status.Fail()) {
|
||||||
response["success"] = llvm::json::Value(false);
|
response["success"] = llvm::json::Value(false);
|
||||||
EmplaceSafeString(response, "message", status.GetCString());
|
EmplaceSafeString(response, "message", status.GetCString());
|
||||||
@ -180,7 +181,7 @@ void AttachRequestHandler::operator()(const llvm::json::Object &request) const {
|
|||||||
|
|
||||||
// Make sure the process is attached and stopped before proceeding as the
|
// Make sure the process is attached and stopped before proceeding as the
|
||||||
// the launch commands are not run using the synchronous mode.
|
// the launch commands are not run using the synchronous mode.
|
||||||
error = dap.WaitForProcessToStop(timeout_seconds);
|
error = dap.WaitForProcessToStop(std::chrono::seconds(timeout_seconds));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error.Success() && core_file.empty()) {
|
if (error.Success() && core_file.empty()) {
|
||||||
|
@ -9,71 +9,26 @@
|
|||||||
#include "DAP.h"
|
#include "DAP.h"
|
||||||
#include "EventHelper.h"
|
#include "EventHelper.h"
|
||||||
#include "JSONUtils.h"
|
#include "JSONUtils.h"
|
||||||
|
#include "LLDBUtils.h"
|
||||||
|
#include "Protocol/ProtocolRequests.h"
|
||||||
#include "RequestHandler.h"
|
#include "RequestHandler.h"
|
||||||
|
#include "llvm/Support/Error.h"
|
||||||
#include "llvm/Support/FileSystem.h"
|
#include "llvm/Support/FileSystem.h"
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
using namespace lldb_dap::protocol;
|
||||||
|
|
||||||
namespace lldb_dap {
|
namespace lldb_dap {
|
||||||
|
|
||||||
// "LaunchRequest": {
|
/// Launch request; value of command field is 'launch'.
|
||||||
// "allOf": [ { "$ref": "#/definitions/Request" }, {
|
Error LaunchRequestHandler::Run(const LaunchRequestArguments &arguments) const {
|
||||||
// "type": "object",
|
dap.SetConfiguration(arguments.configuration, /*is_attach=*/false);
|
||||||
// "description": "Launch request; value of command field is 'launch'.",
|
dap.last_launch_request = arguments;
|
||||||
// "properties": {
|
dap.stop_at_entry = arguments.stopOnEntry;
|
||||||
// "command": {
|
|
||||||
// "type": "string",
|
if (!arguments.launchCommands.empty() && arguments.runInTerminal)
|
||||||
// "enum": [ "launch" ]
|
return make_error<DAPError>(
|
||||||
// },
|
"launchCommands and runInTerminal are mutually exclusive");
|
||||||
// "arguments": {
|
|
||||||
// "$ref": "#/definitions/LaunchRequestArguments"
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// "required": [ "command", "arguments" ]
|
|
||||||
// }]
|
|
||||||
// },
|
|
||||||
// "LaunchRequestArguments": {
|
|
||||||
// "type": "object",
|
|
||||||
// "description": "Arguments for 'launch' request.",
|
|
||||||
// "properties": {
|
|
||||||
// "noDebug": {
|
|
||||||
// "type": "boolean",
|
|
||||||
// "description": "If noDebug is true the launch request should launch
|
|
||||||
// the program without enabling debugging."
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// "LaunchResponse": {
|
|
||||||
// "allOf": [ { "$ref": "#/definitions/Response" }, {
|
|
||||||
// "type": "object",
|
|
||||||
// "description": "Response to 'launch' request. This is just an
|
|
||||||
// acknowledgement, so no body field is required."
|
|
||||||
// }]
|
|
||||||
// }
|
|
||||||
void LaunchRequestHandler::operator()(const llvm::json::Object &request) const {
|
|
||||||
dap.is_attach = false;
|
|
||||||
dap.last_launch_or_attach_request = request;
|
|
||||||
llvm::json::Object response;
|
|
||||||
FillResponse(request, response);
|
|
||||||
const auto *arguments = request.getObject("arguments");
|
|
||||||
dap.configuration.initCommands = GetStrings(arguments, "initCommands");
|
|
||||||
dap.configuration.preRunCommands = GetStrings(arguments, "preRunCommands");
|
|
||||||
dap.configuration.stopCommands = GetStrings(arguments, "stopCommands");
|
|
||||||
dap.configuration.exitCommands = GetStrings(arguments, "exitCommands");
|
|
||||||
dap.configuration.terminateCommands =
|
|
||||||
GetStrings(arguments, "terminateCommands");
|
|
||||||
dap.configuration.postRunCommands = GetStrings(arguments, "postRunCommands");
|
|
||||||
dap.stop_at_entry = GetBoolean(arguments, "stopOnEntry").value_or(false);
|
|
||||||
const llvm::StringRef debuggerRoot =
|
|
||||||
GetString(arguments, "debuggerRoot").value_or("");
|
|
||||||
dap.configuration.enableAutoVariableSummaries =
|
|
||||||
GetBoolean(arguments, "enableAutoVariableSummaries").value_or(false);
|
|
||||||
dap.configuration.enableSyntheticChildDebugging =
|
|
||||||
GetBoolean(arguments, "enableSyntheticChildDebugging").value_or(false);
|
|
||||||
dap.configuration.displayExtendedBacktrace =
|
|
||||||
GetBoolean(arguments, "displayExtendedBacktrace").value_or(false);
|
|
||||||
dap.configuration.commandEscapePrefix =
|
|
||||||
GetString(arguments, "commandEscapePrefix").value_or("`");
|
|
||||||
dap.SetFrameFormat(GetString(arguments, "customFrameFormat").value_or(""));
|
|
||||||
dap.SetThreadFormat(GetString(arguments, "customThreadFormat").value_or(""));
|
|
||||||
|
|
||||||
PrintWelcomeMessage();
|
PrintWelcomeMessage();
|
||||||
|
|
||||||
@ -81,55 +36,44 @@ void LaunchRequestHandler::operator()(const llvm::json::Object &request) const {
|
|||||||
// in the debug map of the main executable have relative paths which
|
// in the debug map of the main executable have relative paths which
|
||||||
// require the lldb-dap binary to have its working directory set to that
|
// require the lldb-dap binary to have its working directory set to that
|
||||||
// relative root for the .o files in order to be able to load debug info.
|
// relative root for the .o files in order to be able to load debug info.
|
||||||
if (!debuggerRoot.empty())
|
const std::string debugger_root = dap.configuration.debuggerRoot.value_or("");
|
||||||
llvm::sys::fs::set_current_path(debuggerRoot);
|
if (!debugger_root.empty())
|
||||||
|
sys::fs::set_current_path(debugger_root);
|
||||||
|
|
||||||
// Run any initialize LLDB commands the user specified in the launch.json.
|
// 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
|
// This is run before target is created, so commands can't do anything with
|
||||||
// the targets - preRunCommands are run with the target.
|
// the targets - preRunCommands are run with the target.
|
||||||
if (llvm::Error err = dap.RunInitCommands()) {
|
if (Error err = dap.RunInitCommands())
|
||||||
response["success"] = false;
|
return err;
|
||||||
EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
|
|
||||||
dap.SendJSON(llvm::json::Value(std::move(response)));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetSourceMapFromArguments(*arguments);
|
dap.ConfigureSourceMaps();
|
||||||
|
|
||||||
lldb::SBError status;
|
lldb::SBError error;
|
||||||
dap.SetTarget(dap.CreateTargetFromArguments(*arguments, status));
|
lldb::SBTarget target = dap.CreateTarget(error);
|
||||||
if (status.Fail()) {
|
if (error.Fail())
|
||||||
response["success"] = llvm::json::Value(false);
|
return ToError(error);
|
||||||
EmplaceSafeString(response, "message", status.GetCString());
|
|
||||||
dap.SendJSON(llvm::json::Value(std::move(response)));
|
dap.SetTarget(target);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run any pre run LLDB commands the user specified in the launch.json
|
// Run any pre run LLDB commands the user specified in the launch.json
|
||||||
if (llvm::Error err = dap.RunPreRunCommands()) {
|
if (Error err = dap.RunPreRunCommands())
|
||||||
response["success"] = false;
|
return err;
|
||||||
EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
|
|
||||||
dap.SendJSON(llvm::json::Value(std::move(response)));
|
if (Error err = LaunchProcess(arguments))
|
||||||
return;
|
return err;
|
||||||
|
|
||||||
|
dap.RunPostRunCommands();
|
||||||
|
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaunchRequestHandler::PostRun() const {
|
||||||
|
if (dap.target.GetProcess().IsValid()) {
|
||||||
|
// Attach happens when launching with runInTerminal.
|
||||||
|
SendProcessEvent(dap, dap.is_attach ? Attach : Launch);
|
||||||
}
|
}
|
||||||
|
|
||||||
status = LaunchProcess(request);
|
|
||||||
|
|
||||||
if (status.Fail()) {
|
|
||||||
response["success"] = llvm::json::Value(false);
|
|
||||||
EmplaceSafeString(response, "message", std::string(status.GetCString()));
|
|
||||||
} else {
|
|
||||||
dap.RunPostRunCommands();
|
|
||||||
}
|
|
||||||
|
|
||||||
dap.SendJSON(llvm::json::Value(std::move(response)));
|
|
||||||
|
|
||||||
if (!status.Fail()) {
|
|
||||||
if (dap.is_attach)
|
|
||||||
SendProcessEvent(dap, Attach); // this happens when doing runInTerminal
|
|
||||||
else
|
|
||||||
SendProcessEvent(dap, Launch);
|
|
||||||
}
|
|
||||||
dap.SendJSON(CreateEventObject("initialized"));
|
dap.SendJSON(CreateEventObject("initialized"));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace lldb_dap
|
} // namespace lldb_dap
|
||||||
|
@ -10,9 +10,11 @@
|
|||||||
#include "DAP.h"
|
#include "DAP.h"
|
||||||
#include "Handler/ResponseHandler.h"
|
#include "Handler/ResponseHandler.h"
|
||||||
#include "JSONUtils.h"
|
#include "JSONUtils.h"
|
||||||
#include "LLDBUtils.h"
|
|
||||||
#include "Protocol/ProtocolBase.h"
|
#include "Protocol/ProtocolBase.h"
|
||||||
|
#include "Protocol/ProtocolRequests.h"
|
||||||
#include "RunInTerminal.h"
|
#include "RunInTerminal.h"
|
||||||
|
#include "lldb/API/SBDefines.h"
|
||||||
|
#include "lldb/API/SBEnvironment.h"
|
||||||
#include "llvm/Support/Error.h"
|
#include "llvm/Support/Error.h"
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
@ -37,14 +39,12 @@ MakeArgv(const llvm::ArrayRef<std::string> &strs) {
|
|||||||
return argv;
|
return argv;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t SetLaunchFlag(uint32_t flags, const llvm::json::Object *obj,
|
static uint32_t SetLaunchFlag(uint32_t flags, bool flag,
|
||||||
llvm::StringRef key, lldb::LaunchFlags mask) {
|
lldb::LaunchFlags mask) {
|
||||||
if (const auto opt_value = GetBoolean(obj, key)) {
|
if (flag)
|
||||||
if (*opt_value)
|
flags |= mask;
|
||||||
flags |= mask;
|
else
|
||||||
else
|
flags &= ~mask;
|
||||||
flags &= ~mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
@ -99,14 +99,18 @@ void BaseRequestHandler::SetSourceMapFromArguments(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static llvm::Error RunInTerminal(DAP &dap,
|
static llvm::Error
|
||||||
const llvm::json::Object &launch_request,
|
RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) {
|
||||||
const uint64_t timeout_seconds) {
|
|
||||||
if (!dap.clientFeatures.contains(
|
if (!dap.clientFeatures.contains(
|
||||||
protocol::eClientFeatureRunInTerminalRequest))
|
protocol::eClientFeatureRunInTerminalRequest))
|
||||||
return llvm::make_error<DAPError>("Cannot use runInTerminal, feature is "
|
return llvm::make_error<DAPError>("Cannot use runInTerminal, feature is "
|
||||||
"not supported by the connected client");
|
"not supported by the connected client");
|
||||||
|
|
||||||
|
if (!arguments.configuration.program ||
|
||||||
|
arguments.configuration.program->empty())
|
||||||
|
return llvm::make_error<DAPError>(
|
||||||
|
"program must be set to when using runInTerminal");
|
||||||
|
|
||||||
dap.is_attach = true;
|
dap.is_attach = true;
|
||||||
lldb::SBAttachInfo attach_info;
|
lldb::SBAttachInfo attach_info;
|
||||||
|
|
||||||
@ -122,8 +126,10 @@ static llvm::Error RunInTerminal(DAP &dap,
|
|||||||
#if !defined(_WIN32)
|
#if !defined(_WIN32)
|
||||||
debugger_pid = getpid();
|
debugger_pid = getpid();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest(
|
llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest(
|
||||||
launch_request, comm_file.m_path, debugger_pid);
|
*arguments.configuration.program, arguments.args, arguments.env,
|
||||||
|
arguments.cwd.value_or(""), comm_file.m_path, debugger_pid);
|
||||||
dap.SendReverseRequest<LogFailureResponseHandler>("runInTerminal",
|
dap.SendReverseRequest<LogFailureResponseHandler>("runInTerminal",
|
||||||
std::move(reverse_request));
|
std::move(reverse_request));
|
||||||
|
|
||||||
@ -194,74 +200,74 @@ void BaseRequestHandler::Run(const Request &request) {
|
|||||||
// mark the response as cancelled.
|
// mark the response as cancelled.
|
||||||
}
|
}
|
||||||
|
|
||||||
lldb::SBError
|
llvm::Error BaseRequestHandler::LaunchProcess(
|
||||||
BaseRequestHandler::LaunchProcess(const llvm::json::Object &request) const {
|
const protocol::LaunchRequestArguments &arguments) const {
|
||||||
lldb::SBError error;
|
auto launchCommands = arguments.launchCommands;
|
||||||
const auto *arguments = request.getObject("arguments");
|
|
||||||
auto launchCommands = GetStrings(arguments, "launchCommands");
|
|
||||||
|
|
||||||
// Instantiate a launch info instance for the target.
|
// Instantiate a launch info instance for the target.
|
||||||
auto launch_info = dap.target.GetLaunchInfo();
|
auto launch_info = dap.target.GetLaunchInfo();
|
||||||
|
|
||||||
// Grab the current working directory if there is one and set it in the
|
// Grab the current working directory if there is one and set it in the
|
||||||
// launch info.
|
// launch info.
|
||||||
const auto cwd = GetString(arguments, "cwd").value_or("");
|
const auto cwd = arguments.cwd.value_or("");
|
||||||
if (!cwd.empty())
|
if (!cwd.empty())
|
||||||
launch_info.SetWorkingDirectory(cwd.data());
|
launch_info.SetWorkingDirectory(cwd.data());
|
||||||
|
|
||||||
// Extract any extra arguments and append them to our program arguments for
|
// Extract any extra arguments and append them to our program arguments for
|
||||||
// when we launch
|
// when we launch
|
||||||
auto args = GetStrings(arguments, "args");
|
if (!arguments.args.empty())
|
||||||
if (!args.empty())
|
launch_info.SetArguments(MakeArgv(arguments.args).data(), true);
|
||||||
launch_info.SetArguments(MakeArgv(args).data(), true);
|
|
||||||
|
|
||||||
// Pass any environment variables along that the user specified.
|
// Pass any environment variables along that the user specified.
|
||||||
const auto envs = GetEnvironmentFromArguments(*arguments);
|
if (!arguments.env.empty()) {
|
||||||
launch_info.SetEnvironment(envs, true);
|
lldb::SBEnvironment env;
|
||||||
|
for (const auto &kv : arguments.env)
|
||||||
|
env.Set(kv.first().data(), kv.second.c_str(), true);
|
||||||
|
launch_info.SetEnvironment(env, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
launch_info.SetDetachOnError(arguments.detachOnError);
|
||||||
|
launch_info.SetShellExpandArguments(arguments.shellExpandArguments);
|
||||||
|
|
||||||
auto flags = launch_info.GetLaunchFlags();
|
auto flags = launch_info.GetLaunchFlags();
|
||||||
|
flags =
|
||||||
flags = SetLaunchFlag(flags, arguments, "disableASLR",
|
SetLaunchFlag(flags, arguments.disableASLR, lldb::eLaunchFlagDisableASLR);
|
||||||
lldb::eLaunchFlagDisableASLR);
|
flags = SetLaunchFlag(flags, arguments.disableSTDIO,
|
||||||
flags = SetLaunchFlag(flags, arguments, "disableSTDIO",
|
|
||||||
lldb::eLaunchFlagDisableSTDIO);
|
lldb::eLaunchFlagDisableSTDIO);
|
||||||
flags = SetLaunchFlag(flags, arguments, "shellExpandArguments",
|
|
||||||
lldb::eLaunchFlagShellExpandArguments);
|
|
||||||
|
|
||||||
const bool detachOnError =
|
|
||||||
GetBoolean(arguments, "detachOnError").value_or(false);
|
|
||||||
launch_info.SetDetachOnError(detachOnError);
|
|
||||||
launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug |
|
launch_info.SetLaunchFlags(flags | lldb::eLaunchFlagDebug |
|
||||||
lldb::eLaunchFlagStopAtEntry);
|
lldb::eLaunchFlagStopAtEntry);
|
||||||
const auto timeout_seconds =
|
|
||||||
GetInteger<uint64_t>(arguments, "timeout").value_or(30);
|
|
||||||
|
|
||||||
if (GetBoolean(arguments, "runInTerminal").value_or(false)) {
|
if (arguments.runInTerminal) {
|
||||||
if (llvm::Error err = RunInTerminal(dap, request, timeout_seconds))
|
if (llvm::Error err = RunInTerminal(dap, arguments))
|
||||||
error.SetErrorString(llvm::toString(std::move(err)).c_str());
|
return err;
|
||||||
} else if (launchCommands.empty()) {
|
} else if (launchCommands.empty()) {
|
||||||
|
lldb::SBError error;
|
||||||
// Disable async events so the launch will be successful when we return from
|
// Disable async events so the launch will be successful when we return from
|
||||||
// the launch call and the launch will happen synchronously
|
// the launch call and the launch will happen synchronously
|
||||||
dap.debugger.SetAsync(false);
|
dap.debugger.SetAsync(false);
|
||||||
dap.target.Launch(launch_info, error);
|
dap.target.Launch(launch_info, error);
|
||||||
dap.debugger.SetAsync(true);
|
dap.debugger.SetAsync(true);
|
||||||
|
if (error.Fail())
|
||||||
|
return llvm::make_error<DAPError>(error.GetCString());
|
||||||
} else {
|
} else {
|
||||||
// Set the launch info so that run commands can access the configured
|
// Set the launch info so that run commands can access the configured
|
||||||
// launch details.
|
// launch details.
|
||||||
dap.target.SetLaunchInfo(launch_info);
|
dap.target.SetLaunchInfo(launch_info);
|
||||||
if (llvm::Error err = dap.RunLaunchCommands(launchCommands)) {
|
if (llvm::Error err = dap.RunLaunchCommands(launchCommands))
|
||||||
error.SetErrorString(llvm::toString(std::move(err)).c_str());
|
return err;
|
||||||
return error;
|
|
||||||
}
|
|
||||||
// The custom commands might have created a new target so we should use the
|
// The custom commands might have created a new target so we should use the
|
||||||
// selected target after these commands are run.
|
// selected target after these commands are run.
|
||||||
dap.target = dap.debugger.GetSelectedTarget();
|
dap.target = dap.debugger.GetSelectedTarget();
|
||||||
// Make sure the process is launched and stopped at the entry point before
|
// Make sure the process is launched and stopped at the entry point before
|
||||||
// proceeding as the launch commands are not run using the synchronous
|
// proceeding as the launch commands are not run using the synchronous
|
||||||
// mode.
|
// mode.
|
||||||
error = dap.WaitForProcessToStop(timeout_seconds);
|
lldb::SBError error = dap.WaitForProcessToStop(arguments.timeout);
|
||||||
|
if (error.Fail())
|
||||||
|
return llvm::make_error<DAPError>(error.GetCString());
|
||||||
}
|
}
|
||||||
return error;
|
|
||||||
|
return llvm::Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseRequestHandler::PrintWelcomeMessage() const {
|
void BaseRequestHandler::PrintWelcomeMessage() const {
|
||||||
|
@ -71,7 +71,8 @@ protected:
|
|||||||
// runInTerminal if applicable. It doesn't do any of the additional
|
// runInTerminal if applicable. It doesn't do any of the additional
|
||||||
// initialization and bookkeeping stuff that is needed for `request_launch`.
|
// initialization and bookkeeping stuff that is needed for `request_launch`.
|
||||||
// This way we can reuse the process launching logic for RestartRequest too.
|
// This way we can reuse the process launching logic for RestartRequest too.
|
||||||
lldb::SBError LaunchProcess(const llvm::json::Object &request) const;
|
llvm::Error
|
||||||
|
LaunchProcess(const protocol::LaunchRequestArguments &request) const;
|
||||||
|
|
||||||
// Check if the step-granularity is `instruction`.
|
// Check if the step-granularity is `instruction`.
|
||||||
bool HasInstructionGranularity(const llvm::json::Object &request) const;
|
bool HasInstructionGranularity(const llvm::json::Object &request) const;
|
||||||
@ -163,12 +164,21 @@ class RequestHandler : public BaseRequestHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dap.Send(response);
|
dap.Send(response);
|
||||||
|
|
||||||
|
PostRun();
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual Resp Run(const Args &) const = 0;
|
virtual Resp Run(const Args &) const = 0;
|
||||||
|
|
||||||
|
/// A hook for a request handler to run additional operations after the
|
||||||
|
/// request response is sent but before the next request handler.
|
||||||
|
virtual void PostRun() const {};
|
||||||
|
|
||||||
protocol::ErrorResponseBody ToResponse(llvm::Error err) const {
|
protocol::ErrorResponseBody ToResponse(llvm::Error err) const {
|
||||||
protocol::ErrorMessage error_message;
|
protocol::ErrorMessage error_message;
|
||||||
|
// Default to showing the user errors unless otherwise specified by a
|
||||||
|
// DAPError.
|
||||||
|
error_message.showUser = true;
|
||||||
error_message.sendTelemetry = false;
|
error_message.sendTelemetry = false;
|
||||||
if (llvm::Error unhandled = llvm::handleErrors(
|
if (llvm::Error unhandled = llvm::handleErrors(
|
||||||
std::move(err), [&](const DAPError &E) -> llvm::Error {
|
std::move(err), [&](const DAPError &E) -> llvm::Error {
|
||||||
@ -178,8 +188,9 @@ class RequestHandler : public BaseRequestHandler {
|
|||||||
error_message.url = E.getURL();
|
error_message.url = E.getURL();
|
||||||
error_message.urlLabel = E.getURLLabel();
|
error_message.urlLabel = E.getURLLabel();
|
||||||
return llvm::Error::success();
|
return llvm::Error::success();
|
||||||
}))
|
})) {
|
||||||
error_message.format = llvm::toString(std::move(unhandled));
|
error_message.format = llvm::toString(std::move(unhandled));
|
||||||
|
}
|
||||||
protocol::ErrorResponseBody body;
|
protocol::ErrorResponseBody body;
|
||||||
body.error = error_message;
|
body.error = error_message;
|
||||||
return body;
|
return body;
|
||||||
@ -273,11 +284,15 @@ public:
|
|||||||
Run(const protocol::InitializeRequestArguments &args) const override;
|
Run(const protocol::InitializeRequestArguments &args) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LaunchRequestHandler : public LegacyRequestHandler {
|
class LaunchRequestHandler
|
||||||
|
: public RequestHandler<protocol::LaunchRequestArguments,
|
||||||
|
protocol::LaunchResponseBody> {
|
||||||
public:
|
public:
|
||||||
using LegacyRequestHandler::LegacyRequestHandler;
|
using RequestHandler::RequestHandler;
|
||||||
static llvm::StringLiteral GetCommand() { return "launch"; }
|
static llvm::StringLiteral GetCommand() { return "launch"; }
|
||||||
void operator()(const llvm::json::Object &request) const override;
|
llvm::Error
|
||||||
|
Run(const protocol::LaunchRequestArguments &arguments) const override;
|
||||||
|
void PostRun() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RestartRequestHandler : public LegacyRequestHandler {
|
class RestartRequestHandler : public LegacyRequestHandler {
|
||||||
|
@ -9,9 +9,10 @@
|
|||||||
#include "DAP.h"
|
#include "DAP.h"
|
||||||
#include "EventHelper.h"
|
#include "EventHelper.h"
|
||||||
#include "JSONUtils.h"
|
#include "JSONUtils.h"
|
||||||
|
#include "Protocol/ProtocolRequests.h"
|
||||||
#include "RequestHandler.h"
|
#include "RequestHandler.h"
|
||||||
#include "lldb/API/SBListener.h"
|
#include "llvm/Support/JSON.h"
|
||||||
#include "llvm/Support/FileSystem.h"
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
|
||||||
namespace lldb_dap {
|
namespace lldb_dap {
|
||||||
|
|
||||||
@ -60,7 +61,7 @@ void RestartRequestHandler::operator()(
|
|||||||
const llvm::json::Object &request) const {
|
const llvm::json::Object &request) const {
|
||||||
llvm::json::Object response;
|
llvm::json::Object response;
|
||||||
FillResponse(request, response);
|
FillResponse(request, response);
|
||||||
if (!dap.last_launch_or_attach_request) {
|
if (!dap.target.GetProcess().IsValid()) {
|
||||||
response["success"] = llvm::json::Value(false);
|
response["success"] = llvm::json::Value(false);
|
||||||
EmplaceSafeString(response, "message",
|
EmplaceSafeString(response, "message",
|
||||||
"Restart request received but no process was launched.");
|
"Restart request received but no process was launched.");
|
||||||
@ -77,7 +78,7 @@ void RestartRequestHandler::operator()(
|
|||||||
// implementation detail. The adapter *did* launch the process in response to
|
// 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
|
// a "launch" command, so we can still stop it and re-run it. This is why we
|
||||||
// don't just check `dap.is_attach`.
|
// don't just check `dap.is_attach`.
|
||||||
if (GetString(*dap.last_launch_or_attach_request, "command") == "attach") {
|
if (!dap.last_launch_request) {
|
||||||
response["success"] = llvm::json::Value(false);
|
response["success"] = llvm::json::Value(false);
|
||||||
EmplaceSafeString(response, "message",
|
EmplaceSafeString(response, "message",
|
||||||
"Restarting an \"attach\" session is not supported.");
|
"Restarting an \"attach\" session is not supported.");
|
||||||
@ -85,15 +86,30 @@ void RestartRequestHandler::operator()(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The optional `arguments` field in RestartRequest can contain an updated
|
const llvm::json::Object *arguments = request.getObject("arguments");
|
||||||
// version of the launch arguments. If there's one, use it.
|
if (arguments) {
|
||||||
const auto *restart_arguments = request.getObject("arguments");
|
// The optional `arguments` field in RestartRequest can contain an updated
|
||||||
if (restart_arguments) {
|
// version of the launch arguments. If there's one, use it.
|
||||||
const auto *launch_request_arguments =
|
if (const llvm::json::Value *restart_arguments =
|
||||||
restart_arguments->getObject("arguments");
|
arguments->get("arguments")) {
|
||||||
if (launch_request_arguments) {
|
protocol::LaunchRequestArguments updated_arguments;
|
||||||
(*dap.last_launch_or_attach_request)["arguments"] =
|
llvm::json::Path::Root root;
|
||||||
llvm::json::Value(llvm::json::Object(*launch_request_arguments));
|
if (!fromJSON(*restart_arguments, updated_arguments, root)) {
|
||||||
|
response["success"] = llvm::json::Value(false);
|
||||||
|
EmplaceSafeString(
|
||||||
|
response, "message",
|
||||||
|
llvm::formatv("Failed to parse updated launch arguments: {0}",
|
||||||
|
llvm::toString(root.getError()))
|
||||||
|
.str());
|
||||||
|
dap.SendJSON(llvm::json::Value(std::move(response)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dap.last_launch_request = updated_arguments;
|
||||||
|
// Update DAP configuration based on the latest copy of the launch
|
||||||
|
// arguments.
|
||||||
|
dap.SetConfiguration(updated_arguments.configuration, false);
|
||||||
|
dap.stop_at_entry = updated_arguments.stopOnEntry;
|
||||||
|
dap.ConfigureSourceMaps();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +132,15 @@ void RestartRequestHandler::operator()(
|
|||||||
dap.thread_ids.clear();
|
dap.thread_ids.clear();
|
||||||
}
|
}
|
||||||
dap.debugger.SetAsync(true);
|
dap.debugger.SetAsync(true);
|
||||||
LaunchProcess(*dap.last_launch_or_attach_request);
|
|
||||||
|
// FIXME: Should we run 'preRunCommands'?
|
||||||
|
// FIXME: Should we add a 'preRestartCommands'?
|
||||||
|
if (llvm::Error err = LaunchProcess(*dap.last_launch_request)) {
|
||||||
|
response["success"] = llvm::json::Value(false);
|
||||||
|
EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
|
||||||
|
dap.SendJSON(llvm::json::Value(std::move(response)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// This is normally done after receiving a "configuration done" request.
|
// This is normally done after receiving a "configuration done" request.
|
||||||
// Because we're restarting, configuration has already happened so we can
|
// Because we're restarting, configuration has already happened so we can
|
||||||
@ -129,4 +153,5 @@ void RestartRequestHandler::operator()(
|
|||||||
|
|
||||||
dap.SendJSON(llvm::json::Value(std::move(response)));
|
dap.SendJSON(llvm::json::Value(std::move(response)));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace lldb_dap
|
} // namespace lldb_dap
|
||||||
|
@ -1423,47 +1423,39 @@ llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit &unit) {
|
|||||||
|
|
||||||
/// See
|
/// See
|
||||||
/// https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_RunInTerminal
|
/// https://microsoft.github.io/debug-adapter-protocol/specification#Reverse_Requests_RunInTerminal
|
||||||
llvm::json::Object
|
llvm::json::Object CreateRunInTerminalReverseRequest(
|
||||||
CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request,
|
llvm::StringRef program, const std::vector<std::string> &args,
|
||||||
llvm::StringRef comm_file,
|
const llvm::StringMap<std::string> env, llvm::StringRef cwd,
|
||||||
lldb::pid_t debugger_pid) {
|
llvm::StringRef comm_file, lldb::pid_t debugger_pid) {
|
||||||
llvm::json::Object run_in_terminal_args;
|
llvm::json::Object run_in_terminal_args;
|
||||||
// This indicates the IDE to open an embedded terminal, instead of opening
|
// This indicates the IDE to open an embedded terminal, instead of opening
|
||||||
// the terminal in a new window.
|
// the terminal in a new window.
|
||||||
run_in_terminal_args.try_emplace("kind", "integrated");
|
run_in_terminal_args.try_emplace("kind", "integrated");
|
||||||
|
|
||||||
const auto *launch_request_arguments = launch_request.getObject("arguments");
|
|
||||||
// The program path must be the first entry in the "args" field
|
// The program path must be the first entry in the "args" field
|
||||||
std::vector<std::string> args = {DAP::debug_adapter_path.str(), "--comm-file",
|
std::vector<std::string> req_args = {DAP::debug_adapter_path.str(),
|
||||||
comm_file.str()};
|
"--comm-file", comm_file.str()};
|
||||||
if (debugger_pid != LLDB_INVALID_PROCESS_ID) {
|
if (debugger_pid != LLDB_INVALID_PROCESS_ID) {
|
||||||
args.push_back("--debugger-pid");
|
req_args.push_back("--debugger-pid");
|
||||||
args.push_back(std::to_string(debugger_pid));
|
req_args.push_back(std::to_string(debugger_pid));
|
||||||
}
|
}
|
||||||
args.push_back("--launch-target");
|
req_args.push_back("--launch-target");
|
||||||
args.push_back(
|
req_args.push_back(program.str());
|
||||||
GetString(launch_request_arguments, "program").value_or("").str());
|
req_args.insert(req_args.end(), args.begin(), args.end());
|
||||||
std::vector<std::string> target_args =
|
|
||||||
GetStrings(launch_request_arguments, "args");
|
|
||||||
args.insert(args.end(), target_args.begin(), target_args.end());
|
|
||||||
run_in_terminal_args.try_emplace("args", args);
|
run_in_terminal_args.try_emplace("args", args);
|
||||||
|
|
||||||
const auto cwd = GetString(launch_request_arguments, "cwd").value_or("");
|
|
||||||
if (!cwd.empty())
|
if (!cwd.empty())
|
||||||
run_in_terminal_args.try_emplace("cwd", cwd);
|
run_in_terminal_args.try_emplace("cwd", cwd);
|
||||||
|
|
||||||
auto envs = GetEnvironmentFromArguments(*launch_request_arguments);
|
if (!env.empty()) {
|
||||||
llvm::json::Object env_json;
|
llvm::json::Object env_json;
|
||||||
for (size_t index = 0, env_count = envs.GetNumValues(); index < env_count;
|
for (const auto &kv : env) {
|
||||||
index++) {
|
if (!kv.first().empty())
|
||||||
llvm::StringRef key = envs.GetNameAtIndex(index);
|
env_json.try_emplace(kv.first(), kv.second);
|
||||||
llvm::StringRef value = envs.GetValueAtIndex(index);
|
}
|
||||||
|
run_in_terminal_args.try_emplace("env",
|
||||||
if (!key.empty())
|
llvm::json::Value(std::move(env_json)));
|
||||||
env_json.try_emplace(key, value);
|
|
||||||
}
|
}
|
||||||
run_in_terminal_args.try_emplace("env",
|
|
||||||
llvm::json::Value(std::move(env_json)));
|
|
||||||
|
|
||||||
return run_in_terminal_args;
|
return run_in_terminal_args;
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "lldb/API/SBType.h"
|
#include "lldb/API/SBType.h"
|
||||||
#include "lldb/API/SBValue.h"
|
#include "lldb/API/SBValue.h"
|
||||||
#include "lldb/lldb-types.h"
|
#include "lldb/lldb-types.h"
|
||||||
|
#include "llvm/ADT/StringMap.h"
|
||||||
#include "llvm/ADT/StringRef.h"
|
#include "llvm/ADT/StringRef.h"
|
||||||
#include "llvm/Support/JSON.h"
|
#include "llvm/Support/JSON.h"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@ -563,9 +564,17 @@ llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit &unit);
|
|||||||
|
|
||||||
/// Create a runInTerminal reverse request object
|
/// Create a runInTerminal reverse request object
|
||||||
///
|
///
|
||||||
/// \param[in] launch_request
|
/// \param[in] program
|
||||||
/// The original launch_request object whose fields are used to construct
|
/// Path to the program to run in the terminal.
|
||||||
/// the reverse request object.
|
///
|
||||||
|
/// \param[in] args
|
||||||
|
/// The arguments for the program.
|
||||||
|
///
|
||||||
|
/// \param[in] env
|
||||||
|
/// The environment variables to set in the terminal.
|
||||||
|
///
|
||||||
|
/// \param[in] cwd
|
||||||
|
/// The working directory for the run in terminal request.
|
||||||
///
|
///
|
||||||
/// \param[in] comm_file
|
/// \param[in] comm_file
|
||||||
/// The fifo file used to communicate the with the target launcher.
|
/// The fifo file used to communicate the with the target launcher.
|
||||||
@ -578,10 +587,10 @@ llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit &unit);
|
|||||||
/// \return
|
/// \return
|
||||||
/// A "runInTerminal" JSON object that follows the specification outlined by
|
/// A "runInTerminal" JSON object that follows the specification outlined by
|
||||||
/// Microsoft.
|
/// Microsoft.
|
||||||
llvm::json::Object
|
llvm::json::Object CreateRunInTerminalReverseRequest(
|
||||||
CreateRunInTerminalReverseRequest(const llvm::json::Object &launch_request,
|
llvm::StringRef program, const std::vector<std::string> &args,
|
||||||
llvm::StringRef comm_file,
|
const llvm::StringMap<std::string> env, llvm::StringRef cwd,
|
||||||
lldb::pid_t debugger_pid);
|
llvm::StringRef comm_file, lldb::pid_t debugger_pid);
|
||||||
|
|
||||||
/// Create a "Terminated" JSON object that contains statistics
|
/// Create a "Terminated" JSON object that contains statistics
|
||||||
///
|
///
|
||||||
|
@ -7,11 +7,19 @@
|
|||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "LLDBUtils.h"
|
#include "LLDBUtils.h"
|
||||||
#include "DAP.h"
|
|
||||||
#include "JSONUtils.h"
|
#include "JSONUtils.h"
|
||||||
|
#include "lldb/API/SBCommandInterpreter.h"
|
||||||
|
#include "lldb/API/SBCommandReturnObject.h"
|
||||||
|
#include "lldb/API/SBDebugger.h"
|
||||||
|
#include "lldb/API/SBFrame.h"
|
||||||
#include "lldb/API/SBStringList.h"
|
#include "lldb/API/SBStringList.h"
|
||||||
|
#include "lldb/API/SBThread.h"
|
||||||
|
#include "lldb/lldb-enumerations.h"
|
||||||
|
#include "llvm/ADT/ArrayRef.h"
|
||||||
|
#include "llvm/Support/JSON.h"
|
||||||
|
#include "llvm/Support/raw_ostream.h"
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
namespace lldb_dap {
|
namespace lldb_dap {
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "Protocol/ProtocolRequests.h"
|
#include "Protocol/ProtocolRequests.h"
|
||||||
|
#include "llvm/ADT/DenseMap.h"
|
||||||
#include "llvm/ADT/StringMap.h"
|
#include "llvm/ADT/StringMap.h"
|
||||||
#include "llvm/ADT/StringRef.h"
|
#include "llvm/ADT/StringRef.h"
|
||||||
#include "llvm/Support/JSON.h"
|
#include "llvm/Support/JSON.h"
|
||||||
@ -14,6 +15,129 @@
|
|||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
|
||||||
|
// The 'env' field is either an object as a map of strings or as an array of
|
||||||
|
// strings formatted like 'key=value'.
|
||||||
|
static bool parseEnv(const json::Value &Params, StringMap<std::string> &env,
|
||||||
|
json::Path P) {
|
||||||
|
const json::Object *O = Params.getAsObject();
|
||||||
|
if (!O) {
|
||||||
|
P.report("expected object");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const json::Value *value = O->get("env");
|
||||||
|
if (!value)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (const json::Object *env_obj = value->getAsObject()) {
|
||||||
|
for (const auto &kv : *env_obj) {
|
||||||
|
const std::optional<StringRef> value = kv.second.getAsString();
|
||||||
|
if (!value) {
|
||||||
|
P.field("env").field(kv.first).report("expected string value");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
env.insert({kv.first.str(), value->str()});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const json::Array *env_arr = value->getAsArray()) {
|
||||||
|
for (size_t i = 0; i < env_arr->size(); ++i) {
|
||||||
|
const std::optional<StringRef> value = (*env_arr)[i].getAsString();
|
||||||
|
if (!value) {
|
||||||
|
P.field("env").index(i).report("expected string");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::pair<StringRef, StringRef> kv = value->split("=");
|
||||||
|
env.insert({kv.first, kv.second.str()});
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
P.field("env").report("invalid format, expected array or object");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parseTimeout(const json::Value &Params, std::chrono::seconds &S,
|
||||||
|
json::Path P) {
|
||||||
|
const json::Object *O = Params.getAsObject();
|
||||||
|
if (!O) {
|
||||||
|
P.report("expected object");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const json::Value *value = O->get("timeout");
|
||||||
|
if (!value)
|
||||||
|
return true;
|
||||||
|
std::optional<double> timeout = value->getAsNumber();
|
||||||
|
if (!timeout) {
|
||||||
|
P.field("timeout").report("expected number");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
S = std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
|
std::chrono::duration<double>(*value->getAsNumber()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
parseSourceMap(const json::Value &Params,
|
||||||
|
std::vector<std::pair<std::string, std::string>> &sourceMap,
|
||||||
|
json::Path P) {
|
||||||
|
const json::Object *O = Params.getAsObject();
|
||||||
|
if (!O) {
|
||||||
|
P.report("expected object");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const json::Value *value = O->get("sourceMap");
|
||||||
|
if (!value)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (const json::Object *map_obj = value->getAsObject()) {
|
||||||
|
for (const auto &kv : *map_obj) {
|
||||||
|
const std::optional<StringRef> value = kv.second.getAsString();
|
||||||
|
if (!value) {
|
||||||
|
P.field("sourceMap").field(kv.first).report("expected string value");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sourceMap.emplace_back(std::make_pair(kv.first.str(), value->str()));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const json::Array *env_arr = value->getAsArray()) {
|
||||||
|
for (size_t i = 0; i < env_arr->size(); ++i) {
|
||||||
|
const json::Array *kv = (*env_arr)[i].getAsArray();
|
||||||
|
if (!kv) {
|
||||||
|
P.field("sourceMap").index(i).report("expected array");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (kv->size() != 2) {
|
||||||
|
P.field("sourceMap").index(i).report("expected array of pairs");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const std::optional<StringRef> first = (*kv)[0].getAsString();
|
||||||
|
if (!first) {
|
||||||
|
P.field("sourceMap").index(0).report("expected string");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const std::optional<StringRef> second = (*kv)[1].getAsString();
|
||||||
|
if (!second) {
|
||||||
|
P.field("sourceMap").index(1).report("expected string");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sourceMap.emplace_back(std::make_pair(*first, second->str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
P.report("invalid format, expected array or object");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
namespace lldb_dap::protocol {
|
namespace lldb_dap::protocol {
|
||||||
|
|
||||||
bool fromJSON(const llvm::json::Value &Params, CancelArguments &CA,
|
bool fromJSON(const llvm::json::Value &Params, CancelArguments &CA,
|
||||||
@ -31,8 +155,7 @@ bool fromJSON(const json::Value &Params, DisconnectArguments &DA,
|
|||||||
O.map("suspendDebuggee", DA.suspendDebuggee);
|
O.map("suspendDebuggee", DA.suspendDebuggee);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fromJSON(const llvm::json::Value &Params, PathFormat &PF,
|
bool fromJSON(const json::Value &Params, PathFormat &PF, json::Path P) {
|
||||||
llvm::json::Path P) {
|
|
||||||
auto rawPathFormat = Params.getAsString();
|
auto rawPathFormat = Params.getAsString();
|
||||||
if (!rawPathFormat) {
|
if (!rawPathFormat) {
|
||||||
P.report("expected a string");
|
P.report("expected a string");
|
||||||
@ -53,7 +176,7 @@ bool fromJSON(const llvm::json::Value &Params, PathFormat &PF,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const llvm::StringMap<ClientFeature> ClientFeatureByKey{
|
static const StringMap<ClientFeature> ClientFeatureByKey{
|
||||||
{"supportsVariableType", eClientFeatureVariableType},
|
{"supportsVariableType", eClientFeatureVariableType},
|
||||||
{"supportsVariablePaging", eClientFeatureVariablePaging},
|
{"supportsVariablePaging", eClientFeatureVariablePaging},
|
||||||
{"supportsRunInTerminalRequest", eClientFeatureRunInTerminalRequest},
|
{"supportsRunInTerminalRequest", eClientFeatureRunInTerminalRequest},
|
||||||
@ -66,8 +189,8 @@ static const llvm::StringMap<ClientFeature> ClientFeatureByKey{
|
|||||||
{"supportsStartDebuggingRequest", eClientFeatureStartDebuggingRequest},
|
{"supportsStartDebuggingRequest", eClientFeatureStartDebuggingRequest},
|
||||||
{"supportsANSIStyling", eClientFeatureANSIStyling}};
|
{"supportsANSIStyling", eClientFeatureANSIStyling}};
|
||||||
|
|
||||||
bool fromJSON(const llvm::json::Value &Params, InitializeRequestArguments &IRA,
|
bool fromJSON(const json::Value &Params, InitializeRequestArguments &IRA,
|
||||||
llvm::json::Path P) {
|
json::Path P) {
|
||||||
json::ObjectMapper OM(Params, P);
|
json::ObjectMapper OM(Params, P);
|
||||||
if (!OM)
|
if (!OM)
|
||||||
return false;
|
return false;
|
||||||
@ -98,6 +221,46 @@ bool fromJSON(const llvm::json::Value &Params, InitializeRequestArguments &IRA,
|
|||||||
OM.map("$__lldb_sourceInitFile", IRA.lldbExtSourceInitFile);
|
OM.map("$__lldb_sourceInitFile", IRA.lldbExtSourceInitFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool fromJSON(const json::Value &Params, Configuration &C, json::Path P) {
|
||||||
|
json::ObjectMapper O(Params, P);
|
||||||
|
return O.map("debuggerRoot", C.debuggerRoot) &&
|
||||||
|
O.mapOptional("enableAutoVariableSummaries",
|
||||||
|
C.enableAutoVariableSummaries) &&
|
||||||
|
O.mapOptional("enableSyntheticChildDebugging",
|
||||||
|
C.enableSyntheticChildDebugging) &&
|
||||||
|
O.mapOptional("displayExtendedBacktrace",
|
||||||
|
C.displayExtendedBacktrace) &&
|
||||||
|
O.mapOptional("commandEscapePrefix", C.commandEscapePrefix) &&
|
||||||
|
O.map("customFrameFormat", C.customFrameFormat) &&
|
||||||
|
O.map("customThreadFormat", C.customThreadFormat) &&
|
||||||
|
O.map("sourcePath", C.sourcePath) &&
|
||||||
|
O.mapOptional("initCommands", C.initCommands) &&
|
||||||
|
O.mapOptional("preRunCommands", C.preRunCommands) &&
|
||||||
|
O.mapOptional("postRunCommands", C.postRunCommands) &&
|
||||||
|
O.mapOptional("stopCommands", C.stopCommands) &&
|
||||||
|
O.mapOptional("exitCommands", C.exitCommands) &&
|
||||||
|
O.mapOptional("terminateCommands", C.terminateCommands) &&
|
||||||
|
O.map("program", C.program) && O.map("targetTriple", C.targetTriple) &&
|
||||||
|
O.map("platformName", C.platformName) &&
|
||||||
|
parseSourceMap(Params, C.sourceMap, P);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fromJSON(const json::Value &Params, LaunchRequestArguments &LRA,
|
||||||
|
json::Path P) {
|
||||||
|
json::ObjectMapper O(Params, P);
|
||||||
|
return O && fromJSON(Params, LRA.configuration, P) &&
|
||||||
|
O.mapOptional("noDebug", LRA.noDebug) &&
|
||||||
|
O.mapOptional("launchCommands", LRA.launchCommands) &&
|
||||||
|
O.map("cwd", LRA.cwd) && O.mapOptional("args", LRA.args) &&
|
||||||
|
O.mapOptional("detachOnError", LRA.detachOnError) &&
|
||||||
|
O.mapOptional("disableASLR", LRA.disableASLR) &&
|
||||||
|
O.mapOptional("disableSTDIO", LRA.disableSTDIO) &&
|
||||||
|
O.mapOptional("shellExpandArguments", LRA.shellExpandArguments) &&
|
||||||
|
O.mapOptional("stopOnEntry", LRA.stopOnEntry) &&
|
||||||
|
O.mapOptional("runInTerminal", LRA.runInTerminal) &&
|
||||||
|
parseEnv(Params, LRA.env, P) && parseTimeout(Params, LRA.timeout, P);
|
||||||
|
}
|
||||||
|
|
||||||
bool fromJSON(const json::Value &Params, SourceArguments &SA, json::Path P) {
|
bool fromJSON(const json::Value &Params, SourceArguments &SA, json::Path P) {
|
||||||
json::ObjectMapper O(Params, P);
|
json::ObjectMapper O(Params, P);
|
||||||
return O && O.map("source", SA.source) &&
|
return O && O.map("source", SA.source) &&
|
||||||
|
@ -24,7 +24,9 @@
|
|||||||
#include "Protocol/ProtocolTypes.h"
|
#include "Protocol/ProtocolTypes.h"
|
||||||
#include "lldb/lldb-defines.h"
|
#include "lldb/lldb-defines.h"
|
||||||
#include "llvm/ADT/DenseSet.h"
|
#include "llvm/ADT/DenseSet.h"
|
||||||
|
#include "llvm/ADT/StringMap.h"
|
||||||
#include "llvm/Support/JSON.h"
|
#include "llvm/Support/JSON.h"
|
||||||
|
#include <chrono>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -212,8 +214,86 @@ struct Configuration {
|
|||||||
|
|
||||||
/// LLDB commands executed when the debugging session ends.
|
/// LLDB commands executed when the debugging session ends.
|
||||||
std::vector<std::string> terminateCommands;
|
std::vector<std::string> terminateCommands;
|
||||||
|
|
||||||
|
/// Path to the executable.
|
||||||
|
///
|
||||||
|
/// *NOTE:* When launching, either `launchCommands` or `program` must be
|
||||||
|
/// configured. If both are configured then `launchCommands` takes priority.
|
||||||
|
std::optional<std::string> program;
|
||||||
|
|
||||||
|
/// Target triple for the program (arch-vendor-os). If not set, inferred from
|
||||||
|
/// the binary.
|
||||||
|
std::optional<std::string> targetTriple;
|
||||||
|
|
||||||
|
/// Specify name of the platform to use for this target, creating the platform
|
||||||
|
/// if necessary.
|
||||||
|
std::optional<std::string> platformName;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// lldb-dap specific launch arguments.
|
||||||
|
struct LaunchRequestArguments {
|
||||||
|
/// Common lldb-dap configuration values for launching/attaching operations.
|
||||||
|
Configuration configuration;
|
||||||
|
|
||||||
|
/// If true, the launch request should launch the program without enabling
|
||||||
|
/// debugging.
|
||||||
|
bool noDebug = false;
|
||||||
|
|
||||||
|
/// Launch specific operations.
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
/// LLDB commands executed to launch the program.
|
||||||
|
///
|
||||||
|
/// *NOTE:* Either launchCommands or program must be configured.
|
||||||
|
///
|
||||||
|
/// If set, takes priority over the 'program' when launching the target.
|
||||||
|
std::vector<std::string> launchCommands;
|
||||||
|
|
||||||
|
/// The program working directory.
|
||||||
|
std::optional<std::string> cwd;
|
||||||
|
|
||||||
|
/// An array of command line argument strings to be passed to the program
|
||||||
|
/// being launched.
|
||||||
|
std::vector<std::string> args;
|
||||||
|
|
||||||
|
/// Environment variables to set when launching the program. The format of
|
||||||
|
/// each environment variable string is "VAR=VALUE" for environment variables
|
||||||
|
/// with values or just "VAR" for environment variables with no values.
|
||||||
|
llvm::StringMap<std::string> env;
|
||||||
|
|
||||||
|
/// If set, then the client stub should detach rather than killing the debugee
|
||||||
|
/// if it loses connection with lldb.
|
||||||
|
bool detachOnError = false;
|
||||||
|
|
||||||
|
/// Disable ASLR (Address Space Layout Randomization) when launching the
|
||||||
|
/// process.
|
||||||
|
bool disableASLR = true;
|
||||||
|
|
||||||
|
/// Do not set up for terminal I/O to go to running process.
|
||||||
|
bool disableSTDIO = false;
|
||||||
|
|
||||||
|
/// Set whether to shell expand arguments to the process when launching.
|
||||||
|
bool shellExpandArguments = false;
|
||||||
|
|
||||||
|
/// Stop at the entry point of the program when launching a process.
|
||||||
|
bool stopOnEntry = false;
|
||||||
|
|
||||||
|
/// Launch the program inside an integrated terminal in the IDE. Useful for
|
||||||
|
/// debugging interactive command line programs.
|
||||||
|
bool runInTerminal = false;
|
||||||
|
|
||||||
|
/// Optional timeout for `runInTerminal` requests.
|
||||||
|
std::chrono::seconds timeout = std::chrono::seconds(30);
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
};
|
||||||
|
bool fromJSON(const llvm::json::Value &, LaunchRequestArguments &,
|
||||||
|
llvm::json::Path);
|
||||||
|
|
||||||
|
/// Response to `launch` request. This is just an acknowledgement, so no body
|
||||||
|
/// field is required.
|
||||||
|
using LaunchResponseBody = VoidResponse;
|
||||||
|
|
||||||
/// Arguments for `source` request.
|
/// Arguments for `source` request.
|
||||||
struct SourceArguments {
|
struct SourceArguments {
|
||||||
/// Specifies the source content to load. Either `source.path` or
|
/// Specifies the source content to load. Either `source.path` or
|
||||||
|
Loading…
x
Reference in New Issue
Block a user