Index: include/lldb/Target/Platform.h =================================================================== --- include/lldb/Target/Platform.h +++ include/lldb/Target/Platform.h @@ -987,6 +987,12 @@ virtual uint32_t GetDefaultMemoryCacheLineSize() { return 0; } + virtual Error + LaunchRemoteGdbServer(lldb::pid_t &pid, std::string &connect_url) + { + return Error ("Not implemented"); + } + 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 process 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 @@ -1248,6 +1248,77 @@ }; //---------------------------------------------------------------------- +// "platform process connect " +//---------------------------------------------------------------------- +class CommandObjectPlatformProcessConnect : public CommandObjectParsed +{ +public: + CommandObjectPlatformProcessConnect (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "platform process connect", + "Connect to a remote platform with a pre-specified inferior launch config.", + "platform process connect ", + 0) + { + } + + ~CommandObjectPlatformProcessConnect () override = default; + +protected: + bool + DoExecute (Args& args, CommandReturnObject &result) override + { + Stream &ostrm = result.GetOutputStream(); + + PlatformSP platform_sp (m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); + if (!platform_sp) + { + result.AppendError ("no platform is currently selected\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Error error (platform_sp->ConnectRemote (args)); + if (error.Fail()) + { + result.AppendErrorWithFormat ("%s\n", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + platform_sp->GetStatus (ostrm); + + lldb::pid_t pid = 0; + std::string connect_url; + error = platform_sp->LaunchRemoteGdbServer(pid, connect_url); + if (error.Fail()) + { + result.AppendErrorWithFormat ("%s\n", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + StreamString command; + command.Printf("process connect %s", connect_url.c_str()); + return m_interpreter.HandleCommand(command.GetData(), LazyBool::eLazyBoolNo, result); + } + + Options * + GetOptions () override + { + PlatformSP platform_sp (m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); + OptionGroupOptions* m_platform_options = NULL; + if (platform_sp) + { + m_platform_options = platform_sp->GetConnectionOptions(m_interpreter); + if (m_platform_options != NULL && !m_platform_options->m_did_finalize) + m_platform_options->Finalize(); + } + return m_platform_options; + } +}; + +//---------------------------------------------------------------------- // "platform process launch" //---------------------------------------------------------------------- class CommandObjectPlatformProcessLaunch : public CommandObjectParsed @@ -1967,19 +2038,17 @@ CommandObjectMultiword (interpreter, "platform process", "A set of commands to query, launch and attach to platform processes", - "platform process [attach|launch|list] ...") - { - LoadSubCommand ("attach", CommandObjectSP (new CommandObjectPlatformProcessAttach (interpreter))); - LoadSubCommand ("launch", CommandObjectSP (new CommandObjectPlatformProcessLaunch (interpreter))); - LoadSubCommand ("info" , CommandObjectSP (new CommandObjectPlatformProcessInfo (interpreter))); - LoadSubCommand ("list" , CommandObjectSP (new CommandObjectPlatformProcessList (interpreter))); - - } - - ~CommandObjectPlatformProcess () override + "platform process []") { + LoadSubCommand ("attach" , CommandObjectSP (new CommandObjectPlatformProcessAttach (interpreter))); + LoadSubCommand ("connect", CommandObjectSP (new CommandObjectPlatformProcessConnect (interpreter))); + LoadSubCommand ("launch" , CommandObjectSP (new CommandObjectPlatformProcessLaunch (interpreter))); + LoadSubCommand ("info" , CommandObjectSP (new CommandObjectPlatformProcessInfo (interpreter))); + LoadSubCommand ("list" , CommandObjectSP (new CommandObjectPlatformProcessList (interpreter))); } - + + ~CommandObjectPlatformProcess () override = default; + private: //------------------------------------------------------------------ // For CommandObjectPlatform only Index: source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h =================================================================== --- source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h +++ source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.h @@ -44,8 +44,8 @@ std::map m_port_forwards; llvm::Optional m_socket_namespace; - bool - LaunchGDBServer (lldb::pid_t &pid, std::string &connect_url) override; + Error + LaunchRemoteGdbServer (lldb::pid_t &pid, std::string &connect_url) override; bool KillSpawnedProcess (lldb::pid_t pid) override; Index: source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp =================================================================== --- source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp +++ source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp @@ -88,13 +88,13 @@ DeleteForwardPortWithAdb(it.second, m_device_id); } -bool -PlatformAndroidRemoteGDBServer::LaunchGDBServer (lldb::pid_t &pid, std::string &connect_url) +Error +PlatformAndroidRemoteGDBServer::LaunchRemoteGdbServer (lldb::pid_t &pid, std::string &connect_url) { uint16_t remote_port = 0; std::string socket_name; if (!m_gdb_client.LaunchGDBServer ("127.0.0.1", pid, remote_port, socket_name)) - return false; + return Error("Failed to launch gdbserver on the remote host"); Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); @@ -105,7 +105,7 @@ if (error.Success() && log) log->Printf("gdbserver connect URL: %s", connect_url.c_str()); - return error.Success(); + return error; } bool Index: source/Plugins/Platform/POSIX/PlatformPOSIX.h =================================================================== --- source/Plugins/Platform/POSIX/PlatformPOSIX.h +++ source/Plugins/Platform/POSIX/PlatformPOSIX.h @@ -173,11 +173,14 @@ lldb_private::Error DisconnectRemote () override; + lldb_private::Error + LaunchRemoteGdbServer(lldb::pid_t &pid, std::string &connect_url) override; + protected: std::unique_ptr m_options; - + lldb::PlatformSP m_remote_platform_sp; // Allow multiple ways to connect to a remote POSIX-compliant OS - + private: DISALLOW_COPY_AND_ASSIGN (PlatformPOSIX); }; Index: source/Plugins/Platform/POSIX/PlatformPOSIX.cpp =================================================================== --- source/Plugins/Platform/POSIX/PlatformPOSIX.cpp +++ source/Plugins/Platform/POSIX/PlatformPOSIX.cpp @@ -846,6 +846,16 @@ void PlatformPOSIX::CalculateTrapHandlerSymbolNames () -{ +{ m_trap_handlers.push_back (ConstString ("_sigtramp")); -} +} + +Error +PlatformPOSIX::LaunchRemoteGdbServer (lldb::pid_t &pid, std::string &connect_url) +{ + if (IsHost()) + return Error("Not supported during local debugging"); + if (!m_remote_platform_sp) + return Error("The platform currently isn't connected"); + return m_remote_platform_sp->LaunchRemoteGdbServer(pid, connect_url); +} 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,13 @@ const lldb::UnixSignalsSP & GetRemoteUnixSignals() override; + // Launch the debug server on the remote host - caller connects to launched + // debug server using connect_url. + // Subclasses should override this method if they want to do extra actions before or + // after launching the debug server. + Error + LaunchRemoteGdbServer (lldb::pid_t &pid, std::string &connect_url) 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 @@ -225,12 +232,6 @@ lldb::UnixSignalsSP m_remote_signals_sp; - // Launch the debug server on the remote host - caller connects to launched - // debug server using connect_url. - // Subclasses should override this method if they want to do extra actions before or - // after launching the debug server. - virtual bool - LaunchGDBServer (lldb::pid_t &pid, std::string &connect_url); virtual bool KillSpawnedProcess (lldb::pid_t pid); Index: source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp =================================================================== --- source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp +++ source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp @@ -574,7 +574,7 @@ { lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID; std::string connect_url; - if (!LaunchGDBServer(debugserver_pid, connect_url)) + if (LaunchRemoteGdbServer(debugserver_pid, connect_url).Fail()) { error.SetErrorStringWithFormat ("unable to launch a GDB server on '%s'", GetHostname ()); } @@ -629,8 +629,8 @@ } -bool -PlatformRemoteGDBServer::LaunchGDBServer (lldb::pid_t &pid, std::string &connect_url) +Error +PlatformRemoteGDBServer::LaunchRemoteGdbServer (lldb::pid_t &pid, std::string &connect_url) { ArchSpec remote_arch = GetRemoteSystemArchitecture (); llvm::Triple &remote_triple = remote_arch.GetTriple (); @@ -652,13 +652,13 @@ } if (!launch_result) - return false; + return Error("Failed to launch gdbserver on the remote host"); connect_url = MakeGdbServerUrl(m_platform_scheme, m_platform_hostname, port, (socket_name.empty()) ? nullptr : socket_name.c_str()); - return true; + return Error(); } bool @@ -680,7 +680,7 @@ { lldb::pid_t debugserver_pid = LLDB_INVALID_PROCESS_ID; std::string connect_url; - if (!LaunchGDBServer(debugserver_pid, connect_url)) + if (LaunchRemoteGdbServer(debugserver_pid, connect_url).Fail()) { error.SetErrorStringWithFormat ("unable to launch a GDB server on '%s'", GetHostname ()); } 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/GDBRemoteCommunicationServerPlatform.h =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.h @@ -66,6 +66,9 @@ void SetPortOffset (uint16_t port_offset); + void + SetInferiorArguments (const lldb_private::Args& args); + protected: const Socket::SocketProtocol m_socket_protocol; const std::string m_socket_scheme; @@ -75,6 +78,7 @@ PortMap m_port_map; uint16_t m_port_offset; + lldb_private::Args m_inferior_arguments; PacketResult Handle_qLaunchGDBServer (StringExtractorGDBRemote &packet); Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp =================================================================== --- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp +++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp @@ -165,7 +165,8 @@ Error error = StartDebugserverProcess (url.str().c_str(), nullptr, debugserver_launch_info, - port_ptr); + port_ptr, + m_inferior_arguments); lldb::pid_t debugserver_pid = debugserver_launch_info.GetProcessID(); @@ -553,7 +554,13 @@ } void -GDBRemoteCommunicationServerPlatform::SetPortOffset (uint16_t port_offset) +GDBRemoteCommunicationServerPlatform::SetPortOffset(uint16_t port_offset) { m_port_offset = port_offset; } + +void +GDBRemoteCommunicationServerPlatform::SetInferiorArguments(const Args& args) +{ + m_inferior_arguments = args; +} 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 @@ -317,7 +323,9 @@ do { GDBRemoteCommunicationServerPlatform platform(acceptor_up->GetSocketProtocol(), acceptor_up->GetSocketScheme()); - + if (inferior_arguments.GetArgumentCount() > 0) + platform.SetInferiorArguments(inferior_arguments); + if (port_offset > 0) platform.SetPortOffset(port_offset);