[Lldb-commits] [lldb] r240354 - Reduced packet counts to the remote GDB server where possible.

Greg Clayton gclayton at apple.com
Mon Jun 22 16:12:45 PDT 2015


Author: gclayton
Date: Mon Jun 22 18:12:45 2015
New Revision: 240354

URL: http://llvm.org/viewvc/llvm-project?rev=240354&view=rev
Log:
Reduced packet counts to the remote GDB server where possible.

We have been working on reducing the packet count that is sent between LLDB and the debugserver on MacOSX and iOS. Our approach to this was to reduce the packets required when debugging multiple threads. We currently make one qThreadStopInfoXXXX call (where XXXX is the thread ID in hex) per thread except the thread that stopped with a stop reply packet. In order to implement multiple thread infos in a single reply, we need to use structured data, which means JSON. The new jThreadsInfo packet will attempt to retrieve all thread infos in a single packet. The data is very similar to the stop reply packets, but 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 dicitionaries with all of the key value pairs that are normally in the stop reply packet. Including the expedited registers. Notice that is also contains expedited memory in the "memory" key. Any values in this memory will get included in a new L1 cache in lldb_private::Process where if a memory read request is made and that memory request fits into one of the L1 memory cache blocks, it will use that memory data. If a memory request fails in the L1 cache, it will fall back to the L2 cache which is the same block sized caching we were using before these changes. This allows a process to expedite memory that you are likely to use and it reduces 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!

We will try these packets out and if successful, we should port these to lldb-server in the near future. 

<rdar://problem/21494354>


Added:
    lldb/trunk/tools/debugserver/source/JSONGenerator.h
Modified:
    lldb/trunk/include/lldb/Core/RangeMap.h
    lldb/trunk/include/lldb/Core/StructuredData.h
    lldb/trunk/include/lldb/Target/Memory.h
    lldb/trunk/source/API/SBThread.cpp
    lldb/trunk/source/Core/StructuredData.cpp
    lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
    lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
    lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
    lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
    lldb/trunk/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp
    lldb/trunk/source/Target/Memory.cpp
    lldb/trunk/tools/debugserver/debugserver.xcodeproj/project.pbxproj
    lldb/trunk/tools/debugserver/source/RNBRemote.cpp
    lldb/trunk/tools/debugserver/source/RNBRemote.h

