JDevlieghere created this revision. JDevlieghere added reviewers: labath, jingham. Herald added a subscriber: abidh.
In synchronous mode, the `process connect` command and its aliases should wait for the stop event before claiming the command is complete. Currently, the stop event is always handled asynchronously by the debugger. The implementation takes the same approach as `Process::ResumeSynchronous` which hijacks the event and handles it on the current thread. Similarly, after this patch, the stop event is part of the command return object, which is the property used by the test case. Most of the discussion for this patch took place in D83446 <https://reviews.llvm.org/D83446> Repository: rLLDB LLDB https://reviews.llvm.org/D83728 Files: lldb/include/lldb/Target/Platform.h lldb/source/Commands/CommandObjectProcess.cpp lldb/source/Target/Platform.cpp lldb/test/API/functionalities/gdb_remote_client/TestProcessConnect.py
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() 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/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/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
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits