Skip to content

Commit 4a4bb12

Browse files
committedJul 16, 2015
Add jThreadsInfo support to lldb-server
Summary: This commit adds initial support for the jThreadsInfo packet to lldb-server. The current implementation does not expedite inferior memory. I have also added a description of the new packet to our protocol documentation (mostly taken from Greg's earlier commit message). Reviewers: clayborg, ovyalov, tberghammer Subscribers: lldb-commits Differential Revision: http://reviews.llvm.org/D11187 llvm-svn: 242402
1 parent a0cd89a commit 4a4bb12

File tree

7 files changed

+243
-30
lines changed

7 files changed

+243
-30
lines changed
 

‎lldb/docs/lldb-gdb-remote.txt

+64
Original file line numberDiff line numberDiff line change
@@ -1530,3 +1530,67 @@ for this region.
15301530
// Low. If this packet is absent, lldb will read the Mach-O headers/load
15311531
// commands out of memory.
15321532
//----------------------------------------------------------------------
1533+
1534+
//----------------------------------------------------------------------
1535+
// "jThreadsInfo"
1536+
//
1537+
// BRIEF
1538+
// Ask for the server for thread stop information of all threads.
1539+
//
1540+
// PRIORITY TO IMPLEMENT
1541+
// Low. This is a performance optimization, which speeds up debugging by avoiding
1542+
// multiple round-trips for retrieving thread information. The information from this
1543+
// packet can be retrieved using a combination of qThreadStopInfo and m packets.
1544+
//----------------------------------------------------------------------
1545+
1546+
The data in this packet is very similar to the stop reply packets, but is packaged in
1547+
JSON and uses JSON arrays where applicable. The JSON output looks like:
1548+
[
1549+
{ "tid":1580681,
1550+
"metype":6,
1551+
"medata":[2,0],
1552+
"reason":"exception",
1553+
"qaddr":140735118423168,
1554+
"registers": {
1555+
"0":"8000000000000000",
1556+
"1":"0000000000000000",
1557+
"2":"20fabf5fff7f0000",
1558+
"3":"e8f8bf5fff7f0000",
1559+
"4":"0100000000000000",
1560+
"5":"d8f8bf5fff7f0000",
1561+
"6":"b0f8bf5fff7f0000",
1562+
"7":"20f4bf5fff7f0000",
1563+
"8":"8000000000000000",
1564+
"9":"61a8db78a61500db",
1565+
"10":"3200000000000000",
1566+
"11":"4602000000000000",
1567+
"12":"0000000000000000",
1568+
"13":"0000000000000000",
1569+
"14":"0000000000000000",
1570+
"15":"0000000000000000",
1571+
"16":"960b000001000000",
1572+
"17":"0202000000000000",
1573+
"18":"2b00000000000000",
1574+
"19":"0000000000000000",
1575+
"20":"0000000000000000"
1576+
},
1577+
"memory":[
1578+
{"address":140734799804592,"bytes":"c8f8bf5fff7f0000c9a59e8cff7f0000"},
1579+
{"address":140734799804616,"bytes":"00000000000000000100000000000000"}
1580+
]
1581+
}
1582+
]
1583+
1584+
It contains an array of dictionaries with all of the key value pairs that are
1585+
normally in the stop reply packet, including the expedited registers. The registers are
1586+
passed as hex-encoded JSON string in debuggee-endian byte order. Note that the register
1587+
numbers are decimal numbers, unlike the stop-reply packet, where they are written in
1588+
hex. The packet also contains expedited memory in the "memory" key. This allows the
1589+
server to expedite memory that the client is likely to use (e.g., areas around the
1590+
stack pointer, which are needed for computing backtraces) and it reduces the packet
1591+
count.
1592+
1593+
On MacOSX with debugserver, we expedite the frame pointer backchain for a thread
1594+
(up to 256 entries) by reading 2 pointers worth of bytes at the frame pointer (for
1595+
the previous FP and PC), and follow the backchain. Most backtraces on MacOSX and
1596+
iOS now don't require us to read any memory!

‎lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp

+167-28
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "lldb/Host/common/NativeRegisterContext.h"
4444
#include "lldb/Host/common/NativeProcessProtocol.h"
4545
#include "lldb/Host/common/NativeThreadProtocol.h"
46+
#include "lldb/Utility/JSON.h"
4647

4748
// Project includes
4849
#include "Utility/StringExtractorGDBRemote.h"
@@ -161,6 +162,8 @@ GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers()
161162
&GDBRemoteCommunicationServerLLGS::Handle_qsThreadInfo);
162163
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qThreadStopInfo,
163164
&GDBRemoteCommunicationServerLLGS::Handle_qThreadStopInfo);
165+
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_jThreadsInfo,
166+
&GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo);
164167
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qWatchpointSupportInfo,
165168
&GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo);
166169
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qXfer_auxv_read,
@@ -455,6 +458,83 @@ WriteRegisterValueInHexFixedWidth (StreamString &response,
455458
}
456459
}
457460