Modified: lldb/trunk/include/lldb/Core/RangeMap.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Core/RangeMap.h?rev=240354&r1=240353&r2=240354&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Core/RangeMap.h (original)
+++ lldb/trunk/include/lldb/Core/RangeMap.h Mon Jun 22 18:12:45 2015
@@ -128,9 +128,10 @@ namespace lldb_private {
         {
             return Contains(range.GetRangeBase()) && ContainsEndInclusive(range.GetRangeEnd());
         }
-        
+
+        // Returns true if the two ranges adjoing or intersect
         bool
-        Overlap (const Range &rhs) const
+        DoesAdjoinOrIntersect (const Range &rhs) const
         {
             const BaseType lhs_base = this->GetRangeBase();
             const BaseType rhs_base = rhs.GetRangeBase();
@@ -139,7 +140,19 @@ namespace lldb_private {
             bool result = (lhs_base <= rhs_end) && (lhs_end >= rhs_base);
             return result;
         }
-        
+
+        // Returns true if the two ranges intersect
+        bool
+        DoesIntersect (const Range &rhs) const
+        {
+            const BaseType lhs_base = this->GetRangeBase();
+            const BaseType rhs_base = rhs.GetRangeBase();
+            const BaseType lhs_end = this->GetRangeEnd();
+            const BaseType rhs_end = rhs.GetRangeEnd();
+            bool result = (lhs_base < rhs_end) && (lhs_end > rhs_base);
+            return result;
+        }
+
         bool
         operator < (const Range &rhs) const
         {
@@ -241,7 +254,7 @@ namespace lldb_private {
                 // don't end up allocating and making a new collection for no reason
                 for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++)
                 {
-                    if (prev != end && prev->Overlap(*pos))
+                    if (prev != end && prev->DoesAdjoinOrIntersect(*pos))
                     {
                         can_combine = true;
                         break;
@@ -255,7 +268,7 @@ namespace lldb_private {
                     Collection minimal_ranges;
                     for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++)
                     {
-                        if (prev != end && prev->Overlap(*pos))
+                        if (prev != end && prev->DoesAdjoinOrIntersect(*pos))
                             minimal_ranges.back().SetRangeEnd (std::max<BaseType>(prev->GetRangeEnd(), pos->GetRangeEnd()));
                         else
                             minimal_ranges.push_back (*pos);
@@ -521,7 +534,7 @@ namespace lldb_private {
                 // don't end up allocating and making a new collection for no reason
                 for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++)
                 {
-                    if (prev != end && prev->Overlap(*pos))
+                    if (prev != end && prev->DoesAdjoinOrIntersect(*pos))
                     {
                         can_combine = true;
                         break;
@@ -535,7 +548,7 @@ namespace lldb_private {
                     Collection minimal_ranges;
                     for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++)
                     {
-                        if (prev != end && prev->Overlap(*pos))
+                        if (prev != end && prev->DoesAdjoinOrIntersect(*pos))
                             minimal_ranges.back().SetRangeEnd (std::max<BaseType>(prev->GetRangeEnd(), pos->GetRangeEnd()));
                         else
                             minimal_ranges.push_back (*pos);

Modified: lldb/trunk/include/lldb/Core/StructuredData.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Core/StructuredData.h?rev=240354&r1=240353&r2=240354&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Core/StructuredData.h (original)
+++ lldb/trunk/include/lldb/Core/StructuredData.h Mon Jun 22 18:12:45 2015
@@ -13,10 +13,11 @@
 // C Includes
 // C++ Includes
 
+#include <functional>
 #include <map>
+#include <string>
 #include <utility>
 #include <vector>
-#include <string>
 
 #include "llvm/ADT/StringRef.h"
 
@@ -140,6 +141,15 @@ public:
             return NULL;
         }
 
+        uint64_t
+        GetIntegerValue (uint64_t fail_value = 0)
+        {
+            Integer *integer = GetAsInteger ();
+            if (integer)
+                return integer->GetValue();
+            return fail_value;
+        }
+
         Float *
         GetAsFloat ()
         {
@@ -148,6 +158,15 @@ public:
             return NULL;
         }
 
+        double
+        GetFloatValue (double fail_value = 0.0)
+        {
+            Float *f = GetAsFloat ();
+            if (f)
+                return f->GetValue();
+            return fail_value;
+        }
+
         Boolean *
         GetAsBoolean ()
         {
@@ -156,6 +175,15 @@ public:
             return NULL;
         }
 
+        bool
+        GetBooleanValue (bool fail_value = false)
+        {
+            Boolean *b = GetAsBoolean ();
+            if (b)
+                return b->GetValue();
+            return fail_value;
+        }
+
         String *
         GetAsString ()
         {
@@ -164,6 +192,19 @@ public:
             return NULL;
         }
 
+        std::string
+        GetStringValue(const char *fail_value = NULL)
+        {
+            String *s = GetAsString ();
+            if (s)
+                return s->GetValue();
+
+            if (fail_value && fail_value[0])
+                return std::string(fail_value);
+
+            return std::string();
+        }
+
         Generic *
         GetAsGeneric()
         {
@@ -197,6 +238,17 @@ public:
         {
         }
 
+        void
+        ForEach (std::function <bool(Object* object)> const &foreach_callback) const
+        {
+            for (const auto &object_sp : m_items)
+            {
+                if (foreach_callback(object_sp.get()) == false)
+                    break;
+            }
+        }
+
+
         size_t
         GetSize() const
         {
@@ -447,7 +499,7 @@ public:
             m_value = string;
         }
 
-        std::string
+        const std::string &
         GetValue ()
         {
             return m_value;
@@ -462,6 +514,7 @@ public:
     class Dictionary : public Object
     {
     public:
+
         Dictionary () :
             Object (Type::eTypeDictionary),
             m_dict ()
@@ -478,6 +531,16 @@ public:
             return m_dict.size();
         }
 
+        void
+        ForEach (std::function <bool(ConstString key, Object* object)> const &callback) const
+        {
+            for (const auto &pair : m_dict)
+            {
+                if (callback (pair.first, pair.second.get()) == false)
+                    break;
+            }
+        }
+
         ObjectSP
         GetKeys() const
         {
@@ -628,33 +691,25 @@ public:
         void
         AddIntegerItem (llvm::StringRef key, uint64_t value)
         {
-            ObjectSP val_obj (new Integer());
-            val_obj->GetAsInteger()->SetValue (value);
-            AddItem (key, val_obj);
+            AddItem (key, ObjectSP (new Integer(value)));
         }
 
         void
         AddFloatItem (llvm::StringRef key, double value)
         {
-            ObjectSP val_obj (new Float());
-            val_obj->GetAsFloat()->SetValue (value);
-            AddItem (key, val_obj);
+            AddItem (key, ObjectSP (new Float(value)));
         }
 
         void
         AddStringItem (llvm::StringRef key, std::string value)
         {
-            ObjectSP val_obj (new String());
-            val_obj->GetAsString()->SetValue (value);
-            AddItem (key, val_obj);
+            AddItem (key, ObjectSP (new String(std::move(value))));
         }
 
         void
         AddBooleanItem (llvm::StringRef key, bool value)
         {
-            ObjectSP val_obj (new Boolean());
-            val_obj->GetAsBoolean()->SetValue (value);
-            AddItem (key, val_obj);
+            AddItem (key, ObjectSP (new Boolean(value)));
         }
 
         void Dump(Stream &s) const override;
@@ -690,9 +745,9 @@ public:
     class Generic : public Object
     {
       public:
-        explicit Generic(void *object = nullptr)
-            : Object(Type::eTypeGeneric)
-            , m_object(object)
+        explicit Generic(void *object = nullptr) :
+            Object (Type::eTypeGeneric),
+            m_object (object)
         {
         }
 

Modified: lldb/trunk/include/lldb/Target/Memory.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Target/Memory.h?rev=240354&r1=240353&r2=240354&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Target/Memory.h (original)
+++ lldb/trunk/include/lldb/Target/Memory.h Mon Jun 22 18:12:45 2015
@@ -52,7 +52,7 @@ namespace lldb_private {
         uint32_t
         GetMemoryCacheLineSize() const
         {
-            return m_cache_line_byte_size ;
+            return m_L2_cache_line_byte_size ;
         }
         
         void
@@ -61,17 +61,26 @@ namespace lldb_private {
         bool
         RemoveInvalidRange (lldb::addr_t base_addr, lldb::addr_t byte_size);
 
+        // Allow external sources to populate data into the L1 memory cache
+        void
+        AddL1CacheData(lldb::addr_t addr, const void *src, size_t src_len);
+
+        void
+        AddL1CacheData(lldb::addr_t addr, const lldb::DataBufferSP &data_buffer_sp);
+
     protected:
         typedef std::map<lldb::addr_t, lldb::DataBufferSP> BlockMap;
         typedef RangeArray<lldb::addr_t, lldb::addr_t, 4> InvalidRanges;
+        typedef Range<lldb::addr_t, lldb::addr_t> AddrRange;
         //------------------------------------------------------------------
         // Classes that inherit from MemoryCache can see and modify these
         //------------------------------------------------------------------
-        Process &m_process;
-        uint32_t m_cache_line_byte_size;
         Mutex m_mutex;
-        BlockMap m_cache;
+        BlockMap m_L1_cache; // A first level memory cache whose chunk sizes vary that will be used only if the memory read fits entirely in a chunk
+        BlockMap m_L2_cache; // A memory cache of fixed size chinks (m_L2_cache_line_byte_size bytes in size each)
         InvalidRanges m_invalid_ranges;
+        Process &m_process;
+        uint32_t m_L2_cache_line_byte_size;
     private:
         DISALLOW_COPY_AND_ASSIGN (MemoryCache);
     };

Modified: lldb/trunk/source/API/SBThread.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/API/SBThread.cpp?rev=240354&r1=240353&r2=240354&view=diff
==============================================================================
--- lldb/trunk/source/API/SBThread.cpp (original)
+++ lldb/trunk/source/API/SBThread.cpp Mon Jun 22 18:12:45 2015
@@ -114,13 +114,13 @@ SBThread::GetQueue () const
         else
         {
             if (log)
-                log->Printf ("SBThread(%p)::GetQueueKind() => error: process is running",
+                log->Printf ("SBThread(%p)::GetQueue() => error: process is running",
                              static_cast<void*>(exe_ctx.GetThreadPtr()));
         }
     }
 
     if (log)
-        log->Printf ("SBThread(%p)::GetQueueKind () => SBQueue(%p)",
+        log->Printf ("SBThread(%p)::GetQueue () => SBQueue(%p)",
                      static_cast<void*>(exe_ctx.GetThreadPtr()), static_cast<void*>(queue_sp.get()));
 
     return sb_queue;

Modified: lldb/trunk/source/Core/StructuredData.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Core/StructuredData.cpp?rev=240354&r1=240353&r2=240354&view=diff
==============================================================================
--- lldb/trunk/source/Core/StructuredData.cpp (original)
+++ lldb/trunk/source/Core/StructuredData.cpp Mon Jun 22 18:12:45 2015
@@ -298,6 +298,10 @@ StructuredData::ParseJSON (std::string j
             {
                 object_sp = read_json_object (&c);
             }
+            else if (*c == '[')
+            {
+                object_sp = read_json_array (&c);
+            }
             else
             {
                 // We have bad characters here, this is likely an illegal JSON string.

Modified: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp?rev=240354&r1=240353&r2=240354&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp (original)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp Mon Jun 22 18:12:45 2015
@@ -98,6 +98,7 @@ GDBRemoteCommunicationClient::GDBRemoteC
     m_supports_z4 (true),
     m_supports_QEnvironment (true),
     m_supports_QEnvironmentHexEncoded (true),
+    m_supports_jThreadsInfo (true),
     m_curr_pid (LLDB_INVALID_PROCESS_ID),
     m_curr_tid (LLDB_INVALID_THREAD_ID),
     m_curr_tid_run (LLDB_INVALID_THREAD_ID),
@@ -601,6 +602,32 @@ GDBRemoteCommunicationClient::GetpPacket
     return m_supports_p;
 }
 
+StructuredData::ObjectSP
+GDBRemoteCommunicationClient::GetThreadsInfo()
+{
+    // Get information on all threads at one using the "jThreadsInfo" packet
+    StructuredData::ObjectSP object_sp;
+
+    if (m_supports_jThreadsInfo)
+    {
+        StringExtractorGDBRemote response;
+        m_supports_jThreadExtendedInfo = eLazyBoolNo;
+        if (SendPacketAndWaitForResponse("jThreadsInfo", response, false) == PacketResult::Success)
+        {
+            if (response.IsUnsupportedResponse())
+            {
+                m_supports_jThreadsInfo = false;
+            }
+            else if (!response.Empty())
+            {
+                object_sp = StructuredData::ParseJSON (response.GetStringRef());
+            }
+        }
+    }
+    return object_sp;
+}
+
+
 bool
 GDBRemoteCommunicationClient::GetThreadExtendedInfoSupported ()
 {

Modified: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h?rev=240354&r1=240353&r2=240354&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h (original)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h Mon Jun 22 18:12:45 2015
@@ -17,6 +17,7 @@
 // Other libraries and framework includes
 // Project includes
 #include "lldb/Core/ArchSpec.h"
+#include "lldb/Core/StructuredData.h"
 #include "lldb/Target/Process.h"
 
 #include "GDBRemoteCommunication.h"
@@ -537,6 +538,9 @@ public:
     bool
     AvoidGPackets(ProcessGDBRemote *process);
 
+    StructuredData::ObjectSP
+    GetThreadsInfo();
+
     bool
     GetThreadExtendedInfoSupported();
 
@@ -615,7 +619,8 @@ protected:
         m_supports_z3:1,
         m_supports_z4:1,
         m_supports_QEnvironment:1,
-        m_supports_QEnvironmentHexEncoded:1;
+        m_supports_QEnvironmentHexEncoded:1,
+        m_supports_jThreadsInfo:1;
     
     lldb::pid_t m_curr_pid;
     lldb::tid_t m_curr_tid;         // Current gdb remote protocol thread index for all other operations

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=240354&r1=240353&r2=240354&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp (original)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp Mon Jun 22 18:12:45 2015
@@ -378,6 +378,7 @@ ProcessGDBRemote::ProcessGDBRemote(Targe
     m_async_broadcaster (NULL, "lldb.process.gdb-remote.async-broadcaster"),
     m_async_thread_state_mutex(Mutex::eMutexTypeRecursive),
     m_thread_ids (),
+    m_threads_info_sp (),
     m_continue_c_tids (),
     m_continue_C_tids (),
     m_continue_s_tids (),
@@ -1784,6 +1785,356 @@ ProcessGDBRemote::UpdateThreadList (Thre
     return true;
 }
 
+bool
+ProcessGDBRemote::CalculateThreadStopInfo (ThreadGDBRemote *thread)
+{
+    // See if we got thread stop infos for all threads via the "jThreadsInfo" packet
+    if (m_threads_info_sp)
+    {
+        StructuredData::Array *thread_infos = m_threads_info_sp->GetAsArray();
+        if (thread_infos)
+        {
+            lldb::tid_t tid;
+            const size_t n = thread_infos->GetSize();
+            for (size_t i=0; i<n; ++i)
+            {
+                StructuredData::Dictionary *thread_dict = thread_infos->GetItemAtIndex(i)->GetAsDictionary();
+                if (thread_dict)
+                {
+                    if (thread_dict->GetValueForKeyAsInteger<lldb::tid_t>("tid", tid, LLDB_INVALID_THREAD_ID))
+                    {
+                        if (tid == thread->GetID())
+                            return SetThreadStopInfo(thread_dict);
+                    }
+                }
+            }
+        }
+    }
+
+    // Fall back to using the qThreadStopInfo packet
+    StringExtractorGDBRemote stop_packet;
+    if (GetGDBRemote().GetThreadStopInfo(thread->GetProtocolID(), stop_packet))
+        return SetThreadStopInfo (stop_packet) == eStateStopped;
+    return false;
+}
+
+
+ThreadSP
+ProcessGDBRemote::SetThreadStopInfo (lldb::tid_t tid,
+                                     uint8_t signo,
+                                     const std::string &thread_name,
+                                     const std::string &reason,
+                                     const std::string &description,
+                                     uint32_t exc_type,
+                                     const std::vector<addr_t> &exc_data,
+                                     addr_t thread_dispatch_qaddr)
+{
+    ThreadSP thread_sp;
+    if (tid != LLDB_INVALID_THREAD_ID)
+    {
+        // Scope for "locker" below
+        {
+            // m_thread_list_real does have its own mutex, but we need to
+            // hold onto the mutex between the call to m_thread_list_real.FindThreadByID(...)
+            // and the m_thread_list_real.AddThread(...) so it doesn't change on us
+            Mutex::Locker locker (m_thread_list_real.GetMutex ());
+            thread_sp = m_thread_list_real.FindThreadByProtocolID(tid, false);
+
+            if (!thread_sp)
+            {
+                // Create the thread if we need to
+                thread_sp.reset (new ThreadGDBRemote (*this, tid));
+                m_thread_list_real.AddThread(thread_sp);
+            }
+        }
+
+        if (thread_sp)
+        {
+            ThreadGDBRemote *gdb_thread = static_cast<ThreadGDBRemote *> (thread_sp.get());
+
+            // Clear the stop info just in case we don't set it to anything
+            thread_sp->SetStopInfo (StopInfoSP());
+            thread_sp->SetName (thread_name.empty() ? NULL : thread_name.c_str());
+
+            gdb_thread->SetThreadDispatchQAddr (thread_dispatch_qaddr);
+            if (exc_type != 0)
+            {
+                const size_t exc_data_size = exc_data.size();
+
+                thread_sp->SetStopInfo (StopInfoMachException::CreateStopReasonWithMachException (*thread_sp,
+                                                                                                  exc_type,
+                                                                                                  exc_data_size,
+                                                                                                  exc_data_size >= 1 ? exc_data[0] : 0,
+                                                                                                  exc_data_size >= 2 ? exc_data[1] : 0,
+                                                                                                  exc_data_size >= 3 ? exc_data[2] : 0));
+            }
+            else
+            {
+                bool handled = false;
+                bool did_exec = false;
+                if (!reason.empty())
+                {
+                    if (reason.compare("trace") == 0)
+                    {
+                        thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp));
+                        handled = true;
+                    }
+                    else if (reason.compare("breakpoint") == 0)
+                    {
+                        addr_t pc = thread_sp->GetRegisterContext()->GetPC();
+                        lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc);
+                        if (bp_site_sp)
+                        {
+                            // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread,
+                            // we can just report no reason.  We don't need to worry about stepping over the breakpoint here, that
+                            // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc.
+                            handled = true;
+                            if (bp_site_sp->ValidForThisThread (thread_sp.get()))
+                            {
+                                thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID()));
+                            }
+                            else
+                            {
+                                StopInfoSP invalid_stop_info_sp;
+                                thread_sp->SetStopInfo (invalid_stop_info_sp);
+                            }
+                        }
+                    }
+                    else if (reason.compare("trap") == 0)
+                    {
+                        // Let the trap just use the standard signal stop reason below...
+                    }
+                    else if (reason.compare("watchpoint") == 0)
+                    {
+                        StringExtractor desc_extractor(description.c_str());
+                        addr_t wp_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS);
+                        uint32_t wp_index = desc_extractor.GetU32(LLDB_INVALID_INDEX32);
+                        watch_id_t watch_id = LLDB_INVALID_WATCH_ID;
+                        if (wp_addr != LLDB_INVALID_ADDRESS)
+                        {
+                            WatchpointSP wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_addr);
+                            if (wp_sp)
+                            {
+                                wp_sp->SetHardwareIndex(wp_index);
+                                watch_id = wp_sp->GetID();
+                            }
+                        }
+                        if (watch_id == LLDB_INVALID_WATCH_ID)
+                        {
+                            Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_WATCHPOINTS));
+                            if (log) log->Printf ("failed to find watchpoint");
+                        }
+                        thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithWatchpointID (*thread_sp, watch_id));
+                        handled = true;
+                    }
+                    else if (reason.compare("exception") == 0)
+                    {
+                        thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException(*thread_sp, description.c_str()));
+                        handled = true;
+                    }
+                    else if (reason.compare("exec") == 0)
+                    {
+                        did_exec = true;
+                        thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithExec(*thread_sp));
+                        handled = true;
+                    }
+                }
+
+                if (!handled && signo && did_exec == false)
+                {
+                    if (signo == SIGTRAP)
+                    {
+                        // Currently we are going to assume SIGTRAP means we are either
+                        // hitting a breakpoint or hardware single stepping.
+                        handled = true;
+                        addr_t pc = thread_sp->GetRegisterContext()->GetPC() + m_breakpoint_pc_offset;
+                        lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc);
+
+                        if (bp_site_sp)
+                        {
+                            // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread,
+                            // we can just report no reason.  We don't need to worry about stepping over the breakpoint here, that
+                            // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc.
+                            if (bp_site_sp->ValidForThisThread (thread_sp.get()))
+                            {
+                                if(m_breakpoint_pc_offset != 0)
+                                    thread_sp->GetRegisterContext()->SetPC(pc);
+                                thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID()));
+                            }
+                            else
+                            {
+                                StopInfoSP invalid_stop_info_sp;
+                                thread_sp->SetStopInfo (invalid_stop_info_sp);
+                            }
+                        }
+                        else
+                        {
+                            // If we were stepping then assume the stop was the result of the trace.  If we were
+                            // not stepping then report the SIGTRAP.
+                            // FIXME: We are still missing the case where we single step over a trap instruction.
+                            if (thread_sp->GetTemporaryResumeState() == eStateStepping)
+                                thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp));
+                            else
+                                thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal(*thread_sp, signo, description.c_str()));
+                        }
+                    }
+                    if (!handled)
+                        thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal (*thread_sp, signo, description.c_str()));
+                }
+
+                if (!description.empty())
+                {
+                    lldb::StopInfoSP stop_info_sp (thread_sp->GetStopInfo ());
+                    if (stop_info_sp)
+                    {
+                        const char *stop_info_desc = stop_info_sp->GetDescription();
+                        if (!stop_info_desc || !stop_info_desc[0])
+                            stop_info_sp->SetDescription (description.c_str());
+                    }
+                    else
+                    {
+                        thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException (*thread_sp, description.c_str()));
+                    }
+                }
+            }
+        }
+    }
+    return thread_sp;
+}
+
+StateType
+ProcessGDBRemote::SetThreadStopInfo (StructuredData::Dictionary *thread_dict)
+{
+    static ConstString g_key_tid("tid");
+    static ConstString g_key_name("name");
+    static ConstString g_key_reason("reason");
+    static ConstString g_key_metype("metype");
+    static ConstString g_key_medata("medata");
+    static ConstString g_key_qaddr("qaddr");
+    static ConstString g_key_registers("registers");
+    static ConstString g_key_memory("memory");
+    static ConstString g_key_address("address");
+    static ConstString g_key_bytes("bytes");
+    static ConstString g_key_description("description");
+
+    // Stop with signal and thread info
+    lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
+    uint8_t signo = 0;
+    std::string value;
+    std::string thread_name;
+    std::string reason;
+    std::string description;
+    uint32_t exc_type = 0;
+    std::vector<addr_t> exc_data;
+    addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS;
+    StructuredData::Dictionary *registers_dict = nullptr;
+
+    // Iterate through all of the thread dictionary key/value pairs from the structured data dictionary
+
+    thread_dict->ForEach([this, &tid, &registers_dict, &thread_name, &signo, &reason, &description, &exc_type, &exc_data, &thread_dispatch_qaddr](ConstString key, StructuredData::Object* object) -> bool
+    {
+        if (key == g_key_tid)
+        {
+            // thread in big endian hex
+            tid = object->GetIntegerValue(LLDB_INVALID_THREAD_ID);
+        }
+        else if (key == g_key_metype)
+        {
+            // exception type in big endian hex
+            exc_type = object->GetIntegerValue(0);
+        }
+        else if (key == g_key_medata)
+        {
+            // exception data in big endian hex
+            StructuredData::Array *array = object->GetAsArray();
+            if (array)
+            {
+                array->ForEach([&exc_data](StructuredData::Object* object) -> bool {
+                    exc_data.push_back(object->GetIntegerValue());
+                    return true; // Keep iterating through all array items
+                });
+            }
+        }
+        else if (key == g_key_name)
+        {
+            thread_name = std::move(object->GetStringValue());
+        }
+        else if (key == g_key_qaddr)
+        {
+            thread_dispatch_qaddr = object->GetIntegerValue(LLDB_INVALID_ADDRESS);
+        }
+        else if (key == g_key_reason)
+        {
+            reason = std::move(object->GetStringValue());
+        }
+        else if (key == g_key_description)
+        {
+            description = std::move(object->GetStringValue());
+        }
+        else if (key == g_key_registers)
+        {
+            registers_dict = object->GetAsDictionary();
+        }
+        else if (key == g_key_memory)
+        {
+            StructuredData::Array *array = object->GetAsArray();
+            if (array)
+            {
+                array->ForEach([this](StructuredData::Object* object) -> bool {
+                    StructuredData::Dictionary *mem_cache_dict = object->GetAsDictionary();
+                    if (mem_cache_dict)
+                    {
+                        lldb::addr_t mem_cache_addr = LLDB_INVALID_ADDRESS;
+                        if (mem_cache_dict->GetValueForKeyAsInteger<lldb::addr_t>("address", mem_cache_addr))
+                        {
+                            if (mem_cache_addr != LLDB_INVALID_ADDRESS)
+                            {
+                                StringExtractor bytes;
+                                if (mem_cache_dict->GetValueForKeyAsString("bytes", bytes.GetStringRef()))
+                                {
+                                    bytes.SetFilePos(0);
+
+                                    const size_t byte_size = bytes.GetStringRef().size()/2;
+                                    DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0));
+                                    const size_t bytes_copied = bytes.GetHexBytes (data_buffer_sp->GetBytes(), byte_size, 0);
+                                    if (bytes_copied == byte_size)
+                                        m_memory_cache.AddL1CacheData(mem_cache_addr, data_buffer_sp);
+                                }
+                            }
+                        }
+                    }
+                    return true; // Keep iterating through all array items
+                });
+            }
+
+        }
+        return true; // Keep iterating through all dictionary key/value pairs
+    });
+
+
+    ThreadSP thread_sp = SetThreadStopInfo (tid, signo, thread_name, reason, description, exc_type, exc_data, thread_dispatch_qaddr);
+    if (thread_sp)
+    {
+        // Process any expedited register values so we don't have to read them with p/P or g/G packets
+        if (registers_dict)
+        {
+            ThreadGDBRemote *gdb_thread = static_cast<ThreadGDBRemote *> (thread_sp.get());
+            
+            registers_dict->ForEach([gdb_thread](ConstString key, StructuredData::Object* object) -> bool {
+                const uint32_t reg = StringConvert::ToUInt32 (key.GetCString(), UINT32_MAX, 10);
+                if (reg != UINT32_MAX)
+                {
+                    StringExtractor reg_value_extractor;
+                    reg_value_extractor.GetStringRef() = std::move(object->GetStringValue());
+                    gdb_thread->PrivateSetRegisterValue (reg, reg_value_extractor);
+                }
+                return true; // Keep iterating through all array items
+            });
+        }
+        return eStateStopped;
+    }
+    return eStateExited;
+}
 
 StateType
 ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet)
