labath created this revision.
labath added reviewers: clayborg, ovyalov, tberghammer.
labath added a subscriber: lldb-commits.

This commit adds initial support for the jThreadsInfo packet to lldb-server. 
The current
implementation does not expedite inferior memory. It also does not send over 
all GPR, but only
the most important ones (PC, SP, FP, RA), as for some reasons writing the 
registers is incredibly
slow in lldb-server (I intend to investigate this further). Nevertheless, this 
implementation
speeds up stepping of a heavily multi-threaded inferior significantly (about 
50%).

I have also added a description of the new packet to our protocol documentation 
(mostly taken
from Greg's earlier commit message).

http://reviews.llvm.org/D11187

Files:
  docs/lldb-gdb-remote.txt
  source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
  source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
  source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
  source/Utility/StringExtractorGDBRemote.cpp
  source/Utility/StringExtractorGDBRemote.h
  test/functionalities/thread/concurrent_events/TestConcurrentEvents.py

Index: test/functionalities/thread/concurrent_events/TestConcurrentEvents.py
===================================================================
--- test/functionalities/thread/concurrent_events/TestConcurrentEvents.py
+++ test/functionalities/thread/concurrent_events/TestConcurrentEvents.py
@@ -503,7 +503,7 @@
 
             # 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
Index: source/Utility/StringExtractorGDBRemote.h
===================================================================
--- source/Utility/StringExtractorGDBRemote.h
+++ source/Utility/StringExtractorGDBRemote.h
@@ -97,6 +97,7 @@
         eServerPacketType_QSyncThreadState,
         eServerPacketType_QThreadSuffixSupported,
 
+        eServerPacketType_jThreadsInfo,
         eServerPacketType_qsThreadInfo,
         eServerPacketType_qfThreadInfo,
         eServerPacketType_qGetPid,
Index: source/Utility/StringExtractorGDBRemote.cpp
===================================================================
--- source/Utility/StringExtractorGDBRemote.cpp
+++ source/Utility/StringExtractorGDBRemote.cpp
@@ -221,7 +221,9 @@
         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:"))
Index: source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
===================================================================
--- source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -1397,6 +1397,7 @@
     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 @@
     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 @@
             }
 
         }
+        else if (key == g_key_signal)
+            signo = object->GetIntegerValue(LLDB_INVALID_SIGNAL_NUMBER);
         return true; // Keep iterating through all dictionary key/value pairs
     });
 
Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
===================================================================
--- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -241,6 +241,9 @@
     Handle_qThreadStopInfo (StringExtractorGDBRemote &packet);
 
     PacketResult
+    Handle_jThreadsInfo (StringExtractorGDBRemote &packet);
+
+    PacketResult
     Handle_qWatchpointSupportInfo (StringExtractorGDBRemote &packet);
 
     PacketResult
Index: source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
===================================================================
--- source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -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::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,85 @@
     }
 }
 
+static JSONObject::SP
+GetGPRAsJSON(NativeThreadProtocol &thread)
+{
+    Log *log (GetLogIfAnyCategoriesSet (LIBLLDB_LOG_THREAD));
+
+    NativeRegisterContextSP reg_ctx_sp = thread.GetRegisterContext ();
+    if (! reg_ctx_sp)
+        return nullptr;
+
+    static constexpr int k_expedited_registers[] = {
+        LLDB_REGNUM_GENERIC_PC, LLDB_REGNUM_GENERIC_SP, LLDB_REGNUM_GENERIC_FP, LLDB_REGNUM_GENERIC_RA
+    };
+    JSONObject::SP register_object_sp (new JSONObject);
+    for (int regnum: k_expedited_registers)
+    {
+        const uint32_t regindex =
+            reg_ctx_sp->ConvertRegisterKindToRegisterNumber(eRegisterKindGeneric, regnum);
+        if (regindex == LLDB_INVALID_REGNUM)
+            continue; // The target does not support the given register.
+
+        const RegisterInfo *const reg_info_p = reg_ctx_sp->GetRegisterInfoAtIndex(regindex);
+        if (reg_info_p == nullptr)
+        {
+            if (log)
+                log->Printf("%s failed to get register info for register index %" PRIu32,
+                        __FUNCTION__, regindex);
+            continue;
+        }
+
+        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>", regnum, error.AsCString ());
+            continue;
+        }
+
+        StreamString stream;
+        stream.Printf("%d", regindex);
+        std::string reg_num_str = stream.GetString();
+
+        stream.Clear();
+        WriteRegisterValueInHexFixedWidth(stream, reg_ctx_sp, *reg_info_p, &reg_value);
+
+        register_object_sp->SetObject(reg_num_str, JSONString::SP(new 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";
+        break;
+    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 +677,7 @@
         }
     }
 
-    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 +2693,92 @@
 }
 
 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 (new 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 = GetGPRAsJSON(*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 (new 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.
Index: docs/lldb-gdb-remote.txt
===================================================================
--- docs/lldb-gdb-remote.txt
+++ docs/lldb-gdb-remote.txt
@@ -1530,3 +1530,66 @@
 //  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. Notice that
+is 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!
_______________________________________________
lldb-commits mailing list
lldb-commits@cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/lldb-commits

Reply via email to