461+
static JSONObject::SP
462+
GetRegistersAsJSON(NativeThreadProtocol &thread)
463+
{
464+
Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_THREAD));
465+
466+
NativeRegisterContextSP reg_ctx_sp = thread.GetRegisterContext ();
467+
if (! reg_ctx_sp)
468+
return nullptr;
469+
470+
JSONObject::SP register_object_sp = std::make_shared<JSONObject>();
471+
// Expedite all registers in the first register set (i.e. should be GPRs) that are not contained in other registers.
472+
const RegisterSet *reg_set_p = reg_ctx_sp->GetRegisterSet(0);
473+
if (! reg_set_p)
474+
return nullptr;
475+
476+
for (const uint32_t *reg_num_p = reg_set_p->registers; *reg_num_p != LLDB_INVALID_REGNUM; ++reg_num_p)
477+
{
478+
const RegisterInfo *const reg_info_p = reg_ctx_sp->GetRegisterInfoAtIndex(*reg_num_p);
479+
if (reg_info_p == nullptr)
480+
{
481+
if (log)
482+
log->Printf("%s failed to get register info for register index %" PRIu32,
483+
__FUNCTION__, *reg_num_p);
484+
continue;
485+
}
486+
487+
if (reg_info_p->value_regs != nullptr)
488+
continue; // Only expedite registers that are not contained in other registers.
489+
490+
RegisterValue reg_value;
491+
Error error = reg_ctx_sp->ReadRegister(reg_info_p, reg_value);
492+
if (error.Fail())
493+
{
494+
if (log)
495+
log->Printf("%s failed to read register '%s' index %" PRIu32 ": %s", __FUNCTION__,
496+
reg_info_p->name ? reg_info_p->name : "<unnamed-register>", *reg_num_p,
497+
error.AsCString ());
498+
continue;
499+
}
500+
501+
StreamString stream;
502+
WriteRegisterValueInHexFixedWidth(stream, reg_ctx_sp, *reg_info_p, &reg_value);
503+
504+
register_object_sp->SetObject(std::to_string(*reg_num_p),
505+
std::make_shared<JSONString>(stream.GetString()));
506+
}
507+
508+
return register_object_sp;
509+
}
510+
511+
static const char *
512+
GetStopReasonString(StopReason stop_reason)
513+
{
514+
switch (stop_reason)
515+
{
516+
case eStopReasonTrace:
517+
return "trace";
518+
case eStopReasonBreakpoint:
519+
return "breakpoint";
520+
case eStopReasonWatchpoint:
521+
return "watchpoint";
522+
case eStopReasonSignal:
523+
return "signal";
524+
case eStopReasonException:
525+
return "exception";
526+
case eStopReasonExec:
527+
return "exec";
528+
case eStopReasonInstrumentation:
529+
case eStopReasonInvalid:
530+
case eStopReasonPlanComplete:
531+
case eStopReasonThreadExiting:
532+
case eStopReasonNone:
533+
break; // ignored
534+
}
535+
return nullptr;
536+
}
537+
458538
GDBRemoteCommunication::PacketResult
459539
GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread (lldb::tid_t tid)
460540
{
@@ -595,34 +675,7 @@ GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread (lldb::tid_t tid)
595675
}
596676
}
597677