@@ -1814,8 +2165,9 @@ ProcessGDBRemote::SetThreadStopInfo (Str
                 BuildDynamicRegisterInfo (true);
             }
             // Stop with signal and thread info
+            lldb::tid_t tid = LLDB_INVALID_THREAD_ID;
             const uint8_t signo = stop_packet.GetHexU8();
-            std::string name;
+            std::string key;
             std::string value;
             std::string thread_name;
             std::string reason;
@@ -1823,48 +2175,26 @@ ProcessGDBRemote::SetThreadStopInfo (Str
             uint32_t exc_type = 0;
             std::vector<addr_t> exc_data;
             addr_t thread_dispatch_qaddr = LLDB_INVALID_ADDRESS;
-            ThreadSP thread_sp;
-            ThreadGDBRemote *gdb_thread = NULL;
-
-            while (stop_packet.GetNameColonValue(name, value))
+            typedef std::map<uint32_t, std::string> ExpeditedRegisterMap;
+            ExpeditedRegisterMap expedited_register_map;
+            while (stop_packet.GetNameColonValue(key, value))
             {
-                if (name.compare("metype") == 0)
+                if (key.compare("metype") == 0)
                 {
                     // exception type in big endian hex
                     exc_type = StringConvert::ToUInt32 (value.c_str(), 0, 16);
                 }
-                else if (name.compare("medata") == 0)
+                else if (key.compare("medata") == 0)
                 {
                     // exception data in big endian hex
                     exc_data.push_back(StringConvert::ToUInt64 (value.c_str(), 0, 16));
                 }
-                else if (name.compare("thread") == 0)
+                else if (key.compare("thread") == 0)
                 {
                     // thread in big endian hex
-                    lldb::tid_t tid = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16);
-                    // m_thread_list_real does have its own mutex, but we need to
-                    // hold onto the mutex between the call to m_thread_list_real.FindThreadByID(...)
-                    // and the m_thread_list_real.AddThread(...) so it doesn't change on us
-                    Mutex::Locker locker (m_thread_list_real.GetMutex ());
-                    thread_sp = m_thread_list_real.FindThreadByProtocolID(tid, false);
-
-                    if (!thread_sp)
-                    {
-                        // Create the thread if we need to
-                        thread_sp.reset (new ThreadGDBRemote (*this, tid));
-                        Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_THREAD));
-                        if (log && log->GetMask().Test(GDBR_LOG_VERBOSE))
-                            log->Printf ("ProcessGDBRemote::%s Adding new thread: %p for thread ID: 0x%" PRIx64 ".\n",
-                                         __FUNCTION__,
-                                         static_cast<void*>(thread_sp.get()),
-                                         thread_sp->GetID());
-
-                        m_thread_list_real.AddThread(thread_sp);
-                    }
-                    gdb_thread = static_cast<ThreadGDBRemote *> (thread_sp.get());
-
+                    tid = StringConvert::ToUInt64 (value.c_str(), LLDB_INVALID_THREAD_ID, 16);
                 }
