diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt index f8e81eaff8606..490aa4710af1a 100644 --- a/lldb/tools/lldb-dap/CMakeLists.txt +++ b/lldb/tools/lldb-dap/CMakeLists.txt @@ -8,6 +8,7 @@ add_public_tablegen_target(LLDBDAPOptionsTableGen) add_lldb_library(lldbDAP Breakpoint.cpp BreakpointBase.cpp + CommandPlugins.cpp DAP.cpp DAPError.cpp DAPLog.cpp diff --git a/lldb/tools/lldb-dap/CommandPlugins.cpp b/lldb/tools/lldb-dap/CommandPlugins.cpp new file mode 100644 index 0000000000000..4e7aa029e0f22 --- /dev/null +++ b/lldb/tools/lldb-dap/CommandPlugins.cpp @@ -0,0 +1,163 @@ +//===-- CommandPlugins.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "CommandPlugins.h" +#include "Handler/ResponseHandler.h" +#include "JSONUtils.h" +#include "lldb/API/SBStream.h" + +using namespace lldb_dap; + +bool StartDebuggingCommand::DoExecute(lldb::SBDebugger debugger, char **command, + lldb::SBCommandReturnObject &result) { + // Command format like: `start-debugging ` + if (!command) { + result.SetError("Invalid use of start-debugging, expected format " + "`start-debugging `."); + return false; + } + + if (!command[0] || llvm::StringRef(command[0]).empty()) { + result.SetError("start-debugging request type missing."); + return false; + } + + if (!command[1] || llvm::StringRef(command[1]).empty()) { + result.SetError("start-debugging debug configuration missing."); + return false; + } + + llvm::StringRef request{command[0]}; + std::string raw_configuration{command[1]}; + + llvm::Expected configuration = + llvm::json::parse(raw_configuration); + + if (!configuration) { + llvm::Error err = configuration.takeError(); + std::string msg = "Failed to parse json configuration: " + + llvm::toString(std::move(err)) + "\n\n" + + raw_configuration; + result.SetError(msg.c_str()); + return false; + } + + dap.SendReverseRequest( + "startDebugging", + llvm::json::Object{{"request", request}, + {"configuration", std::move(*configuration)}}); + + result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult); + + return true; +} + +bool ReplModeCommand::DoExecute(lldb::SBDebugger debugger, char **command, + lldb::SBCommandReturnObject &result) { + // Command format like: `repl-mode ?` + // If a new mode is not specified report the current mode. + if (!command || llvm::StringRef(command[0]).empty()) { + std::string mode; + switch (dap.repl_mode) { + case ReplMode::Variable: + mode = "variable"; + break; + case ReplMode::Command: + mode = "command"; + break; + case ReplMode::Auto: + mode = "auto"; + break; + } + + result.Printf("lldb-dap repl-mode %s.\n", mode.c_str()); + result.SetStatus(lldb::eReturnStatusSuccessFinishResult); + + return true; + } + + llvm::StringRef new_mode{command[0]}; + + if (new_mode == "variable") { + dap.repl_mode = ReplMode::Variable; + } else if (new_mode == "command") { + dap.repl_mode = ReplMode::Command; + } else if (new_mode == "auto") { + dap.repl_mode = ReplMode::Auto; + } else { + lldb::SBStream error_message; + error_message.Printf("Invalid repl-mode '%s'. Expected one of 'variable', " + "'command' or 'auto'.\n", + new_mode.data()); + result.SetError(error_message.GetData()); + return false; + } + + result.Printf("lldb-dap repl-mode %s set.\n", new_mode.data()); + result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult); + return true; +} + +/// Sends a DAP event with an optional body. +/// +/// https://code.visualstudio.com/api/references/vscode-api#debug.onDidReceiveDebugSessionCustomEvent +bool SendEventCommand::DoExecute(lldb::SBDebugger debugger, char **command, + lldb::SBCommandReturnObject &result) { + // Command format like: `send-event ?` + if (!command || !command[0] || llvm::StringRef(command[0]).empty()) { + result.SetError("Not enough arguments found, expected format " + "`lldb-dap send-event ?`."); + return false; + } + + llvm::StringRef name{command[0]}; + // Events that are stateful and should be handled by lldb-dap internally. + const std::array internal_events{"breakpoint", "capabilities", "continued", + "exited", "initialize", "loadedSource", + "module", "process", "stopped", + "terminated", "thread"}; + if (llvm::is_contained(internal_events, name)) { + std::string msg = + llvm::formatv("Invalid use of lldb-dap send-event, event \"{0}\" " + "should be handled by lldb-dap internally.", + name) + .str(); + result.SetError(msg.c_str()); + return false; + } + + llvm::json::Object event(CreateEventObject(name)); + + if (command[1] && !llvm::StringRef(command[1]).empty()) { + // See if we have unused arguments. + if (command[2]) { + result.SetError( + "Additional arguments found, expected `lldb-dap send-event " + " ?`."); + return false; + } + + llvm::StringRef raw_body{command[1]}; + + llvm::Expected body = llvm::json::parse(raw_body); + + if (!body) { + llvm::Error err = body.takeError(); + std::string msg = "Failed to parse custom event body: " + + llvm::toString(std::move(err)); + result.SetError(msg.c_str()); + return false; + } + + event.try_emplace("body", std::move(*body)); + } + + dap.SendJSON(llvm::json::Value(std::move(event))); + result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult); + return true; +} diff --git a/lldb/tools/lldb-dap/CommandPlugins.h b/lldb/tools/lldb-dap/CommandPlugins.h new file mode 100644 index 0000000000000..011c7fd2da2a1 --- /dev/null +++ b/lldb/tools/lldb-dap/CommandPlugins.h @@ -0,0 +1,40 @@ +//===-- CommandPlugins.h --------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_TOOLS_LLDB_DAP_COMMANDPLUGINS_H +#define LLDB_TOOLS_LLDB_DAP_COMMANDPLUGINS_H + +#include "DAP.h" +#include "lldb/API/SBCommandInterpreter.h" + +namespace lldb_dap { + +struct StartDebuggingCommand : public lldb::SBCommandPluginInterface { + DAP &dap; + explicit StartDebuggingCommand(DAP &d) : dap(d) {}; + bool DoExecute(lldb::SBDebugger debugger, char **command, + lldb::SBCommandReturnObject &result) override; +}; + +struct ReplModeCommand : public lldb::SBCommandPluginInterface { + DAP &dap; + explicit ReplModeCommand(DAP &d) : dap(d) {}; + bool DoExecute(lldb::SBDebugger debugger, char **command, + lldb::SBCommandReturnObject &result) override; +}; + +struct SendEventCommand : public lldb::SBCommandPluginInterface { + DAP &dap; + explicit SendEventCommand(DAP &d) : dap(d) {}; + bool DoExecute(lldb::SBDebugger debugger, char **command, + lldb::SBCommandReturnObject &result) override; +}; + +} // namespace lldb_dap + +#endif diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 8868131622243..af7a04a215fec 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -1017,159 +1017,6 @@ lldb::SBError DAP::WaitForProcessToStop(std::chrono::seconds seconds) { return error; } -bool StartDebuggingRequestHandler::DoExecute( - lldb::SBDebugger debugger, char **command, - lldb::SBCommandReturnObject &result) { - // Command format like: `start-debugging ` - if (!command) { - result.SetError("Invalid use of start-debugging, expected format " - "`start-debugging `."); - return false; - } - - if (!command[0] || llvm::StringRef(command[0]).empty()) { - result.SetError("start-debugging request type missing."); - return false; - } - - if (!command[1] || llvm::StringRef(command[1]).empty()) { - result.SetError("start-debugging debug configuration missing."); - return false; - } - - llvm::StringRef request{command[0]}; - std::string raw_configuration{command[1]}; - - llvm::Expected configuration = - llvm::json::parse(raw_configuration); - - if (!configuration) { - llvm::Error err = configuration.takeError(); - std::string msg = "Failed to parse json configuration: " + - llvm::toString(std::move(err)) + "\n\n" + - raw_configuration; - result.SetError(msg.c_str()); - return false; - } - - dap.SendReverseRequest( - "startDebugging", - llvm::json::Object{{"request", request}, - {"configuration", std::move(*configuration)}}); - - result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult); - - return true; -} - -bool ReplModeRequestHandler::DoExecute(lldb::SBDebugger debugger, - char **command, - lldb::SBCommandReturnObject &result) { - // Command format like: `repl-mode ?` - // If a new mode is not specified report the current mode. - if (!command || llvm::StringRef(command[0]).empty()) { - std::string mode; - switch (dap.repl_mode) { - case ReplMode::Variable: - mode = "variable"; - break; - case ReplMode::Command: - mode = "command"; - break; - case ReplMode::Auto: - mode = "auto"; - break; - } - - result.Printf("lldb-dap repl-mode %s.\n", mode.c_str()); - result.SetStatus(lldb::eReturnStatusSuccessFinishResult); - - return true; - } - - llvm::StringRef new_mode{command[0]}; - - if (new_mode == "variable") { - dap.repl_mode = ReplMode::Variable; - } else if (new_mode == "command") { - dap.repl_mode = ReplMode::Command; - } else if (new_mode == "auto") { - dap.repl_mode = ReplMode::Auto; - } else { - lldb::SBStream error_message; - error_message.Printf("Invalid repl-mode '%s'. Expected one of 'variable', " - "'command' or 'auto'.\n", - new_mode.data()); - result.SetError(error_message.GetData()); - return false; - } - - result.Printf("lldb-dap repl-mode %s set.\n", new_mode.data()); - result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult); - return true; -} - -// Sends a DAP event with an optional body. -// -// See -// https://code.visualstudio.com/api/references/vscode-api#debug.onDidReceiveDebugSessionCustomEvent -bool SendEventRequestHandler::DoExecute(lldb::SBDebugger debugger, - char **command, - lldb::SBCommandReturnObject &result) { - // Command format like: `send-event ?` - if (!command || !command[0] || llvm::StringRef(command[0]).empty()) { - result.SetError("Not enough arguments found, expected format " - "`lldb-dap send-event ?`."); - return false; - } - - llvm::StringRef name{command[0]}; - // Events that are stateful and should be handled by lldb-dap internally. - const std::array internal_events{"breakpoint", "capabilities", "continued", - "exited", "initialize", "loadedSource", - "module", "process", "stopped", - "terminated", "thread"}; - if (llvm::is_contained(internal_events, name)) { - std::string msg = - llvm::formatv("Invalid use of lldb-dap send-event, event \"{0}\" " - "should be handled by lldb-dap internally.", - name) - .str(); - result.SetError(msg.c_str()); - return false; - } - - llvm::json::Object event(CreateEventObject(name)); - - if (command[1] && !llvm::StringRef(command[1]).empty()) { - // See if we have unused arguments. - if (command[2]) { - result.SetError( - "Additional arguments found, expected `lldb-dap send-event " - " ?`."); - return false; - } - - llvm::StringRef raw_body{command[1]}; - - llvm::Expected body = llvm::json::parse(raw_body); - - if (!body) { - llvm::Error err = body.takeError(); - std::string msg = "Failed to parse custom event body: " + - llvm::toString(std::move(err)); - result.SetError(msg.c_str()); - return false; - } - - event.try_emplace("body", std::move(*body)); - } - - dap.SendJSON(llvm::json::Value(std::move(event))); - result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult); - return true; -} - void DAP::ConfigureSourceMaps() { if (configuration.sourceMap.empty() && configuration.sourcePath.empty()) return; diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index eafc92776fca7..44b8153ba2725 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -83,27 +83,6 @@ enum class PacketStatus { enum class ReplMode { Variable = 0, Command, Auto }; -struct StartDebuggingRequestHandler : public lldb::SBCommandPluginInterface { - DAP &dap; - explicit StartDebuggingRequestHandler(DAP &d) : dap(d) {}; - bool DoExecute(lldb::SBDebugger debugger, char **command, - lldb::SBCommandReturnObject &result) override; -}; - -struct ReplModeRequestHandler : public lldb::SBCommandPluginInterface { - DAP &dap; - explicit ReplModeRequestHandler(DAP &d) : dap(d) {}; - bool DoExecute(lldb::SBDebugger debugger, char **command, - lldb::SBCommandReturnObject &result) override; -}; - -struct SendEventRequestHandler : public lldb::SBCommandPluginInterface { - DAP &dap; - explicit SendEventRequestHandler(DAP &d) : dap(d) {}; - bool DoExecute(lldb::SBDebugger debugger, char **command, - lldb::SBCommandReturnObject &result) override; -}; - struct DAP { /// Path to the lldb-dap binary itself. static llvm::StringRef debug_adapter_path; diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp index 0a178406b5a69..dcd02d61ca4f4 100644 --- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp +++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "CommandPlugins.h" #include "DAP.h" #include "EventHelper.h" #include "JSONUtils.h" @@ -59,14 +60,14 @@ llvm::Expected InitializeRequestHandler::Run( if (arguments.supportedFeatures.contains( eClientFeatureStartDebuggingRequest)) { cmd.AddCommand( - "start-debugging", new StartDebuggingRequestHandler(dap), + "start-debugging", new StartDebuggingCommand(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(dap), + "repl-mode", new ReplModeCommand(dap), "Get or set the repl behavior of lldb-dap evaluation requests."); - cmd.AddCommand("send-event", new SendEventRequestHandler(dap), + cmd.AddCommand("send-event", new SendEventCommand(dap), "Sends an DAP event to the client."); if (arguments.supportedFeatures.contains(eClientFeatureProgressReporting))