forked from OSchip/llvm-project
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:
parent
dda7f8cfd0
commit
9f72b3a1ce
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue