Move packet construction from GDBRemoteRegisterContext go the communication class

Summary:
When saving/restoring registers the GDBRemoteRegisterContext class was manually constructing
the register save/restore packets. This creates appropriate helper functions in
GDBRemoteCommunicationClient, and switches the class to use those. It also removes what a
duplicate packet send in some of those functions, a thing that I can only attribute to a bad
merge artefact.

I also add a test framework for testing gdb-remote client functionality and add tests for the new
functions I introduced. I'd like to be able to test the register context changes in isolation as
well, but currently there doesn't seem to be a way to reasonably construct a standalone register
context object, so we'll have to rely on the end-to-end tests to verify that.

Reviewers: clayborg

Subscribers: lldb-commits

Differential Revision: https://reviews.llvm.org/D23553

llvm-svn: 278915
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index 0933ada..bfc2d4a 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -3526,6 +3526,57 @@
     }
     return false;
 }
+
+bool
+GDBRemoteCommunicationClient::WriteRegister(lldb::tid_t tid, uint32_t reg_num, llvm::StringRef data)
+{
+    Lock lock(*this, false);
+    if (!lock)
+    {
+        if (Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS | GDBR_LOG_PACKETS))
+            log->Printf("GDBRemoteCommunicationClient::%s: Didn't get sequence mutex for P packet.", __FUNCTION__);
+        return false;
+    }
+
+    const bool thread_suffix_supported = GetThreadSuffixSupported();
+    if (!thread_suffix_supported && !SetCurrentThread(tid))
+        return false;
+
+    StreamString packet;
+    packet.Printf("P%x=", reg_num);
+    packet.PutBytesAsRawHex8(data.data(), data.size(), endian::InlHostByteOrder(), endian::InlHostByteOrder());
+    if (thread_suffix_supported)
+        packet.Printf(";thread:%4.4" PRIx64 ";", tid);
+    StringExtractorGDBRemote response;
+    return SendPacketAndWaitForResponse(packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success &&
+           response.IsOKResponse();
+}
+
+bool
+GDBRemoteCommunicationClient::WriteAllRegisters(lldb::tid_t tid, llvm::StringRef data)
+{
+    Lock lock(*this, false);
+    if (!lock)
+    {
+        if (Log *log = ProcessGDBRemoteLog::GetLogIfAnyCategoryIsSet(GDBR_LOG_PROCESS | GDBR_LOG_PACKETS))
+            log->Printf("GDBRemoteCommunicationClient::%s: Didn't get sequence mutex for G packet.", __FUNCTION__);
+        return false;
+    }
+
+    const bool thread_suffix_supported = GetThreadSuffixSupported();
+    if (!thread_suffix_supported && !SetCurrentThread(tid))
+        return false;
+
+    StreamString packet;
+    packet.PutChar('G');
+    packet.Write(data.data(), data.size());
+    if (thread_suffix_supported)
+        packet.Printf(";thread:%4.4" PRIx64 ";", tid);
+    StringExtractorGDBRemote response;
+    return SendPacketAndWaitForResponse(packet.GetData(), packet.GetSize(), response, false) == PacketResult::Success &&
+           response.IsOKResponse();
+}
+
 bool
 GDBRemoteCommunicationClient::SaveRegisterState (lldb::tid_t tid, uint32_t &save_id)
 {
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index 83502f4..676386f3 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -497,8 +497,15 @@
                       StringExtractorGDBRemote &response);
 
     bool