-                else if (name.compare("threads") == 0)
+                else if (key.compare("threads") == 0)
                 {
                     Mutex::Locker locker(m_thread_list_real.GetMutex());
                     m_thread_ids.clear();
@@ -1886,7 +2216,7 @@ ProcessGDBRemote::SetThreadStopInfo (Str
                     if (tid != LLDB_INVALID_THREAD_ID)
                         m_thread_ids.push_back (tid);
                 }
-                else if (name.compare("hexname") == 0)
+                else if (key.compare("hexname") == 0)
                 {
                     StringExtractor name_extractor;
                     // Swap "value" over into "name_extractor"
@@ -1895,19 +2225,19 @@ ProcessGDBRemote::SetThreadStopInfo (Str
                     name_extractor.GetHexByteString (value);
                     thread_name.swap (value);
                 }
-                else if (name.compare("name") == 0)
+                else if (key.compare("name") == 0)
                 {
                     thread_name.swap (value);
                 }
-                else if (name.compare("qaddr") == 0)
+                else if (key.compare("qaddr") == 0)
                 {
                     thread_dispatch_qaddr = StringConvert::ToUInt64 (value.c_str(), 0, 16);
                 }
-                else if (name.compare("reason") == 0)
+                else if (key.compare("reason") == 0)
                 {
                     reason.swap(value);
                 }
-                else if (name.compare("description") == 0)
+                else if (key.compare("description") == 0)
                 {
                     StringExtractor desc_extractor;
                     // Swap "value" over into "name_extractor"
@@ -1916,34 +2246,49 @@ ProcessGDBRemote::SetThreadStopInfo (Str
                     desc_extractor.GetHexByteString (value);
                     description.swap(value);
                 }
-                else if (name.size() == 2 && ::isxdigit(name[0]) && ::isxdigit(name[1]))
+                else if (key.compare("memory") == 0)
                 {
-                    // We have a register number that contains an expedited
-                    // register value. Lets supply this register to our thread
-                    // so it won't have to go and read it.
-                    if (gdb_thread)
-                    {
-                        uint32_t reg = StringConvert::ToUInt32 (name.c_str(), UINT32_MAX, 16);
-
-                        if (reg != UINT32_MAX)
+                    // Expedited memory. GDB servers can choose to send back expedited memory
+                    // that can populate the L1 memory cache in the process so that things like
+                    // the frame pointer backchain can be expedited. This will help stack
+                    // backtracing be more efficient by not having to send as many memory read
+                    // requests down the remote GDB server.
+
+                    // Key/value pair format: memory:<addr>=<bytes>;
+                    // <addr> is a number whose base will be interpreted by the prefix:
+                    //      "0x[0-9a-fA-F]+" for hex
+                    //      "0[0-7]+" for octal
+                    //      "[1-9]+" for decimal
+                    // <bytes> is native endian ASCII hex bytes just like the register values
+                    llvm::StringRef value_ref(value);
+                    std::pair<llvm::StringRef, llvm::StringRef> pair;
+                    pair = value_ref.split('=');
+                    if (!pair.first.empty() && !pair.second.empty())
+                    {
+                        std::string addr_str(pair.first.str());
+                        const lldb::addr_t mem_cache_addr = StringConvert::ToUInt64(addr_str.c_str(), LLDB_INVALID_ADDRESS, 0);
+                        if (mem_cache_addr != LLDB_INVALID_ADDRESS)
                         {
-                            StringExtractor reg_value_extractor;
-                            // Swap "value" over into "reg_value_extractor"
-                            reg_value_extractor.GetStringRef().swap(value);
-                            if (!gdb_thread->PrivateSetRegisterValue (reg, reg_value_extractor))
-                            {
-                                Host::SetCrashDescriptionWithFormat("Setting thread register '%s' (decoded to %u (0x%x)) with value '%s' for stop packet: '%s'", 
-                                                                    name.c_str(), 
-                                                                    reg, 
-                                                                    reg, 
-                                                                    reg_value_extractor.GetStringRef().c_str(), 
-                                                                    stop_packet.GetStringRef().c_str());
-                            }
+                            StringExtractor bytes;
+                            bytes.GetStringRef() = std::move(pair.second.str());
+                            const size_t byte_size = bytes.GetStringRef().size()/2;
+                            DataBufferSP data_buffer_sp(new DataBufferHeap(byte_size, 0));
+                            const size_t bytes_copied = bytes.GetHexBytes (data_buffer_sp->GetBytes(), byte_size, 0);
+                            if (bytes_copied == byte_size)
+                                m_memory_cache.AddL1CacheData(mem_cache_addr, data_buffer_sp);
                         }
                     }
                 }
+                else if (key.size() == 2 && ::isxdigit(key[0]) && ::isxdigit(key[1]))
+                {
+                    uint32_t reg = StringConvert::ToUInt32 (key.c_str(), UINT32_MAX, 16);
+                    if (reg != UINT32_MAX)
+                        expedited_register_map[reg] = std::move(value);
+                }
             }
 
+            ThreadSP thread_sp = SetThreadStopInfo (tid, signo, thread_name, reason, description, exc_type, exc_data, thread_dispatch_qaddr);
+
             // If the response is old style 'S' packet which does not provide us with thread information
             // then update the thread list and choose the first one.
             if (!thread_sp)
@@ -1954,156 +2299,23 @@ ProcessGDBRemote::SetThreadStopInfo (Str
                 {
                     Mutex::Locker locker (m_thread_list_real.GetMutex ());
                     thread_sp = m_thread_list_real.FindThreadByProtocolID (m_thread_ids.front (), false);
-                    if (thread_sp)
-                        gdb_thread = static_cast<ThreadGDBRemote *> (thread_sp.get ());
                 }
             }
 
-            if (thread_sp)
+            if (thread_sp && !expedited_register_map.empty())
             {
-                // Clear the stop info just in case we don't set it to anything
-                thread_sp->SetStopInfo (StopInfoSP());
-
-                gdb_thread->SetThreadDispatchQAddr (thread_dispatch_qaddr);
-                gdb_thread->SetName (thread_name.empty() ? NULL : thread_name.c_str());
-                if (exc_type != 0)
-                {
-                    const size_t exc_data_size = exc_data.size();
-
-                    thread_sp->SetStopInfo (StopInfoMachException::CreateStopReasonWithMachException (*thread_sp,
-                                                                                                      exc_type,
-                                                                                                      exc_data_size,
-                                                                                                      exc_data_size >= 1 ? exc_data[0] : 0,
-                                                                                                      exc_data_size >= 2 ? exc_data[1] : 0,
-                                                                                                      exc_data_size >= 3 ? exc_data[2] : 0));
-                }
-                else
-                {
-                    bool handled = false;
-                    bool did_exec = false;
-                    if (!reason.empty())
-                    {
-                        if (reason.compare("trace") == 0)
-                        {
-                            thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp));
-                            handled = true;
-                        }
-                        else if (reason.compare("breakpoint") == 0)
-                        {
-                            addr_t pc = thread_sp->GetRegisterContext()->GetPC();
-                            lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc);
-                            if (bp_site_sp)
-                            {
-                                // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread,
-                                // we can just report no reason.  We don't need to worry about stepping over the breakpoint here, that
-                                // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc.
-                                handled = true;
-                                if (bp_site_sp->ValidForThisThread (thread_sp.get()))
-                                {
-                                    thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID()));
-                                }
-                                else
-                                {
-                                    StopInfoSP invalid_stop_info_sp;
-                                    thread_sp->SetStopInfo (invalid_stop_info_sp);
-                                }
-                            }
-                        }
-                        else if (reason.compare("trap") == 0)
-                        {
-                            // Let the trap just use the standard signal stop reason below...
-                        }
-                        else if (reason.compare("watchpoint") == 0)
-                        {
-                            StringExtractor desc_extractor(description.c_str());
-                            addr_t wp_addr = desc_extractor.GetU64(LLDB_INVALID_ADDRESS);
-                            uint32_t wp_index = desc_extractor.GetU32(LLDB_INVALID_INDEX32);
-                            watch_id_t watch_id = LLDB_INVALID_WATCH_ID;
-                            if (wp_addr != LLDB_INVALID_ADDRESS)
-                            {
-                                WatchpointSP wp_sp = GetTarget().GetWatchpointList().FindByAddress(wp_addr);
-                                if (wp_sp)
-                                {
-                                    wp_sp->SetHardwareIndex(wp_index);
-                                    watch_id = wp_sp->GetID();
-                                }
-                            }
-                            if (watch_id == LLDB_INVALID_WATCH_ID)
-                            {
-                                Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_WATCHPOINTS));
-                                if (log) log->Printf ("failed to find watchpoint");
-                            }
-                            thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithWatchpointID (*thread_sp, watch_id));
-                            handled = true;
-                        }
-                        else if (reason.compare("exception") == 0)
-                        {
-                            thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException(*thread_sp, description.c_str()));
-                            handled = true;
-                        }
-                        else if (reason.compare("exec") == 0)
-                        {
-                            did_exec = true;
-                            thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithExec(*thread_sp));
-                            handled = true;
-                        }
-                    }
-
-                    if (!handled && signo && did_exec == false)
-                    {
-                        if (signo == SIGTRAP)
-                        {
-                            // Currently we are going to assume SIGTRAP means we are either
-                            // hitting a breakpoint or hardware single stepping. 
-                            handled = true;
-                            addr_t pc = thread_sp->GetRegisterContext()->GetPC() + m_breakpoint_pc_offset;
-                            lldb::BreakpointSiteSP bp_site_sp = thread_sp->GetProcess()->GetBreakpointSiteList().FindByAddress(pc);
-
-                            if (bp_site_sp)
-                            {
-                                // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread,
-                                // we can just report no reason.  We don't need to worry about stepping over the breakpoint here, that
-                                // will be taken care of when the thread resumes and notices that there's a breakpoint under the pc.
-                                if (bp_site_sp->ValidForThisThread (thread_sp.get()))
-                                {
-                                    if(m_breakpoint_pc_offset != 0)
-                                        thread_sp->GetRegisterContext()->SetPC(pc);
-                                    thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID (*thread_sp, bp_site_sp->GetID()));
-                                }
-                                else
-                                {
-                                    StopInfoSP invalid_stop_info_sp;
-                                    thread_sp->SetStopInfo (invalid_stop_info_sp);
-                                }
-                            }
-                            else
-                            {
-                                // If we were stepping then assume the stop was the result of the trace.  If we were
-                                // not stepping then report the SIGTRAP.
-                                // FIXME: We are still missing the case where we single step over a trap instruction.
-                                if (thread_sp->GetTemporaryResumeState() == eStateStepping)
-                                    thread_sp->SetStopInfo (StopInfo::CreateStopReasonToTrace (*thread_sp));
-                                else
-                                    thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal(*thread_sp, signo, description.c_str()));
-                            }
-                        }
-                        if (!handled)
-                            thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithSignal (*thread_sp, signo, description.c_str()));
-                    }
-
-                    if (!description.empty())
-                    {
-                        lldb::StopInfoSP stop_info_sp (thread_sp->GetStopInfo ());
-                        if (stop_info_sp)
-                        {
-                            const char *stop_info_desc = stop_info_sp->GetDescription();
-                            if (!stop_info_desc || !stop_info_desc[0])
-                                stop_info_sp->SetDescription (description.c_str());
-                        }
-                        else
-                        {
-                            thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException (*thread_sp, description.c_str()));
-                        }
+                // Process any expedited register values so we don't have to read them with p/P or g/G packets
+                ThreadGDBRemote *gdb_thread = static_cast<ThreadGDBRemote *> (thread_sp.get());
+                for (const auto &pair : expedited_register_map)
+                {
+                    StringExtractor reg_value_extractor;
+                    reg_value_extractor.GetStringRef() = pair.second;
+                    if (!gdb_thread->PrivateSetRegisterValue (pair.first, reg_value_extractor))
+                    {
+                        Host::SetCrashDescriptionWithFormat("Setting thread register '0x%2.2x' with value '%s' for stop packet: '%s'",
+                                                            pair.first,
+                                                            reg_value_extractor.GetStringRef().c_str(),
+                                                            stop_packet.GetStringRef().c_str());
                     }
                 }
             }
