Completed more work on the KDP darwin kernel debugging Process plug-in.
Implemented connect, disconnect, reattach, version, and hostinfo.

Modified the ConnectionFileDescriptor class to be able to handle UDP. 

Added a new Stream subclass called StreamBuffer that is backed by a
llvm::SmallVector for better efficiency.

Modified the DataExtractor class to have a static function that can
dump hex bytes into a stream. This is currently being used to dump incoming
binary packet data in the KDP plug-in.



git-svn-id: https://llvm.org/svn/llvm-project/llvdb/trunk@135338 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp b/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp
index 2287e40..06096af 100644
--- a/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp
+++ b/source/Plugins/Process/MacOSX-Kernel/CommunicationKDP.cpp
@@ -16,14 +16,13 @@
 
 // C++ Includes
 // Other libraries and framework includes
+#include "lldb/Core/DataBufferHeap.h"
 #include "lldb/Core/DataExtractor.h"
 #include "lldb/Core/Log.h"
-#include "lldb/Core/StreamString.h"
 #include "lldb/Host/FileSpec.h"
 #include "lldb/Host/Host.h"
 #include "lldb/Host/TimeValue.h"
 #include "lldb/Target/Process.h"
-#include "Utility/StringExtractor.h"
 
 // Project includes
 #include "ProcessKDPLog.h"
@@ -38,13 +37,19 @@
 //----------------------------------------------------------------------
 CommunicationKDP::CommunicationKDP (const char *comm_name) :
     Communication(comm_name),
+    m_byte_order (eByteOrderLittle),
     m_packet_timeout (1),
     m_sequence_mutex (Mutex::eMutexTypeRecursive),
     m_public_is_running (false),
     m_private_is_running (false),
-    m_session_key (0),
-    m_request_sequence_id (0),
-    m_exception_sequence_id (0)
+    m_session_key (0u),
+    m_request_sequence_id (0u),
+    m_exception_sequence_id (0u),
+    m_kdp_version_version (0u),
+    m_kdp_version_feature (0u),
+    m_kdp_hostinfo_cpu_mask (0u),
+    m_kdp_hostinfo_cpu_type (0u),
+    m_kdp_hostinfo_cpu_subtype (0u)
 {
 }
 
@@ -60,27 +65,61 @@
 }
 
 bool
-CommunicationKDP::SendRequestPacket (const StreamString &request_packet)
+CommunicationKDP::SendRequestPacket (const PacketStreamType &request_packet)
 {
     Mutex::Locker locker(m_sequence_mutex);
     return SendRequestPacketNoLock (request_packet);
 }
 
+#if 0
+typedef struct {
+	uint8_t     request;	// Either: CommandType | ePacketTypeRequest, or CommandType | ePacketTypeReply
+	uint8_t     sequence;
+	uint16_t    length;		// Length of entire packet including this header
+	uint32_t	key;		// Session key
+} kdp_hdr_t;
+#endif
+
 void
-CommunicationKDP::MakeRequestPacketHeader (RequestType request_type, 
-                                           StreamString &request_packet)
+CommunicationKDP::MakeRequestPacketHeader (CommandType request_type, 
+                                           PacketStreamType &request_packet,
+                                           uint16_t request_length)
 {
     request_packet.Clear();
-    request_packet.PutHex32 (request_type);             // Set the request type
-    request_packet.PutHex8  (ePacketTypeRequest);       // Set the packet type
-    request_packet.PutHex8  (++m_request_sequence_id);  // Sequence number
-    request_packet.PutHex16 (0);                        // Pad1 and Pad2 bytes
-    request_packet.PutHex32 (m_session_key);            // Session key
+    request_packet.PutHex8 (request_type | ePacketTypeRequest); // Set the request type
+    request_packet.PutHex8 (m_request_sequence_id++);           // Sequence number
+    request_packet.PutHex16 (request_length);                   // Length of the packet including this header
+    request_packet.PutHex32 (m_session_key);                    // Session key
 }
 
