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 @@ -68,7 +68,7 @@ /// \return /// An Status object indicating the success or failure of the /// attach operation. - Status AttachWaitProcess(llvm::StringRef process_name); + Status AttachWaitProcess(llvm::StringRef process_name, bool include_existing); // NativeProcessProtocol::NativeDelegate overrides void InitializeDelegate(NativeProcessProtocol *process) override; @@ -183,6 +183,10 @@ PacketResult Handle_vAttachWait(StringExtractorGDBRemote &packet); + PacketResult Handle_qVAttachOrWaitSupported(StringExtractorGDBRemote &packet); + + PacketResult Handle_vAttachOrWait(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 @@ -162,6 +162,12 @@ RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_vAttachWait, &GDBRemoteCommunicationServerLLGS::Handle_vAttachWait); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qVAttachOrWaitSupported, + &GDBRemoteCommunicationServerLLGS::Handle_qVAttachOrWaitSupported); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vAttachOrWait, + &GDBRemoteCommunicationServerLLGS::Handle_vAttachOrWait); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_vCont, &GDBRemoteCommunicationServerLLGS::Handle_vCont); @@ -338,7 +344,7 @@ } Status GDBRemoteCommunicationServerLLGS::AttachWaitProcess( - llvm::StringRef process_name) { + llvm::StringRef process_name, bool include_existing) { Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); std::chrono::milliseconds polling_interval = std::chrono::milliseconds(1); @@ -347,13 +353,17 @@ ProcessInstanceInfoList exclusion_list; ProcessInstanceInfoMatch match_info; match_info.GetProcessInfo().GetExecutableFile().SetFile( - process_name, llvm::sys::path::Style::posix); - match_info.SetNameMatchType(NameMatch::EndsWith); + process_name, llvm::sys::path::Style::native); + match_info.SetNameMatchType(NameMatch::Equals); - // Create the excluded process list before polling begins. - Host::FindProcesses(match_info, exclusion_list); - LLDB_LOG(log, "placed '{0}' processes in the exclusion list.", - exclusion_list.size()); + if (include_existing) { + LLDB_LOG(log, "including existing processes in search"); + } else { + // Create the excluded process list before polling begins. + Host::FindProcesses(match_info, exclusion_list); + LLDB_LOG(log, "placed '{0}' processes in the exclusion list.", + exclusion_list.size()); + } LLDB_LOG(log, "waiting for '{0}' to appear", process_name); @@ -3275,7 +3285,43 @@ LLDB_LOG(log, "attempting to attach to process named '{0}'", process_name); - Status error = AttachWaitProcess(process_name); + Status error = AttachWaitProcess(process_name, false); + if (error.Fail()) { + 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_qVAttachOrWaitSupported( + StringExtractorGDBRemote &packet) { + return SendOKResponse(); +} + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_vAttachOrWait( + StringExtractorGDBRemote &packet) { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + + // Consume the ';' after the identifier. + packet.SetFilePos(strlen("vAttachOrWait")); + + if (!packet.GetBytesLeft() || packet.GetChar() != ';') + return SendIllFormedResponse(packet, "vAttachOrWait missing expected ';'"); + + // Allocate the buffer for the process name from vAttachWait. + std::string process_name; + if (!packet.GetHexByteString(process_name)) + return SendIllFormedResponse(packet, + "vAttachOrWait failed to parse process name"); + + LLDB_LOG(log, "attempting to attach to process named '{0}'", process_name); + + Status error = AttachWaitProcess(process_name, true); if (error.Fail()) { LLDB_LOG(log, "failed to attach to process named '{0}': {1}", process_name, error); diff --git a/lldb/test/API/tools/lldb-server/TestGdbRemoteAttachOrWait.py b/lldb/test/API/tools/lldb-server/TestGdbRemoteAttachOrWait.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/tools/lldb-server/TestGdbRemoteAttachOrWait.py @@ -0,0 +1,111 @@ + +import os +from time import sleep + +import gdbremote_testcase +import lldbgdbserverutils +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestGdbRemoteAttachOrWait(gdbremote_testcase.GdbRemoteTestCaseBase): + + mydir = TestBase.compute_mydir(__file__) + + def test_launch_before_attach_with_vAttachOrWait(self): + exe = '%s_%d' % (self.testMethodName, os.getpid()) + self.build(dictionary={'EXE': exe}) + self.set_inferior_startup_attach_manually() + + # Start the inferior, start the debug monitor, nothing is attached yet. + procs = self.prep_debug_monitor_and_inferior( + inferior_args=["sleep:60"], + inferior_exe_path=self.getBuildArtifact(exe)) + 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)) + + # Add attach packets. + self.test_sequence.add_log_lines([ + # Do the attach. + "read packet: $vAttachOrWait;{}#00".format(lldbgdbserverutils.gdbremote_hex_encode_string(exe)), + # 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) + + def test_launch_after_attach_with_vAttachOrWait(self): + exe = '%s_%d' % (self.testMethodName, os.getpid()) + self.build(dictionary={'EXE': exe}) + self.set_inferior_startup_attach_manually() + + server = self.connect_to_debug_monitor() + self.assertIsNotNone(server) + + self.add_no_ack_remote_stream() + self.test_sequence.add_log_lines([ + # Do the attach. + "read packet: $vAttachOrWait;{}#00".format(lldbgdbserverutils.gdbremote_hex_encode_string(exe)), + ], True) + # Run the stream until attachWait. + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Sleep so we're sure that the inferior is launched after we ask for the attach. + sleep(1) + + # Launch the inferior. + inferior = self.launch_process_for_attach( + inferior_args=["sleep:60"], + exe_path=self.getBuildArtifact(exe)) + self.assertIsNotNone(inferior) + self.assertTrue(inferior.pid > 0) + self.assertTrue( + lldbgdbserverutils.process_is_running( + inferior.pid, True)) + + # Make sure the attach succeeded. + self.test_sequence.add_log_lines([ + {"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 sending the response.. + 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)