llgs: implement qThreadStopInfo.

This change implements this ticket:
http://llvm.org/bugs/show_bug.cgi?id=20899

Adds the qThreadStopInfo RSP command for llgs and includes a test that
verifies both debugserver and llgs respond with something reasonable
on a multithreaded app.

llvm-svn: 217549
This commit is contained in:
Todd Fiala 2014-09-10 21:28:38 +00:00
parent c435adcde0
commit 1109ed4245
3 changed files with 114 additions and 2 deletions

View File

@ -429,6 +429,10 @@ GDBRemoteCommunicationServer::GetPacketAndSendResponse (uint32_t timeout_usec,
case StringExtractorGDBRemote::eServerPacketType_vAttach:
packet_result = Handle_vAttach (packet);
break;
case StringExtractorGDBRemote::eServerPacketType_qThreadStopInfo:
packet_result = Handle_qThreadStopInfo (packet);
break;
}
}
else
@ -2378,8 +2382,6 @@ GDBRemoteCommunicationServer::Handle_vCont_actions (StringExtractorGDBRemote &pa
return SendUnimplementedResponse (packet.GetStringRef().c_str());
}
// We handle $vCont messages for c.
// TODO add C, s and S.
StreamString response;
response.Printf("vCont;c;C;s;S");
@ -4181,6 +4183,26 @@ GDBRemoteCommunicationServer::Handle_vAttach (StringExtractorGDBRemote &packet)
return PacketResult::Success;
}
GDBRemoteCommunicationServer::PacketResult
GDBRemoteCommunicationServer::Handle_qThreadStopInfo (StringExtractorGDBRemote &packet)
{
Log *log (GetLogIfAnyCategoriesSet(LIBLLDB_LOG_THREAD));
// We don't support if we're not llgs.
if (!IsGdbServer())
return SendUnimplementedResponse ("only supported for lldb-gdbserver");
packet.SetFilePos (strlen("qThreadStopInfo"));
const lldb::tid_t tid = packet.GetHexMaxU32 (false, LLDB_INVALID_THREAD_ID);
if (tid == LLDB_INVALID_THREAD_ID)
{
if (log)
log->Printf ("GDBRemoteCommunicationServer::%s failed, could not parse thread id from request \"%s\"", __FUNCTION__, packet.GetStringRef ().c_str ());
return SendErrorResponse (0x15);
}
return SendStopReplyPacketForThread (tid);
}
void
GDBRemoteCommunicationServer::FlushInferiorOutput ()
{

View File

@ -463,6 +463,9 @@ protected:
PacketResult
Handle_vAttach (StringExtractorGDBRemote &packet);
PacketResult
Handle_qThreadStopInfo (StringExtractorGDBRemote &packet);
void
SetCurrentThreadID (lldb::tid_t tid);

View File

@ -0,0 +1,87 @@
import unittest2
import gdbremote_testcase
from lldbtest import *
class TestGdbRemote_qThreadStopInfo(gdbremote_testcase.GdbRemoteTestCaseBase):
def gather_stop_replies_via_qThreadStopInfo(self, thread_count):
# Set up the inferior args.
inferior_args=[]
for i in range(thread_count - 1):
inferior_args.append("thread:new")
inferior_args.append("sleep:10")
procs = self.prep_debug_monitor_and_inferior(inferior_args=inferior_args)
# Assumes test_sequence has anything added needed to setup the initial state.
# (Like optionally enabling QThreadsInStopReply.)
self.test_sequence.add_log_lines([
"read packet: $c#00"
], True)
context = self.expect_gdbremote_sequence()
self.assertIsNotNone(context)
# Give threads time to start up, then break.
time.sleep(1)
self.reset_test_sequence()
self.test_sequence.add_log_lines([
"read packet: {}".format(chr(03)),
{"direction":"send", "regex":r"^\$T([0-9a-fA-F]+)([^#]+)#[0-9a-fA-F]{2}$", "capture":{1:"stop_result", 2:"key_vals_text"} },
], True)
context = self.expect_gdbremote_sequence()
self.assertIsNotNone(context)
# Wait until all threads have started.
threads = self.wait_for_thread_count(thread_count, timeout_seconds=3)
self.assertIsNotNone(threads)
self.assertEquals(len(threads), thread_count)
# Grab stop reply for each thread via qThreadStopInfo{tid:hex}.
for thread in threads:
# Run the qThreadStopInfo command.
self.reset_test_sequence()
self.test_sequence.add_log_lines([
"read packet: $qThreadStopInfo{:x}#00".format(thread),
{"direction":"send", "regex":r"^\$T([0-9a-fA-F]+)([^#]+)#[0-9a-fA-F]{2}$", "capture":{1:"stop_result", 2:"key_vals_text"} },
], True)
context = self.expect_gdbremote_sequence()
self.assertIsNotNone(context)
# Parse stop reply contents.
key_vals_text = context.get("key_vals_text")
self.assertIsNotNone(key_vals_text)
kv_dict = self.parse_key_val_dict(key_vals_text)
self.assertIsNotNone(kv_dict)
# Verify there is a thread.
kv_thread = kv_dict.get("thread")
self.assertIsNotNone(kv_thread)
self.assertEquals(int(kv_thread, 16), thread)
return threads
def qThreadStopInfo_works_for_multiple_threads(self, thread_count):
# Gather threads from stop notification when QThreadsInStopReply is enabled.
# stop_reply_threads = self.gather_stop_replies_via_qThreadStopInfo(self.ENABLE_THREADS_IN_STOP_REPLY_ENTRIES, thread_count)
stop_reply_threads = self.gather_stop_replies_via_qThreadStopInfo(thread_count)
self.assertEquals(len(stop_reply_threads), thread_count)
@debugserver_test
@dsym_test
def test_qThreadStopInfo_works_for_multiple_threads_debugserver_dsym(self):
self.init_debugserver_test()
self.buildDsym()
self.set_inferior_startup_launch()
self.qThreadStopInfo_works_for_multiple_threads(5)
@llgs_test
@dwarf_test
def test_qThreadStopInfo_works_for_multiple_threads_llgs_dwarf(self):
self.init_llgs_test()
self.buildDwarf()
self.set_inferior_startup_launch()
self.qThreadStopInfo_works_for_multiple_threads(5)
if __name__ == '__main__':
unittest2.main()