+bool
+CommunicationKDP::SendRequestAndGetReply (const CommandType command,
+                                          const uint8_t request_sequence_id,
+                                          const PacketStreamType &request_packet, 
+                                          DataExtractor &reply_packet)
+{
+
+    Mutex::Locker locker(m_sequence_mutex);    
+    if (SendRequestPacketNoLock(request_packet))
+    {
+        if (WaitForPacketWithTimeoutMicroSecondsNoLock (reply_packet, m_packet_timeout))
+        {
+            uint32_t offset = 0;
+            const uint8_t reply_command = reply_packet.GetU8 (&offset);
+            const uint8_t reply_sequence_id = reply_packet.GetU8 (&offset);
+            if ((reply_command & eCommandTypeMask) == command)
+            {
+                if (request_sequence_id == reply_sequence_id)
+                    return true;
+            }
+        }
+    }
+    reply_packet.Clear();
+    return false;
+}
 
 bool
-CommunicationKDP::SendRequestPacketNoLock (const StreamString &request_packet)
+CommunicationKDP::SendRequestPacketNoLock (const PacketStreamType &request_packet)
 {
     if (IsConnected())
     {
@@ -90,22 +129,11 @@
         LogSP log (ProcessKDPLog::GetLogIfAllCategoriesSet (KDP_LOG_PACKETS));
         if (log)
         {
-            StreamString log_strm;
-            DataExtractor data (packet_data, 
-                                packet_size,
-                                request_packet.GetByteOrder(),
-                                request_packet.GetAddressByteSize());
-            data.Dump (&log_strm, 
-                       0, 
-                       eFormatBytes,
-                       1, 
-                       packet_size,
-                       32,  // Num bytes per line
-                       0,   // Base address
-                       0, 
-                       0);
+            PacketStreamType log_strm;
             
-            log->Printf("request packet: <%u>\n%s", packet_size, log_strm.GetString().c_str());
+            DataExtractor::DumpHexBytes (&log_strm, packet_data, packet_size, 0);
+            
+            log->Printf("request packet: <%u>\n%s", packet_size, log_strm.GetData());
         }
         ConnectionStatus status = eConnectionStatusSuccess;
 
@@ -137,14 +165,14 @@
 }
 
 size_t
-CommunicationKDP::WaitForPacketWithTimeoutMicroSeconds (StringExtractor &packet, uint32_t timeout_usec)
+CommunicationKDP::WaitForPacketWithTimeoutMicroSeconds (DataExtractor &packet, uint32_t timeout_usec)
 {
     Mutex::Locker locker(m_sequence_mutex);
     return WaitForPacketWithTimeoutMicroSecondsNoLock (packet, timeout_usec);
 }
 
 size_t
-CommunicationKDP::WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtractor &packet, uint32_t timeout_usec)
+CommunicationKDP::WaitForPacketWithTimeoutMicroSecondsNoLock (DataExtractor &packet, uint32_t timeout_usec)
 {
     uint8_t buffer[8192];
     Error error;
@@ -153,7 +181,7 @@
 
     // Check for a packet from our cache first without trying any reading...
     if (CheckForPacket (NULL, 0, packet))
-        return packet.GetStringRef().size();
+        return packet.GetByteSize();
 
     bool timed_out = false;
     while (IsConnected() && !timed_out)
@@ -172,7 +200,7 @@
         if (bytes_read > 0)
         {
             if (CheckForPacket (buffer, bytes_read, packet))
-                return packet.GetStringRef().size();
+                return packet.GetByteSize();
         }
         else
         {
@@ -199,7 +227,7 @@
 }
 
 bool
-CommunicationKDP::CheckForPacket (const uint8_t *src, size_t src_len, StringExtractor &packet)
+CommunicationKDP::CheckForPacket (const uint8_t *src, size_t src_len, DataExtractor &packet)
 {
     // Put the packet data into the buffer in a thread safe fashion
     Mutex::Locker locker(m_bytes_mutex);
@@ -210,43 +238,229 @@
     {
         if (log && log->GetVerbose())
         {
-            StreamString s;
-            log->Printf ("CommunicationKDP::%s adding %u bytes: %.*s",
+            PacketStreamType log_strm;
+            DataExtractor::DumpHexBytes (&log_strm, src, src_len, 0);
+            log->Printf ("CommunicationKDP::%s adding %u bytes: %s",
                          __FUNCTION__, 
                          (uint32_t)src_len, 
-                         (uint32_t)src_len, 
-                         src);
+                         log_strm.GetData());
         }
         m_bytes.append ((const char *)src, src_len);
     }
 
-    // Parse up the packets into gdb remote packets
-    if (!m_bytes.empty())
+    // Make sure we at least have enough bytes for a packet header
+    const size_t bytes_available = m_bytes.size();
+    if (bytes_available >= 8)
     {
-        // TODO: Figure out if we have a full packet reply
+        packet.SetData (&m_bytes[0], bytes_available, m_byte_order);
+        uint32_t offset = 0;
+        uint8_t reply_command = packet.GetU8(&offset);
+        switch (reply_command)
+        {
+        case ePacketTypeReply | eCommandTypeConnect:
+        case ePacketTypeReply | eCommandTypeDisconnect:
+        case ePacketTypeReply | eCommandTypeHostInfo:
+        case ePacketTypeReply | eCommandTypeVersion:
+        case ePacketTypeReply | eCommandTypeMaxBytes:
+        case ePacketTypeReply | eCommandTypeReadMemory:
+        case ePacketTypeReply | eCommandTypeWriteMemory:
+        case ePacketTypeReply | eCommandTypeReadRegisters:
+        case ePacketTypeReply | eCommandTypeWriteRegisters:
+        case ePacketTypeReply | eCommandTypeLoad:
+        case ePacketTypeReply | eCommandTypeImagePath:
+        case ePacketTypeReply | eCommandTypeSuspend:
+        case ePacketTypeReply | eCommandTypeResume:
+        case ePacketTypeReply | eCommandTypeException:
+        case ePacketTypeReply | eCommandTypeTermination:
+        case ePacketTypeReply | eCommandTypeBreakpointSet:
+        case ePacketTypeReply | eCommandTypeBreakpointRemove:
+        case ePacketTypeReply | eCommandTypeRegions:
+        case ePacketTypeReply | eCommandTypeReattach:
+        case ePacketTypeReply | eCommandTypeHostReboot:
+        case ePacketTypeReply | eCommandTypeReadMemory64:
+        case ePacketTypeReply | eCommandTypeWriteMemory64:
+        case ePacketTypeReply | eCommandTypeBreakpointSet64:
+        case ePacketTypeReply | eCommandTypeBreakpointRemove64:
+        case ePacketTypeReply | eCommandTypeKernelVersion:
+            {
+                offset = 2;
+                const uint16_t length = packet.GetU16 (&offset);
+                if (length <= bytes_available)
+                {
+                    // We have an entire packet ready, we need to copy the data
+                    // bytes into a buffer that will be owned by the packet and
+                    // erase the bytes from our communcation buffer "m_bytes"
+                    packet.SetData (DataBufferSP (new DataBufferHeap (&m_bytes[0], length)));
+                    m_bytes.erase (0, length);
+                    return true;
+                }
+            }
+            break;
+
+        default:
+            // Unrecognized reply command byte, erase this byte and try to get back on track
+            if (log)
+                log->Printf ("CommunicationKDP::%s: tossing junk byte: 0x%2.2x", 
+                             __FUNCTION__, 
+                             (uint8_t)m_bytes[0]);
+            m_bytes.erase(0, 1);
+            break;
+        }
     }
     packet.Clear();
     return false;
 }
 
 
-CommunicationKDP::ErrorType
+bool
 CommunicationKDP::Connect (uint16_t reply_port, 
                            uint16_t exc_port, 
                            const char *greeting)
 {
-    StreamString request_packet (Stream::eBinary, 4, eByteOrderLittle);
-    MakeRequestPacketHeader (eRequestTypeConnect, request_packet);
+    PacketStreamType request_packet (Stream::eBinary, 4, m_byte_order);
+    if (greeting == NULL)
+        greeting = "";
+
+    const CommandType command = eCommandTypeConnect;
+    // Length is 82 uint16_t and the length of the greeting C string
+    const uint32_t command_length = 8 + 2 + 2 + ::strlen(greeting);
+    const uint32_t request_sequence_id = m_request_sequence_id;
+    MakeRequestPacketHeader (command, request_packet, command_length);
     request_packet.PutHex16(reply_port);
     request_packet.PutHex16(exc_port);
     request_packet.PutCString(greeting);
-    
-    return eErrorUnimplemented;
+    DataExtractor reply_packet;
+    return SendRequestAndGetReply (command, request_sequence_id, request_packet, reply_packet);
 }
 
-CommunicationKDP::ErrorType
+void
+CommunicationKDP::ClearKDPSettings ()
+{
+    m_request_sequence_id = 0;
+    m_kdp_version_version = 0;
+    m_kdp_version_feature = 0;
+    m_kdp_hostinfo_cpu_mask = 0;
+    m_kdp_hostinfo_cpu_type = 0;
+    m_kdp_hostinfo_cpu_subtype = 0;
+}
+
+bool
+CommunicationKDP::Reattach (uint16_t reply_port)
+{
+    PacketStreamType request_packet (Stream::eBinary, 4, m_byte_order);
+    const CommandType command = eCommandTypeReattach;
+    // Length is 8 bytes for the header plus 2 bytes for the reply UDP port
+    const uint32_t command_length = 8 + 2;
+    const uint32_t request_sequence_id = m_request_sequence_id;
+    MakeRequestPacketHeader (command, request_packet, command_length);
+    request_packet.PutHex16(reply_port);
+    DataExtractor reply_packet;
+    if (SendRequestAndGetReply (command, request_sequence_id, request_packet, reply_packet))
+    {
+        // Reset the sequence ID to zero for reattach
+        ClearKDPSettings ();
+        uint32_t offset = 4;
+        m_session_key = reply_packet.GetU32 (&offset);
+        return true;
+    }
+    return false;
+}
+
+uint32_t
+CommunicationKDP::GetVersion ()
+{
+    if (!VersionIsValid())
+        SendRequestVersion();
+    return m_kdp_version_version;
+}
+
+uint32_t
+CommunicationKDP::GetFeatureFlags ()
+{
+    if (!VersionIsValid())
+        SendRequestVersion();
+    return m_kdp_version_feature;
+}
+
+bool
+CommunicationKDP::SendRequestVersion ()
+{
+    PacketStreamType request_packet (Stream::eBinary, 4, m_byte_order);
+    const CommandType command = eCommandTypeVersion;
+    const uint32_t command_length = 8;
+    const uint32_t request_sequence_id = m_request_sequence_id;
+    MakeRequestPacketHeader (command, request_packet, command_length);
+    DataExtractor reply_packet;
+    if (SendRequestAndGetReply (command, request_sequence_id, request_packet, reply_packet))
+    {
+        // Reset the sequence ID to zero for reattach
+        uint32_t offset = 8;
+        m_kdp_version_version = reply_packet.GetU32 (&offset);
+        m_kdp_version_feature = reply_packet.GetU32 (&offset);
+        return true;
+    }
+    return false;
+}
+
+uint32_t
+CommunicationKDP::GetCPUMask ()
+{
+    if (!HostInfoIsValid())
+        SendRequestHostInfo();
+    return m_kdp_hostinfo_cpu_mask;
+}
+
+uint32_t
+CommunicationKDP::GetCPUType ()
+{
+    if (!HostInfoIsValid())
+        SendRequestHostInfo();
+    return m_kdp_hostinfo_cpu_type;
+}
+
+uint32_t
+CommunicationKDP::GetCPUSubtype ()
+{
+    if (!HostInfoIsValid())
+        SendRequestHostInfo();
+    return m_kdp_hostinfo_cpu_subtype;
+}
+
+bool
+CommunicationKDP::SendRequestHostInfo ()
+{
+    PacketStreamType request_packet (Stream::eBinary, 4, m_byte_order);
+    const CommandType command = eCommandTypeHostInfo;
+    const uint32_t command_length = 8;
+    const uint32_t request_sequence_id = m_request_sequence_id;
+    MakeRequestPacketHeader (command, request_packet, command_length);
+    DataExtractor reply_packet;
+    if (SendRequestAndGetReply (command, request_sequence_id, request_packet, reply_packet))
+    {
+        // Reset the sequence ID to zero for reattach
+        uint32_t offset = 8;
+        m_kdp_hostinfo_cpu_mask = reply_packet.GetU32 (&offset);
+        m_kdp_hostinfo_cpu_type = reply_packet.GetU32 (&offset);
+        m_kdp_hostinfo_cpu_subtype = reply_packet.GetU32 (&offset);
+        return true;
+    }
+    return false;
+}
+
+bool
 CommunicationKDP::Disconnect ()
 {
-    return eErrorUnimplemented;
+    PacketStreamType request_packet (Stream::eBinary, 4, m_byte_order);
+    const CommandType command = eCommandTypeDisconnect;
+    const uint32_t command_length = 8;
+    const uint32_t request_sequence_id = m_request_sequence_id;
+    MakeRequestPacketHeader (command, request_packet, command_length);
+    DataExtractor reply_packet;
+    if (SendRequestAndGetReply (command, request_sequence_id, request_packet, reply_packet))
+    {
+        // Are we supposed to get a reply for disconnect?
+    }
+    ClearKDPSettings ();
+    return true;
 }