-    SaveRegisterState (lldb::tid_t tid, uint32_t &save_id);
-    
+    WriteRegister(lldb::tid_t tid, uint32_t reg_num, // eRegisterKindProcessPlugin register number
+                  llvm::StringRef data);
+
+    bool
+    WriteAllRegisters(lldb::tid_t tid, llvm::StringRef data);
+
+    bool
+    SaveRegisterState(lldb::tid_t tid, uint32_t &save_id);
+
     bool
     RestoreRegisterState (lldb::tid_t tid, uint32_t save_id);
 
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp
index 3e24934..05b2a32 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp
@@ -576,70 +576,40 @@
     if (lock)
     {
         SyncThreadState(process);
-        
-        char packet[32];
-        const bool thread_suffix_supported = gdb_comm.GetThreadSuffixSupported();
-        ProcessSP process_sp (m_thread.GetProcess());
-        if (thread_suffix_supported || static_cast<ProcessGDBRemote *>(process_sp.get())->GetGDBRemote().SetCurrentThread(m_thread.GetProtocolID()))
+
+        if (use_g_packet && gdb_comm.ReadAllRegisters(m_thread.GetProtocolID(), response))
         {
-            int packet_len = 0;
-            if (thread_suffix_supported)
-                packet_len = ::snprintf (packet, sizeof(packet), "g;thread:%4.4" PRIx64, m_thread.GetProtocolID());
-            else
-                packet_len = ::snprintf (packet, sizeof(packet), "g");
-            assert (packet_len < ((int)sizeof(packet) - 1));
+            if (response.IsErrorResponse())
+                return false;
 
-            if (use_g_packet && gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, false) == GDBRemoteCommunication::PacketResult::Success)
+            std::string &response_str = response.GetStringRef();
+            if (!isxdigit(response_str[0]))
+                return false;
+
+            data_sp.reset(new DataBufferHeap(response_str.c_str(), response_str.size()));
+            return true;
+        }
+        else
+        {
+            // For the use_g_packet == false case, we're going to read each register
+            // individually and store them as binary data in a buffer instead of as ascii
+            // characters.
+            const RegisterInfo *reg_info;
+
+            // data_sp will take ownership of this DataBufferHeap pointer soon.
+            DataBufferSP reg_ctx(new DataBufferHeap(m_reg_info.GetRegisterDataByteSize(), 0));
+
+            for (uint32_t i = 0; (reg_info = GetRegisterInfoAtIndex(i)) != NULL; i++)
             {
-                int packet_len = 0;
-                if (thread_suffix_supported)
-                    packet_len = ::snprintf (packet, sizeof(packet), "g;thread:%4.4" PRIx64, m_thread.GetProtocolID());
-                else
-                    packet_len = ::snprintf (packet, sizeof(packet), "g");
-                assert (packet_len < ((int)sizeof(packet) - 1));
-    
-                if (gdb_comm.SendPacketAndWaitForResponse(packet, packet_len, response, false) == GDBRemoteCommunication::PacketResult::Success)
-                {
-                    if (response.IsErrorResponse())
-                        return false;
-    
-                    std::string &response_str = response.GetStringRef();
-                    if (isxdigit(response_str[0]))
-                    {
-                        response_str.insert(0, 1, 'G');
-                        if (thread_suffix_supported)
-                        {
-                            char thread_id_cstr[64];
-                            ::snprintf (thread_id_cstr, sizeof(thread_id_cstr), ";thread:%4.4" PRIx64 ";", m_thread.GetProtocolID());
-                            response_str.append (thread_id_cstr);
-                        }
-                        data_sp.reset (new DataBufferHeap (response_str.c_str(), response_str.size()));
-                        return true;
-                    }
-                }
+                if (reg_info->value_regs) // skip registers that are slices of real registers
+                    continue;
+                ReadRegisterBytes(reg_info, m_reg_data);
+                // ReadRegisterBytes saves the contents of the register in to the m_reg_data buffer
             }
-            else
-            {
-                // For the use_g_packet == false case, we're going to read each register 
-                // individually and store them as binary data in a buffer instead of as ascii
-                // characters.
-                const RegisterInfo *reg_info;
+            memcpy(reg_ctx->GetBytes(), m_reg_data.GetDataStart(), m_reg_info.GetRegisterDataByteSize());
 
-                // data_sp will take ownership of this DataBufferHeap pointer soon.
-                DataBufferSP reg_ctx(new DataBufferHeap(m_reg_info.GetRegisterDataByteSize(), 0));
-
-                for (uint32_t i = 0; (reg_info = GetRegisterInfoAtIndex (i)) != NULL; i++)
-                {
-                    if (reg_info->value_regs) // skip registers that are slices of real registers
-                        continue;
-                    ReadRegisterBytes (reg_info, m_reg_data);
-                    // ReadRegisterBytes saves the contents of the register in to the m_reg_data buffer
-                }
-                memcpy (reg_ctx->GetBytes(), m_reg_data.GetDataStart(), m_reg_info.GetRegisterDataByteSize());
-
-                data_sp = reg_ctx;
-                return true;
-            }
+            data_sp = reg_ctx;
+            return true;
         }
     }
     else