598-
const char* reason_str = nullptr;
599-
switch (tid_stop_info.reason)
600-
{
601-
case eStopReasonTrace:
602-
reason_str = "trace";
603-
break;
604-
case eStopReasonBreakpoint:
605-
reason_str = "breakpoint";
606-
break;
607-
case eStopReasonWatchpoint:
608-
reason_str = "watchpoint";
609-
break;
610-
case eStopReasonSignal:
611-
reason_str = "signal";
612-
break;
613-
case eStopReasonException:
614-
reason_str = "exception";
615-
break;
616-
case eStopReasonExec:
617-
reason_str = "exec";
618-
break;
619-
case eStopReasonInstrumentation:
620-
case eStopReasonInvalid:
621-
case eStopReasonPlanComplete:
622-
case eStopReasonThreadExiting:
623-
case eStopReasonNone:
624-
break;
625-
}
678+
const char* reason_str = GetStopReasonString(tid_stop_info.reason);
626679
if (reason_str != nullptr)
627680
{
628681
response.Printf ("reason:%s;", reason_str);
@@ -2637,6 +2690,92 @@ GDBRemoteCommunicationServerLLGS::Handle_qThreadStopInfo (StringExtractorGDBRemo
26372690
return SendStopReplyPacketForThread (tid);
26382691
}
26392692

2693+
GDBRemoteCommunication::PacketResult
2694+
GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo (StringExtractorGDBRemote &)
2695+
{
2696+
Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD));
2697+
2698+
// Ensure we have a debugged process.
2699+
if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID))
2700+
return SendErrorResponse (50);
2701+
2702+
if (log)
2703+
log->Printf ("GDBRemoteCommunicationServerLLGS::%s preparing packet for pid %" PRIu64,
2704+
__FUNCTION__, m_debugged_process_sp->GetID());
2705+
2706+
JSONArray threads_array;
2707+
2708+
// Ensure we can get info on the given thread.
2709+
uint32_t thread_idx = 0;
2710+
for ( NativeThreadProtocolSP thread_sp;
2711+
(thread_sp = m_debugged_process_sp->GetThreadAtIndex(thread_idx)) != nullptr;
2712+
++thread_idx)
2713+
{
2714+
2715+
JSONObject::SP thread_obj_sp = std::make_shared<JSONObject>();
2716+
2717+
lldb::tid_t tid = thread_sp->GetID();
2718+
2719+
// Grab the reason this thread stopped.
2720+
struct ThreadStopInfo tid_stop_info;
2721+
std::string description;
2722+
if (!thread_sp->GetStopReason (tid_stop_info, description))
2723+
return SendErrorResponse (52);
2724+
2725+
const int signum = tid_stop_info.details.signal.signo;
2726+
if (log)
2727+
{
2728+
log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 " got signal signo = %d, reason = %d, exc_type = %" PRIu64,
2729+
__FUNCTION__,
2730+
m_debugged_process_sp->GetID (),
2731+
tid,
2732+
signum,
2733+
tid_stop_info.reason,
2734+
tid_stop_info.details.exception.type);
2735+
}
2736+
2737+
thread_obj_sp->SetObject("tid", std::make_shared<JSONNumber>(tid));
2738+
if (signum != LLDB_INVALID_SIGNAL_NUMBER)
2739+
thread_obj_sp->SetObject("signal", std::make_shared<JSONNumber>(uint64_t(signum)));
2740+
2741+
const std::string thread_name = thread_sp->GetName ();
2742+
if (! thread_name.empty())
2743+
thread_obj_sp->SetObject("name", std::make_shared<JSONString>(thread_name));
2744+
2745+
if (JSONObject::SP registers_sp = GetRegistersAsJSON(*thread_sp))
2746+
thread_obj_sp->SetObject("registers", registers_sp);
2747+
2748+
if (const char *stop_reason_str = GetStopReasonString(tid_stop_info.reason))
2749+
thread_obj_sp->SetObject("reason", std::make_shared<JSONString>(stop_reason_str));
2750+
2751+
if (! description.empty())
2752+
thread_obj_sp->SetObject("description", std::make_shared<JSONString>(description));
2753+
2754+
if ((tid_stop_info.reason == eStopReasonException) && tid_stop_info.details.exception.type)
2755+
{
2756+
thread_obj_sp->SetObject("metype",
2757+
std::make_shared<JSONNumber>(tid_stop_info.details.exception.type));
2758+
2759+
JSONArray::SP medata_array_sp = std::make_shared<JSONArray>();
2760+
for (uint32_t i = 0; i < tid_stop_info.details.exception.data_count; ++i)
2761+
{
2762+
medata_array_sp->AppendObject(std::make_shared<JSONNumber>(
2763+
tid_stop_info.details.exception.data[i]));
2764+
}
2765+
thread_obj_sp->SetObject("medata", medata_array_sp);
2766+
}
2767+
2768+
threads_array.AppendObject(thread_obj_sp);
2769+
}
2770+
// TODO: Expedite interesting regions of inferior memory
2771+
2772+
StreamString response;
2773+
threads_array.Write(response);
2774+
StreamGDBRemote escaped_response;
2775+
escaped_response.PutEscapedBytes(response.GetData(), response.GetSize());
2776+
return SendPacketNoLock (escaped_response.GetData(), escaped_response.GetSize());
2777+
}
2778+
26402779
GDBRemoteCommunication::PacketResult
26412780
GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo (StringExtractorGDBRemote &packet)
26422781
{

‎lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h

+3
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,9 @@ class GDBRemoteCommunicationServerLLGS :
240240
PacketResult
241241
Handle_qThreadStopInfo (StringExtractorGDBRemote &packet);
242242

243+
PacketResult
244+
Handle_jThreadsInfo (StringExtractorGDBRemote &packet);
245+
243246
PacketResult
244247
Handle_qWatchpointSupportInfo (StringExtractorGDBRemote &packet);
245248

‎lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -1397,6 +1397,7 @@ ProcessGDBRemote::WillResume ()
13971397
m_continue_C_tids.clear();
13981398
m_continue_s_tids.clear();
13991399
m_continue_S_tids.clear();
1400+
m_threads_info_sp.reset();
14001401
return Error();
14011402
}
14021403

