https://github.com/ashgti updated https://github.com/llvm/llvm-project/pull/116392
>From 547549573aba86c9bf9b9c2c189d49a3d8f62413 Mon Sep 17 00:00:00 2001 From: John Harrison <harj...@google.com> Date: Mon, 2 Dec 2024 08:36:52 -0800 Subject: [PATCH 1/2] [lldb-dap] Refactoring lldb-dap listening mode to support multiple connections. This adjusts the lldb-dap listening mode to allow for multiple connections. Each client gets a new instanceo of DAP and an associated lldb::SBDebugger instance. --- .../test/tools/lldb-dap/dap_server.py | 91 +++-- .../test/tools/lldb-dap/lldbdap_testcase.py | 29 +- lldb/test/API/tools/lldb-dap/server/Makefile | 3 + .../tools/lldb-dap/server/TestDAP_server.py | 72 ++++ lldb/test/API/tools/lldb-dap/server/main.c | 6 + lldb/tools/lldb-dap/CMakeLists.txt | 9 +- lldb/tools/lldb-dap/DAP.cpp | 103 +++--- lldb/tools/lldb-dap/DAP.h | 70 ++-- lldb/tools/lldb-dap/IOStream.cpp | 27 +- lldb/tools/lldb-dap/IOStream.h | 8 +- lldb/tools/lldb-dap/Options.td | 13 +- lldb/tools/lldb-dap/OutputRedirector.cpp | 44 ++- lldb/tools/lldb-dap/OutputRedirector.h | 35 +- lldb/tools/lldb-dap/lldb-dap.cpp | 313 +++++++++++------- 14 files changed, 555 insertions(+), 268 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/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py index c29992ce9c7848..37679462118531 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 @@ -1150,38 +1150,42 @@ def request_setInstructionBreakpoints(self, memory_reference=[]): } return self.send_recv(command_dict) + class DebugAdaptorServer(DebugCommunication): def __init__( self, executable=None, - port=None, + launch=True, + connection=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, connection = DebugAdaptorServer.launch( + executable, + connection=connection, + log_file=log_file, + env=env, ) + + if connection: + if connection.startswith("unix-connect://"): # unix-connect:///path + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + s.connect(connection.removeprefix("unix-connect://")) + elif connection.startswith("connection://"): # connection://[host]:port + host, port = connection.removeprefix("connection://").rsplit(":", 1) + # create_connection with try both ipv4 and ipv6. + s = socket.create_connection((host.strip("[]"), int(port))) + else: + raise ValueError("invalid connection: {}".format(connection)) 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): @@ -1196,6 +1200,53 @@ def terminate(self): self.process.wait() self.process = None + @classmethod + def launch( + cls, executable: str, /, connection=None, log_file=None, env=None + ) -> tuple[subprocess.Popen, str]: + adaptor_env = os.environ.copy() + if env: + adaptor_env.update(env) + + if log_file: + adaptor_env["LLDBDAP_LOG"] = log_file + + args = [executable] + bufsize = -1 + if connection: + bufsize = 0 + args.append("--connection") + args.append(connection) + + proc = subprocess.Popen( + args, + bufsize=bufsize, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=sys.stdout, + env=adaptor_env, + ) + + if connection: + # If a conneciton is specified, lldb-dap will print the listening + # address once the listener is made to stdout. The listener is + # formatted like `tcp://host:port` or `unix:///path`. + expected_prefix = "Listening for: " + out = proc.stdout.readline().decode() + if not out.startswith(expected_prefix): + proc.kill() + raise ValueError( + "lldb-dap failed to print listening address, expected '{}', got '{}'".format( + expected_prefix, out + ) + ) + + # If the listener expanded into multiple addresses, use the first. + connection = out.removeprefix(expected_prefix).rstrip("\r\n") + return proc, connection + + return proc, None + def attach_options_specified(options): if options.pid is not None: 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..6b1216c8837f7c 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 @@ -10,10 +10,10 @@ class DAPTestCaseBase(TestBase): # set timeout based on whether ASAN was enabled or not. Increase # timeout by a factor of 10 if ASAN is enabled. - timeoutval = 10 * (10 if ('ASAN_OPTIONS' in os.environ) else 1) + 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, connection=None): """Create the Visual Studio Code debug adaptor""" self.assertTrue( is_exe(self.lldbDAPExec), "lldb-dap must exist and be executable" @@ -21,14 +21,25 @@ 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, + connection=connection, 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, + lldbDAPConnection=None, + ): self.build() - self.create_debug_adaptor(lldbDAPEnv) + self.create_debug_adaptor( + env=lldbDAPEnv, + launch=lldbDAPLaunch, + connection=lldbDAPConnection, + ) def set_source_breakpoints(self, source_path, lines, data=None): """Sets source breakpoints and returns an array of strings containing @@ -475,11 +486,17 @@ def build_and_launch( customThreadFormat=None, launchCommands=None, expectFailure=False, + lldbDAPLaunch=True, + lldbDAPConnection=None, ): """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=lldbDAPEnv, + lldbDAPLaunch=lldbDAPLaunch, + lldbDAPConnection=lldbDAPConnection, + ) 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..c90e6b8da0a303 --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py @@ -0,0 +1,72 @@ +""" +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, connection): + log_file_path = self.getBuildArtifact("dap.txt") + server, connection = dap_server.DebugAdaptorServer.launch( + self.lldbDAPExec, connection, 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, connection=connection) + 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, connection=connection) + 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. + """ + self.do_test_server(connection="tcp://localhost:0") + + @skipIfWindows + def test_server_unix_socket(self): + """ + Test launching a binary with a lldb-dap in server mode on a unix socket. + """ + dir = tempfile.gettempdir() + name = dir + "/dap-connection-" + str(os.getpid()) + + def cleanup(): + os.unlink(name) + + self.addTearDownHook(cleanup) + self.do_test_server(connection="unix://" + name) 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/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt index d68098bf7b3266..43fc18873feb33 100644 --- a/lldb/tools/lldb-dap/CMakeLists.txt +++ b/lldb/tools/lldb-dap/CMakeLists.txt @@ -1,7 +1,3 @@ -if ( CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "NetBSD" ) - list(APPEND extra_libs lldbHost) -endif () - if (HAVE_LIBPTHREAD) list(APPEND extra_libs pthread) endif () @@ -26,9 +22,11 @@ add_lldb_tool(lldb-dap lldb-dap.cpp Breakpoint.cpp BreakpointBase.cpp + DAP.cpp ExceptionBreakpoint.cpp FifoFiles.cpp FunctionBreakpoint.cpp + InstructionBreakpoint.cpp IOStream.cpp JSONUtils.cpp LLDBUtils.cpp @@ -36,12 +34,11 @@ add_lldb_tool(lldb-dap ProgressEvent.cpp RunInTerminal.cpp SourceBreakpoint.cpp - DAP.cpp Watchpoint.cpp - InstructionBreakpoint.cpp LINK_LIBS liblldb + lldbHost ${extra_libs} LINK_COMPONENTS diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index 35250d9eef608a..243117782dbe84 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -14,11 +14,14 @@ #include "DAP.h" #include "JSONUtils.h" #include "LLDBUtils.h" +#include "OutputRedirector.h" #include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBLanguageRuntime.h" #include "lldb/API/SBListener.h" #include "lldb/API/SBStream.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" #include "llvm/Support/FormatVariadic.h" #if defined(_WIN32) @@ -32,10 +35,11 @@ using namespace lldb_dap; namespace lldb_dap { -DAP::DAP(llvm::StringRef path, ReplMode repl_mode) - : debug_adaptor_path(path), broadcaster("lldb-dap"), - exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID), - stop_at_entry(false), is_attach(false), +DAP::DAP(llvm::StringRef path, std::ofstream *log, ReplMode repl_mode, + std::vector<std::string> pre_init_commands) + : debug_adaptor_path(path), broadcaster("lldb-dap"), log(log), + exception_breakpoints(), pre_init_commands(pre_init_commands), + focus_tid(LLDB_INVALID_THREAD_ID), stop_at_entry(false), is_attach(false), enable_auto_variable_summaries(false), enable_synthetic_child_debugging(false), display_extended_backtrace(false), @@ -43,24 +47,34 @@ DAP::DAP(llvm::StringRef path, ReplMode repl_mode) 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(repl_mode) { - 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; +llvm::Error DAP::ConfigureIO(int out_fd, int err_fd) { + llvm::Expected<int> new_stdout_fd = + redirector.RedirectFd(out_fd, [this](llvm::StringRef data) { + SendOutput(OutputType::Stdout, data); + }); + if (auto Err = new_stdout_fd.takeError()) { + return Err; + } + llvm::Expected<int> new_stderr_fd = + redirector.RedirectFd(err_fd, [this](llvm::StringRef data) { + SendOutput(OutputType::Stderr, data); + }); + if (auto Err = new_stderr_fd.takeError()) { + return Err; + } + + out = lldb::SBFile(/*fd=*/new_stdout_fd.get(), /*mode=*/"w", + /*transfer_ownership=*/false); + err = lldb::SBFile(/*fd=*/new_stderr_fd.get(), /*mode=*/"w", + /*transfer_ownership=*/false); + + return llvm::Error::success(); +} + /// Return string with first character capitalized. static std::string capitalize(llvm::StringRef str) { if (str.empty()) @@ -208,19 +222,19 @@ std::string DAP::ReadJSON() { std::string json_str; int length; - if (!input.read_expected(log.get(), "Content-Length: ")) + if (!input.read_expected(log, "Content-Length: ")) return json_str; - if (!input.read_line(log.get(), length_str)) + if (!input.read_line(log, length_str)) return json_str; if (!llvm::to_integer(length_str, length)) return json_str; - if (!input.read_expected(log.get(), "\r\n")) + if (!input.read_expected(log, "\r\n")) return json_str; - if (!input.read_full(log.get(), length, json_str)) + if (!input.read_full(log, length, json_str)) return json_str; if (log) { @@ -526,11 +540,14 @@ ReplMode DAP::DetectReplMode(lldb::SBFrame frame, std::string &expression, // If we have both a variable and command, warn the user about the conflict. if (term_is_command && term_is_variable) { - llvm::errs() - << "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 '" - << command_escape_prefix << "' as a prefix.\n"; + SendOutput( + OutputType::Stderr, + llvm::formatv( + "Warning: Expression '{0}' is both an LLDB command and variable. " + "It will be evaluated as a variable. To evaluate the expression " + "as an LLDB command, use '{1}' as a prefix.", + term, command_escape_prefix) + .str()); } // Variables take preference to commands in auto, since commands can always @@ -673,9 +690,8 @@ PacketStatus DAP::GetNextObject(llvm::json::Object &object) { return PacketStatus::JSONMalformed; } - if (log) { + if (log) *log << llvm::formatv("{0:2}", *json_value).str() << std::endl; - } llvm::json::Object *object_ptr = json_value->getAsObject(); if (!object_ptr) { @@ -705,8 +721,15 @@ bool DAP::HandleObject(const llvm::json::Object &object) { if (packet_type == "response") { auto id = GetSigned(object, "request_seq", 0); - ResponseCallback response_handler = [](llvm::Expected<llvm::json::Value>) { - llvm::errs() << "Unhandled response\n"; + ResponseCallback response_handler = [](auto dap, auto value) { + if (value) + dap.SendOutput(OutputType::Stderr, + llvm::formatv("unexpected response: {0}", *value).str()); + else + dap.SendOutput(OutputType::Stderr, + llvm::formatv("unexpected response: {0}", + llvm::toString(value.takeError())) + .str()); }; { @@ -724,14 +747,15 @@ bool DAP::HandleObject(const llvm::json::Object &object) { if (auto *B = object.get("body")) { Result = std::move(*B); } - response_handler(Result); + response_handler(*this, Result); } else { llvm::StringRef message = GetString(object, "message"); if (message.empty()) { message = "Unknown error, response failed"; } - response_handler(llvm::createStringError( - std::error_code(-1, std::generic_category()), message)); + response_handler( + *this, llvm::createStringError( + std::error_code(-1, std::generic_category()), message)); } return true; @@ -904,11 +928,14 @@ bool StartDebuggingRequestHandler::DoExecute( "startDebugging", llvm::json::Object{{"request", request}, {"configuration", std::move(*configuration)}}, - [](llvm::Expected<llvm::json::Value> value) { + [](auto dap, auto value) { if (!value) { llvm::Error err = value.takeError(); - llvm::errs() << "reverse start debugging request failed: " - << llvm::toString(std::move(err)) << "\n"; + dap.SendOutput( + OutputType::Console, + llvm::formatv("reverse start debugging request failed: {0}", + llvm::toString(std::move(err))) + .str()); } }); diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index ae496236f13369..4b17b0ceb011af 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -9,36 +9,33 @@ #ifndef LLDB_TOOLS_LLDB_DAP_DAP_H #define LLDB_TOOLS_LLDB_DAP_DAP_H -#include <cstdio> -#include <iosfwd> -#include <map> -#include <optional> -#include <thread> - -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/JSON.h" -#include "llvm/Support/Threading.h" -#include "llvm/Support/raw_ostream.h" - +#include "ExceptionBreakpoint.h" +#include "FunctionBreakpoint.h" +#include "IOStream.h" +#include "InstructionBreakpoint.h" +#include "OutputRedirector.h" +#include "ProgressEvent.h" +#include "SourceBreakpoint.h" #include "lldb/API/SBAttachInfo.h" #include "lldb/API/SBCommandInterpreter.h" #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" #include "lldb/API/SBThread.h" - -#include "ExceptionBreakpoint.h" -#include "FunctionBreakpoint.h" -#include "IOStream.h" -#include "InstructionBreakpoint.h" -#include "ProgressEvent.h" -#include "SourceBreakpoint.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/Threading.h" +#include "llvm/Support/raw_ostream.h" +#include <map> +#include <optional> +#include <thread> #define VARREF_LOCALS (int64_t)1 #define VARREF_GLOBALS (int64_t)2 @@ -64,7 +61,7 @@ enum DAPBroadcasterBits { }; typedef void (*RequestCallback)(DAP &dap, const llvm::json::Object &command); -typedef void (*ResponseCallback)(llvm::Expected<llvm::json::Value> value); +typedef void (*ResponseCallback)(DAP &dap, llvm::Expected<llvm::json::Value> value); enum class PacketStatus { Success = 0, @@ -140,13 +137,16 @@ struct DAP { llvm::StringRef 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::ofstream *log; llvm::StringMap<SourceBreakpointMap> source_breakpoints; FunctionBreakpointMap function_breakpoints; InstructionBreakpointMap instruction_breakpoints; @@ -197,11 +197,33 @@ struct DAP { // empty; if the previous expression was a variable expression, this string // will contain that expression. std::string last_nonempty_var_expression; + OutputRedirector redirector; - DAP(llvm::StringRef path, ReplMode repl_mode); + DAP(llvm::StringRef path, std::ofstream *log, ReplMode repl_mode, + std::vector<std::string> pre_init_commands); ~DAP(); + + DAP() = delete; DAP(const DAP &rhs) = delete; void operator=(const DAP &rhs) = delete; + + void disconnect(); + + /// Configures the DAP session stdout and stderr to redirect to the DAP + /// connection. + /// + /// Errors in this operation will be printed to the log file and the IDE's + /// console output as well. + /// + /// \param[in] out_fd + /// If not -1, uses the given file descriptor as stdout. + /// + /// \param[in] err_fd + /// If not -1, uses the given file descriptor as stderr. + /// + /// \return An error indiciating if the configuration was applied. + llvm::Error ConfigureIO(int out_fd = -1, int err_fd = -1); + ExceptionBreakpoint *GetExceptionBreakpoint(const std::string &filter); ExceptionBreakpoint *GetExceptionBreakpoint(const lldb::break_id_t bp_id); diff --git a/lldb/tools/lldb-dap/IOStream.cpp b/lldb/tools/lldb-dap/IOStream.cpp index d2e8ec40b0a7b8..d07410af7b2319 100644 --- a/lldb/tools/lldb-dap/IOStream.cpp +++ b/lldb/tools/lldb-dap/IOStream.cpp @@ -7,6 +7,8 @@ //===----------------------------------------------------------------------===// #include "IOStream.h" +#include <fstream> +#include <string> #if defined(_WIN32) #include <io.h> @@ -16,9 +18,6 @@ #include <unistd.h> #endif -#include <fstream> -#include <string> - using namespace lldb_dap; StreamDescriptor::StreamDescriptor() = default; @@ -27,23 +26,9 @@ StreamDescriptor::StreamDescriptor(StreamDescriptor &&other) { *this = std::move(other); } -StreamDescriptor::~StreamDescriptor() { - if (!m_close) - return; - - if (m_is_socket) -#if defined(_WIN32) - ::closesocket(m_socket); -#else - ::close(m_socket); -#endif - else - ::close(m_fd); -} +StreamDescriptor::~StreamDescriptor() = default; StreamDescriptor &StreamDescriptor::operator=(StreamDescriptor &&other) { - m_close = other.m_close; - other.m_close = false; m_is_socket = other.m_is_socket; if (m_is_socket) m_socket = other.m_socket; @@ -52,19 +37,17 @@ StreamDescriptor &StreamDescriptor::operator=(StreamDescriptor &&other) { return *this; } -StreamDescriptor StreamDescriptor::from_socket(SOCKET s, bool close) { +StreamDescriptor StreamDescriptor::from_socket(SOCKET s) { StreamDescriptor sd; sd.m_is_socket = true; sd.m_socket = s; - sd.m_close = close; return sd; } -StreamDescriptor StreamDescriptor::from_file(int fd, bool close) { +StreamDescriptor StreamDescriptor::from_file(int fd) { StreamDescriptor sd; sd.m_is_socket = false; sd.m_fd = fd; - sd.m_close = close; return sd; } diff --git a/lldb/tools/lldb-dap/IOStream.h b/lldb/tools/lldb-dap/IOStream.h index 57d5fd458b7165..7296c1dba213e3 100644 --- a/lldb/tools/lldb-dap/IOStream.h +++ b/lldb/tools/lldb-dap/IOStream.h @@ -22,7 +22,6 @@ typedef int SOCKET; #endif #include "llvm/ADT/StringRef.h" - #include <fstream> #include <string> @@ -32,17 +31,16 @@ typedef int SOCKET; // treat them identically. namespace lldb_dap { struct StreamDescriptor { - StreamDescriptor(); + explicit StreamDescriptor(); ~StreamDescriptor(); StreamDescriptor(StreamDescriptor &&other); StreamDescriptor &operator=(StreamDescriptor &&other); - static StreamDescriptor from_socket(SOCKET s, bool close); - static StreamDescriptor from_file(int fd, bool close); + static StreamDescriptor from_socket(SOCKET s); + static StreamDescriptor from_file(int fd); bool m_is_socket = false; - bool m_close = false; union { int m_fd; SOCKET m_socket; diff --git a/lldb/tools/lldb-dap/Options.td b/lldb/tools/lldb-dap/Options.td index d7b4a065abec01..41d8912ed6c6bf 100644 --- a/lldb/tools/lldb-dap/Options.td +++ b/lldb/tools/lldb-dap/Options.td @@ -17,12 +17,10 @@ def: Flag<["-"], "g">, Alias<wait_for_debugger>, HelpText<"Alias for --wait-for-debugger">; -def port: S<"port">, - MetaVarName<"<port>">, - HelpText<"Communicate with the lldb-dap tool over the defined port.">; -def: Separate<["-"], "p">, - Alias<port>, - HelpText<"Alias for --port">; +def connection: S<"connection">, + MetaVarName<"<connection>">, + HelpText<"Communicate with the lldb-dap tool over the specified connection. " + "Connections are specified like 'tcp://[host]:port' or 'unix:///path'.">; def launch_target: S<"launch-target">, MetaVarName<"<target>">, @@ -42,7 +40,8 @@ def debugger_pid: S<"debugger-pid">, def repl_mode: S<"repl-mode">, MetaVarName<"<mode>">, - HelpText<"The mode for handling repl evaluation requests, supported modes: variable, command, auto.">; + HelpText<"The mode for handling repl evaluation requests, supported modes: " + "variable, command, auto.">; def pre_init_command: S<"pre-init-command">, MetaVarName<"<command>">, diff --git a/lldb/tools/lldb-dap/OutputRedirector.cpp b/lldb/tools/lldb-dap/OutputRedirector.cpp index 2c2f49569869b4..6a63ed5a95e319 100644 --- a/lldb/tools/lldb-dap/OutputRedirector.cpp +++ b/lldb/tools/lldb-dap/OutputRedirector.cpp @@ -16,29 +16,45 @@ #include "DAP.h" #include "OutputRedirector.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include <thread> +#include <utility> +#include <vector> using namespace llvm; namespace lldb_dap { -Error RedirectFd(int fd, std::function<void(llvm::StringRef)> callback) { +OutputRedirector::~OutputRedirector() { + // Make a best effort cleanup of any redirected FDs. + for (auto &[from, to] : m_redirects) + ::dup2(to, from); // ignoring errors + + for (const auto &fd : m_fds) + close(fd); +} + +Expected<int> +OutputRedirector::RedirectFd(int fd, + std::function<void(llvm::StringRef)> callback) { int new_fd[2]; #if defined(_WIN32) - if (_pipe(new_fd, 4096, O_TEXT) == -1) { + if (_pipe(new_fd, 4096, O_TEXT) == -1) #else - if (pipe(new_fd) == -1) { + if (pipe(new_fd) == -1) #endif - int error = errno; - return createStringError(inconvertibleErrorCode(), - "Couldn't create new pipe for fd %d. %s", fd, - strerror(error)); - } + return createStringError(llvm::errnoAsErrorCode(), + "Couldn't create new pipe for fd %d.", fd); + + m_fds.push_back(new_fd[0]); + m_fds.push_back(new_fd[1]); + + if (fd != -1) { + if (dup2(new_fd[1], fd) == -1) + return createStringError(llvm::errnoAsErrorCode(), + "Couldn't override the fd %d.", fd); - if (dup2(new_fd[1], fd) == -1) { - int error = errno; - return createStringError(inconvertibleErrorCode(), - "Couldn't override the fd %d. %s", fd, - strerror(error)); + m_redirects.push_back(std::make_pair(new_fd[1], fd)); } int read_fd = new_fd[0]; @@ -57,7 +73,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..59de19d5ff1b6b 100644 --- a/lldb/tools/lldb-dap/OutputRedirector.h +++ b/lldb/tools/lldb-dap/OutputRedirector.h @@ -11,15 +11,38 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" +#include <utility> +#include <vector> namespace lldb_dap { -/// Redirects the output of a given file descriptor to a callback. -/// -/// \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); +/// Manages the lifetime of file descriptor redirects. +struct OutputRedirector { + OutputRedirector() = default; + + OutputRedirector(const OutputRedirector &) = delete; + OutputRedirector &operator=(const OutputRedirector &) = delete; + + ~OutputRedirector(); + + /// 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 new file handle for the output. + llvm::Expected<int> RedirectFd(int fd, + std::function<void(llvm::StringRef)> callback); + +private: + std::vector<int> m_fds; // owned fds, closed on dealloc. + std::vector<std::pair<int, int>> + m_redirects; // pairs (new, old) of redirected fds. +}; } // namespace lldb_dap diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp index 3bfc578806021e..68b482e80e4170 100644 --- a/lldb/tools/lldb-dap/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/lldb-dap.cpp @@ -10,31 +10,39 @@ #include "FifoFiles.h" #include "JSONUtils.h" #include "LLDBUtils.h" -#include "OutputRedirector.h" #include "RunInTerminal.h" #include "Watchpoint.h" #include "lldb/API/SBDeclaration.h" +#include "lldb/API/SBFile.h" #include "lldb/API/SBInstruction.h" #include "lldb/API/SBListener.h" #include "lldb/API/SBMemoryRegionInfo.h" #include "lldb/API/SBStream.h" #include "lldb/API/SBStringList.h" #include "lldb/Host/Config.h" +#include "lldb/Host/MainLoop.h" +#include "lldb/Host/MainLoopBase.h" +#include "lldb/Host/Socket.h" +#include "lldb/Host/common/TCPSocket.h" +#include "lldb/Host/posix/DomainSocket.h" +#include "lldb/Utility/Status.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/OptTable.h" #include "llvm/Option/Option.h" #include "llvm/Support/Base64.h" -#include "llvm/Support/Errno.h" +#include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <array> @@ -44,13 +52,18 @@ #include <cstdio> #include <cstdlib> #include <cstring> +#include <fstream> +#include <iostream> #include <map> #include <memory> #include <optional> +#include <ostream> #include <set> #include <sys/stat.h> #include <sys/types.h> +#include <system_error> #include <thread> +#include <utility> #include <vector> #if defined(_WIN32) @@ -66,6 +79,7 @@ #else #include <netinet/in.h> #include <sys/socket.h> +#include <sys/un.h> #include <unistd.h> #endif @@ -81,6 +95,13 @@ typedef int socklen_t; #endif using namespace lldb_dap; +using lldb_private::DomainSocket; +using lldb_private::MainLoop; +using lldb_private::MainLoopBase; +using lldb_private::NativeSocket; +using lldb_private::Socket; +using lldb_private::Status; +using lldb_private::TCPSocket; namespace { using namespace llvm::opt; @@ -116,6 +137,8 @@ enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch }; /// Page size used for reporting addtional frames in the 'stackTrace' request. constexpr int StackPageSize = 20; +void RegisterRequestCallbacks(DAP &dap); + /// Prints a welcome message on the editor if the preprocessor variable /// LLDB_DAP_WELCOME_MESSAGE is defined. static void PrintWelcomeMessage(DAP &dap) { @@ -137,44 +160,6 @@ lldb::SBValueList *GetTopLevelScope(DAP &dap, int64_t variablesReference) { } } -SOCKET AcceptConnection(DAP &dap, int portno) { - // Accept a socket connection from any host on "portno". - SOCKET newsockfd = -1; - struct sockaddr_in serv_addr, cli_addr; - SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0); - if (sockfd < 0) { - if (dap.log) - *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 (dap.log) - *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 (dap.log) - *dap.log << "error: accept (" << strerror(errno) << ")" << std::endl; - } -#if defined(_WIN32) - closesocket(sockfd); -#else - close(sockfd); -#endif - } - return newsockfd; -} - std::vector<const char *> MakeArgv(const llvm::ArrayRef<std::string> &strs) { // Create and return an array of "const char *", one for each C string in // "strs" and terminate the list with a NULL. This can be used for argument @@ -306,12 +291,11 @@ void SendThreadStoppedEvent(DAP &dap) { if (dap.log) *dap.log << "error: SendThreadStoppedEvent() when process" " isn't stopped (" - << lldb::SBDebugger::StateAsCString(state) << ')' << std::endl; + << lldb::SBDebugger::StateAsCString(state) << ")\n"; } } else { if (dap.log) - *dap.log << "error: SendThreadStoppedEvent() invalid process" - << std::endl; + *dap.log << "error: SendThreadStoppedEvent() invalid process\n"; } dap.RunStopCommands(); } @@ -1868,7 +1852,21 @@ void request_initialize(DAP &dap, const llvm::json::Object &request) { // which may affect the outcome of tests. bool source_init_file = GetBoolean(arguments, "sourceInitFile", true); - dap.debugger = lldb::SBDebugger::Create(source_init_file); + 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))); @@ -2012,15 +2010,16 @@ llvm::Error request_runInTerminal(DAP &dap, #endif llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest( 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"; - } - }); + dap.SendReverseRequest( + "runInTerminal", std::move(reverse_request), [](auto dap, auto value) { + if (!value) { + dap.SendOutput( + OutputType::Stderr, + llvm::formatv("runInTerminal request failed: {}", + llvm::toString(std::move(value.takeError()))) + .str()); + } + }); if (llvm::Expected<lldb::pid_t> pid = comm_channel.GetLauncherPid()) attach_info.SetProcessID(*pid); @@ -4907,36 +4906,27 @@ static void redirection_test() { 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. -static int SetupStdoutStderrRedirection(DAP &dap) { - int stdoutfd = fileno(stdout); - int new_stdout_fd = dup(stdoutfd); - 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); - }; - if (llvm::Error err = RedirectFd(stdoutfd, output_callback_stdout)) { - std::string error_message = llvm::toString(std::move(err)); - if (dap.log) - *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 (dap.log) - *dap.log << error_message << std::endl; - output_callback_stderr(error_message); +static llvm::Expected<std::pair<Socket::SocketProtocol, std::string>> +parseConnection(llvm::StringRef conn) { + if (conn.contains("://")) { + llvm::StringRef scheme, rest; + std::tie(scheme, rest) = conn.split("://"); + + if (scheme == "unix" || scheme == "unix-connect") { + return std::make_pair(Socket::ProtocolUnixDomain, rest.str()); + } + if (scheme == "tcp" || scheme == "connect") { + return std::make_pair(Socket::ProtocolTcp, rest.str()); + } + } else if (conn.starts_with("/")) { + return std::make_pair(Socket::ProtocolUnixDomain, conn.str()); + } else if (conn.contains(":")) { + return std::make_pair(Socket::ProtocolTcp, conn.str()); } - return new_stdout_fd; + return llvm::createStringError( + "expected '[unix://]/path' or '[tcp://][host]:port', got '%s'.", + conn.str().c_str()); } int main(int argc, char *argv[]) { @@ -5009,17 +4999,20 @@ int main(int argc, char *argv[]) { } } - int portno = -1; - if (auto *arg = input_args.getLastArg(OPT_port)) { - const auto *optarg = arg->getValue(); - char *remainder; - portno = strtol(optarg, &remainder, 0); - if (remainder == optarg || *remainder != '\0') { - fprintf(stderr, "'%s' is not a valid port number.\n", optarg); - return EXIT_FAILURE; - } + std::string connection; + if (auto *arg = input_args.getLastArg(OPT_connection)) { + const auto *path = arg->getValue(); + connection.assign(path); } + const char *log_file_path = getenv("LLDBDAP_LOG"); + std::ofstream *log = nullptr; + if (log_file_path) + log = new std::ofstream(log_file_path); + + const auto pre_init_commands = + input_args.getAllArgValues(OPT_pre_init_command); + #if !defined(_WIN32) if (input_args.hasArg(OPT_wait_for_debugger)) { printf("Paused waiting for debugger to attach (pid = %i)...\n", getpid()); @@ -5028,48 +5021,128 @@ int main(int argc, char *argv[]) { #endif // 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(); }); - DAP dap = DAP(program_path.str(), default_repl_mode); + auto HandleClient = [=](int out_fd, int err_fd, StreamDescriptor input, + StreamDescriptor output) { + DAP dap = DAP(program_path, log, default_repl_mode, pre_init_commands); + dap.input.descriptor = std::move(input); + dap.output.descriptor = std::move(output); + RegisterRequestCallbacks(dap); + + if (auto Err = dap.ConfigureIO(out_fd, err_fd)) { + if (log) + *log << "configureIO failed: " << llvm::toStringWithoutConsuming(Err) + << "\n"; + std::cerr << "failed to configureIO: " << llvm::toString(std::move(Err)) + << std::endl; + return false; + } - RegisterRequestCallbacks(dap); + // used only by TestVSCode_redirection_to_console.py + if (getenv("LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION") != nullptr) + redirection_test(); - // stdout/stderr redirection to the IDE's console - int new_stdout_fd = SetupStdoutStderrRedirection(dap); - - if (portno != -1) { - printf("Listening on port %i...\n", portno); - SOCKET socket_fd = AcceptConnection(dap, portno); - if (socket_fd >= 0) { - dap.input.descriptor = StreamDescriptor::from_socket(socket_fd, true); - dap.output.descriptor = StreamDescriptor::from_socket(socket_fd, false); - } else { + if (auto Err = dap.Loop()) { + if (log) + *log << "Transport Error: " << llvm::toStringWithoutConsuming(Err) + << "\n"; + + std::cerr << "Transpot Error: " << llvm::toString(std::move(Err)) + << std::endl; + + return false; + } + + if (log) + *log << "connection closed\n"; + + return true; + }; + + if (!connection.empty()) { + auto maybeProtoclAndName = parseConnection(connection); + if (auto Err = maybeProtoclAndName.takeError()) { + std::cerr << "Invalid connection specification " + << llvm::toString(std::move(Err)) << std::endl; return EXIT_FAILURE; } - } else { - dap.input.descriptor = StreamDescriptor::from_file(fileno(stdin), false); - dap.output.descriptor = StreamDescriptor::from_file(new_stdout_fd, false); - /// used only by TestVSCode_redirection_to_console.py - if (getenv("LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION") != nullptr) - redirection_test(); - } + Socket::SocketProtocol protocol; + std::string name; + std::tie(protocol, name) = *maybeProtoclAndName; + + Status error; + std::unique_ptr<Socket> listener = Socket::Create(protocol, false, error); + if (error.Fail()) { + std::cerr << "Failed to create listener for protocol " + << Socket::FindSchemeByProtocol(protocol) + << ", error: " << llvm::toString(error.takeError()) + << std::endl; + return EXIT_FAILURE; + } - for (const std::string &arg : - input_args.getAllArgValues(OPT_pre_init_command)) { - dap.pre_init_commands.push_back(arg); - } + error = listener->Listen(name, /* backlog */ 5); + if (error.Fail()) { + std::cerr << "Failed to listen, error: " + << llvm::toString(error.takeError()) << std::endl; + return EXIT_FAILURE; + } - bool CleanExit = true; - if (auto Err = dap.Loop()) { - if (dap.log) - *dap.log << "Transport Error: " << llvm::toString(std::move(Err)) << "\n"; - CleanExit = false; + std::string address = listener->GetListeningConnectionURI(); + + if (log) + *log << "started with connection listeners " << address << "\n"; + + std::cout << "Listening for: " << address << std::endl; + // Ensure listening address are flushed for calles to retrieve the resolve + // address. + std::flush(std::cout); + + MainLoop mainloop; + mainloop.RegisterSignal( + SIGHUP, [](auto &RL) { RL.RequestTermination(); }, error); + + auto OnAccept = [=](std::unique_ptr<Socket> client) { + // Start a thread for each connection, unblocking the listening thread. + std::thread([=, client = std::move(client)]() { + HandleClient( + /*out_fd=*/-1, /*err_fd=*/-1, + StreamDescriptor::from_socket(client->GetNativeSocket()), + StreamDescriptor::from_socket(client->GetNativeSocket())); + }).detach(); + }; + + auto handles = listener->Accept(mainloop, OnAccept); + if (auto Err = handles.takeError()) { + std::cerr << "failed to register accept() with the main loop: " + << llvm::toString(std::move(Err)) << std::endl; + return EXIT_FAILURE; + } + + error = mainloop.Run(); + if (error.Fail()) { + std::cerr << "failed to accept()" << llvm::toString(error.takeError()) + << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; } - return CleanExit ? EXIT_SUCCESS : EXIT_FAILURE; + // stdout/stderr redirection to the IDE's console + int new_stdout_fd = dup(fileno(stdout)); + bool clean_exit = HandleClient(fileno(stdout), fileno(stderr), + StreamDescriptor::from_file(fileno(stdin)), + StreamDescriptor::from_file(new_stdout_fd)); + + return clean_exit ? EXIT_SUCCESS : EXIT_FAILURE; } >From 31ead1b2c5af659ffa622b77ed2fac06dc65a421 Mon Sep 17 00:00:00 2001 From: John Harrison <harj...@google.com> Date: Tue, 3 Dec 2024 15:48:21 -0800 Subject: [PATCH 2/2] Rebasing on lldb host changes. --- .../test/tools/lldb-dap/dap_server.py | 2 +- lldb/test/Shell/DAP/TestOptions.test | 4 +-- lldb/tools/lldb-dap/lldb-dap.cpp | 36 +++++++++---------- 3 files changed, 19 insertions(+), 23 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 37679462118531..8003062ea8df1f 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 @@ -1242,7 +1242,7 @@ def launch( ) # If the listener expanded into multiple addresses, use the first. - connection = out.removeprefix(expected_prefix).rstrip("\r\n") + connection = out.removeprefix(expected_prefix).rstrip("\r\n").split(",", 1)[0] return proc, connection return proc, None diff --git a/lldb/test/Shell/DAP/TestOptions.test b/lldb/test/Shell/DAP/TestOptions.test index e37e9116e3cddb..d290cdae590fd6 100644 --- a/lldb/test/Shell/DAP/TestOptions.test +++ b/lldb/test/Shell/DAP/TestOptions.test @@ -1,8 +1,8 @@ # RUN: lldb-dap --help | FileCheck %s +# CHECK: --connection # CHECK: -g # CHECK: --help # CHECK: -h -# CHECK: --port -# CHECK: -p +# CHECK: --repl-mode # CHECK: --wait-for-debugger diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp index 68b482e80e4170..0bec52c5281a3d 100644 --- a/lldb/tools/lldb-dap/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/lldb-dap.cpp @@ -5042,8 +5042,7 @@ int main(int argc, char *argv[]) { if (log) *log << "configureIO failed: " << llvm::toStringWithoutConsuming(Err) << "\n"; - std::cerr << "failed to configureIO: " << llvm::toString(std::move(Err)) - << std::endl; + llvm::errs() << "failed to configureIO: " << Err << "\n"; return false; } @@ -5056,8 +5055,7 @@ int main(int argc, char *argv[]) { *log << "Transport Error: " << llvm::toStringWithoutConsuming(Err) << "\n"; - std::cerr << "Transpot Error: " << llvm::toString(std::move(Err)) - << std::endl; + llvm::errs() << "Transpot Error: " << Err << "\n"; return false; } @@ -5071,8 +5069,8 @@ int main(int argc, char *argv[]) { if (!connection.empty()) { auto maybeProtoclAndName = parseConnection(connection); if (auto Err = maybeProtoclAndName.takeError()) { - std::cerr << "Invalid connection specification " - << llvm::toString(std::move(Err)) << std::endl; + llvm::errs() << "Invalid connection specification " + << Err << "\n"; return EXIT_FAILURE; } @@ -5081,31 +5079,30 @@ int main(int argc, char *argv[]) { std::tie(protocol, name) = *maybeProtoclAndName; Status error; - std::unique_ptr<Socket> listener = Socket::Create(protocol, false, error); + std::unique_ptr<Socket> listener = Socket::Create(protocol, error); if (error.Fail()) { - std::cerr << "Failed to create listener for protocol " - << Socket::FindSchemeByProtocol(protocol) - << ", error: " << llvm::toString(error.takeError()) - << std::endl; + llvm::errs() << "Failed to create listener for protocol " + << Socket::FindSchemeByProtocol(protocol) + << ", error: " << error.takeError() << "\n"; return EXIT_FAILURE; } error = listener->Listen(name, /* backlog */ 5); if (error.Fail()) { - std::cerr << "Failed to listen, error: " - << llvm::toString(error.takeError()) << std::endl; + llvm::errs() << "Failed to listen, error: " + << error.takeError() << "\n"; return EXIT_FAILURE; } - std::string address = listener->GetListeningConnectionURI(); + std::string address = llvm::join(listener->GetListeningConnectionURI(), ", "); if (log) *log << "started with connection listeners " << address << "\n"; - std::cout << "Listening for: " << address << std::endl; + llvm::outs() << "Listening for: " << address << "\n"; // Ensure listening address are flushed for calles to retrieve the resolve // address. - std::flush(std::cout); + llvm::outs().flush(); MainLoop mainloop; mainloop.RegisterSignal( @@ -5123,15 +5120,14 @@ int main(int argc, char *argv[]) { auto handles = listener->Accept(mainloop, OnAccept); if (auto Err = handles.takeError()) { - std::cerr << "failed to register accept() with the main loop: " - << llvm::toString(std::move(Err)) << std::endl; + llvm::errs() << "failed to register accept() with the main loop: " + << Err << "\n"; return EXIT_FAILURE; } error = mainloop.Run(); if (error.Fail()) { - std::cerr << "failed to accept()" << llvm::toString(error.takeError()) - << std::endl; + llvm::errs() << "failed to accept()" << error.takeError() << "\n"; return EXIT_FAILURE; } _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits