Index: lldb/include/lldb/Target/Platform.h =================================================================== --- lldb/include/lldb/Target/Platform.h +++ lldb/include/lldb/Target/Platform.h @@ -372,9 +372,13 @@ virtual lldb::ProcessSP ConnectProcess(llvm::StringRef connect_url, llvm::StringRef plugin_name, - lldb_private::Debugger &debugger, - lldb_private::Target *target, - lldb_private::Status &error); + Debugger &debugger, Target *target, + Status &error); + + virtual lldb::ProcessSP + ConnectProcessSynchronous(llvm::StringRef connect_url, + llvm::StringRef plugin_name, Debugger &debugger, + Stream &stream, Target *target, Status &error); /// Attach to an existing process using a process ID. /// @@ -848,6 +852,10 @@ } protected: + lldb::ProcessSP DoConnectProcess(llvm::StringRef connect_url, + llvm::StringRef plugin_name, + Debugger &debugger, Stream *stream, + Target *target, Status &error); bool m_is_host; // Set to true when we are able to actually set the OS version while being // connected. For remote platforms, we might set the version ahead of time Index: lldb/source/Commands/CommandObjectProcess.cpp =================================================================== --- lldb/source/Commands/CommandObjectProcess.cpp +++ lldb/source/Commands/CommandObjectProcess.cpp @@ -820,9 +820,15 @@ Status error; Debugger &debugger = GetDebugger(); PlatformSP platform_sp = m_interpreter.GetPlatform(true); - ProcessSP process_sp = platform_sp->ConnectProcess( - command.GetArgumentAtIndex(0), plugin_name, debugger, - debugger.GetSelectedTarget().get(), error); + ProcessSP process_sp = + debugger.GetAsyncExecution() + ? platform_sp->ConnectProcess( + command.GetArgumentAtIndex(0), plugin_name, debugger, + debugger.GetSelectedTarget().get(), error) + : platform_sp->ConnectProcessSynchronous( + command.GetArgumentAtIndex(0), plugin_name, debugger, + result.GetOutputStream(), debugger.GetSelectedTarget().get(), + error); if (error.Fail() || process_sp == nullptr) { result.AppendError(error.AsCString("Error connecting to the process")); result.SetStatus(eReturnStatusFailed); Index: lldb/source/Target/Platform.cpp =================================================================== --- lldb/source/Target/Platform.cpp +++ lldb/source/Target/Platform.cpp @@ -1774,9 +1774,23 @@ lldb::ProcessSP Platform::ConnectProcess(llvm::StringRef connect_url, llvm::StringRef plugin_name, - lldb_private::Debugger &debugger, - lldb_private::Target *target, - lldb_private::Status &error) { + Debugger &debugger, Target *target, + Status &error) { + return DoConnectProcess(connect_url, plugin_name, debugger, nullptr, target, + error); +} + +lldb::ProcessSP Platform::ConnectProcessSynchronous( + llvm::StringRef connect_url, llvm::StringRef plugin_name, + Debugger &debugger, Stream &stream, Target *target, Status &error) { + return DoConnectProcess(connect_url, plugin_name, debugger, &stream, target, + error); +} + +lldb::ProcessSP Platform::DoConnectProcess(llvm::StringRef connect_url, + llvm::StringRef plugin_name, + Debugger &debugger, Stream *stream, + Target *target, Status &error) { error.Clear(); if (!target) { @@ -1803,13 +1817,32 @@ lldb::ProcessSP process_sp = target->CreateProcess(debugger.GetListener(), plugin_name, nullptr); + if (!process_sp) return nullptr; + // If this private method is called with a stream we are synchronous. + const bool synchronous = stream != nullptr; + + ListenerSP listener_sp( + Listener::MakeListener("lldb.Process.ConnectProcess.hijack")); + if (synchronous) + process_sp->HijackProcessEvents(listener_sp); + error = process_sp->ConnectRemote(connect_url); if (error.Fail()) return nullptr; + if (synchronous) { + EventSP event_sp; + process_sp->WaitForProcessToStop(llvm::None, &event_sp, true, listener_sp, + nullptr); + process_sp->RestoreProcessEvents(); + bool pop_process_io_handler = false; + Process::HandleProcessStateChangedEvent(event_sp, stream, + pop_process_io_handler); + } + return process_sp; } Index: lldb/test/API/functionalities/gdb_remote_client/TestProcessConnect.py =================================================================== --- /dev/null +++ lldb/test/API/functionalities/gdb_remote_client/TestProcessConnect.py @@ -0,0 +1,89 @@ +import lldb +import binascii +import os +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from gdbclientutils import * + + +def hexlify(string): + return binascii.hexlify(string.encode()).decode() + + +class ProcesConnectResponder(MockGDBServerResponder): + def __init__(self): + MockGDBServerResponder.__init__(self) + self.currentQsProc = 0 + self.all_users = False + + def qfProcessInfo(self, packet): + if "all_users:1" in packet: + self.all_users = True + name = hexlify("/a/test_process") + args = "-".join( + map(hexlify, + ["/system/bin/sh", "-c", "/data/local/tmp/lldb-server"])) + return "pid:10;ppid:1;uid:2;gid:3;euid:4;egid:5;name:" + name + ";args:" + args + ";" + else: + self.all_users = False + return "E04" + + def qsProcessInfo(self): + if self.all_users: + if self.currentQsProc == 0: + self.currentQsProc = 1 + name = hexlify("/b/another_test_process") + # This intentionally has a badly encoded argument + args = "X".join(map(hexlify, ["/system/bin/ls", "--help"])) + return "pid:11;ppid:2;uid:3;gid:4;euid:5;egid:6;name:" + name + ";args:" + args + ";" + elif self.currentQsProc == 1: + self.currentQsProc = 0 + return "E04" + else: + return "E04" + + +class TestProcesConnect(GDBRemoteTestBase): + def test_gdb_remote_sync(self): + """Test the gdb-remote command in synchronous mode""" + self.server.responder = ProcesConnectResponder() + try: + self.dbg.SetAsync(False) + self.expect("gdb-remote %d" % self.server.port, + substrs=['Process', 'stopped']) + finally: + self.dbg.GetSelectedPlatform().DisconnectRemote() + + def test_gdb_remote_async(self): + """Test the gdb-remote command in asynchronous mode""" + self.server.responder = ProcesConnectResponder() + try: + self.dbg.SetAsync(True) + self.expect("gdb-remote %d" % self.server.port, + matching=False, + substrs=['Process', 'stopped']) + finally: + self.dbg.GetSelectedPlatform().DisconnectRemote() + + def test_process_connect_sync(self): + """Test the gdb-remote command in synchronous mode""" + self.server.responder = ProcesConnectResponder() + try: + self.dbg.SetAsync(False) + self.expect("process connect connect://localhost:%d" % + self.server.port, + substrs=['Process', 'stopped']) + finally: + self.dbg.GetSelectedPlatform().DisconnectRemote() + + def test_process_connect_async(self): + """Test the gdb-remote command in asynchronous mode""" + self.server.responder = ProcesConnectResponder() + try: + self.dbg.SetAsync(True) + self.expect("process connect connect://localhost:%d" % + self.server.port, + matching=False, + substrs=['Process', 'stopped']) + finally: + self.dbg.GetSelectedPlatform().DisconnectRemote()