Index: docs/lldb-gdb-remote.txt =================================================================== --- docs/lldb-gdb-remote.txt +++ docs/lldb-gdb-remote.txt @@ -1597,3 +1597,29 @@ (up to 256 entries) by reading 2 pointers worth of bytes at the frame pointer (for the previous FP and PC), and follow the backchain. Most backtraces on MacOSX and iOS now don't require us to read any memory! + +//---------------------------------------------------------------------- +// "qQueryGDBServer" +// +// BRIEF +// Ask the platform for the list of gdbservers we have to connect +// +// PRIORITY TO IMPLEMENT +// Low. The packet is required to support connecting to gdbserver started +// by the platform instance automatically. +//---------------------------------------------------------------------- + +If the remote platform automatically started one or more gdbserver instance (without +lldb asking it) then it have to return the list of port number or socket name for +each of them what can be used by lldb to connect to those instances. + +The data in this packet is a JSON array of JSON objects with the following keys: +"port": (optional) +"socket_name": (optional) + +Example packet: +[ + { "port": 1234 }, + { "port": 5432 }, + { "socket_name": "foo" } +] Index: include/lldb/Target/Platform.h =================================================================== --- include/lldb/Target/Platform.h +++ include/lldb/Target/Platform.h @@ -487,6 +487,13 @@ Target *target, // Can be nullptr, if nullptr create a new target, else use existing one Error &error); + virtual lldb::ProcessSP + ConnectProcess (const char* connect_url, + const char* plugin_name, + lldb_private::Debugger &debugger, + lldb_private::Target *target, + lldb_private::Error &error); + //------------------------------------------------------------------ /// Attach to an existing process using a process ID. /// @@ -1019,6 +1026,9 @@ virtual Error UnloadImage (lldb_private::Process* process, uint32_t image_token); + virtual size_t + GetPendingGdbServerList(std::vector& connection_urls); + protected: bool m_is_host; // Set to true when we are able to actually set the OS version while Index: packages/Python/lldbsuite/test/lldbtest.py =================================================================== --- packages/Python/lldbsuite/test/lldbtest.py +++ packages/Python/lldbsuite/test/lldbtest.py @@ -1098,7 +1098,7 @@ # @skipIf(bugnumber, ["linux"], "gcc", ['>=', '4.9'], ['i386']), skip for gcc>=4.9 on linux with i386 # TODO: refactor current code, to make skipIfxxx functions to call this function -def skipIf(bugnumber=None, oslist=None, compiler=None, compiler_version=None, archs=None, debug_info=None, swig_version=None, py_version=None): +def skipIf(bugnumber=None, oslist=None, compiler=None, compiler_version=None, archs=None, debug_info=None, swig_version=None, py_version=None, remote=None): def fn(self): oslist_passes = oslist is None or self.getPlatform() in oslist compiler_passes = compiler is None or (compiler in self.getCompiler() and self.expectedCompilerVersion(compiler_version)) @@ -1106,13 +1106,15 @@ debug_info_passes = debug_info is None or self.debug_info in debug_info swig_version_passes = (swig_version is None) or (not hasattr(lldb, 'swig_version')) or (check_expected_version(swig_version[0], swig_version[1], lldb.swig_version)) py_version_passes = (py_version is None) or check_expected_version(py_version[0], py_version[1], sys.version_info) + remote_passes = (remote is None) or (remote == (lldb.remote_platform is not None)) return (oslist_passes and compiler_passes and arch_passes and debug_info_passes and swig_version_passes and - py_version_passes) + py_version_passes and + remote_passes) local_vars = locals() args = [x for x in inspect.getargspec(skipIf).args] Index: packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py =================================================================== --- packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py +++ packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py @@ -146,29 +146,30 @@ return stub_port + def run_shell_cmd(self, cmd): + platform = self.dbg.GetSelectedPlatform() + shell_cmd = lldb.SBPlatformShellCommand(cmd) + err = platform.Run(shell_cmd) + if err.Fail() or shell_cmd.GetStatus(): + m = "remote_platform.RunShellCommand('%s') failed:\n" % cmd + m += ">>> return code: %d\n" % shell_cmd.GetStatus() + if err.Fail(): + m += ">>> %s\n" % str(err).strip() + m += ">>> %s\n" % (shell_cmd.GetOutput() or + "Command generated no output.") + raise Exception(m) + return shell_cmd.GetOutput().strip() + def init_llgs_test(self, use_named_pipe=True): if lldb.remote_platform: - def run_shell_cmd(cmd): - platform = self.dbg.GetSelectedPlatform() - shell_cmd = lldb.SBPlatformShellCommand(cmd) - err = platform.Run(shell_cmd) - if err.Fail() or shell_cmd.GetStatus(): - m = "remote_platform.RunShellCommand('%s') failed:\n" % cmd - m += ">>> return code: %d\n" % shell_cmd.GetStatus() - if err.Fail(): - m += ">>> %s\n" % str(err).strip() - m += ">>> %s\n" % (shell_cmd.GetOutput() or - "Command generated no output.") - raise Exception(m) - return shell_cmd.GetOutput().strip() # Remote platforms don't support named pipe based port negotiation use_named_pipe = False # Grab the ppid from /proc/[shell pid]/stat - shell_stat = run_shell_cmd("cat /proc/$$/stat") + shell_stat = self.run_shell_cmd("cat /proc/$$/stat") # [pid] ([executable]) [state] [*ppid*] pid = re.match(r"^\d+ \(.+\) . (\d+)", shell_stat).group(1) - ls_output = run_shell_cmd("ls -l /proc/%s/exe" % pid) + ls_output = self.run_shell_cmd("ls -l /proc/%s/exe" % pid) exe = ls_output.split()[-1] # If the binary has been deleted, the link name has " (deleted)" appended. Index: packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/Makefile =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules Index: packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/TestPlatformProcessConnect.py =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/TestPlatformProcessConnect.py @@ -0,0 +1,55 @@ +from __future__ import print_function + +import gdbremote_testcase +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + +class TestPlatformProcessConnect(gdbremote_testcase.GdbRemoteTestCaseBase): + mydir = TestBase.compute_mydir(__file__) + + @llgs_test + @no_debug_info_test + @skipIf(remote=False) + def test_platform_process_connect(self): + self.build() + self.init_llgs_test(False) + + working_dir = lldb.remote_platform.GetWorkingDirectory() + err = lldb.remote_platform.Put(lldb.SBFileSpec(os.path.join(os.getcwd(), "a.out")), + lldb.SBFileSpec(os.path.join(working_dir, "a.out"))) + if err.Fail(): + raise RuntimeError("Unable copy '%s' to '%s'.\n>>> %s" % (f, wd, err.GetCString())) + + port_file = "%s/port" % working_dir + commandline_args = ["platform", "--listen", "*:0", "--socket-file", port_file, "--", "%s/a.out" % working_dir, "foo"] + self.spawnSubprocess(self.debug_monitor_exe, commandline_args, install_remote=False) + self.addTearDownHook(self.cleanupSubprocesses) + new_port = self.run_shell_cmd("while [ ! -f %s ]; do sleep 0.25; done && cat %s" % (port_file, port_file)) + + new_debugger = lldb.SBDebugger.Create() + new_debugger.SetAsync(False) + def del_debugger(): + del new_debugger + self.addTearDownHook(del_debugger) + + new_platform = lldb.SBPlatform(lldb.remote_platform.GetName()) + new_debugger.SetSelectedPlatform(new_platform) + new_interpreter = new_debugger.GetCommandInterpreter() + + m = re.search("(.*):[0-9]+", lldb.platform_url) + command = "platform connect %s:%s" % (m.group(1), new_port) + result = lldb.SBCommandReturnObject() + new_interpreter.HandleCommand(command, result) + self.assertTrue(result.Succeeded(), "platform process connect failed: %s" % result.GetOutput()) + + target = new_debugger.GetSelectedTarget() + process = target.GetProcess() + thread = process.GetThreadAtIndex(0) + + breakpoint = target.BreakpointCreateByName("main") + process.Continue() + + frame = thread.GetFrameAtIndex(0) + self.assertEqual(frame.GetFunction().GetName(), "main") + self.assertEqual(frame.FindVariable("argc").GetValueAsSigned(), 2) + process.Continue() Index: packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/main.cpp =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/tools/lldb-server/platform-process-connect/main.cpp @@ -0,0 +1,7 @@ +#include + +int main (int argc, char **argv) +{ + printf("argc: %d\n", argc); + return argv[0][0]; +} Index: source/Commands/CommandObjectPlatform.cpp =================================================================== --- source/Commands/CommandObjectPlatform.cpp +++ source/Commands/CommandObjectPlatform.cpp @@ -409,12 +409,29 @@ if (error.Success()) { platform_sp->GetStatus (ostrm); - result.SetStatus (eReturnStatusSuccessFinishResult); + result.SetStatus (eReturnStatusSuccessFinishResult); + + std::vector connection_urls; + platform_sp->GetPendingGdbServerList (connection_urls); + for (const auto& url : connection_urls) + { + platform_sp->ConnectProcess(url.c_str(), + nullptr, + m_interpreter.GetDebugger(), + nullptr, + error); + if (error.Fail()) + { + result.AppendError(error.AsCString()); + result.SetStatus (eReturnStatusFailed); + break; + } + } } else { result.AppendErrorWithFormat ("%s\n", error.AsCString()); - result.SetStatus (eReturnStatusFailed); + result.SetStatus (eReturnStatusFailed); } } else Index: source/Commands/CommandObjectProcess.cpp =================================================================== --- source/Commands/CommandObjectProcess.cpp +++ source/Commands/CommandObjectProcess.cpp @@ -1051,78 +1051,46 @@ bool DoExecute (Args& command, CommandReturnObject &result) override { - - TargetSP target_sp (m_interpreter.GetDebugger().GetSelectedTarget()); - Error error; - Process *process = m_exe_ctx.GetProcessPtr(); - if (process) + if (command.GetArgumentCount() != 1) { - if (process->IsAlive()) - { - result.AppendErrorWithFormat ("Process %" PRIu64 " is currently being debugged, kill the process before connecting.\n", - process->GetID()); - result.SetStatus (eReturnStatusFailed); - return false; - } + result.AppendErrorWithFormat ("'%s' takes exactly one argument:\nUsage: %s\n", + m_cmd_name.c_str(), + m_cmd_syntax.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; } + - if (!target_sp) + Process *process = m_exe_ctx.GetProcessPtr(); + if (process && process->IsAlive()) { - // If there isn't a current target create one. - - error = m_interpreter.GetDebugger().GetTargetList().CreateTarget (m_interpreter.GetDebugger(), - NULL, - NULL, - false, - NULL, // No platform options - target_sp); - if (!target_sp || error.Fail()) - { - result.AppendError(error.AsCString("Error creating target")); - result.SetStatus (eReturnStatusFailed); - return false; - } - m_interpreter.GetDebugger().GetTargetList().SetSelectedTarget(target_sp.get()); + result.AppendErrorWithFormat ("Process %" PRIu64 " is currently being debugged, kill the process before connecting.\n", + process->GetID()); + result.SetStatus (eReturnStatusFailed); + return false; } - - if (command.GetArgumentCount() == 1) - { - const char *plugin_name = NULL; - if (!m_options.plugin_name.empty()) - plugin_name = m_options.plugin_name.c_str(); - const char *remote_url = command.GetArgumentAtIndex(0); - process = target_sp->CreateProcess (m_interpreter.GetDebugger().GetListener(), plugin_name, NULL).get(); - - if (process) - { - error = process->ConnectRemote (process->GetTarget().GetDebugger().GetOutputFile().get(), remote_url); + const char *plugin_name = nullptr; + if (!m_options.plugin_name.empty()) + plugin_name = m_options.plugin_name.c_str(); - if (error.Fail()) - { - result.AppendError(error.AsCString("Remote connect failed")); - result.SetStatus (eReturnStatusFailed); - target_sp->DeleteCurrentProcess(); - return false; - } - } - else - { - result.AppendErrorWithFormat ("Unable to find process plug-in for remote URL '%s'.\nPlease specify a process plug-in name with the --plugin option, or specify an object file using the \"file\" command.\n", - remote_url); - result.SetStatus (eReturnStatusFailed); - } - } - else + Error error; + Debugger& debugger = m_interpreter.GetDebugger(); + PlatformSP platform_sp = m_interpreter.GetPlatform(true); + ProcessSP process_sp = platform_sp->ConnectProcess(command.GetArgumentAtIndex(0), + plugin_name, + debugger, + debugger.GetSelectedTarget().get(), + error); + if (error.Fail() || process_sp == nullptr) { - result.AppendErrorWithFormat ("'%s' takes exactly one argument:\nUsage: %s\n", - m_cmd_name.c_str(), - m_cmd_syntax.c_str()); + result.AppendError(error.AsCString("Error connecting to the process")); result.SetStatus (eReturnStatusFailed); + return false; } - return result.Succeeded(); + return true; } - + CommandOptions m_options; }; Index: source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h =================================================================== --- source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h +++ source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h @@ -39,6 +39,13 @@ Error DisconnectRemote () override; + lldb::ProcessSP + ConnectProcess (const char* connect_url, + const char* plugin_name, + lldb_private::Debugger &debugger, + lldb_private::Target *target, + lldb_private::Error &error) override; + protected: std::string m_device_id; std::map m_port_forwards; Index: source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp =================================================================== --- source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp +++ source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp @@ -223,3 +223,38 @@ return error; } + +lldb::ProcessSP +PlatformAndroidRemoteGDBServer::ConnectProcess(const char* connect_url, + const char* plugin_name, + lldb_private::Debugger &debugger, + lldb_private::Target *target, + lldb_private::Error &error) +{ + // We don't have the pid of the remote gdbserver when it isn't started by us but we still want + // to store the list of prot forwards we set up in our port forward map. Generate a fake pid for + // these cases what won't collide with any other valid pid on android. + static lldb::pid_t s_remote_gdbserver_fake_pid = 0xffffffffffffffffULL; + + int remote_port; + std::string scheme, host, path; + if (!UriParser::Parse(connect_url, scheme, host, remote_port, path)) + { + error.SetErrorStringWithFormat("Invalid URL: %s", connect_url); + return nullptr; + } + + std::string new_connect_url; + error = MakeConnectURL(s_remote_gdbserver_fake_pid--, + (remote_port < 0) ? 0 : remote_port, + path.c_str(), + new_connect_url); + if (error.Fail()) + return nullptr; + + return PlatformRemoteGDBServer::ConnectProcess(new_connect_url.c_str(), + plugin_name, + debugger, + target, + error); +} Index: source/Plugins/Platform/POSIX/PlatformPOSIX.h =================================================================== --- source/Plugins/Platform/POSIX/PlatformPOSIX.h +++ source/Plugins/Platform/POSIX/PlatformPOSIX.h @@ -181,6 +181,16 @@ lldb_private::Error UnloadImage (lldb_private::Process* process, uint32_t image_token) override; + lldb::ProcessSP + ConnectProcess (const char* connect_url, + const char* plugin_name, + lldb_private::Debugger &debugger, + lldb_private::Target *target, + lldb_private::Error &error) override; + + size_t + GetPendingGdbServerList(std::vector& connection_urls) override; + protected: std::unique_ptr m_options; lldb::PlatformSP m_remote_platform_sp; // Allow multiple ways to connect to a remote POSIX-compliant OS Index: source/Plugins/Platform/POSIX/PlatformPOSIX.cpp =================================================================== --- source/Plugins/Platform/POSIX/PlatformPOSIX.cpp +++ source/Plugins/Platform/POSIX/PlatformPOSIX.cpp @@ -984,6 +984,31 @@ process->ResetImageToken(image_token); } return Error(); +} + +lldb::ProcessSP +PlatformPOSIX::ConnectProcess (const char* connect_url, + const char* plugin_name, + lldb_private::Debugger &debugger, + lldb_private::Target *target, + lldb_private::Error &error) +{ + if (m_remote_platform_sp) + return m_remote_platform_sp->ConnectProcess(connect_url, + plugin_name, + debugger, + target, + error); + + return Platform::ConnectProcess(connect_url, plugin_name, debugger, target, error); +} + +size_t +PlatformPOSIX::GetPendingGdbServerList (std::vector& connection_urls) +{ + if (m_remote_platform_sp) + return m_remote_platform_sp->GetPendingGdbServerList(connection_urls); + return Platform::GetPendingGdbServerList(connection_urls); } const char* Index: source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h =================================================================== --- source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h +++ source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h @@ -217,6 +217,16 @@ const lldb::UnixSignalsSP & GetRemoteUnixSignals() override; + lldb::ProcessSP + ConnectProcess (const char* connect_url, + const char* plugin_name, + lldb_private::Debugger &debugger, + lldb_private::Target *target, + lldb_private::Error &error) override; + + size_t + GetPendingGdbServerList(std::vector& connection_urls) override; + protected: process_gdb_remote::GDBRemoteCommunicationClient m_gdb_client; std::string m_platform_description; // After we connect we can get a more complete description of what we are connected to Index: source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp =================================================================== --- source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp +++ source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp @@ -21,6 +21,7 @@ #include "lldb/Core/ModuleList.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamFile.h" #include "lldb/Core/StreamString.h" #include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/FileSpec.h" @@ -984,3 +985,34 @@ result.Write(path, strlen(path)); return result.GetString(); } + +lldb::ProcessSP +PlatformRemoteGDBServer::ConnectProcess(const char* connect_url, + const char* plugin_name, + lldb_private::Debugger &debugger, + lldb_private::Target *target, + lldb_private::Error &error) +{ + if (!IsRemote() || !IsConnected()) + { + error.SetErrorString("Not connected to remote gdb server"); + return nullptr; + } + return Platform::ConnectProcess(connect_url, plugin_name, debugger, target, error); +} + +size_t +PlatformRemoteGDBServer::GetPendingGdbServerList(std::vector& connection_urls) +{ + std::vector> remote_servers; + m_gdb_client.QueryGDBServer(remote_servers); + for (const auto& gdbserver : remote_servers) + { + const char* socket_name_cstr = gdbserver.second.empty() ? nullptr : gdbserver.second.c_str(); + connection_urls.emplace_back(MakeGdbServerUrl(m_platform_scheme, + m_platform_hostname, + gdbserver.first, + socket_name_cstr)); + } + return connection_urls.size(); +} Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h @@ -25,6 +25,7 @@ #include "lldb/Host/Mutex.h" #include "lldb/Host/Predicate.h" #include "lldb/Host/TimeValue.h" +#include "lldb/Interpreter/Args.h" #include "Utility/StringExtractorGDBRemote.h" @@ -168,7 +169,8 @@ StartDebugserverProcess(const char *url, Platform *platform, // If non nullptr, then check with the platform for the GDB server binary if it can't be located ProcessLaunchInfo &launch_info, - uint16_t *port); + uint16_t *port, + const Args& inferior_args = Args()); void DumpHistory(Stream &strm); Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -1115,7 +1115,8 @@ GDBRemoteCommunication::StartDebugserverProcess (const char *url, Platform *platform, ProcessLaunchInfo &launch_info, - uint16_t *port) + uint16_t *port, + const Args& inferior_args) { Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PROCESS)); if (log) @@ -1328,6 +1329,20 @@ } } while (has_env_var); + if (inferior_args.GetArgumentCount() > 0) + { + debugserver_args.AppendArgument ("--"); + debugserver_args.AppendArguments (inferior_args); + } + + // Copy the current environment to the gdbserver/debugserver instance + StringList env; + if (Host::GetEnvironment(env)) + { + for (size_t i = 0; i < env.GetSize(); ++i) + launch_info.GetEnvironmentEntries().AppendArgument(env[i].c_str()); + } + // Close STDIN, STDOUT and STDERR. launch_info.AppendCloseFileAction (STDIN_FILENO); launch_info.AppendCloseFileAction (STDOUT_FILENO); Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -119,7 +119,10 @@ lldb::pid_t &pid, uint16_t &port, std::string &socket_name); - + + size_t + QueryGDBServer (std::vector>& connection_urls); + bool KillSpawnedProcess (lldb::pid_t pid); Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -3383,6 +3383,43 @@ return false; } +size_t +GDBRemoteCommunicationClient::QueryGDBServer (std::vector>& connection_urls) +{ + connection_urls.clear(); + + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qQueryGDBServer", response, false) != PacketResult::Success) + return 0; + + StructuredData::ObjectSP data = StructuredData::ParseJSON(response.GetStringRef()); + if (!data) + return 0; + + StructuredData::Array* array = data->GetAsArray(); + if (!array) + return 0; + + for (size_t i = 0, count = array->GetSize(); i < count; ++i) + { + StructuredData::Dictionary* element = nullptr; + if (!array->GetItemAtIndexAsDictionary(i, element)) + continue; + + uint16_t port = 0; + if (StructuredData::ObjectSP port_osp = element->GetValueForKey(llvm::StringRef("port"))) + port = port_osp->GetIntegerValue(0); + + std::string socket_name; + if (StructuredData::ObjectSP socket_name_osp = element->GetValueForKey(llvm::StringRef("socket_name"))) + socket_name = socket_name_osp->GetStringValue(); + + if (port != 0 || !socket_name.empty()) + connection_urls.emplace_back(port, socket_name); + } + return connection_urls.size(); +} + bool GDBRemoteCommunicationClient::KillSpawnedProcess (lldb::pid_t pid) { Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h @@ -66,6 +66,19 @@ void SetPortOffset (uint16_t port_offset); + void + SetInferiorArguments (const lldb_private::Args& args); + + Error + LaunchGDBServer(const lldb_private::Args& args, + std::string hostname, + lldb::pid_t& pid, + uint16_t& port, + std::string& socket_name); + + void + SetPendingGdbServer(lldb::pid_t pid, uint16_t port, const std::string& socket_name); + protected: const Socket::SocketProtocol m_socket_protocol; const std::string m_socket_scheme; @@ -75,11 +88,15 @@ PortMap m_port_map; uint16_t m_port_offset; + struct { lldb::pid_t pid; uint16_t port; std::string socket_name; } m_pending_gdb_server; PacketResult Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet); PacketResult + Handle_qQueryGDBServer (StringExtractorGDBRemote &packet); + + PacketResult Handle_qKillSpawnedProcess (StringExtractorGDBRemote &packet); PacketResult Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp @@ -34,6 +34,7 @@ #include "lldb/Target/Platform.h" #include "lldb/Target/Process.h" #include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/JSON.h" // Project includes #include "Utility/StringExtractorGDBRemote.h" @@ -54,7 +55,8 @@ m_spawned_pids_mutex (Mutex::eMutexTypeRecursive), m_platform_sp (Platform::GetHostPlatform ()), m_port_map (), - m_port_offset(0) + m_port_offset(0), + m_pending_gdb_server{ LLDB_INVALID_PROCESS_ID, 0, "" } { RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qC, &GDBRemoteCommunicationServerPlatform::Handle_qC); @@ -62,6 +64,8 @@ &GDBRemoteCommunicationServerPlatform::Handle_qGetWorkingDir); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qLaunchGDBServer, &GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qQueryGDBServer, + &GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qKillSpawnedProcess, &GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qProcessInfo, @@ -90,38 +94,16 @@ { } -GDBRemoteCommunication::PacketResult -GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet) +Error +GDBRemoteCommunicationServerPlatform::LaunchGDBServer(const lldb_private::Args& args, + std::string hostname, + lldb::pid_t& pid, + uint16_t& port, + std::string& socket_name) { -#ifdef _WIN32 - return SendErrorResponse(9); -#else - // Spawn a local debugserver as a platform so we can then attach or launch - // a process... - - Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); - if (log) - log->Printf ("GDBRemoteCommunicationServerPlatform::%s() called", __FUNCTION__); - - // Sleep and wait a bit for debugserver to start to listen... - ConnectionFileDescriptor file_conn; - std::string hostname; - // TODO: /tmp/ should not be hardcoded. User might want to override /tmp - // with the TMPDIR environment variable - packet.SetFilePos(::strlen ("qLaunchGDBServer;")); - std::string name; - std::string value; - uint16_t port = UINT16_MAX; - while (packet.GetNameColonValue(name, value)) - { - if (name.compare ("host") == 0) - hostname.swap(value); - else if (name.compare ("port") == 0) - port = StringConvert::ToUInt32(value.c_str(), 0, 0); - } if (port == UINT16_MAX) port = GetNextAvailablePort(); - + // Spawn a new thread to accept the port that gets bound after // binding to port 0 (zero). @@ -132,6 +114,8 @@ ProcessLaunchInfo debugserver_launch_info; if (hostname.empty()) hostname = "127.0.0.1"; + + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); if (log) log->Printf("Launching debugserver with: %s:%u...", hostname.c_str(), port); @@ -148,11 +132,8 @@ UNUSED_IF_ASSERT_DISABLED(ok); assert(ok); - std::string socket_name; std::ostringstream url; - uint16_t* port_ptr = &port; - url << m_socket_scheme << "://"; if (m_socket_protocol == Socket::ProtocolTcp) url << platform_ip << ":" << port; else @@ -165,56 +146,111 @@ Error error = StartDebugserverProcess (url.str().c_str(), nullptr, debugserver_launch_info, - port_ptr); - - lldb::pid_t debugserver_pid = debugserver_launch_info.GetProcessID(); - + port_ptr, + args); - if (debugserver_pid != LLDB_INVALID_PROCESS_ID) + pid = debugserver_launch_info.GetProcessID(); + if (pid != LLDB_INVALID_PROCESS_ID) { Mutex::Locker locker (m_spawned_pids_mutex); - m_spawned_pids.insert(debugserver_pid); + m_spawned_pids.insert(pid); if (port > 0) - AssociatePortWithProcess(port, debugserver_pid); + AssociatePortWithProcess(port, pid); } else { if (port > 0) - FreePort (port); + FreePort(port); } + return error; +} - if (error.Success()) +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet) +{ +#ifdef _WIN32 + return SendErrorResponse(9); +#else + // Spawn a local debugserver as a platform so we can then attach or launch + // a process... + + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); + if (log) + log->Printf ("GDBRemoteCommunicationServerPlatform::%s() called", __FUNCTION__); + + // Sleep and wait a bit for debugserver to start to listen... + ConnectionFileDescriptor file_conn; + std::string hostname; + // TODO: /tmp/ should not be hardcoded. User might want to override /tmp + // with the TMPDIR environment variable + packet.SetFilePos(::strlen ("qLaunchGDBServer;")); + std::string name; + std::string value; + uint16_t port = UINT16_MAX; + while (packet.GetNameColonValue(name, value)) + { + if (name.compare ("host") == 0) + hostname.swap(value); + else if (name.compare ("port") == 0) + port = StringConvert::ToUInt32(value.c_str(), 0, 0); + } + + lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID; + std::string socket_name; + Error error = LaunchGDBServer(Args(), hostname, debugserver_pid, port, socket_name); + if (error.Fail()) { if (log) - log->Printf ("GDBRemoteCommunicationServerPlatform::%s() debugserver launched successfully as pid %" PRIu64, __FUNCTION__, debugserver_pid); + log->Printf("GDBRemoteCommunicationServerPlatform::%s() debugserver launch failed: %s", __FUNCTION__, error.AsCString ()); + return SendErrorResponse(9); + } - StreamGDBRemote response; - response.Printf("pid:%" PRIu64 ";port:%u;", debugserver_pid, port + m_port_offset); - if (!socket_name.empty()) - { - response.PutCString("socket_name:"); - response.PutCStringAsRawHex8(socket_name.c_str()); - response.PutChar(';'); - } + if (log) + log->Printf ("GDBRemoteCommunicationServerPlatform::%s() debugserver launched successfully as pid %" PRIu64, __FUNCTION__, debugserver_pid); - PacketResult packet_result = SendPacketNoLock(response.GetData(), response.GetSize()); - if (packet_result != PacketResult::Success) - { - if (debugserver_pid != LLDB_INVALID_PROCESS_ID) - ::kill (debugserver_pid, SIGINT); - } - return packet_result; + StreamGDBRemote response; + response.Printf("pid:%" PRIu64 ";port:%u;", debugserver_pid, port + m_port_offset); + if (!socket_name.empty()) + { + response.PutCString("socket_name:"); + response.PutCStringAsRawHex8(socket_name.c_str()); + response.PutChar(';'); } - else + + PacketResult packet_result = SendPacketNoLock(response.GetData(), response.GetSize()); + if (packet_result != PacketResult::Success) { - if (log) - log->Printf ("GDBRemoteCommunicationServerPlatform::%s() debugserver launch failed: %s", __FUNCTION__, error.AsCString ()); + if (debugserver_pid != LLDB_INVALID_PROCESS_ID) + ::kill (debugserver_pid, SIGINT); } - return SendErrorResponse (9); + return packet_result; #endif } GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerPlatform::Handle_qQueryGDBServer (StringExtractorGDBRemote &packet) +{ + if (m_pending_gdb_server.pid == LLDB_INVALID_PROCESS_ID) + return SendErrorResponse(4); + + JSONObject::SP server_sp = std::make_shared(); + server_sp->SetObject("port", std::make_shared(m_pending_gdb_server.port)); + if (!m_pending_gdb_server.socket_name.empty()) + server_sp->SetObject("socket_name", + std::make_shared(m_pending_gdb_server.socket_name.c_str())); + + JSONArray server_list; + server_list.AppendObject(server_sp); + + StreamGDBRemote response; + server_list.Write(response); + + StreamGDBRemote escaped_response; + escaped_response.PutEscapedBytes(response.GetData(), response.GetSize()); + return SendPacketNoLock(escaped_response.GetData(), escaped_response.GetSize()); +} + +GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerPlatform::Handle_qKillSpawnedProcess (StringExtractorGDBRemote &packet) { packet.SetFilePos(::strlen ("qKillSpawnedProcess:")); @@ -553,7 +589,17 @@ } void -GDBRemoteCommunicationServerPlatform::SetPortOffset (uint16_t port_offset) +GDBRemoteCommunicationServerPlatform::SetPortOffset(uint16_t port_offset) { m_port_offset = port_offset; } + +void +GDBRemoteCommunicationServerPlatform::SetPendingGdbServer(lldb::pid_t pid, + uint16_t port, + const std::string& socket_name) +{ + m_pending_gdb_server.pid = pid; + m_pending_gdb_server.port = port; + m_pending_gdb_server.socket_name = socket_name; +} Index: source/Target/Platform.cpp =================================================================== --- source/Target/Platform.cpp +++ source/Target/Platform.cpp @@ -26,6 +26,7 @@ #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamFile.h" #include "lldb/Core/StructuredData.h" #include "lldb/Host/FileSpec.h" #include "lldb/Host/FileSystem.h" @@ -1996,3 +1997,49 @@ { return Error("UnLoadImage is not supported on the current platform"); } + +size_t +Platform::GetPendingGdbServerList(std::vector& connection_urls) +{ + connection_urls.clear(); + return 0; +} + +lldb::ProcessSP +Platform::ConnectProcess(const char* connect_url, + const char* plugin_name, + lldb_private::Debugger &debugger, + lldb_private::Target *target, + lldb_private::Error &error) +{ + error.Clear(); + + if (!target) + { + TargetSP new_target_sp; + error = debugger.GetTargetList().CreateTarget(debugger, + nullptr, + nullptr, + false, + nullptr, + new_target_sp); + target = new_target_sp.get(); + } + + if (!target || error.Fail()) + return nullptr; + + debugger.GetTargetList().SetSelectedTarget(target); + + lldb::ProcessSP process_sp = target->CreateProcess(debugger.GetListener(), + plugin_name, + nullptr); + if (!process_sp) + return nullptr; + + error = process_sp->ConnectRemote(debugger.GetOutputFile().get(), connect_url); + if (error.Fail()) + return nullptr; + + return process_sp; +} Index: source/Utility/StringExtractorGDBRemote.h =================================================================== --- source/Utility/StringExtractorGDBRemote.h +++ source/Utility/StringExtractorGDBRemote.h @@ -54,6 +54,7 @@ eServerPacketType_qGroupName, eServerPacketType_qHostInfo, eServerPacketType_qLaunchGDBServer, + eServerPacketType_qQueryGDBServer, eServerPacketType_qKillSpawnedProcess, eServerPacketType_qLaunchSuccess, eServerPacketType_qModuleInfo, Index: source/Utility/StringExtractorGDBRemote.cpp =================================================================== --- source/Utility/StringExtractorGDBRemote.cpp +++ source/Utility/StringExtractorGDBRemote.cpp @@ -163,12 +163,12 @@ case 'K': if (PACKET_STARTS_WITH ("qKillSpawnedProcess")) return eServerPacketType_qKillSpawnedProcess; break; - + case 'L': if (PACKET_STARTS_WITH ("qLaunchGDBServer")) return eServerPacketType_qLaunchGDBServer; if (PACKET_MATCHES ("qLaunchSuccess")) return eServerPacketType_qLaunchSuccess; break; - + case 'M': if (PACKET_STARTS_WITH ("qMemoryRegionInfo:")) return eServerPacketType_qMemoryRegionInfo; if (PACKET_MATCHES ("qMemoryRegionInfo")) return eServerPacketType_qMemoryRegionInfoSupported; @@ -182,7 +182,11 @@ if (PACKET_STARTS_WITH ("qPlatform_chmod:")) return eServerPacketType_qPlatform_chmod; if (PACKET_MATCHES ("qProcessInfo")) return eServerPacketType_qProcessInfo; break; - + + case 'Q': + if (PACKET_MATCHES ("qQueryGDBServer")) return eServerPacketType_qQueryGDBServer; + break; + case 'R': if (PACKET_STARTS_WITH ("qRcmd,")) return eServerPacketType_qRcmd; if (PACKET_STARTS_WITH ("qRegisterInfo")) return eServerPacketType_qRegisterInfo; Index: tools/lldb-server/lldb-platform.cpp =================================================================== --- tools/lldb-server/lldb-platform.cpp +++ tools/lldb-server/lldb-platform.cpp @@ -285,6 +285,12 @@ exit(option_error); } + // Skip any options we consumed with getopt_long_only. + argc -= optind; + argv += optind; + lldb_private::Args inferior_arguments; + inferior_arguments.SetArguments(argc, const_cast(argv)); + const bool children_inherit_listen_socket = false; // the test suite makes many connections in parallel, let's not miss any. // The highest this should get reasonably is a function of the number @@ -309,7 +315,7 @@ error = save_socket_id_to_file(acceptor_up->GetLocalSocketId(), socket_file); if (error.Fail()) { - fprintf(stderr, "failed to write socket id to %s: %s", socket_file.GetPath().c_str(), error.AsCString()); + fprintf(stderr, "failed to write socket id to %s: %s\n", socket_file.GetPath().c_str(), error.AsCString()); return 1; } } @@ -317,7 +323,7 @@ do { GDBRemoteCommunicationServerPlatform platform(acceptor_up->GetSocketProtocol(), acceptor_up->GetSocketScheme()); - + if (port_offset > 0) platform.SetPortOffset(port_offset); @@ -365,6 +371,22 @@ if (platform.IsConnected()) { + if (inferior_arguments.GetArgumentCount() > 0) + { + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + uint16_t port = 0; + std::string socket_name; + Error error = platform.LaunchGDBServer(inferior_arguments, + "", // hostname + pid, + port, + socket_name); + if (error.Success()) + platform.SetPendingGdbServer(pid, port, socket_name); + else + fprintf(stderr, "failed to start gdbserver: %s\n", error.AsCString()); + } + // After we connected, we need to get an initial ack from... if (platform.HandshakeWithClient()) {