@@ -2163,6 +2375,11 @@ ProcessGDBRemote::RefreshStateAfterStop
         m_initial_tid = LLDB_INVALID_THREAD_ID;
     }
 
+    // Fetch the threads via an efficient packet that gets stop infos for all threads
+    // only if we have more than one thread
+    if (m_thread_ids.size() > 1)
+        m_threads_info_sp = m_gdb_comm.GetThreadsInfo();
+
     // Let all threads recover from stopping and do any clean up based
     // on the previous thread state (if any).
     m_thread_list_real.RefreshStateAfterStop();

Modified: lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h?rev=240354&r1=240353&r2=240354&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h (original)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h Mon Jun 22 18:12:45 2015
@@ -323,6 +323,9 @@ protected:
     void
     GetMaxMemorySize();
 
+    bool
+    CalculateThreadStopInfo (ThreadGDBRemote *thread);
+
     //------------------------------------------------------------------
     /// Broadcaster event bits definitions.
     //------------------------------------------------------------------
@@ -346,6 +349,7 @@ protected:
     typedef std::vector< std::pair<lldb::tid_t,int> > tid_sig_collection;
     typedef std::map<lldb::addr_t, lldb::addr_t> MMapMap;
     tid_collection m_thread_ids; // Thread IDs for all threads. This list gets updated after stopping
+    StructuredData::ObjectSP m_threads_info_sp; // Stop info for all threads if "jThreadsInfo" packet is supported
     tid_collection m_continue_c_tids;                  // 'c' for continue
     tid_sig_collection m_continue_C_tids; // 'C' for continue with signal
     tid_collection m_continue_s_tids;                  // 's' for step
@@ -379,6 +383,19 @@ protected:
     lldb::StateType
     SetThreadStopInfo (StringExtractor& stop_packet);
 
+    lldb::StateType
+    SetThreadStopInfo (StructuredData::Dictionary *thread_dict);
+
+    lldb::ThreadSP
+    SetThreadStopInfo (lldb::tid_t tid,
+                       uint8_t signo,
+                       const std::string &thread_name,
+                       const std::string &reason,
+                       const std::string &description,
+                       uint32_t exc_type,
+                       const std::vector<lldb::addr_t> &exc_data,
+                       lldb::addr_t thread_dispatch_qaddr);
+
     void
     HandleStopReplySequence ();
 

Modified: lldb/trunk/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp?rev=240354&r1=240353&r2=240354&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp (original)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp Mon Jun 22 18:12:45 2015
@@ -284,12 +284,7 @@ ThreadGDBRemote::CalculateStopInfo ()
 {
     ProcessSP process_sp (GetProcess());
     if (process_sp)
-    {
-        StringExtractorGDBRemote stop_packet;
-        ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(process_sp.get());
-        if (gdb_process->GetGDBRemote().GetThreadStopInfo(GetProtocolID(), stop_packet))
-            return gdb_process->SetThreadStopInfo (stop_packet) == eStateStopped;
-    }
+        return static_cast<ProcessGDBRemote *>(process_sp.get())->CalculateThreadStopInfo(this);
     return false;
 }
 

Modified: lldb/trunk/source/Target/Memory.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Target/Memory.cpp?rev=240354&r1=240353&r2=240354&view=diff
==============================================================================
--- lldb/trunk/source/Target/Memory.cpp (original)
+++ lldb/trunk/source/Target/Memory.cpp Mon Jun 22 18:12:45 2015
@@ -14,8 +14,9 @@
 // Other libraries and framework includes
 // Project includes
 #include "lldb/Core/DataBufferHeap.h"
-#include "lldb/Core/State.h"
 #include "lldb/Core/Log.h"
+#include "lldb/Core/RangeMap.h"
+#include "lldb/Core/State.h"
 #include "lldb/Target/Process.h"
 
 using namespace lldb;
@@ -25,11 +26,12 @@ using namespace lldb_private;
 // MemoryCache constructor
 //----------------------------------------------------------------------
 MemoryCache::MemoryCache(Process &process) :
-    m_process (process),
-    m_cache_line_byte_size (process.GetMemoryCacheLineSize()),
     m_mutex (Mutex::eMutexTypeRecursive),
-    m_cache (),
-    m_invalid_ranges ()
+    m_L1_cache (),
+    m_L2_cache (),
+    m_invalid_ranges (),
+    m_process (process),
+    m_L2_cache_line_byte_size (process.GetMemoryCacheLineSize())
 {
 }
 
@@ -44,10 +46,24 @@ void
 MemoryCache::Clear(bool clear_invalid_ranges)
 {
     Mutex::Locker locker (m_mutex);
-    m_cache.clear();
+    m_L1_cache.clear();
+    m_L2_cache.clear();
     if (clear_invalid_ranges)
         m_invalid_ranges.Clear();
-    m_cache_line_byte_size = m_process.GetMemoryCacheLineSize();
+    m_L2_cache_line_byte_size = m_process.GetMemoryCacheLineSize();
+}
+
+void
+MemoryCache::AddL1CacheData(lldb::addr_t addr, const void *src, size_t src_len)
+{
+    AddL1CacheData(addr,DataBufferSP (new DataBufferHeap(DataBufferHeap(src, src_len))));
+}
+
+void
+MemoryCache::AddL1CacheData(lldb::addr_t addr, const DataBufferSP &data_buffer_sp)
+{
+    Mutex::Locker locker (m_mutex);
+    m_L1_cache[addr] = data_buffer_sp;
 }
 
 void
