Author: labath Date: Thu Jul 16 09:14:35 2015 New Revision: 242402 URL: http://llvm.org/viewvc/llvm-project?rev=242402&view=rev Log: 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 Modified: lldb/trunk/docs/lldb-gdb-remote.txt lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp lldb/trunk/source/Utility/StringExtractorGDBRemote.cpp lldb/trunk/source/Utility/StringExtractorGDBRemote.h lldb/trunk/test/functionalities/thread/concurrent_events/TestConcurrentEvents.py Modified: lldb/trunk/docs/lldb-gdb-remote.txt URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/docs/lldb-gdb-remote.txt?rev=242402&r1=242401&r2=242402&view=diff ============================================================================== --- lldb/trunk/docs/lldb-gdb-remote.txt (original) +++ lldb/trunk/docs/lldb-gdb-remote.txt Thu Jul 16 09:14:35 2015 @@ -1530,3 +1530,67 @@ for this region. // Low. If this packet is absent, lldb will read the Mach-O headers/load // commands out of memory. //---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// "jThreadsInfo" +// +// BRIEF +// Ask for the server for thread stop information of all threads. +// +// PRIORITY TO IMPLEMENT +// Low. This is a performance optimization, which speeds up debugging by avoiding +// multiple round-trips for retrieving thread information. The information from this +// packet can be retrieved using a combination of qThreadStopInfo and m packets. +//---------------------------------------------------------------------- + +The data in this packet is very similar to the stop reply packets, but is packaged in +JSON and uses JSON arrays where applicable. The JSON output looks like: + [ + { "tid":1580681, + "metype":6, + "medata":[2,0], + "reason":"exception", + "qaddr":140735118423168, + "registers": { + "0":"8000000000000000", + "1":"0000000000000000", + "2":"20fabf5fff7f0000", + "3":"e8f8bf5fff7f0000", + "4":"0100000000000000", + "5":"d8f8bf5fff7f0000", + "6":"b0f8bf5fff7f0000", + "7":"20f4bf5fff7f0000", + "8":"8000000000000000", + "9":"61a8db78a61500db", + "10":"3200000000000000", + "11":"4602000000000000", + "12":"0000000000000000", + "13":"0000000000000000", + "14":"0000000000000000", + "15":"0000000000000000", + "16":"960b000001000000", + "17":"0202000000000000", + "18":"2b00000000000000", + "19":"0000000000000000", + "20":"0000000000000000" + }, + "memory":[ + {"address":140734799804592,"bytes":"c8f8bf5fff7f0000c9a59e8cff7f0000"}, + {"address":140734799804616,"bytes":"00000000000000000100000000000000"} + ] + } + ] + +It contains an array of dictionaries with all of the key value pairs that are +normally in the stop reply packet, including the expedited registers. The registers are +passed as hex-encoded JSON string in debuggee-endian byte order. Note that the register +numbers are decimal numbers, unlike the stop-reply packet, where they are written in +hex. The packet also contains expedited memory in the "memory" key. This allows the +server to expedite memory that the client is likely to use (e.g., areas around the +stack pointer, which are needed for computing backtraces) and it reduces the packet +count. + +On MacOSX with debugserver, we expedite the frame pointer backchain for a thread +(up to 256 entries) by reading 2 pointers worth of bytes at the frame pointer (for +the previous FP and PC), and follow the backchain. Most backtraces on MacOSX and +iOS now don't require us to read any memory! Modified: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp?rev=242402&r1=242401&r2=242402&view=diff ============================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp (original) +++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp Thu Jul 16 09:14:35 2015 @@ -43,6 +43,7 @@ #include "lldb/Host/common/NativeRegisterContext.h" #include "lldb/Host/common/NativeProcessProtocol.h" #include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/Utility/JSON.h" // Project includes #include "Utility/StringExtractorGDBRemote.h" @@ -161,6 +162,8 @@ GDBRemoteCommunicationServerLLGS::Regist &GDBRemoteCommunicationServerLLGS::Handle_qsThreadInfo); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qThreadStopInfo, &GDBRemoteCommunicationServerLLGS::Handle_qThreadStopInfo); + RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_jThreadsInfo, + &GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qWatchpointSupportInfo, &GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo); RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_qXfer_auxv_read, @@ -455,6 +458,83 @@ WriteRegisterValueInHexFixedWidth (Strea } } +static JSONObject::SP +GetRegistersAsJSON(NativeThreadProtocol &thread) +{ + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_THREAD)); + + NativeRegisterContextSP reg_ctx_sp = thread.GetRegisterContext (); + if (! reg_ctx_sp) + return nullptr; + + JSONObject::SP register_object_sp = std::make_shared<JSONObject>(); + // Expedite all registers in the first register set (i.e. should be GPRs) that are not contained in other registers. + const RegisterSet *reg_set_p = reg_ctx_sp->GetRegisterSet(0); + if (! reg_set_p) + return nullptr; + + for (const uint32_t *reg_num_p = reg_set_p->registers; *reg_num_p != LLDB_INVALID_REGNUM; ++reg_num_p) + { + const RegisterInfo *const reg_info_p = reg_ctx_sp->GetRegisterInfoAtIndex(*reg_num_p); + if (reg_info_p == nullptr) + { + if (log) + log->Printf("%s failed to get register info for register index %" PRIu32, + __FUNCTION__, *reg_num_p); + continue; + } + + if (reg_info_p->value_regs != nullptr) + continue; // Only expedite registers that are not contained in other registers. + + RegisterValue reg_value; + Error error = reg_ctx_sp->ReadRegister(reg_info_p, reg_value); + if (error.Fail()) + { + if (log) + log->Printf("%s failed to read register '%s' index %" PRIu32 ": %s", __FUNCTION__, + reg_info_p->name ? reg_info_p->name : "<unnamed-register>", *reg_num_p, + error.AsCString ()); + continue; + } + + StreamString stream; + WriteRegisterValueInHexFixedWidth(stream, reg_ctx_sp, *reg_info_p, ®_value); + + register_object_sp->SetObject(std::to_string(*reg_num_p), + std::make_shared<JSONString>(stream.GetString())); + } + + return register_object_sp; +} + +static const char * +GetStopReasonString(StopReason stop_reason) +{ + switch (stop_reason) + { + case eStopReasonTrace: + return "trace"; + case eStopReasonBreakpoint: + return "breakpoint"; + case eStopReasonWatchpoint: + return "watchpoint"; + case eStopReasonSignal: + return "signal"; + case eStopReasonException: + return "exception"; + case eStopReasonExec: + return "exec"; + case eStopReasonInstrumentation: + case eStopReasonInvalid: + case eStopReasonPlanComplete: + case eStopReasonThreadExiting: + case eStopReasonNone: + break; // ignored + } + return nullptr; +} + GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::SendStopReplyPacketForThread (lldb::tid_t tid) { @@ -595,34 +675,7 @@ GDBRemoteCommunicationServerLLGS::SendSt } } - const char* reason_str = nullptr; - switch (tid_stop_info.reason) - { - case eStopReasonTrace: - reason_str = "trace"; - break; - case eStopReasonBreakpoint: - reason_str = "breakpoint"; - break; - case eStopReasonWatchpoint: - reason_str = "watchpoint"; - break; - case eStopReasonSignal: - reason_str = "signal"; - break; - case eStopReasonException: - reason_str = "exception"; - break; - case eStopReasonExec: - reason_str = "exec"; - break; - case eStopReasonInstrumentation: - case eStopReasonInvalid: - case eStopReasonPlanComplete: - case eStopReasonThreadExiting: - case eStopReasonNone: - break; - } + const char* reason_str = GetStopReasonString(tid_stop_info.reason); if (reason_str != nullptr) { response.Printf ("reason:%s;", reason_str); @@ -2638,6 +2691,92 @@ GDBRemoteCommunicationServerLLGS::Handle } GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo (StringExtractorGDBRemote &) +{ + Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_THREAD)); + + // Ensure we have a debugged process. + if (!m_debugged_process_sp || (m_debugged_process_sp->GetID () == LLDB_INVALID_PROCESS_ID)) + return SendErrorResponse (50); + + if (log) + log->Printf ("GDBRemoteCommunicationServerLLGS::%s preparing packet for pid %" PRIu64, + __FUNCTION__, m_debugged_process_sp->GetID()); + + JSONArray threads_array; + + // Ensure we can get info on the given thread. + uint32_t thread_idx = 0; + for ( NativeThreadProtocolSP thread_sp; + (thread_sp = m_debugged_process_sp->GetThreadAtIndex(thread_idx)) != nullptr; + ++thread_idx) + { + + JSONObject::SP thread_obj_sp = std::make_shared<JSONObject>(); + + lldb::tid_t tid = thread_sp->GetID(); + + // Grab the reason this thread stopped. + struct ThreadStopInfo tid_stop_info; + std::string description; + if (!thread_sp->GetStopReason (tid_stop_info, description)) + return SendErrorResponse (52); + + const int signum = tid_stop_info.details.signal.signo; + if (log) + { + log->Printf ("GDBRemoteCommunicationServerLLGS::%s pid %" PRIu64 " tid %" PRIu64 " got signal signo = %d, reason = %d, exc_type = %" PRIu64, + __FUNCTION__, + m_debugged_process_sp->GetID (), + tid, + signum, + tid_stop_info.reason, + tid_stop_info.details.exception.type); + } + + thread_obj_sp->SetObject("tid", std::make_shared<JSONNumber>(tid)); + if (signum != LLDB_INVALID_SIGNAL_NUMBER) + thread_obj_sp->SetObject("signal", std::make_shared<JSONNumber>(uint64_t(signum))); + + const std::string thread_name = thread_sp->GetName (); + if (! thread_name.empty()) + thread_obj_sp->SetObject("name", std::make_shared<JSONString>(thread_name)); + + if (JSONObject::SP registers_sp = GetRegistersAsJSON(*thread_sp)) + thread_obj_sp->SetObject("registers", registers_sp); + + if (const char *stop_reason_str = GetStopReasonString(tid_stop_info.reason)) + thread_obj_sp->SetObject("reason", std::make_shared<JSONString>(stop_reason_str)); + + if (! description.empty()) + thread_obj_sp->SetObject("description", std::make_shared<JSONString>(description)); + + if ((tid_stop_info.reason == eStopReasonException) && tid_stop_info.details.exception.type) + { + thread_obj_sp->SetObject("metype", + std::make_shared<JSONNumber>(tid_stop_info.details.exception.type)); + + JSONArray::SP medata_array_sp = std::make_shared<JSONArray>(); + for (uint32_t i = 0; i < tid_stop_info.details.exception.data_count; ++i) + { + medata_array_sp->AppendObject(std::make_shared<JSONNumber>( + tid_stop_info.details.exception.data[i])); + } + thread_obj_sp->SetObject("medata", medata_array_sp); + } + + threads_array.AppendObject(thread_obj_sp); + } + // TODO: Expedite interesting regions of inferior memory + + StreamString response; + threads_array.Write(response); + StreamGDBRemote escaped_response; + escaped_response.PutEscapedBytes(response.GetData(), response.GetSize()); + return SendPacketNoLock (escaped_response.GetData(), escaped_response.GetSize()); +} + +GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo (StringExtractorGDBRemote &packet) { // Fail if we don't have a current process. Modified: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h?rev=242402&r1=242401&r2=242402&view=diff ============================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h (original) +++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h Thu Jul 16 09:14:35 2015 @@ -241,6 +241,9 @@ protected: Handle_qThreadStopInfo (StringExtractorGDBRemote &packet); PacketResult + Handle_jThreadsInfo (StringExtractorGDBRemote &packet); + + PacketResult Handle_qWatchpointSupportInfo (StringExtractorGDBRemote &packet); PacketResult Modified: lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp?rev=242402&r1=242401&r2=242402&view=diff ============================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp (original) +++ lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp Thu Jul 16 09:14:35 2015 @@ -1397,6 +1397,7 @@ ProcessGDBRemote::WillResume () m_continue_C_tids.clear(); m_continue_s_tids.clear(); m_continue_S_tids.clear(); + m_threads_info_sp.reset(); return Error(); } @@ -2094,6 +2095,7 @@ ProcessGDBRemote::SetThreadStopInfo (Str static ConstString g_key_address("address"); static ConstString g_key_bytes("bytes"); static ConstString g_key_description("description"); + static ConstString g_key_signal("signal"); // Stop with signal and thread info lldb::tid_t tid = LLDB_INVALID_THREAD_ID; @@ -2238,6 +2240,8 @@ ProcessGDBRemote::SetThreadStopInfo (Str } } + else if (key == g_key_signal) + signo = object->GetIntegerValue(LLDB_INVALID_SIGNAL_NUMBER); return true; // Keep iterating through all dictionary key/value pairs }); Modified: lldb/trunk/source/Utility/StringExtractorGDBRemote.cpp URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Utility/StringExtractorGDBRemote.cpp?rev=242402&r1=242401&r2=242402&view=diff ============================================================================== --- lldb/trunk/source/Utility/StringExtractorGDBRemote.cpp (original) +++ lldb/trunk/source/Utility/StringExtractorGDBRemote.cpp Thu Jul 16 09:14:35 2015 @@ -221,7 +221,9 @@ StringExtractorGDBRemote::GetServerPacke break; case 'j': - if (PACKET_MATCHES("jSignalInfo")) return eServerPacketType_jSignalsInfo; + if (PACKET_MATCHES("jSignalInfo")) return eServerPacketType_jSignalsInfo; + if (PACKET_MATCHES("jThreadsInfo")) return eServerPacketType_jThreadsInfo; + case 'v': if (PACKET_STARTS_WITH("vFile:")) Modified: lldb/trunk/source/Utility/StringExtractorGDBRemote.h URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Utility/StringExtractorGDBRemote.h?rev=242402&r1=242401&r2=242402&view=diff ============================================================================== --- lldb/trunk/source/Utility/StringExtractorGDBRemote.h (original) +++ lldb/trunk/source/Utility/StringExtractorGDBRemote.h Thu Jul 16 09:14:35 2015 @@ -97,6 +97,7 @@ public: eServerPacketType_QSyncThreadState, eServerPacketType_QThreadSuffixSupported, + eServerPacketType_jThreadsInfo, eServerPacketType_qsThreadInfo, eServerPacketType_qfThreadInfo, eServerPacketType_qGetPid, Modified: lldb/trunk/test/functionalities/thread/concurrent_events/TestConcurrentEvents.py URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/functionalities/thread/concurrent_events/TestConcurrentEvents.py?rev=242402&r1=242401&r2=242402&view=diff ============================================================================== --- lldb/trunk/test/functionalities/thread/concurrent_events/TestConcurrentEvents.py (original) +++ lldb/trunk/test/functionalities/thread/concurrent_events/TestConcurrentEvents.py Thu Jul 16 09:14:35 2015 @@ -503,7 +503,7 @@ class ConcurrentEventsTestCase(TestBase) # The inferior process should have exited without crashing self.assertEqual(0, self.crash_count, "Unexpected thread(s) in crashed state") - self.assertTrue(self.inferior_process.GetState() == lldb.eStateExited, PROCESS_EXITED) + self.assertEqual(self.inferior_process.GetState(), lldb.eStateExited, PROCESS_EXITED) # Verify the number of actions took place matches expected numbers expected_breakpoint_threads = num_delay_breakpoint_threads + num_breakpoint_threads _______________________________________________ lldb-commits mailing list [email protected] http://lists.cs.uiuc.edu/mailman/listinfo/lldb-commits