@@ -684,236 +654,154 @@
     GDBRemoteClientBase::Lock lock(gdb_comm, false);
     if (lock)
     {
-        const bool thread_suffix_supported = gdb_comm.GetThreadSuffixSupported();
-        ProcessSP process_sp (m_thread.GetProcess());
-        if (thread_suffix_supported || static_cast<ProcessGDBRemote *>(process_sp.get())->GetGDBRemote().SetCurrentThread(m_thread.GetProtocolID()))
+        // The data_sp contains the G response packet.
+        llvm::StringRef data(reinterpret_cast<const char *>(data_sp->GetBytes()), data_sp->GetByteSize());
+        if (use_g_packet)
         {
-            // The data_sp contains the entire G response packet including the
-            // G, and if the thread suffix is supported, it has the thread suffix
-            // as well.
-            const char *G_packet = (const char *)data_sp->GetBytes();
-            size_t G_packet_len = data_sp->GetByteSize();
-            if (use_g_packet
-                && gdb_comm.SendPacketAndWaitForResponse (G_packet,
-                                                          G_packet_len,
-                                                          response,
-                                                          false) == GDBRemoteCommunication::PacketResult::Success)
+            if (gdb_comm.WriteAllRegisters(m_thread.GetProtocolID(), data))
+                return true;
+
+            uint32_t num_restored = 0;
+            // We need to manually go through all of the registers and
+            // restore them manually
+
+            response.GetStringRef() = data;
+            DataBufferHeap buffer(data.size() / 2, 0);
+
+            const uint32_t bytes_extracted = response.GetHexBytes(buffer.GetBytes(), buffer.GetByteSize(), '\xcc');
+
+            DataExtractor restore_data(buffer.GetBytes(), buffer.GetByteSize(), m_reg_data.GetByteOrder(),
+                                       m_reg_data.GetAddressByteSize());
+
+            if (bytes_extracted < restore_data.GetByteSize())
+                restore_data.SetData(restore_data.GetDataStart(), bytes_extracted, m_reg_data.GetByteOrder());
+
+            const RegisterInfo *reg_info;
+
+            // The g packet contents may either include the slice registers (registers defined in
+            // terms of other registers, e.g. eax is a subset of rax) or not.  The slice registers
+            // should NOT be in the g packet, but some implementations may incorrectly include them.
+            //
+            // If the slice registers are included in the packet, we must step over the slice registers
+            // when parsing the packet -- relying on the RegisterInfo byte_offset field would be incorrect.
+            // If the slice registers are not included, then using the byte_offset values into the
+            // data buffer is the best way to find individual register values.
+
+            uint64_t size_including_slice_registers = 0;
+            uint64_t size_not_including_slice_registers = 0;
+            uint64_t size_by_highest_offset = 0;
+
+            for (uint32_t reg_idx = 0; (reg_info = GetRegisterInfoAtIndex(reg_idx)) != NULL; ++reg_idx)
             {
-                // The data_sp contains the entire G response packet including the
-                // G, and if the thread suffix is supported, it has the thread suffix
-                // as well.
-                const char *G_packet = (const char *)data_sp->GetBytes();
-                size_t G_packet_len = data_sp->GetByteSize();
-                if (gdb_comm.SendPacketAndWaitForResponse (G_packet,
-                                                           G_packet_len,
-                                                           response,
-                                                           false) == GDBRemoteCommunication::PacketResult::Success)
-                {
-                    if (response.IsOKResponse())
-                        return true;
-                    else if (response.IsErrorResponse())
-                    {
-                        uint32_t num_restored = 0;
-                        // We need to manually go through all of the registers and
-                        // restore them manually
-    
-                        response.GetStringRef().assign (G_packet, G_packet_len);
-                        response.SetFilePos(1); // Skip the leading 'G'
+                size_including_slice_registers += reg_info->byte_size;
+                if (reg_info->value_regs == NULL)
+                    size_not_including_slice_registers += reg_info->byte_size;
+                if (reg_info->byte_offset >= size_by_highest_offset)
+                    size_by_highest_offset = reg_info->byte_offset + reg_info->byte_size;
+            }
 
-                        // G_packet_len is hex-ascii characters plus prefix 'G' plus suffix thread specifier.
-                        // This means buffer will be a little more than 2x larger than necessary but we resize
-                        // it down once we've extracted all hex ascii chars from the packet.
-                        DataBufferHeap buffer (G_packet_len, 0);
-
-                        const uint32_t bytes_extracted = response.GetHexBytes (buffer.GetBytes(),
-                                                                               buffer.GetByteSize(),
-                                                                               '\xcc');
-
-                        DataExtractor restore_data (buffer.GetBytes(),
-                                                    buffer.GetByteSize(),
-                                                    m_reg_data.GetByteOrder(),
-                                                    m_reg_data.GetAddressByteSize());
-
-                        if (bytes_extracted < restore_data.GetByteSize())
-                            restore_data.SetData(restore_data.GetDataStart(), bytes_extracted, m_reg_data.GetByteOrder());
-    
-                        const RegisterInfo *reg_info;
-
-                        // The g packet contents may either include the slice registers (registers defined in
-                        // terms of other registers, e.g. eax is a subset of rax) or not.  The slice registers 
-                        // should NOT be in the g packet, but some implementations may incorrectly include them.
-                        // 
-                        // If the slice registers are included in the packet, we must step over the slice registers 
-                        // when parsing the packet -- relying on the RegisterInfo byte_offset field would be incorrect.
-                        // If the slice registers are not included, then using the byte_offset values into the
-                        // data buffer is the best way to find individual register values.
-
-                        uint64_t size_including_slice_registers = 0;
-                        uint64_t size_not_including_slice_registers = 0;
-                        uint64_t size_by_highest_offset = 0;
-
-                        for (uint32_t reg_idx=0; (reg_info = GetRegisterInfoAtIndex (reg_idx)) != NULL; ++reg_idx)
-                        {
-                            size_including_slice_registers += reg_info->byte_size;
-                            if (reg_info->value_regs == NULL)
-                                size_not_including_slice_registers += reg_info->byte_size;
-                            if (reg_info->byte_offset >= size_by_highest_offset)
-                                size_by_highest_offset = reg_info->byte_offset + reg_info->byte_size;
-                        }
-
-                        bool use_byte_offset_into_buffer;
-                        if (size_by_highest_offset == restore_data.GetByteSize())
-                        {
-                            // The size of the packet agrees with the highest offset: + size in the register file
-                            use_byte_offset_into_buffer = true;
-                        }
-                        else if (size_not_including_slice_registers == restore_data.GetByteSize())
-                        {
-                            // The size of the packet is the same as concatenating all of the registers sequentially,
-                            // skipping the slice registers
-                            use_byte_offset_into_buffer = true;
-                        }
-                        else if (size_including_slice_registers == restore_data.GetByteSize())
-                        {
-                            // The slice registers are present in the packet (when they shouldn't be).
-                            // Don't try to use the RegisterInfo byte_offset into the restore_data, it will
-                            // point to the wrong place.
-                            use_byte_offset_into_buffer = false;
-                        }
-                        else {
-                            // None of our expected sizes match the actual g packet data we're looking at.
-                            // The most conservative approach here is to use the running total byte offset.
-                            use_byte_offset_into_buffer = false;
-                        }
-
-                        // In case our register definitions don't include the correct offsets,
-                        // keep track of the size of each reg & compute offset based on that.
-                        uint32_t running_byte_offset = 0;
-                        for (uint32_t reg_idx=0; (reg_info = GetRegisterInfoAtIndex (reg_idx)) != NULL; ++reg_idx, running_byte_offset += reg_info->byte_size)
-                        {
-                            // Skip composite aka slice registers (e.g. eax is a slice of rax).
-                            if (reg_info->value_regs)
-                                continue;
-
-                            const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
-
-                            uint32_t register_offset;
-                            if (use_byte_offset_into_buffer)
-                            {
-                                register_offset = reg_info->byte_offset;
-                            }
-                            else
-                            {
-                                register_offset = running_byte_offset;
-                            }
-
-                            // Only write down the registers that need to be written
-                            // if we are going to be doing registers individually.
-                            bool write_reg = true;
-                            const uint32_t reg_byte_size = reg_info->byte_size;
-    
-                            const char *restore_src = (const char *)restore_data.PeekData(register_offset, reg_byte_size);
-                            if (restore_src)
-                            {
-                                StreamString packet;
-                                packet.Printf ("P%x=", reg_info->kinds[eRegisterKindProcessPlugin]);
-                                packet.PutBytesAsRawHex8 (restore_src,
-                                                          reg_byte_size,
-                                                          endian::InlHostByteOrder(),
-                                                          endian::InlHostByteOrder());
-
-                                if (thread_suffix_supported)
-                                    packet.Printf (";thread:%4.4" PRIx64 ";", m_thread.GetProtocolID());
-
-                                SetRegisterIsValid(reg, false);
-                                if (gdb_comm.SendPacketAndWaitForResponse(packet.GetString().c_str(),
-                                                                          packet.GetString().size(),
-                                                                          response,
-                                                                          false) == GDBRemoteCommunication::PacketResult::Success)
-                                {
-                                    const char *current_src = (const char *)m_reg_data.PeekData(register_offset, reg_byte_size);
-                                    if (current_src)
-                                        write_reg = memcmp (current_src, restore_src, reg_byte_size) != 0;
-                                }
-    
-                                if (write_reg)
-                                {
-                                    StreamString packet;
-                                    packet.Printf ("P%x=", reg_info->kinds[eRegisterKindProcessPlugin]);
-                                    packet.PutBytesAsRawHex8 (restore_src,
-                                                              reg_byte_size,
-                                                              endian::InlHostByteOrder(),
-                                                              endian::InlHostByteOrder());
-    
-                                    if (thread_suffix_supported)
-                                        packet.Printf (";thread:%4.4" PRIx64 ";", m_thread.GetProtocolID());
-    
-                                    SetRegisterIsValid(reg, false);
-                                    if (gdb_comm.SendPacketAndWaitForResponse(packet.GetString().c_str(),
-                                                                              packet.GetString().size(),
-                                                                              response,
-                                                                              false) == GDBRemoteCommunication::PacketResult::Success)
-                                    {
-                                        if (response.IsOKResponse())
-                                            ++num_restored;
-                                    }
-                                }
-                            }
-                        }
-                        return num_restored > 0;
-                    }
-                }
+            bool use_byte_offset_into_buffer;
+            if (size_by_highest_offset == restore_data.GetByteSize())
+            {
+                // The size of the packet agrees with the highest offset: + size in the register file
+                use_byte_offset_into_buffer = true;
+            }
+            else if (size_not_including_slice_registers == restore_data.GetByteSize())
+            {
+                // The size of the packet is the same as concatenating all of the registers sequentially,
+                // skipping the slice registers
+                use_byte_offset_into_buffer = true;
+            }
+            else if (size_including_slice_registers == restore_data.GetByteSize())
+            {
+                // The slice registers are present in the packet (when they shouldn't be).
+                // Don't try to use the RegisterInfo byte_offset into the restore_data, it will
+                // point to the wrong place.
+                use_byte_offset_into_buffer = false;
             }
             else
             {
-                // For the use_g_packet == false case, we're going to write each register 
-                // individually.  The data buffer is binary data in this case, instead of 
-                // ascii characters.
-
-                bool arm64_debugserver = false;
-                if (m_thread.GetProcess().get())
-                {
-                    const ArchSpec &arch = m_thread.GetProcess()->GetTarget().GetArchitecture();
-                    if (arch.IsValid()
-                        && arch.GetMachine() == llvm::Triple::aarch64
-                        && arch.GetTriple().getVendor() == llvm::Triple::Apple
-                        && arch.GetTriple().getOS() == llvm::Triple::IOS)
-                    {
-                        arm64_debugserver = true;
-                    }
-                }
-                uint32_t num_restored = 0;
-                const RegisterInfo *reg_info;
-                for (uint32_t i = 0; (reg_info = GetRegisterInfoAtIndex (i)) != NULL; i++)
-                {
-                    if (reg_info->value_regs) // skip registers that are slices of real registers
-                        continue;
-                    // Skip the fpsr and fpcr floating point status/control register writing to
-                    // work around a bug in an older version of debugserver that would lead to
-                    // register context corruption when writing fpsr/fpcr.
-                    if (arm64_debugserver &&
-                        (strcmp (reg_info->name, "fpsr") == 0 || strcmp (reg_info->name, "fpcr") == 0))
-                    {
-                        continue;
-                    }
-                    StreamString packet;
-                    packet.Printf ("P%x=", reg_info->kinds[eRegisterKindProcessPlugin]);
-                    packet.PutBytesAsRawHex8 (data_sp->GetBytes() + reg_info->byte_offset, reg_info->byte_size, endian::InlHostByteOrder(), endian::InlHostByteOrder());
-                    if (thread_suffix_supported)
-                        packet.Printf (";thread:%4.4" PRIx64 ";", m_thread.GetProtocolID());
-
-                    SetRegisterIsValid(reg_info, false);
-                    if (gdb_comm.SendPacketAndWaitForResponse(packet.GetString().c_str(),
-                                                              packet.GetString().size(),
-                                                              response,
-                                                              false) == GDBRemoteCommunication::PacketResult::Success)
-                    {
-                        if (response.IsOKResponse())
-                            ++num_restored;
-                    }
-                }
-                return num_restored > 0;
+                // None of our expected sizes match the actual g packet data we're looking at.
+                // The most conservative approach here is to use the running total byte offset.
+                use_byte_offset_into_buffer = false;
             }
+
+            // In case our register definitions don't include the correct offsets,
+            // keep track of the size of each reg & compute offset based on that.
+            uint32_t running_byte_offset = 0;
+            for (uint32_t reg_idx = 0; (reg_info = GetRegisterInfoAtIndex(reg_idx)) != NULL;
+                 ++reg_idx, running_byte_offset += reg_info->byte_size)
+            {
+                // Skip composite aka slice registers (e.g. eax is a slice of rax).
+                if (reg_info->value_regs)
+                    continue;
+
+                const uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+
+                uint32_t register_offset;
+                if (use_byte_offset_into_buffer)
+                {
+                    register_offset = reg_info->byte_offset;
+                }
+                else
+                {
+                    register_offset = running_byte_offset;
+                }
+
+                const uint32_t reg_byte_size = reg_info->byte_size;
+
+                const char *restore_src = (const char *)restore_data.PeekData(register_offset, reg_byte_size);
+                if (restore_src)
+                {
+                    SetRegisterIsValid(reg, false);
+                    if (gdb_comm.WriteRegister(m_thread.GetProtocolID(), reg_info->kinds[eRegisterKindProcessPlugin],
+                                               llvm::StringRef(restore_src, reg_byte_size)))
+                        ++num_restored;
+                }
+            }
+            return num_restored > 0;
+        }
+        else
+        {
+            // For the use_g_packet == false case, we're going to write each register
+            // individually.  The data buffer is binary data in this case, instead of
+            // ascii characters.
+
+            bool arm64_debugserver = false;
+            if (m_thread.GetProcess().get())
+            {
+                const ArchSpec &arch = m_thread.GetProcess()->GetTarget().GetArchitecture();
+                if (arch.IsValid() && arch.GetMachine() == llvm::Triple::aarch64 &&
+                    arch.GetTriple().getVendor() == llvm::Triple::Apple &&
+                    arch.GetTriple().getOS() == llvm::Triple::IOS)
+                {
+                    arm64_debugserver = true;
+                }
+            }
+            uint32_t num_restored = 0;
+            const RegisterInfo *reg_info;
+            for (uint32_t i = 0; (reg_info = GetRegisterInfoAtIndex(i)) != NULL; i++)
+            {
+                if (reg_info->value_regs) // skip registers that are slices of real registers
+                    continue;
+                // Skip the fpsr and fpcr floating point status/control register writing to
+                // work around a bug in an older version of debugserver that would lead to
+                // register context corruption when writing fpsr/fpcr.
+                if (arm64_debugserver && (strcmp(reg_info->name, "fpsr") == 0 || strcmp(reg_info->name, "fpcr") == 0))
+                {
+                    continue;
+                }
+
+                SetRegisterIsValid(reg_info, false);
+                if (gdb_comm.WriteRegister(
+                        m_thread.GetProtocolID(), reg_info->kinds[eRegisterKindProcessPlugin],
+                        llvm::StringRef(reinterpret_cast<const char *>(data_sp->GetBytes() + reg_info->byte_offset),
+                                        reg_info->byte_size)))
+                {
+                    ++num_restored;
+                }
+            }
+            return num_restored > 0;
         }
     }
     else