Modify debugserver to follow gdb remote $qC protocol definition.

$qC from debugserver now returns the current thread's thread-id (and, like $?, will set a current thread if one is not already selected).  Previously it was returning the current process id.

lldb will now query $qProcessInfo to retrieve the process id.  The process id is now cached lazily and reset like other cached values.  Retrieval of the process id will fall back to the old $qC method for vendor==Apple and os==iOS if the qProcessInfo retrieval fails.

Added a gdb remote protocol-level test to verify that $qProcessInfo reports a valid process id after launching a process, while the process is in the initial stopped state.  Verifies the given process id is a currently valid process on host OSes for which we know how to check (MacOSX, Linux, {Free/Net}BSD).  Ignores the live process check for OSes where we don't know how to do this.  (I saw no portable way to do this in stock Python without pulling in other libs).

llvm-svn: 208241
This commit is contained in:
Todd Fiala 2014-05-07 19:28:21 +00:00
parent dda7f8cfd0
commit 9f72b3a1ce
5 changed files with 151 additions and 17 deletions

View File

@ -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);

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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());
}