@@ -2094,6 +2095,7 @@ ProcessGDBRemote::SetThreadStopInfo (StructuredData::Dictionary *thread_dict)
20942095
static ConstString g_key_address("address");
20952096
static ConstString g_key_bytes("bytes");
20962097
static ConstString g_key_description("description");
2098+
static ConstString g_key_signal("signal");
20972099

20982100
// Stop with signal and thread info
20992101
lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
@@ -2238,6 +2240,8 @@ ProcessGDBRemote::SetThreadStopInfo (StructuredData::Dictionary *thread_dict)
22382240
}
22392241

22402242
}
2243+
else if (key == g_key_signal)
2244+
signo = object->GetIntegerValue(LLDB_INVALID_SIGNAL_NUMBER);
22412245
return true; // Keep iterating through all dictionary key/value pairs
22422246
});
22432247

‎lldb/source/Utility/StringExtractorGDBRemote.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,9 @@ StringExtractorGDBRemote::GetServerPacketType () const
221221
break;
222222

223223
case 'j':
224-
if (PACKET_MATCHES("jSignalInfo")) return eServerPacketType_jSignalsInfo;
224+
if (PACKET_MATCHES("jSignalInfo")) return eServerPacketType_jSignalsInfo;
225+
if (PACKET_MATCHES("jThreadsInfo")) return eServerPacketType_jThreadsInfo;
226+
225227

226228
case 'v':
227229
if (PACKET_STARTS_WITH("vFile:"))

‎lldb/source/Utility/StringExtractorGDBRemote.h

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ class StringExtractorGDBRemote : public StringExtractor
9797
eServerPacketType_QSyncThreadState,
9898
eServerPacketType_QThreadSuffixSupported,
9999

100+
eServerPacketType_jThreadsInfo,
100101
eServerPacketType_qsThreadInfo,
101102
eServerPacketType_qfThreadInfo,
102103
eServerPacketType_qGetPid,

‎lldb/test/functionalities/thread/concurrent_events/TestConcurrentEvents.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ def do_thread_actions(self,
503503

504504
# The inferior process should have exited without crashing
505505
self.assertEqual(0, self.crash_count, "Unexpected thread(s) in crashed state")
506-
self.assertTrue(self.inferior_process.GetState() == lldb.eStateExited, PROCESS_EXITED)
506+
self.assertEqual(self.inferior_process.GetState(), lldb.eStateExited, PROCESS_EXITED)
507507

508508
# Verify the number of actions took place matches expected numbers
509509
expected_breakpoint_threads = num_delay_breakpoint_threads + num_breakpoint_threads

0 commit comments

Comments
 (0)
Please sign in to comment.