Author: Michał Górny Date: 2021-07-02T10:23:11+02:00 New Revision: b7c140335beb11bcbb2abe51222d7a300cd365e5
URL: https://github.com/llvm/llvm-project/commit/b7c140335beb11bcbb2abe51222d7a300cd365e5 DIFF: https://github.com/llvm/llvm-project/commit/b7c140335beb11bcbb2abe51222d7a300cd365e5.diff LOG: [lldb] [gdb-remote server] Support selecting process via Hg Support using the extended thread-id syntax with Hg packet to select a subprocess. This makes it possible to start providing support for running some of the debugger packets against another subprocesses. Differential Revision: https://reviews.llvm.org/D100261 Added: Modified: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py Removed: ################################################################################ diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp index 000d0249cea91..f6c3ba46bb515 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -2088,16 +2088,6 @@ GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_H(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD)); - // Fail if we don't have a current process. - if (!m_current_process || - (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) { - LLDB_LOGF( - log, - "GDBRemoteCommunicationServerLLGS::%s failed, no process available", - __FUNCTION__); - return SendErrorResponse(0x15); - } - // Parse out which variant of $H is requested. packet.SetFilePos(strlen("H")); if (packet.GetBytesLeft() < 1) { @@ -2109,14 +2099,14 @@ GDBRemoteCommunicationServerLLGS::Handle_H(StringExtractorGDBRemote &packet) { } const char h_variant = packet.GetChar(); - lldb::pid_t default_pid; + NativeProcessProtocol *default_process; switch (h_variant) { case 'g': - default_pid = m_current_process->GetID(); + default_process = m_current_process; break; case 'c': - default_pid = m_continue_process->GetID(); + default_process = m_continue_process; break; default: @@ -2129,16 +2119,32 @@ GDBRemoteCommunicationServerLLGS::Handle_H(StringExtractorGDBRemote &packet) { } // Parse out the thread number. - llvm::Expected<lldb::tid_t> tid_ret = - ReadTid(packet, /*allow_all=*/true, default_pid); - if (!tid_ret) - return SendErrorResponse(tid_ret.takeError()); + auto pid_tid = packet.GetPidTid(default_process ? default_process->GetID() + : LLDB_INVALID_PROCESS_ID); + if (!pid_tid) + return SendErrorResponse(llvm::make_error<StringError>( + inconvertibleErrorCode(), "Malformed thread-id")); + + lldb::pid_t pid = pid_tid->first; + lldb::tid_t tid = pid_tid->second; + + if (pid == StringExtractorGDBRemote::AllProcesses) + return SendUnimplementedResponse("Selecting all processes not supported"); + if (pid == LLDB_INVALID_PROCESS_ID) + return SendErrorResponse(llvm::make_error<StringError>( + inconvertibleErrorCode(), "No current process and no PID provided")); + + // Check the process ID and find respective process instance. + auto new_process_it = m_debugged_processes.find(pid); + if (new_process_it == m_debugged_processes.end()) + return SendErrorResponse(llvm::make_error<StringError>( + inconvertibleErrorCode(), + llvm::formatv("No process with PID {0} debugged", pid))); - lldb::tid_t tid = tid_ret.get(); // Ensure we have the given thread when not specifying -1 (all threads) or 0 // (any thread). if (tid != LLDB_INVALID_THREAD_ID && tid != 0) { - NativeThreadProtocol *thread = m_current_process->GetThreadByID(tid); + NativeThreadProtocol *thread = new_process_it->second->GetThreadByID(tid); if (!thread) { LLDB_LOGF(log, "GDBRemoteCommunicationServerLLGS::%s failed, tid %" PRIu64 @@ -2148,13 +2154,15 @@ GDBRemoteCommunicationServerLLGS::Handle_H(StringExtractorGDBRemote &packet) { } } - // Now switch the given thread type. + // Now switch the given process and thread type. switch (h_variant) { case 'g': + m_current_process = new_process_it->second.get(); SetCurrentThreadID(tid); break; case 'c': + m_continue_process = new_process_it->second.get(); SetContinueThreadID(tid); break; diff --git a/lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py b/lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py index af39dbbb188b2..bcf728c34b797 100644 --- a/lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py +++ b/lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py @@ -16,7 +16,7 @@ def fork_and_detach_test(self, variant): self.reset_test_sequence() # continue and expect fork - fork_regex = "[$]T.*;{}:p([0-9a-f]*)[.]([0-9a-f]*).*".format(variant) + fork_regex = "[$]T.*;{}:p([0-9a-f]+)[.]([0-9a-f]+).*".format(variant) self.test_sequence.add_log_lines([ "read packet: $c#00", {"direction": "send", "regex": fork_regex, @@ -57,3 +57,137 @@ def test_vfork(self): {"direction": "send", "regex": r"[$]W00#.*"}, ], True) self.expect_gdbremote_sequence() + + def fork_and_follow_test(self, variant): + self.build() + self.prep_debug_monitor_and_inferior(inferior_args=[variant]) + self.add_qSupported_packets(["multiprocess+", + "{}-events+".format(variant)]) + ret = self.expect_gdbremote_sequence() + self.assertIn("{}-events+".format(variant), ret["qSupported_response"]) + self.reset_test_sequence() + + # continue and expect fork + procinfo_regex = "[$]pid:([0-9a-f]+);.*" + fork_regex = "[$]T.*;{}:p([0-9a-f]+)[.]([0-9a-f]+).*".format(variant) + self.test_sequence.add_log_lines([ + "read packet: $qProcessInfo#00", + {"direction": "send", "regex": procinfo_regex, + "capture": {1: "parent_pid"}}, + "read packet: $c#00", + {"direction": "send", "regex": fork_regex, + "capture": {1: "pid", 2: "tid"}}, + ], True) + ret = self.expect_gdbremote_sequence() + parent_pid, pid, tid = (int(ret[x], 16) for x + in ("parent_pid", "pid", "tid")) + self.reset_test_sequence() + + # switch to the forked child + self.test_sequence.add_log_lines([ + "read packet: $Hgp{:x}.{:x}#00".format(pid, tid), + {"direction": "send", "regex": r"[$]OK#.*"}, + "read packet: $Hcp{:x}.{:x}#00".format(pid, tid), + {"direction": "send", "regex": r"[$]OK#.*"}, + ], True) + + # detach the parent + self.test_sequence.add_log_lines([ + "read packet: $D;{:x}#00".format(parent_pid), + {"direction": "send", "regex": r"[$]OK#.*"}, + ], True) + ret = self.expect_gdbremote_sequence() + self.reset_test_sequence() + + # resume the child + self.test_sequence.add_log_lines([ + "read packet: $c#00", + {"direction": "send", "regex": r"[$]W00#.*"}, + ], True) + self.expect_gdbremote_sequence() + + @add_test_categories(["fork"]) + def test_fork_follow(self): + self.fork_and_follow_test("fork") + + @add_test_categories(["fork"]) + def test_vfork_follow(self): + self.fork_and_follow_test("vfork") + + @add_test_categories(["fork"]) + def test_select_wrong_pid(self): + self.build() + self.prep_debug_monitor_and_inferior() + self.add_qSupported_packets(["multiprocess+"]) + ret = self.expect_gdbremote_sequence() + self.assertIn("multiprocess+", ret["qSupported_response"]) + self.reset_test_sequence() + + # get process pid + procinfo_regex = "[$]pid:([0-9a-f]+);.*" + self.test_sequence.add_log_lines([ + "read packet: $qProcessInfo#00", + {"direction": "send", "regex": procinfo_regex, + "capture": {1: "pid"}}, + "read packet: $qC#00", + {"direction": "send", "regex": "[$]QC([0-9a-f]+)#.*", + "capture": {1: "tid"}}, + ], True) + ret = self.expect_gdbremote_sequence() + pid, tid = (int(ret[x], 16) for x in ("pid", "tid")) + self.reset_test_sequence() + + # try switching to correct pid + self.test_sequence.add_log_lines([ + "read packet: $Hgp{:x}.{:x}#00".format(pid, tid), + {"direction": "send", "regex": r"[$]OK#.*"}, + "read packet: $Hcp{:x}.{:x}#00".format(pid, tid), + {"direction": "send", "regex": r"[$]OK#.*"}, + ], True) + ret = self.expect_gdbremote_sequence() + + # try switching to invalid tid + self.test_sequence.add_log_lines([ + "read packet: $Hgp{:x}.{:x}#00".format(pid, tid+1), + {"direction": "send", "regex": r"[$]E15#.*"}, + "read packet: $Hcp{:x}.{:x}#00".format(pid, tid+1), + {"direction": "send", "regex": r"[$]E15#.*"}, + ], True) + ret = self.expect_gdbremote_sequence() + + # try switching to invalid pid + self.test_sequence.add_log_lines([ + "read packet: $Hgp{:x}.{:x}#00".format(pid+1, tid), + {"direction": "send", "regex": r"[$]Eff#.*"}, + "read packet: $Hcp{:x}.{:x}#00".format(pid+1, tid), + {"direction": "send", "regex": r"[$]Eff#.*"}, + ], True) + ret = self.expect_gdbremote_sequence() + + def test_detach_current(self): + self.build() + self.prep_debug_monitor_and_inferior() + self.add_qSupported_packets(["multiprocess+"]) + ret = self.expect_gdbremote_sequence() + self.assertIn("multiprocess+", ret["qSupported_response"]) + self.reset_test_sequence() + + # get process pid + procinfo_regex = "[$]pid:([0-9a-f]+);.*" + self.test_sequence.add_log_lines([ + "read packet: $qProcessInfo#00", + {"direction": "send", "regex": procinfo_regex, + "capture": {1: "pid"}}, + ], True) + ret = self.expect_gdbremote_sequence() + pid = int(ret["pid"], 16) + self.reset_test_sequence() + + # detach the process + self.test_sequence.add_log_lines([ + "read packet: $D;{:x}#00".format(pid), + {"direction": "send", "regex": r"[$]OK#.*"}, + "read packet: $qC#00", + {"direction": "send", "regex": r"[$]E44#.*"}, + ], True) + ret = self.expect_gdbremote_sequence() _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits