diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h @@ -49,6 +49,7 @@ /// launch. Status LaunchProcess() override; + /// Attach to a process. /// /// This method supports attaching llgs to a process accessible via the @@ -59,6 +60,17 @@ /// attach operation. Status AttachToProcess(lldb::pid_t pid); + /// Wait to attach to a process with a given name. + /// + /// This method supports waiting for the next instance of a process + /// with a given name and attaching llgs to that via the configured + /// Platform. + /// + /// \return + /// An Status object indicating the success or failure of the + /// attach operation. + Status AttachWaitProcess(llvm::StringRef process_name); + // NativeProcessProtocol::NativeDelegate overrides void InitializeDelegate(NativeProcessProtocol *process) override; @@ -170,6 +182,8 @@ PacketResult Handle_vAttach(StringExtractorGDBRemote &packet); + PacketResult Handle_vAttachWait(StringExtractorGDBRemote &packet); + PacketResult Handle_D(StringExtractorGDBRemote &packet); PacketResult Handle_qThreadStopInfo(StringExtractorGDBRemote &packet); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -159,6 +159,9 @@ RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_vAttach, &GDBRemoteCommunicationServerLLGS::Handle_vAttach); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vAttachWait, + &GDBRemoteCommunicationServerLLGS::Handle_vAttachWait); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_vCont, &GDBRemoteCommunicationServerLLGS::Handle_vCont); @@ -334,6 +337,62 @@ return Status(); } +Status GDBRemoteCommunicationServerLLGS::AttachWaitProcess( + llvm::StringRef process_name) { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + // TODO: Make the polling interval configurable + std::chrono::milliseconds waitfor_interval = std::chrono::seconds(1); + + // Create the matcher used to search the process list + ProcessInstanceInfoList exclusion_list; + ProcessInstanceInfoMatch match_info; + match_info.GetProcessInfo().GetExecutableFile() + .SetFile(process_name, llvm::sys::path::Style::posix); + match_info.SetNameMatchType(NameMatch::EndsWith); + + // Create the excluded process list before polling begins + Host::FindProcesses(match_info, exclusion_list); + + if (log) + LLDB_LOG(log, "waiting for '{0}' to appear", process_name); + + lldb::pid_t waitfor_pid = LLDB_INVALID_PROCESS_ID; + ProcessInstanceInfoList loop_process_list; + + while (waitfor_pid == LLDB_INVALID_PROCESS_ID) { + loop_process_list.clear(); + if (Host::FindProcesses(match_info, loop_process_list)) { + // The for loop is to checking for the first matching process that was + // not in the excluded process list. + for(size_t i = 0; i < loop_process_list.size(); i++) { + waitfor_pid = loop_process_list[i].GetProcessID(); + for(size_t j = 0; j < exclusion_list.size(); j++) { + if (exclusion_list[j].GetProcessID() == waitfor_pid) { + waitfor_pid = LLDB_INVALID_PROCESS_ID; + } + } + + // If waitfor_pid is not in our exclusion list then use it + if (waitfor_pid != LLDB_INVALID_PROCESS_ID) { + if (log) + LLDB_LOG(log, "found pid {1}", waitfor_pid); + break; + } + } + } + // If we have not found the new process sleep until next poll. + if (waitfor_pid == LLDB_INVALID_PROCESS_ID) { + if (log) + LLDB_LOG(log, "sleep {1} seconds", waitfor_interval); + std::this_thread::sleep_for(waitfor_interval); + } + } + + return AttachToProcess(waitfor_pid); +} + + + void GDBRemoteCommunicationServerLLGS::InitializeDelegate( NativeProcessProtocol *process) { assert(process && "process cannot be NULL"); @@ -3188,6 +3247,38 @@ return SendStopReasonForState(m_debugged_process_up->GetState()); } +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_vAttachWait( + StringExtractorGDBRemote &packet) { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + + // Consume the ';' after vAttach. + packet.SetFilePos(strlen("vAttachWait")); + if (!packet.GetBytesLeft() || packet.GetChar() != ';') + return SendIllFormedResponse(packet, "vAttachWait missing expected ';'"); + + // Allocate the buffer for the process name from vAttachWait + std::string process_name; + if (!packet.GetHexByteString(process_name)) + return SendIllFormedResponse(packet, + "vAttachWait failed to parse process name"); + + if (log) + LLDB_LOG(log, "attempting to attach to process named '{0}'", process_name); + + Status error = AttachWaitProcess(process_name); + + if (error.Fail()) { + if (log) + LLDB_LOG(log, "failed to attach to process named '{0}': {1}", + process_name, error); + return SendErrorResponse(error); + } + + // Notify we attached by sending a stop packet. + return SendStopReasonForState(m_debugged_process_up->GetState()); +} + GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_D(StringExtractorGDBRemote &packet) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); diff --git a/lldb/test/API/tools/lldb-server/TestGdbRemoteAttachWait.py b/lldb/test/API/tools/lldb-server/TestGdbRemoteAttachWait.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/tools/lldb-server/TestGdbRemoteAttachWait.py @@ -0,0 +1,69 @@ + + +import gdbremote_testcase +import lldbgdbserverutils +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestGdbRemoteAttachWait(gdbremote_testcase.GdbRemoteTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + def attach_with_vAttachWait(self): + # Add attach packets. + self.test_sequence.add_log_lines([ + # Do the attach. + "read packet: $vAttachWait;{}#00".format(''.join(str(ord(c)) for c in "a.out")), + ], True) + + + # Start the inferior, start the debug monitor, nothing is attached yet. + procs = self.prep_debug_monitor_and_inferior( + inferior_args=["sleep:60"]) + self.assertIsNotNone(procs) + + # Make sure the target process has been launched. + inferior = procs.get("inferior") + self.assertIsNotNone(inferior) + self.assertTrue(inferior.pid > 0) + self.assertTrue( + lldbgdbserverutils.process_is_running( + inferior.pid, True)) + + + self.test_sequence.add_log_lines([ + # Expect a stop notification from the attach. + {"direction": "send", + "regex": r"^\$T([0-9a-fA-F]{2})[^#]*#[0-9a-fA-F]{2}$", + "capture": {1: "stop_signal_hex"}}, + ], True) + self.add_process_info_collection_packets() + + + # Run the stream + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Gather process info response + process_info = self.parse_process_info_response(context) + self.assertIsNotNone(process_info) + + # Ensure the process id matches what we expected. + pid_text = process_info.get('pid', None) + self.assertIsNotNone(pid_text) + reported_pid = int(pid_text, base=16) + self.assertEqual(reported_pid, inferior.pid) + + @debugserver_test + def test_attach_with_vAttach_debugserver(self): + self.build() + self.set_inferior_startup_attach_manually() + self.attach_with_vAttachWait() + + @llgs_test + def test_attach_with_vAttach_llgs(self): + self.build() + self.set_inferior_startup_attach_manually() + self.attach_with_vAttachWait()