diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index 0b642733414d..300042021863 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -57,6 +57,7 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient(bool is_platform) : m_supports_vCont_s (eLazyBoolCalculate), m_supports_vCont_S (eLazyBoolCalculate), m_qHostInfo_is_valid (eLazyBoolCalculate), + m_curr_pid_is_valid (eLazyBoolCalculate), m_qProcessInfo_is_valid (eLazyBoolCalculate), m_qGDBServerVersion_is_valid (eLazyBoolCalculate), m_supports_alloc_dealloc_memory (eLazyBoolCalculate), @@ -86,6 +87,7 @@ GDBRemoteCommunicationClient::GDBRemoteCommunicationClient(bool is_platform) : m_supports_z4 (true), m_supports_QEnvironment (true), m_supports_QEnvironmentHexEncoded (true), + m_curr_pid (LLDB_INVALID_PROCESS_ID), m_curr_tid (LLDB_INVALID_THREAD_ID), m_curr_tid_run (LLDB_INVALID_THREAD_ID), m_num_supported_hardware_watchpoints (0), @@ -308,6 +310,7 @@ GDBRemoteCommunicationClient::ResetDiscoverableSettings() m_supports_x = eLazyBoolCalculate; m_supports_QSaveRegisterState = eLazyBoolCalculate; m_qHostInfo_is_valid = eLazyBoolCalculate; + m_curr_pid_is_valid = eLazyBoolCalculate; m_qProcessInfo_is_valid = eLazyBoolCalculate; m_qGDBServerVersion_is_valid = eLazyBoolCalculate; m_supports_alloc_dealloc_memory = eLazyBoolCalculate; @@ -1178,13 +1181,43 @@ GDBRemoteCommunicationClient::SendInterrupt lldb::pid_t GDBRemoteCommunicationClient::GetCurrentProcessID () { - StringExtractorGDBRemote response; - if (SendPacketAndWaitForResponse("qC", strlen("qC"), response, false) == PacketResult::Success) + if (m_curr_pid_is_valid == eLazyBoolYes) + return m_curr_pid; + + // First try to retrieve the pid via the qProcessInfo request. + GetCurrentProcessInfo (); + if (m_curr_pid_is_valid == eLazyBoolYes) { - if (response.GetChar() == 'Q') - if (response.GetChar() == 'C') - return response.GetHexMaxU32 (false, LLDB_INVALID_PROCESS_ID); + // We really got it. + return m_curr_pid; } + else + { + // For Apple iOS targets, go back and ask the qC packet for its result. In earlier iterations of debugserver, $qC returned + // the process id of the current process. + const llvm::Triple &triple = GetProcessArchitecture().GetTriple(); + if ((triple.getVendor() == llvm::Triple::Apple) && + (triple.getOS() == llvm::Triple::IOS)) + { + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse("qC", strlen("qC"), response, false) == PacketResult::Success) + { + if (response.GetChar() == 'Q') + { + if (response.GetChar() == 'C') + { + m_curr_pid = response.GetHexMaxU32 (false, LLDB_INVALID_PROCESS_ID); + if (m_curr_pid != LLDB_INVALID_PROCESS_ID) + { + m_curr_pid_is_valid = eLazyBoolYes; + return m_curr_pid; + } + } + } + } + } + } + return LLDB_INVALID_PROCESS_ID; } @@ -2346,6 +2379,7 @@ GDBRemoteCommunicationClient::GetCurrentProcessInfo () StringExtractor extractor; ByteOrder byte_order = eByteOrderInvalid; uint32_t num_keys_decoded = 0; + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; while (response.GetNameColonValue(name, value)) { if (name.compare("cputype") == 0) @@ -2388,9 +2422,20 @@ GDBRemoteCommunicationClient::GetCurrentProcessInfo () if (pointer_byte_size != 0) ++num_keys_decoded; } + else if (name.compare("pid") == 0) + { + pid = Args::StringToUInt64(value.c_str(), 0, 16); + if (pid != LLDB_INVALID_PROCESS_ID) + ++num_keys_decoded; + } } if (num_keys_decoded > 0) m_qProcessInfo_is_valid = eLazyBoolYes; + if (pid != LLDB_INVALID_PROCESS_ID) + { + m_curr_pid_is_valid = eLazyBoolYes; + m_curr_pid = pid; + } if (cpu != LLDB_INVALID_CPUTYPE && !os_name.empty() && !vendor_name.empty()) { m_process_arch.SetArchitecture (eArchTypeMachO, cpu, sub); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h index 4293e8b12d74..6b1aec53928f 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -537,6 +537,7 @@ protected: lldb_private::LazyBool m_supports_vCont_s; lldb_private::LazyBool m_supports_vCont_S; lldb_private::LazyBool m_qHostInfo_is_valid; + lldb_private::LazyBool m_curr_pid_is_valid; lldb_private::LazyBool m_qProcessInfo_is_valid; lldb_private::LazyBool m_qGDBServerVersion_is_valid; lldb_private::LazyBool m_supports_alloc_dealloc_memory; @@ -569,7 +570,7 @@ protected: m_supports_QEnvironment:1, m_supports_QEnvironmentHexEncoded:1; - + lldb::pid_t m_curr_pid; lldb::tid_t m_curr_tid; // Current gdb remote protocol thread index for all other operations lldb::tid_t m_curr_tid_run; // Current gdb remote protocol thread index for continue, step, etc diff --git a/lldb/test/tools/lldb-gdbserver/TestLldbGdbServer.py b/lldb/test/tools/lldb-gdbserver/TestLldbGdbServer.py index c093d6e4b42e..727f328ee3b4 100644 --- a/lldb/test/tools/lldb-gdbserver/TestLldbGdbServer.py +++ b/lldb/test/tools/lldb-gdbserver/TestLldbGdbServer.py @@ -108,7 +108,7 @@ class LldbGdbServerTestCase(TestBase): True) def expect_gdbremote_sequence(self): - expect_lldb_gdbserver_replay(self, self.sock, self.test_sequence, self._TIMEOUT_SECONDS, self.logger) + return expect_lldb_gdbserver_replay(self, self.sock, self.test_sequence, self._TIMEOUT_SECONDS, self.logger) @debugserver_test def test_exe_starts_debugserver(self): @@ -324,7 +324,6 @@ class LldbGdbServerTestCase(TestBase): @debugserver_test @dsym_test - @unittest2.expectedFailure() # Possible bug. def test_first_launch_stop_reply_thread_matches_first_qC_debugserver_dsym(self): self.init_debugserver_test() self.buildDsym() @@ -338,7 +337,48 @@ class LldbGdbServerTestCase(TestBase): self.buildDwarf() self.first_launch_stop_reply_thread_matches_first_qC() + def qProcessInfo_returns_running_process(self): + server = self.start_server() + self.assertIsNotNone(server) + # Build launch args + launch_args = [os.path.abspath('a.out'), "hello, world"] + + # Build the expected protocol stream + self.add_no_ack_remote_stream() + self.add_verified_launch_packets(launch_args) + self.test_sequence.add_log_lines( + ["read packet: $qProcessInfo#00", + { "direction":"send", "regex":r"^\$pid:([0-9a-fA-F]+);", "capture":{1:"pid"} }], + True) + + # Run the stream + context = self.expect_gdbremote_sequence() + self.assertIsNotNone(context) + + # Ensure the process id looks reasonable. + pid_text = context.get('pid', None) + self.assertIsNotNone(pid_text) + pid = int(pid_text, base=16) + self.assertNotEqual(0, pid) + + # If possible, verify that the process is running. + self.assertTrue(process_is_running(pid, True)) + + @debugserver_test + @dsym_test + def test_qProcessInfo_returns_running_process_debugserver_dsym(self): + self.init_debugserver_test() + self.buildDsym() + self.qProcessInfo_returns_running_process() + + @llgs_test + @dwarf_test + @unittest2.expectedFailure() + def test_qProcessInfo_returns_running_process_llgs_dwarf(self): + self.init_llgs_test() + self.buildDwarf() + self.qProcessInfo_returns_running_process() if __name__ == '__main__': unittest2.main() diff --git a/lldb/test/tools/lldb-gdbserver/lldbgdbserverutils.py b/lldb/test/tools/lldb-gdbserver/lldbgdbserverutils.py index 0bd44ef7f458..8a70e7b8d71c 100644 --- a/lldb/test/tools/lldb-gdbserver/lldbgdbserverutils.py +++ b/lldb/test/tools/lldb-gdbserver/lldbgdbserverutils.py @@ -3,8 +3,10 @@ import os import os.path +import platform import re import select +import subprocess import time @@ -142,9 +144,10 @@ def expect_lldb_gdbserver_replay( logger: a Python logger instance. Returns: - None if no issues. Raises an exception if the expected communication does not - occur. - + The context dictionary from running the given gdbremote + protocol sequence. This will contain any of the capture + elements specified to any GdbRemoteEntry instances in + test_sequence. """ received_lines = [] receive_buffer = '' @@ -205,7 +208,7 @@ def expect_lldb_gdbserver_replay( if len(received_lines) > 0: received_packet = received_lines.pop(0) context = sequence_entry.assert_match(asserter, received_packet, context=context) - return None + return context def gdbremote_hex_encode_string(str): @@ -405,6 +408,45 @@ class GdbRemoteTestSequence(object): self.logger.info("processed dict sequence to match receiving from remote") self.entries.append(GdbRemoteEntry(is_send_to_remote=False, regex=regex, capture=capture, expect_captures=expect_captures)) +def process_is_running(pid, unknown_value=True): + """If possible, validate that the given pid represents a running process on the local system. + + Args: + + pid: an OS-specific representation of a process id. Should be an integral value. + + unknown_value: value used when we cannot determine how to check running local + processes on the OS. + + Returns: + + If we can figure out how to check running process ids on the given OS: + return True if the process is running, or False otherwise. + + If we don't know how to check running process ids on the given OS: + return the value provided by the unknown_value arg. + """ + if type(pid) != int: + raise Exception("pid must be of type int") + + process_ids = [] + + if platform.system() in ['Darwin', 'Linux', 'FreeBSD', 'NetBSD']: + # Build the list of running process ids + output = subprocess.check_output("ps ax | awk '{ print $1; }'", shell=True) + text_process_ids = output.split('\n')[1:] + # Convert text pids to ints + process_ids = [int(text_pid) for text_pid in text_process_ids if text_pid != ''] + # elif {your_platform_here}: + # fill in process_ids as a list of int type process IDs running on + # the local system. + else: + # Don't know how to get list of running process IDs on this + # OS, so return the "don't know" value. + return unknown_value + + # Check if the pid is in the process_ids + return pid in process_ids if __name__ == '__main__': EXE_PATH = get_lldb_gdbserver_exe() diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp index 71d3b8357c9e..7b592ff1e7a8 100644 --- a/lldb/tools/debugserver/source/RNBRemote.cpp +++ b/lldb/tools/debugserver/source/RNBRemote.cpp @@ -1404,15 +1404,21 @@ RNBRemote::HandlePacket_qRcmd (const char *p) rnb_err_t RNBRemote::HandlePacket_qC (const char *p) { - nub_process_t pid; + nub_thread_t tid; std::ostringstream rep; // If we haven't run the process yet, we tell the debugger the // pid is 0. That way it can know to tell use to run later on. - if (m_ctx.HasValidProcessID()) - pid = m_ctx.ProcessID(); + if (!m_ctx.HasValidProcessID()) + tid = 0; else - pid = 0; - rep << "QC" << std::hex << pid; + { + // Grab the current thread. + tid = DNBProcessGetCurrentThread (m_ctx.ProcessID()); + // Make sure we set the current thread so g and p packets return + // the data the gdb will expect. + SetCurrentThread (tid); + } + rep << "QC" << std::hex << tid; return SendPacket (rep.str()); }