@@ -57,29 +73,44 @@ MemoryCache::Flush (addr_t addr, size_t
         return;
 
     Mutex::Locker locker (m_mutex);
-    if (m_cache.empty())
-        return;
 
-    const uint32_t cache_line_byte_size = m_cache_line_byte_size;
-    const addr_t end_addr = (addr + size - 1);
-    const addr_t first_cache_line_addr = addr - (addr % cache_line_byte_size);
-    const addr_t last_cache_line_addr = end_addr - (end_addr % cache_line_byte_size);
-    // Watch for overflow where size will cause us to go off the end of the
-    // 64 bit address space
-    uint32_t num_cache_lines;
-    if (last_cache_line_addr >= first_cache_line_addr)
-        num_cache_lines = ((last_cache_line_addr - first_cache_line_addr)/cache_line_byte_size) + 1;
-    else
-        num_cache_lines = (UINT64_MAX - first_cache_line_addr + 1)/cache_line_byte_size;
-
-    uint32_t cache_idx = 0;
-    for (addr_t curr_addr = first_cache_line_addr;
-         cache_idx < num_cache_lines;
-         curr_addr += cache_line_byte_size, ++cache_idx)
-    {
-        BlockMap::iterator pos = m_cache.find (curr_addr);
-        if (pos != m_cache.end())
-            m_cache.erase(pos);
+    // Erase any blocks from the L1 cache that intersect with the flush range
+    if (!m_L1_cache.empty())
+    {
+        AddrRange flush_range(addr, size);
+        BlockMap::iterator pos = m_L1_cache.lower_bound(addr);
+        while (pos != m_L1_cache.end())
+        {
+            AddrRange chunk_range(pos->first, pos->second->GetByteSize());
+            if (!chunk_range.DoesIntersect(flush_range))
+                break;
+            pos = m_L1_cache.erase(pos);
+        }
+    }
+
+    if (!m_L2_cache.empty())
+    {
+        const uint32_t cache_line_byte_size = m_L2_cache_line_byte_size;
+        const addr_t end_addr = (addr + size - 1);
+        const addr_t first_cache_line_addr = addr - (addr % cache_line_byte_size);
+        const addr_t last_cache_line_addr = end_addr - (end_addr % cache_line_byte_size);
+        // Watch for overflow where size will cause us to go off the end of the
+        // 64 bit address space
+        uint32_t num_cache_lines;
+        if (last_cache_line_addr >= first_cache_line_addr)
+            num_cache_lines = ((last_cache_line_addr - first_cache_line_addr)/cache_line_byte_size) + 1;
+        else
+            num_cache_lines = (UINT64_MAX - first_cache_line_addr + 1)/cache_line_byte_size;
+
+        uint32_t cache_idx = 0;
+        for (addr_t curr_addr = first_cache_line_addr;
+             cache_idx < num_cache_lines;
+             curr_addr += cache_line_byte_size, ++cache_idx)
+        {
+            BlockMap::iterator pos = m_L2_cache.find (curr_addr);
+            if (pos != m_L2_cache.end())
+                m_L2_cache.erase(pos);
+        }
     }
 }
 
@@ -122,6 +153,39 @@ MemoryCache::Read (addr_t addr,
 {
     size_t bytes_left = dst_len;
 
+    // Check the L1 cache for a range that contain the entire memory read.
+    // If we find a range in the L1 cache that does, we use it. Else we fall
+    // back to reading memory in m_L2_cache_line_byte_size byte sized chunks.
+    // The L1 cache contains chunks of memory that are not required to be
+    // m_L2_cache_line_byte_size bytes in size, so we don't try anything
+    // tricky when reading from them (no partial reads from the L1 cache).
+
+    Mutex::Locker locker(m_mutex);
+    if (!m_L1_cache.empty())
+    {
+        AddrRange read_range(addr, dst_len);
+        BlockMap::iterator pos = m_L1_cache.lower_bound(addr);
+        if (pos != m_L1_cache.end())
+        {
+            AddrRange chunk_range(pos->first, pos->second->GetByteSize());
+            bool match = chunk_range.Contains(read_range);
+            if (!match && pos != m_L1_cache.begin())
+            {
+                --pos;
+                chunk_range.SetRangeBase(pos->first);
+                chunk_range.SetByteSize(pos->second->GetByteSize());
+                match = chunk_range.Contains(read_range);
+            }
+
+            if (match)
+            {
+                memcpy(dst, pos->second->GetBytes() + addr - chunk_range.GetRangeBase(), dst_len);
+                return dst_len;
+            }
+        }
+    }
+
+
     // If this memory read request is larger than the cache line size, then 
     // we (1) try to read as much of it at once as possible, and (2) don't
     // add the data to the memory cache.  We don't want to split a big read
@@ -129,19 +193,22 @@ MemoryCache::Read (addr_t addr,
     // request, it is unlikely that the caller function will ask for the next
     // 4 bytes after the large memory read - so there's little benefit to saving
     // it in the cache.
-    if (dst && dst_len > m_cache_line_byte_size)
+    if (dst && dst_len > m_L2_cache_line_byte_size)
     {
-        return m_process.ReadMemoryFromInferior (addr, dst, dst_len, error);
+        size_t bytes_read = m_process.ReadMemoryFromInferior (addr, dst, dst_len, error);
+        // Add this non block sized range to the L1 cache if we actually read anything
+        if (bytes_read > 0)
+            AddL1CacheData(addr, dst, bytes_read);
+        return bytes_read;
     }
 
     if (dst && bytes_left > 0)
     {
-        const uint32_t cache_line_byte_size = m_cache_line_byte_size;
+        const uint32_t cache_line_byte_size = m_L2_cache_line_byte_size;
         uint8_t *dst_buf = (uint8_t *)dst;
         addr_t curr_addr = addr - (addr % cache_line_byte_size);
         addr_t cache_offset = addr - curr_addr;
-        Mutex::Locker locker (m_mutex);
-        
+
         while (bytes_left > 0)
         {
             if (m_invalid_ranges.FindEntryThatContains(curr_addr))
@@ -150,8 +217,8 @@ MemoryCache::Read (addr_t addr,
                 return dst_len - bytes_left;
             }
 
-            BlockMap::const_iterator pos = m_cache.find (curr_addr);
-            BlockMap::const_iterator end = m_cache.end ();
+            BlockMap::const_iterator pos = m_L2_cache.find (curr_addr);
+            BlockMap::const_iterator end = m_L2_cache.end ();
             
             if (pos != end)
             {
@@ -208,7 +275,7 @@ MemoryCache::Read (addr_t addr,
                 
                 if (process_bytes_read != cache_line_byte_size)
                     data_buffer_heap_ap->SetByteSize (process_bytes_read);
-                m_cache[curr_addr] = DataBufferSP (data_buffer_heap_ap.release());
+                m_L2_cache[curr_addr] = DataBufferSP (data_buffer_heap_ap.release());
                 // We have read data and put it into the cache, continue through the
                 // loop again to get the data out of the cache...
             }

Modified: lldb/trunk/tools/debugserver/debugserver.xcodeproj/project.pbxproj
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/debugserver/debugserver.xcodeproj/project.pbxproj?rev=240354&r1=240353&r2=240354&view=diff
==============================================================================
--- lldb/trunk/tools/debugserver/debugserver.xcodeproj/project.pbxproj (original)
+++ lldb/trunk/tools/debugserver/debugserver.xcodeproj/project.pbxproj Mon Jun 22 18:12:45 2015
@@ -55,6 +55,7 @@
 		26203D1D1641EFB200A662F7 /* com.apple.debugserver.internal.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.debugserver.internal.plist; sourceTree = "<group>"; };
 		26242C390DDBD33C0054A4CC /* debugserver-entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "debugserver-entitlements.plist"; sourceTree = "<group>"; };
 		264D5D571293835600ED4C01 /* DNBArch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DNBArch.cpp; sourceTree = "<group>"; };
+		264F679A1B2F9EB200140093 /* JSONGenerator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSONGenerator.h; sourceTree = "<group>"; };
 		26593A060D4931CC001C9FE3 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ChangeLog; sourceTree = "<group>"; };
 		2660D9CC1192280900958FBD /* StringExtractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StringExtractor.cpp; path = ../../source/Utility/StringExtractor.cpp; sourceTree = SOURCE_ROOT; };
 		2660D9CD1192280900958FBD /* StringExtractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StringExtractor.h; path = ../../source/Utility/StringExtractor.h; sourceTree = SOURCE_ROOT; };
@@ -232,6 +233,7 @@
 				26C637E20C71334A0024798E /* DNBRegisterInfo.cpp */,
 				260E7332114BFFE600D1DFB3 /* DNBThreadResumeActions.h */,
 				260E7331114BFFE600D1DFB3 /* DNBThreadResumeActions.cpp */,
+				264F679A1B2F9EB200140093 /* JSONGenerator.h */,
 				AF67AC000D34604D0022D128 /* PseudoTerminal.h */,
 				AF67ABFF0D34604D0022D128 /* PseudoTerminal.cpp */,
 				26C637FD0C71334A0024798E /* PThreadCondition.h */,

Added: lldb/trunk/tools/debugserver/source/JSONGenerator.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/debugserver/source/JSONGenerator.h?rev=240354&view=auto
==============================================================================
--- lldb/trunk/tools/debugserver/source/JSONGenerator.h (added)
+++ lldb/trunk/tools/debugserver/source/JSONGenerator.h Mon Jun 22 18:12:45 2015
@@ -0,0 +1,490 @@
+//===-- JSONGenerator.h ----------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __JSONGenerator_h_
+#define __JSONGenerator_h_
+
+// C Includes
+// C++ Includes
+
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+//----------------------------------------------------------------------
+/// @class JSONGenerator JSONGenerator.h
+/// @brief A class which can construct structured data for the sole purpose
+/// of printing it in JSON format.
+///
+/// A stripped down version of lldb's StructuredData objects which are much
+/// general purpose.  This variant is intended only for assembling information
+/// and printing it as a JSON string.
+//----------------------------------------------------------------------
+
+class JSONGenerator
+{
+public:
+
+    class Object;
+    class Array;
+    class Integer;
+    class Float;
+    class Boolean;
+    class String;
+    class Dictionary;
+    class Generic;
+
+    typedef std::shared_ptr<Object> ObjectSP;
+    typedef std::shared_ptr<Array> ArraySP;
+    typedef std::shared_ptr<Integer> IntegerSP;
+    typedef std::shared_ptr<Float> FloatSP;
+    typedef std::shared_ptr<Boolean> BooleanSP;
+    typedef std::shared_ptr<String> StringSP;
+    typedef std::shared_ptr<Dictionary> DictionarySP;
+    typedef std::shared_ptr<Generic> GenericSP;
+
+    enum class Type
+    {
+        eTypeInvalid = -1,
+        eTypeNull = 0,
+        eTypeGeneric,
+        eTypeArray,
+        eTypeInteger,
+        eTypeFloat,
+        eTypeBoolean,
+        eTypeString,
+        eTypeDictionary
+    };
+
+    class Object :
+        public std::enable_shared_from_this<Object>
+    {
+    public:
+
+        Object (Type t = Type::eTypeInvalid) :
+            m_type (t)
+        {
+        }
+
+        virtual ~Object ()
+        {
+        }
+
+        virtual bool
+        IsValid() const
+        {
+            return true;
+        }
+
+        virtual void
+        Clear ()
+        {
+            m_type = Type::eTypeInvalid;
+        }
+
+        Type
+        GetType () const
+        {
+            return m_type;
+        }
+
+        void
+        SetType (Type t)
+        {
+            m_type = t;
+        }
+
+        Array *
+        GetAsArray ()
+        {
+            if (m_type == Type::eTypeArray)
+                return (Array *)this;
+            return NULL;
+        }
+
+        Dictionary *
+        GetAsDictionary ()
+        {
+            if (m_type == Type::eTypeDictionary)
+                return (Dictionary *)this;
+            return NULL;
+        }
+
+        Integer *
+        GetAsInteger ()
+        {
+            if (m_type == Type::eTypeInteger)
+                return (Integer *)this;
+            return NULL;
+        }
+
+        Float *
+        GetAsFloat ()
+        {
+            if (m_type == Type::eTypeFloat)
+                return (Float *)this;
+            return NULL;
+        }
+
+        Boolean *
+        GetAsBoolean ()
+        {
+            if (m_type == Type::eTypeBoolean)
+                return (Boolean *)this;
+            return NULL;
+        }
+
+        String *
+        GetAsString ()
+        {
+            if (m_type == Type::eTypeString)
+                return (String *)this;
+            return NULL;
+        }
+
+        Generic *
+        GetAsGeneric()
+        {
+            if (m_type == Type::eTypeGeneric)
+                return (Generic *)this;
+            return NULL;
+        }
+
+        virtual void
+        Dump (std::ostream &s) const = 0; 
+
+    private:
+        Type m_type;
+    };
+
+    class Array : public Object
+    {
+    public:
+        Array () :
+            Object (Type::eTypeArray)
+        {
+        }
+
+        virtual
+        ~Array()
+        {
+        }
+
+        void
+        AddItem(ObjectSP item)
+        {
+            m_items.push_back(item);
+        }
+
+        void Dump(std::ostream &s) const override
+        {
+            s << "[";
+            const size_t arrsize = m_items.size();
+            for (size_t i = 0; i < arrsize; ++i)
+            {
+                m_items[i]->Dump(s);
+                if (i + 1 < arrsize)
+                    s << ",";
+            }
+            s << "]";
+        }
+
+    protected:
+        typedef std::vector<ObjectSP> collection;
+        collection m_items;
+    };
+
+
+    class Integer  : public Object
+    {
+    public:
+        Integer (uint64_t value = 0) :
+            Object (Type::eTypeInteger),
+            m_value (value)
+        {
+        }
+
+        virtual ~Integer()
+        {
+        }
+
+        void
+        SetValue (uint64_t value)
+        {
+            m_value = value;
+        }
+
+        void Dump(std::ostream &s) const override
+        {
+            s << m_value;
+        }
+
+    protected:
+        uint64_t m_value;
+    };
+
+    class Float  : public Object
+    {
+    public:
+        Float (double d = 0.0) :
+            Object (Type::eTypeFloat),
+            m_value (d)
+        {
+        }
+
+        virtual ~Float()
+        {
+        }
+
+        void
+        SetValue (double value)
+        {
+            m_value = value;
+        }
+
+        void Dump(std::ostream &s) const override
+        {
+            s << m_value;
+        }
+
+    protected:
+        double m_value;
+    };
+
+    class Boolean  : public Object
+    {
+    public:
+        Boolean (bool b = false) :
+            Object (Type::eTypeBoolean),
+            m_value (b)
+        {
+        }
+
+        virtual ~Boolean()
+        {
+        }
+
+        void
+        SetValue (bool value)
+        {
+            m_value = value;
+        }
+
+        void Dump(std::ostream &s) const override
+        {
+            if (m_value == true)
+                s << "true";
+            else
+                s << "false";
+        }
+
+    protected:
+        bool m_value;
+    };
+
+
+
+    class String  : public Object
+    {
+    public:
+        String () :
+            Object (Type::eTypeString),
+            m_value ()
+        {
+        }
+
+        String (const std::string &s) :
+            Object (Type::eTypeString),
+            m_value (s)
+        {
+        }
+
+        String (const std::string &&s) :
+            Object (Type::eTypeString),
+            m_value (s)
+        {
+        }
+
+        void
+        SetValue (const std::string &string)
+        {
+            m_value = string;
+        }
+
+        void Dump(std::ostream &s) const override
+        {
+            std::string quoted;
+            const size_t strsize = m_value.size();
+            for (size_t i = 0; i < strsize ; ++i)
+            {
+                char ch = m_value[i];
+                if (ch == '"')
+                    quoted.push_back ('\\');
+                quoted.push_back (ch);
+            }
+            s << '"' << quoted.c_str() << '"';
+        }
+
+    protected:
+        std::string m_value;
+    };
+
+    class Dictionary : public Object
+    {
+    public:
+        Dictionary () :
+            Object (Type::eTypeDictionary),
+            m_dict ()
+        {
+        }
+
+        virtual ~Dictionary()
+        {
+        }
+
+        void
+        AddItem (std::string key, ObjectSP value)
+        {
+            m_dict.push_back(Pair(key, value));
+        }
+
+        void
+        AddIntegerItem (std::string key, uint64_t value)
+        {
+            AddItem (key, ObjectSP (new Integer(value)));
+        }
+
+        void
+        AddFloatItem (std::string key, double value)
+        {
+            AddItem (key, ObjectSP (new Float(value)));
+        }
+
+        void
+        AddStringItem (std::string key, std::string value)
+        {
+            AddItem (key, ObjectSP (new String(std::move(value))));
+        }
+
+        void
+        AddBytesAsHexASCIIString (std::string key, const uint8_t *src, size_t src_len)
+        {
+            if (src && src_len)
+            {
+                std::ostringstream strm;
+                for (size_t i = 0; i < src_len; i++)
+                    strm << std::setfill('0') << std::hex << std::right << std::setw(2) << ((uint32_t)(src[i]));
+                AddItem (key, ObjectSP (new String(std::move(strm.str()))));
+            }
+            else
+            {
+                AddItem (key, ObjectSP (new String()));
+            }
+        }
+
+        void
+        AddBooleanItem (std::string key, bool value)
+        {
+            AddItem (key, ObjectSP (new Boolean(value)));
+        }
+
+        void Dump(std::ostream &s) const override
+        {
+            bool have_printed_one_elem = false;
+            s << "{";
+            for (collection::const_iterator iter = m_dict.begin(); iter != m_dict.end(); ++iter)
+            {
+                if (have_printed_one_elem == false)
+                {
+                    have_printed_one_elem = true;
+                }
+                else
+                {
+                    s << ",";
+                }
+                s << "\"" << iter->first.c_str() << "\":";
+                iter->second->Dump(s);
+            }
+            s << "}";
+        }
+
+    protected:
+        // Keep the dictionary as a vector so the dictionary doesn't reorder itself when you dump it
+        // We aren't accessing keys by name, so this won't affect performance
+        typedef std::pair<std::string, ObjectSP> Pair;
+        typedef std::vector<Pair> collection;
+        collection m_dict;
+    };
+
+    class Null : public Object
+    {
+    public:
+        Null () :
+            Object (Type::eTypeNull)
+        {
+        }
+
+        virtual ~Null()
+        {
+        }
+
+        bool
+        IsValid() const override
+        {
+            return false;
+        }
+
+        void Dump(std::ostream &s) const override
+        {
+            s << "null";
+        }
+
+    protected:
+    };
+
+    class Generic : public Object
+    {
+      public:
+        explicit Generic(void *object = nullptr)
+            : Object(Type::eTypeGeneric)
+            , m_object(object)
+        {
+        }
+
+        void
+        SetValue(void *value)
+        {
+            m_object = value;
+        }
+
+        void *
+        GetValue() const
+        {
+            return m_object;
+        }
+
+        bool
+        IsValid() const override
+        {
+            return m_object != nullptr;
+        }
+
+        void Dump(std::ostream &s) const override;
+
+      private:
+        void *m_object;
+    };
+
+};  // class JSONGenerator
+
+
+
+#endif  // __JSONGenerator_h_

Modified: lldb/trunk/tools/debugserver/source/RNBRemote.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/debugserver/source/RNBRemote.cpp?rev=240354&r1=240353&r2=240354&view=diff
==============================================================================
--- lldb/trunk/tools/debugserver/source/RNBRemote.cpp (original)
+++ lldb/trunk/tools/debugserver/source/RNBRemote.cpp Mon Jun 22 18:12:45 2015
@@ -29,6 +29,7 @@
 #include "DNBDataRef.h"
 #include "DNBLog.h"
 #include "DNBThreadResumeActions.h"
+#include "JSONGenerator.h"
 #include "RNBContext.h"
 #include "RNBServices.h"
 #include "RNBSocket.h"
@@ -195,6 +196,7 @@ RNBRemote::CreatePacketTable  ()
     t.push_back (Packet (query_process_info,            &RNBRemote::HandlePacket_qProcessInfo,     NULL, "qProcessInfo", "Replies with multiple 'key:value;' tuples appended to each other."));
 //  t.push_back (Packet (query_symbol_lookup,           &RNBRemote::HandlePacket_UNIMPLEMENTED, NULL, "qSymbol", "Notify that host debugger is ready to do symbol lookups"));
     t.push_back (Packet (json_query_thread_extended_info,          &RNBRemote::HandlePacket_jThreadExtendedInfo,     NULL, "jThreadExtendedInfo", "Replies with JSON data of thread extended information."));
+    t.push_back (Packet (json_query_threads_info,       &RNBRemote::HandlePacket_jThreadsInfo           , NULL, "jThreadsInfo", "Replies with JSON data with information about all threads."));
     t.push_back (Packet (start_noack_mode,              &RNBRemote::HandlePacket_QStartNoAckMode        , NULL, "QStartNoAckMode", "Request that " DEBUGSERVER_PROGRAM_NAME " stop acking remote protocol packets"));
     t.push_back (Packet (prefix_reg_packets_with_tid,   &RNBRemote::HandlePacket_QThreadSuffixSupported , NULL, "QThreadSuffixSupported", "Check if thread specific packets (register packets 'g', 'G', 'p', and 'P') support having the thread ID appended to the end of the command"));
     t.push_back (Packet (set_logging_mode,              &RNBRemote::HandlePacket_QSetLogging            , NULL, "QSetLogging:", "Check if register packets ('g', 'G', 'p', and 'P' support having the thread ID prefix"));
@@ -2452,6 +2454,52 @@ gdb_regnum_with_fixed_width_hex_register
     }
 }
 
+struct StackMemory
+{
+    uint8_t bytes[2*sizeof(nub_addr_t)];
+    nub_size_t length;
+};
+typedef std::map<nub_addr_t, StackMemory> StackMemoryMap;
+
+
+static void
+ReadStackMemory (nub_process_t pid, nub_thread_t tid, StackMemoryMap &stack_mmap)
+{
+    DNBRegisterValue reg_value;
+    if (DNBThreadGetRegisterValueByID(pid, tid, REGISTER_SET_GENERIC, GENERIC_REGNUM_FP, &reg_value))
+    {
+        uint32_t frame_count = 0;
+        uint64_t fp = 0;
+        if (reg_value.info.size == 4)
+            fp = reg_value.value.uint32;
+        else
+            fp = reg_value.value.uint64;
+        while (fp != 0)
+        {
+            // Make sure we never recurse more than 256 times so we don't recurse too far or
+            // store up too much memory in the expedited cache
+            if (++frame_count > 256)
+                break;
+
+            const nub_size_t read_size = reg_value.info.size*2;
+            StackMemory stack_memory;
+            stack_memory.length = read_size;
+            if (DNBProcessMemoryRead(pid, fp, read_size, stack_memory.bytes) != read_size)
+                break;
+            // Make sure we don't try to put the same stack memory in more than once
+            if (stack_mmap.find(fp) != stack_mmap.end())
+                break;
+            // Put the entry into the cache
+            stack_mmap[fp] = stack_memory;
+            // Dereference the frame pointer to get to the previous frame pointer
+            if (reg_value.info.size == 4)
+                fp = ((uint32_t *)stack_memory.bytes)[0];
+            else
+                fp = ((uint64_t *)stack_memory.bytes)[0];
+        }
+    }
+}
+
 rnb_err_t
 RNBRemote::SendStopReplyPacketForThread (nub_thread_t tid)
 {
@@ -2578,11 +2626,26 @@ RNBRemote::SendStopReplyPacketForThread
         }
         else if (tid_stop_info.details.exception.type)
         {
-            ostrm << "metype:" << std::hex << tid_stop_info.details.exception.type << ";";
-            ostrm << "mecount:" << std::hex << tid_stop_info.details.exception.data_count << ";";
+            ostrm << "metype:" << std::hex << tid_stop_info.details.exception.type << ';';
+            ostrm << "mecount:" << std::hex << tid_stop_info.details.exception.data_count << ';';
             for (int i = 0; i < tid_stop_info.details.exception.data_count; ++i)
-                ostrm << "medata:" << std::hex << tid_stop_info.details.exception.data[i] << ";";
+                ostrm << "medata:" << std::hex << tid_stop_info.details.exception.data[i] << ';';
         }
+
+        // Add expedited stack memory so stack backtracing doesn't need to read anything from the
+        // frame pointer chain.
+        StackMemoryMap stack_mmap;
+        ReadStackMemory (pid, tid, stack_mmap);
+        if (!stack_mmap.empty())
+        {
+            for (const auto &stack_memory : stack_mmap)
+            {
+                ostrm << "memory:" << HEXBASE << stack_memory.first << '=';
+                append_hex_value (ostrm, stack_memory.second.bytes, stack_memory.second.length, false);
+                ostrm << ';';
+            }
+        }
+
         return SendPacket (ostrm.str ());
     }
     return SendPacket("E51");
@@ -4762,11 +4825,136 @@ get_integer_value_for_key_name_from_json
 }
 
 rnb_err_t
+RNBRemote::HandlePacket_jThreadsInfo (const char *p)
+{
+    JSONGenerator::Array threads_array;
+
+    std::ostringstream json;
+    std::ostringstream reply_strm;
+    // If we haven't run the process yet, return an error.
+    if (m_ctx.HasValidProcessID())
+    {
+        nub_process_t pid = m_ctx.ProcessID();
+
+        nub_size_t numthreads = DNBProcessGetNumThreads (pid);
+        for (nub_size_t i = 0; i < numthreads; ++i)
+        {
+            nub_thread_t tid = DNBProcessGetThreadAtIndex (pid, i);
+
+            struct DNBThreadStopInfo tid_stop_info;
+
+            JSONGenerator::DictionarySP thread_dict_sp(new JSONGenerator::Dictionary());
+
+            thread_dict_sp->AddIntegerItem("tid", tid);
+
+            std::string reason_value("none");
+            if (DNBThreadGetStopReason (pid, tid, &tid_stop_info))
+            {
+                switch (tid_stop_info.reason)
+                {
+                    case eStopTypeInvalid:
+                        break;
+                    case eStopTypeSignal:
+                        if (tid_stop_info.details.signal.signo != 0)
+                            reason_value = "signal";
+                        break;
+                    case eStopTypeException:
+                        if (tid_stop_info.details.exception.type != 0)
+                            reason_value = "exception";
+                        break;
+                    case eStopTypeExec:
+                        reason_value = "exec";
+                        break;
+                }
+                if (tid_stop_info.reason == eStopTypeSignal)
+                {
+                    thread_dict_sp->AddIntegerItem("signal", tid_stop_info.details.signal.signo);
+                }
+                else if (tid_stop_info.reason == eStopTypeException && tid_stop_info.details.exception.type != 0)
+                {
+                    thread_dict_sp->AddIntegerItem("metype", tid_stop_info.details.exception.type);
+                    JSONGenerator::ArraySP medata_array_sp(new JSONGenerator::Array());
+                    for (nub_size_t i=0; i<tid_stop_info.details.exception.data_count; ++i)
+                    {
+                        medata_array_sp->AddItem(JSONGenerator::IntegerSP(new JSONGenerator::Integer(tid_stop_info.details.exception.data[i])));
+                    }
+                    thread_dict_sp->AddItem("medata", medata_array_sp);
+                }
+            }
+
+            thread_dict_sp->AddStringItem("reason", reason_value);
+
+            const char *thread_name = DNBThreadGetName (pid, tid);
+            if (thread_name && thread_name[0])
+                thread_dict_sp->AddStringItem("name", thread_name);
+
+
+            thread_identifier_info_data_t thread_ident_info;
+            if (DNBThreadGetIdentifierInfo (pid, tid, &thread_ident_info))
+            {
+                if (thread_ident_info.dispatch_qaddr != 0)
+                    thread_dict_sp->AddIntegerItem("qaddr", thread_ident_info.dispatch_qaddr);
+            }
+            DNBRegisterValue reg_value;
+
+            if (g_reg_entries != NULL)
+            {
+                JSONGenerator::DictionarySP registers_dict_sp(new JSONGenerator::Dictionary());
+
+                for (uint32_t reg = 0; reg < g_num_reg_entries; reg++)
+                {
+                    // Expedite all registers in the first register set that aren't
+                    // contained in other registers
+                    if (g_reg_entries[reg].nub_info.set == 1 &&
+                        g_reg_entries[reg].nub_info.value_regs == NULL)
+                    {
+                        if (!DNBThreadGetRegisterValueByID (pid, tid, g_reg_entries[reg].nub_info.set, g_reg_entries[reg].nub_info.reg, &reg_value))
+                            continue;
+
+                        std::ostringstream reg_num;
+                        reg_num << std::dec << g_reg_entries[reg].gdb_regnum;
+                        // Encode native byte ordered bytes as hex ascii
+                        registers_dict_sp->AddBytesAsHexASCIIString(reg_num.str(), reg_value.value.v_uint8, g_reg_entries[reg].nub_info.size);
+                    }
+                }
+                thread_dict_sp->AddItem("registers", registers_dict_sp);
+            }
+
+            // Add expedited stack memory so stack backtracing doesn't need to read anything from the
+            // frame pointer chain.
+            StackMemoryMap stack_mmap;
+            ReadStackMemory (pid, tid, stack_mmap);
+            if (!stack_mmap.empty())
+            {
+                JSONGenerator::ArraySP memory_array_sp(new JSONGenerator::Array());
+
+                for (const auto &stack_memory : stack_mmap)
+                {
+                    JSONGenerator::DictionarySP stack_memory_sp(new JSONGenerator::Dictionary());
+                    stack_memory_sp->AddIntegerItem("address", stack_memory.first);
+                    stack_memory_sp->AddBytesAsHexASCIIString("bytes", stack_memory.second.bytes, stack_memory.second.length);
+                    memory_array_sp->AddItem(stack_memory_sp);
+                }
+                thread_dict_sp->AddItem("memory", memory_array_sp);
+            }
+            threads_array.AddItem(thread_dict_sp);
+        }
+
+        std::ostringstream strm;
+        threads_array.Dump (strm);
+        std::string binary_packet = binary_encode_string (strm.str());
+        if (!binary_packet.empty())
+            return SendPacket (binary_packet.c_str());
+    }
+    return SendPacket ("E85");
+
+}
+
+rnb_err_t
 RNBRemote::HandlePacket_jThreadExtendedInfo (const char *p)
 {
     nub_process_t pid;
     std::ostringstream json;
-    std::ostringstream reply_strm;
     // If we haven't run the process yet, return an error.
     if (!m_ctx.HasValidProcessID())
     {
@@ -4996,8 +5184,7 @@ RNBRemote::HandlePacket_jThreadExtendedI
 
             json << "}";
             std::string json_quoted = binary_encode_string (json.str());
-            reply_strm << json_quoted;
-            return SendPacket (reply_strm.str());
+            return SendPacket (json_quoted);
         }
     }
     return SendPacket ("OK");

Modified: lldb/trunk/tools/debugserver/source/RNBRemote.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/debugserver/source/RNBRemote.h?rev=240354&r1=240353&r2=240354&view=diff
==============================================================================
--- lldb/trunk/tools/debugserver/source/RNBRemote.h (original)
+++ lldb/trunk/tools/debugserver/source/RNBRemote.h Mon Jun 22 18:12:45 2015
@@ -103,6 +103,7 @@ public:
         query_gdb_server_version,       // 'qGDBServerVersion'
         query_process_info,             // 'qProcessInfo'
         json_query_thread_extended_info,// 'jThreadExtendedInfo'
+        json_query_threads_info,        // 'jThreadsInfo'
         pass_signals_to_inferior,       // 'QPassSignals'
         start_noack_mode,               // 'QStartNoAckMode'
         prefix_reg_packets_with_tid,    // 'QPrefixRegisterPacketsWithThreadID
@@ -190,6 +191,7 @@ public:
     rnb_err_t HandlePacket_qSyncThreadStateSupported (const char *p);
     rnb_err_t HandlePacket_qThreadInfo (const char *p);
     rnb_err_t HandlePacket_jThreadExtendedInfo (const char *p);
+    rnb_err_t HandlePacket_jThreadsInfo (const char *p);
     rnb_err_t HandlePacket_qThreadExtraInfo (const char *p);
     rnb_err_t HandlePacket_qThreadStopInfo (const char *p);
     rnb_err_t HandlePacket_qHostInfo (const char *p);





More information about the lldb-commits mailing list