[Lldb-commits] [lldb] r240066 - Add a new wart, I mean feature, on to gdb-remote protocol: compression.

Jason Molenda jmolenda at apple.com
Thu Jun 18 14:46:06 PDT 2015


Author: jmolenda
Date: Thu Jun 18 16:46:06 2015
New Revision: 240066

URL: http://llvm.org/viewvc/llvm-project?rev=240066&view=rev
Log:
Add a new wart, I mean feature, on to gdb-remote protocol: compression.
For some communication channels, sending large packets can be very 
slow.  In those cases, it may be faster to compress the contents of
the packet on the target device and decompress it on the debug host
system.  For instance, communicating with a device using something
like Bluetooth may be an environment where this tradeoff is a good one.

This patch adds a new field to the response to the "qSupported" packet
(which returns a "qXfer:features:" response) -- SupportedCompressions
and DefaultCompressionMinSize.  These tell you what the remote
stub can support.

lldb, if it wants to enable compression and can handle one of those 
algorithms, it can send a QEnableCompression packet specifying the
algorithm and optionally the minimum packet size to use compression
on.  lldb may have better knowledge about the best tradeoff for
a given communication channel.

I added support to debugserver an lldb to use the zlib APIs
(if -DHAVE_LIBZ=1 is in CFLAGS and -lz is in LDFLAGS) and the
libcompression APIs on Mac OS X 10.11 and later 
(if -DHAVE_LIBCOMPRESSION=1).  libz "zlib-deflate" compression.
libcompression can support deflate, lz4, lzma, and a proprietary
lzfse algorithm.  libcompression has been hand-tuned for Apple
hardware so it should be preferred if available.

debugserver currently only adds the SupportedCompressions when
it is being run on an Apple watch (TARGET_OS_WATCH).  Comment
that #if out from RNBRemote.cpp if you want to enable it to
see how it works.  I haven't tested this on a native system
configuration but surely it will be slower to compress & decompress
the packets in a same-system debug session.

I haven't had a chance to add support for this to 
GDBRemoteCommunciationServer.cpp yet.

<rdar://problem/21090180> 

Modified:
    lldb/trunk/docs/lldb-gdb-remote.txt
    lldb/trunk/lldb.xcodeproj/project.pbxproj
    lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
    lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
    lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
    lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
    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/docs/lldb-gdb-remote.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/docs/lldb-gdb-remote.txt?rev=240066&r1=240065&r2=240066&view=diff
==============================================================================
--- lldb/trunk/docs/lldb-gdb-remote.txt (original)
+++ lldb/trunk/docs/lldb-gdb-remote.txt Thu Jun 18 16:46:06 2015
@@ -1384,3 +1384,66 @@ for this region.
 //
 // on the wire.
 //----------------------------------------------------------------------
+
+//----------------------------------------------------------------------
+// "QEnableCompression"
+//
+// BRIEF
+//  This packet enables compression of the packets that the debug stub sends to lldb.
+//  If the debug stub can support compression, it indictes this in the reply of the 
+//  "qSupported" packet.  e.g.
+//   LLDB SENDS:    qSupported:xmlRegisters=i386,arm,mips
+//   STUB REPLIES:  qXfer:features:read+;SupportedCompressions=lzfse,zlib-deflate,lz4,lzma;DefaultCompressionMinSize=384
+//
+//  If lldb knows how to use any of these compression algorithms, it can ask that this
+//  compression mode be enabled.  It may optionally change the minimum packet size 
+//  where compression is used.  Typically small packets do not benefit from compression,
+//  as well as compression headers -- compression is most beneficial with larger packets.
+//
+//  QEnableCompression:type:zlib-deflate;
+//  or
+//  QEnableCompression:type:zlib-deflate;minsize:512;
+//
+//  The debug stub should reply with an uncompressed "OK" packet to indicate that the
+//  request was accepted.  All further packets the stub sends will use this compression.
+//
+//  Packets are compressed as the last step before they are sent from the stub, and 
+//  decompressed as the first step after they are received.  The packet format in compressed
+//  mode becomes one of two:
+//
+//   $N<uncompressed payload>#00
+//
+//   $C<size of uncompressed payload in base10>:<compressed payload>#00
+//
+//  Where "#00" is the actual checksum value if noack mode is not enabled.  The checksum
+//  value is for the "N<uncompressed payload>" or 
+//  "C<size of uncompressed payload in base10>:<compressed payload>" bytes in the packet.
+//
+//  The size of the uncompressed payload in base10 is provided because it will simplify
+//  decompression if the final buffer size needed is known ahead of time.
+//
+//  Compression on low-latency connections is unlikely to be an improvement.  Particularly
+//  when the debug stub and lldb are running on the same host.  It should only be used
+//  for slow connections, and likely only for larger packets.
+//
+//  Example compression algorithsm that may be used include
+//
+//    zlib-deflate
+//       The raw DEFLATE format as described in IETF RFC 1951.  With the ZLIB library, you
+//       can compress to this format with an initialization like 
+//           deflateInit2 (&stream, 5, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY)
+//       and you can decompress with an initialization like
+//           inflateInit2 (&stream, -15)
+//
+//    lz4
+//       https://en.wikipedia.org/wiki/LZ4_(compression_algorithm)
+//       https://github.com/Cyan4973/lz4
+//       The libcompression APIs on darwin systems call this COMPRESSION_LZ4_RAW.
+//
+//    lzfse
+//       An Apple proprietary compression algorithm implemented in libcompression.
+//
+//    lzma
+//       libcompression implements "LZMA level 6", the default compression for the
+//       open source LZMA implementation.
+//----------------------------------------------------------------------

Modified: lldb/trunk/lldb.xcodeproj/project.pbxproj
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lldb.xcodeproj/project.pbxproj?rev=240066&r1=240065&r2=240066&view=diff
==============================================================================
--- lldb/trunk/lldb.xcodeproj/project.pbxproj (original)
+++ lldb/trunk/lldb.xcodeproj/project.pbxproj Thu Jun 18 16:46:06 2015
@@ -6900,10 +6900,16 @@
 				GCC_WARN_UNUSED_LABEL = YES;
 				GCC_WARN_UNUSED_VALUE = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
+				LLDB_COMPRESSION_CFLAGS = "";
+				"LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+				LLDB_COMPRESSION_LDFLAGS = "";
+				"LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
 				LLDB_DISABLE_PYTHON = 0;
 				"LLDB_DISABLE_PYTHON[sdk=iphoneos*]" = 1;
 				LLDB_FRAMEWORK_INSTALL_DIR = /Applications/Xcode.app/Contents/SharedFrameworks;
 				LLDB_TOOLS_INSTALL_DIR = /usr/bin;
+				LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+				LLDB_ZLIB_LDFLAGS = "-lz";
 				LLVM_BUILD_DIR = "$(SRCROOT)/llvm-build/$(LLVM_CONFIGURATION)";
 				LLVM_BUILD_DIR_ARCH = "$(CURRENT_ARCH)/";
 				LLVM_CONFIGURATION = "Release+Asserts";
@@ -6913,6 +6919,12 @@
 				OTHER_CFLAGS = (
 					"-flimit-debug-info",
 					"-Wparentheses",
+					"$(LLDB_ZLIB_CFLAGS)",
+					"$(LLDB_COMPRESSION_CFLAGS)",
+				);
+				OTHER_LDFLAGS = (
+					"$(LLDB_COMPRESSION_LDFLAGS)",
+					"$(LLDB_ZLIB_LDFLAGS)",
 				);
 				SDKROOT = "";
 				STRIP_INSTALLED_PRODUCT = NO;
@@ -6969,10 +6981,16 @@
 				GCC_WARN_UNUSED_LABEL = YES;
 				GCC_WARN_UNUSED_VALUE = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
+				LLDB_COMPRESSION_CFLAGS = "";
+				"LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+				LLDB_COMPRESSION_LDFLAGS = "";
+				"LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
 				LLDB_DISABLE_PYTHON = 0;
 				"LLDB_DISABLE_PYTHON[sdk=iphoneos*]" = 1;
 				LLDB_FRAMEWORK_INSTALL_DIR = /Applications/Xcode.app/Contents/SharedFrameworks;
 				LLDB_TOOLS_INSTALL_DIR = /usr/bin;
+				LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+				LLDB_ZLIB_LDFLAGS = "-lz";
 				LLVM_BUILD_DIR = "$(SRCROOT)/llvm-build/$(LLVM_CONFIGURATION)";
 				LLVM_BUILD_DIR_ARCH = "$(CURRENT_ARCH)/";
 				LLVM_CONFIGURATION = "Release+Asserts";
@@ -6982,6 +7000,12 @@
 				OTHER_CFLAGS = (
 					"-flimit-debug-info",
 					"-Wparentheses",
+					"$(LLDB_ZLIB_CFLAGS)",
+					"$(LLDB_COMPRESSION_CFLAGS)",
+				);
+				OTHER_LDFLAGS = (
+					"$(LLDB_COMPRESSION_LDFLAGS)",
+					"$(LLDB_ZLIB_LDFLAGS)",
 				);
 				SDKROOT = "";
 				STRIP_INSTALLED_PRODUCT = NO;
@@ -7212,6 +7236,12 @@
 					"$(LLVM_BUILD_DIR)/$(LLVM_BUILD_DIR_ARCH)",
 					"$(inherited)",
 				);
+				LLDB_COMPRESSION_CFLAGS = "";
+				"LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+				LLDB_COMPRESSION_LDFLAGS = "";
+				"LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
+				LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+				LLDB_ZLIB_LDFLAGS = "-lz";
 				OTHER_CPLUSPLUSFLAGS = (
 					"-I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7",
 					"-fno-rtti",
@@ -7278,6 +7308,12 @@
 					"$(LLVM_BUILD_DIR)/$(LLVM_BUILD_DIR_ARCH)",
 					"$(inherited)",
 				);
+				LLDB_COMPRESSION_CFLAGS = "";
+				"LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+				LLDB_COMPRESSION_LDFLAGS = "";
+				"LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
+				LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+				LLDB_ZLIB_LDFLAGS = "-lz";
 				OTHER_CPLUSPLUSFLAGS = (
 					"-I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7",
 					"-fno-rtti",
@@ -7386,6 +7422,12 @@
 				);
 				GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
 				HEADER_SEARCH_PATHS = /usr/include/libxml2;
+				LLDB_COMPRESSION_CFLAGS = "";
+				"LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+				LLDB_COMPRESSION_LDFLAGS = "";
+				"LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
+				LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+				LLDB_ZLIB_LDFLAGS = "-lz";
 				MACH_O_TYPE = staticlib;
 				OTHER_CPLUSPLUSFLAGS = (
 					"-I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7",
@@ -7425,6 +7467,12 @@
 				);
 				GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
 				HEADER_SEARCH_PATHS = /usr/include/libxml2;
+				LLDB_COMPRESSION_CFLAGS = "";
+				"LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+				LLDB_COMPRESSION_LDFLAGS = "";
+				"LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
+				LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+				LLDB_ZLIB_LDFLAGS = "-lz";
 				MACH_O_TYPE = staticlib;
 				OTHER_CPLUSPLUSFLAGS = (
 					"-I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7",
@@ -7464,6 +7512,12 @@
 				);
 				GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
 				HEADER_SEARCH_PATHS = /usr/include/libxml2;
+				LLDB_COMPRESSION_CFLAGS = "";
+				"LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+				LLDB_COMPRESSION_LDFLAGS = "";
+				"LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
+				LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+				LLDB_ZLIB_LDFLAGS = "-lz";
 				MACH_O_TYPE = staticlib;
 				OTHER_CPLUSPLUSFLAGS = (
 					"-I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7",
@@ -7541,12 +7595,18 @@
 				GCC_WARN_UNUSED_LABEL = YES;
 				GCC_WARN_UNUSED_VALUE = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
+				LLDB_COMPRESSION_CFLAGS = "";
+				"LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+				LLDB_COMPRESSION_LDFLAGS = "";
+				"LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
 				LLDB_DISABLE_PYTHON = 0;
 				"LLDB_DISABLE_PYTHON[sdk=iphoneos*]" = 1;
 				LLDB_FRAMEWORK_INSTALL_DIR = /Applications/Xcode.app/Contents/SharedFrameworks;
 				"LLDB_FRAMEWORK_INSTALL_DIR[sdk=iphoneos*]" = /System/Library/PrivateFrameworks;
 				LLDB_TOOLS_INSTALL_DIR = /Applications/Xcode.app/Contents/Developer/usr/bin;
 				"LLDB_TOOLS_INSTALL_DIR[sdk=iphoneos*]" = /usr/local/bin;
+				LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+				LLDB_ZLIB_LDFLAGS = "-lz";
 				LLVM_BUILD_DIR = "$(OBJROOT)/llvm";
 				LLVM_BUILD_DIR_ARCH = "$(CURRENT_ARCH)/";
 				LLVM_CONFIGURATION = Release;
@@ -7555,6 +7615,12 @@
 				OTHER_CFLAGS = (
 					"-flimit-debug-info",
 					"-Wparentheses",
+					"$(LLDB_ZLIB_CFLAGS)",
+					"$(LLDB_COMPRESSION_CFLAGS)",
+				);
+				OTHER_LDFLAGS = (
+					"$(LLDB_COMPRESSION_LDFLAGS)",
+					"$(LLDB_ZLIB_LDFLAGS)",
 				);
 				SDKROOT = "";
 				STRIP_INSTALLED_PRODUCT = NO;
@@ -7632,6 +7698,12 @@
 					"$(LLVM_BUILD_DIR)/$(LLVM_BUILD_DIR_ARCH)",
 					"$(inherited)",
 				);
+				LLDB_COMPRESSION_CFLAGS = "";
+				"LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+				LLDB_COMPRESSION_LDFLAGS = "";
+				"LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
+				LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+				LLDB_ZLIB_LDFLAGS = "-lz";
 				OTHER_CPLUSPLUSFLAGS = (
 					"-I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7",
 					"-fno-rtti",
@@ -8221,10 +8293,16 @@
 				GCC_WARN_UNUSED_LABEL = YES;
 				GCC_WARN_UNUSED_VALUE = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
+				LLDB_COMPRESSION_CFLAGS = "";
+				"LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11internal]" = "-DHAVE_LIBCOMPRESSION=1";
+				LLDB_COMPRESSION_LDFLAGS = "";
+				"LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
 				LLDB_DISABLE_PYTHON = 0;
 				"LLDB_DISABLE_PYTHON[sdk=iphoneos*]" = 1;
 				LLDB_FRAMEWORK_INSTALL_DIR = /Applications/Xcode.app/Contents/SharedFrameworks;
 				LLDB_TOOLS_INSTALL_DIR = /usr/bin;
+				LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+				LLDB_ZLIB_LDFLAGS = "-lz";
 				LLVM_BUILD_DIR = "$(SRCROOT)/llvm-build/$(LLVM_CONFIGURATION)";
 				LLVM_BUILD_DIR_ARCH = "$(CURRENT_ARCH)/";
 				LLVM_CONFIGURATION = "Debug+Asserts";
@@ -8234,6 +8312,12 @@
 				OTHER_CFLAGS = (
 					"-flimit-debug-info",
 					"-Wparentheses",
+					"$(LLDB_ZLIB_CFLAGS)",
+					"$(LLDB_COMPRESSION_CFLAGS)",
+				);
+				OTHER_LDFLAGS = (
+					"$(LLDB_COMPRESSION_LDFLAGS)",
+					"$(LLDB_ZLIB_LDFLAGS)",
 				);
 				SDKROOT = "";
 				STRIP_INSTALLED_PRODUCT = NO;
@@ -8325,6 +8409,12 @@
 					"$(LLVM_BUILD_DIR)/$(LLVM_BUILD_DIR_ARCH)",
 					"$(inherited)",
 				);
+				LLDB_COMPRESSION_CFLAGS = "";
+				"LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+				LLDB_COMPRESSION_LDFLAGS = "";
+				"LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
+				LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+				LLDB_ZLIB_LDFLAGS = "-lz";
 				OTHER_CPLUSPLUSFLAGS = (
 					"-I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7",
 					"-fno-rtti",
@@ -8390,6 +8480,12 @@
 				);
 				GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
 				HEADER_SEARCH_PATHS = /usr/include/libxml2;
+				LLDB_COMPRESSION_CFLAGS = "";
+				"LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+				LLDB_COMPRESSION_LDFLAGS = "";
+				"LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
+				LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+				LLDB_ZLIB_LDFLAGS = "-lz";
 				MACH_O_TYPE = staticlib;
 				OTHER_CPLUSPLUSFLAGS = (
 					"-I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7",

Modified: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp?rev=240066&r1=240065&r2=240066&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp (original)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp Thu Jun 18 16:46:06 2015
@@ -42,6 +42,14 @@
 # define DEBUGSERVER_BASENAME    "lldb-server"
 #endif
 
+#if defined (HAVE_LIBCOMPRESSION)
+#include <compression.h>
+#endif
+
+#if defined (HAVE_LIBZ)
+#include <zlib.h>
+#endif
+
 using namespace lldb;
 using namespace lldb_private;
 using namespace lldb_private::process_gdb_remote;
@@ -158,6 +166,7 @@ GDBRemoteCommunication::GDBRemoteCommuni
     m_private_is_running (false),
     m_history (512),
     m_send_acks (true),
+    m_compression_type (CompressionType::None),
     m_listen_url ()
 {
 }
@@ -546,6 +555,226 @@ GDBRemoteCommunication::WaitForPacketWit
         return PacketResult::ErrorReplyFailed;
 }
 
+bool
+GDBRemoteCommunication::DecompressPacket ()
+{
+    Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet (GDBR_LOG_PACKETS));
+
+    if (!CompressionIsEnabled())
+        return true;
+
+    size_t pkt_size = m_bytes.size();
+    if (pkt_size < 6)
+        return true;
+    if (m_bytes[0] != '$' && m_bytes[0] != '%')
+        return true;
+    if (m_bytes[1] != 'C' && m_bytes[1] != 'N')
+        return true;
+    if (m_bytes[pkt_size - 3] != '#')
+        return true;
+    if (!::isxdigit (m_bytes[pkt_size - 2]) || !::isxdigit (m_bytes[pkt_size - 1]))
+        return true;
+
+    size_t content_length = pkt_size - 5;   // not counting '$', 'C' | 'N', '#', & the two hex checksum chars
+    size_t content_start = 2;               // The first character of the compressed/not-compressed text of the packet
+    size_t hash_mark_idx = pkt_size - 3;    // The '#' character marking the end of the packet
+    size_t checksum_idx = pkt_size - 2;     // The first character of the two hex checksum characters
+
+    // Compressed packets ("$C") start with a base10 number which is the size of the uncompressed payload,
+    // then a : and then the compressed data.  e.g. $C1024:<binary>#00
+    // Update content_start and content_length to only include the <binary> part of the packet.
+
+    uint64_t decompressed_bufsize = ULONG_MAX;
+    if (m_bytes[1] == 'C')
+    {
+        size_t i = content_start;
+        while (i < hash_mark_idx && isdigit(m_bytes[i]))
+            i++;
+        if (i < hash_mark_idx && m_bytes[i] == ':')
+        {
+            i++;
+            content_start = i;
+            content_length = hash_mark_idx - content_start;
+            std::string bufsize_str (m_bytes.data() + 2, i - 2 - 1);
+            errno = 0;
+            decompressed_bufsize = ::strtoul (bufsize_str.c_str(), NULL, 10);
+            if (errno != 0 || decompressed_bufsize == ULONG_MAX)
+            {
+                m_bytes.erase (0, pkt_size);
+                return false;
+            }
+        }
+    }
+
+    if (GetSendAcks ())
+    {
+        char packet_checksum_cstr[3];
+        packet_checksum_cstr[0] = m_bytes[checksum_idx];
+        packet_checksum_cstr[1] = m_bytes[checksum_idx + 1];
+        packet_checksum_cstr[2] = '\0';
+        long packet_checksum = strtol (packet_checksum_cstr, NULL, 16);
+
+        long actual_checksum = CalculcateChecksum (m_bytes.data() + 1, hash_mark_idx - 1);
+        bool success = packet_checksum == actual_checksum;
+        if (!success)
+        {
+            if (log)
+                log->Printf ("error: checksum mismatch: %.*s expected 0x%2.2x, got 0x%2.2x", 
+                             (int)(pkt_size), 
+                             m_bytes.c_str(),
+                             (uint8_t)packet_checksum,
+                             (uint8_t)actual_checksum);
+        }
+        // Send the ack or nack if needed
+        if (!success)
+        {
+            SendNack();
+            m_bytes.erase (0, pkt_size);
+            return false;
+        }
+        else
+        {
+            SendAck();
+        }
+    }
+
+    if (m_bytes[1] == 'N')
+    {
+        // This packet was not compressed -- delete the 'N' character at the 
+        // start and the packet may be processed as-is.
+        m_bytes.erase(1, 1);
+        return true;
+    }
+
+    // Reverse the gdb-remote binary escaping that was done to the compressed text to
+    // guard characters like '$', '#', '}', etc.
+    std::vector<uint8_t> unescaped_content;
+    unescaped_content.reserve (content_length);
+    size_t i = content_start;
+    while (i < hash_mark_idx)
+    {
+        if (m_bytes[i] == '}')
+        {
+            i++;
+            unescaped_content.push_back (m_bytes[i] ^ 0x20);
+        }
+        else
+        {
+            unescaped_content.push_back (m_bytes[i]);
+        }
+        i++;
+    }
+
+    uint8_t *decompressed_buffer = nullptr;
+    size_t decompressed_bytes = 0;
+
+    if (decompressed_bufsize != ULONG_MAX)
+    {
+        decompressed_buffer = (uint8_t *) malloc (decompressed_bufsize + 1);
+        if (decompressed_buffer == nullptr)
+        {
+            m_bytes.erase (0, pkt_size);
+            return false;
+        }
+
+    }
+
+#if defined (HAVE_LIBCOMPRESSION)
+    // libcompression is weak linked so check that compression_decode_buffer() is available
+    if (compression_decode_buffer != NULL &&
+        (m_compression_type == CompressionType::ZlibDeflate 
+         || m_compression_type == CompressionType::LZFSE
+         || m_compression_type == CompressionType::LZ4))
+    {
+        compression_algorithm compression_type;
+        if (m_compression_type == CompressionType::ZlibDeflate)
+            compression_type = COMPRESSION_ZLIB;
+        else if (m_compression_type == CompressionType::LZFSE)
+            compression_type = COMPRESSION_LZFSE;
+        else if (m_compression_type == CompressionType::LZ4)
+            compression_type = COMPRESSION_LZ4_RAW;
+        else if (m_compression_type == CompressionType::LZMA)
+            compression_type = COMPRESSION_LZMA;
+
+
+        // If we have the expected size of the decompressed payload, we can allocate
+        // the right-sized buffer and do it.  If we don't have that information, we'll
+        // need to try decoding into a big buffer and if the buffer wasn't big enough,
+        // increase it and try again.
+
+        if (decompressed_bufsize != ULONG_MAX && decompressed_buffer != nullptr)
+        {
+            decompressed_bytes = compression_decode_buffer (decompressed_buffer, decompressed_bufsize + 10 ,
+                                                        (uint8_t*) unescaped_content.data(),
+                                                        unescaped_content.size(),
+                                                        NULL,
+                                                        compression_type);
+        }
+    }
+#endif
+
+#if defined (HAVE_LIBZ)
+    if (decompressed_bytes == 0 
+        && decompressed_bufsize != ULONG_MAX
+        && decompressed_buffer != nullptr 
+        && m_compression_type == CompressionType::ZlibDeflate)
+    {
+        z_stream stream;
+        memset (&stream, 0, sizeof (z_stream));
+        stream.next_in = (Bytef *) unescaped_content.data();
+        stream.avail_in = (uInt) unescaped_content.size();
+        stream.total_in = 0;
+        stream.next_out = (Bytef *) decompressed_buffer;
+        stream.avail_out = decompressed_bufsize;
+        stream.total_out = 0;
+        stream.zalloc = Z_NULL;
+        stream.zfree = Z_NULL;
+        stream.opaque = Z_NULL;
+
+        if (inflateInit2 (&stream, -15) == Z_OK)
+        {
+            int status = inflate (&stream, Z_NO_FLUSH);
+            inflateEnd (&stream);
+            if (status == Z_STREAM_END)
+            {
+                decompressed_bytes = stream.total_out;
+            }
+        }
+    }
+#endif
+
+    if (decompressed_bytes == 0 || decompressed_buffer == nullptr)
+    {
+        if (decompressed_buffer)
+            free (decompressed_buffer);
+        m_bytes.erase (0, pkt_size);
+        return false;
+    }
+
+    std::string new_packet;
+    new_packet.reserve (decompressed_bytes + 6);
+    new_packet.push_back (m_bytes[0]);
+    new_packet.append ((const char *) decompressed_buffer, decompressed_bytes);
+    new_packet.push_back ('#');
+    if (GetSendAcks ())
+    {
+        uint8_t decompressed_checksum = CalculcateChecksum ((const char *) decompressed_buffer, decompressed_bytes);
+        char decompressed_checksum_str[3];
+        snprintf (decompressed_checksum_str, 3, "%02x", decompressed_checksum);
+        new_packet.append (decompressed_checksum_str);
+    }
+    else
+    {
+        new_packet.push_back ('0');
+        new_packet.push_back ('0');
+    }
+
+    m_bytes = new_packet;
+
+    free (decompressed_buffer);
+    return true;
+}
+
 GDBRemoteCommunication::PacketType
 GDBRemoteCommunication::CheckForPacket (const uint8_t *src, size_t src_len, StringExtractorGDBRemote &packet)
 {
@@ -581,6 +810,17 @@ GDBRemoteCommunication::CheckForPacket (
         size_t total_length = 0;
         size_t checksum_idx = std::string::npos;
 
+        // Size of packet before it is decompressed, for logging purposes
+        size_t original_packet_size = m_bytes.size();
+        if (CompressionIsEnabled())
+        {
+            if (DecompressPacket() == false)
+            {
+                packet.Clear();
+                return GDBRemoteCommunication::PacketType::Standard;
+            }
+        }
+
         switch (m_bytes[0])
         {
             case '+':       // Look for ack
@@ -664,12 +904,10 @@ GDBRemoteCommunication::CheckForPacket (
             assert (content_length <= m_bytes.size());
             assert (total_length <= m_bytes.size());
             assert (content_length <= total_length);
-            const size_t content_end = content_start + content_length;
+            size_t content_end = content_start + content_length;
 
             bool success = true;
             std::string &packet_str = packet.GetStringRef();
-            
-            
             if (log)
             {
                 // If logging was just enabled and we have history, then dump out what
@@ -693,7 +931,10 @@ GDBRemoteCommunication::CheckForPacket (
                 {
                     StreamString strm;
                     // Packet header...
-                    strm.Printf("<%4" PRIu64 "> read packet: %c", (uint64_t)total_length, m_bytes[0]);
+                    if (CompressionIsEnabled())
+                        strm.Printf("<%4" PRIu64 ":%" PRIu64 "> read packet: %c", (uint64_t) original_packet_size, (uint64_t)total_length, m_bytes[0]);
+                    else
+                        strm.Printf("<%4" PRIu64 "> read packet: %c", (uint64_t)total_length, m_bytes[0]);
                     for (size_t i=content_start; i<content_end; ++i)
                     {
                         // Remove binary escaped bytes when displaying the packet...
@@ -716,7 +957,10 @@ GDBRemoteCommunication::CheckForPacket (
                 }
                 else
                 {
-                    log->Printf("<%4" PRIu64 "> read packet: %.*s", (uint64_t)total_length, (int)(total_length), m_bytes.c_str());
+                    if (CompressionIsEnabled())
+                        log->Printf("<%4" PRIu64 ":%" PRIu64 "> read packet: %.*s", (uint64_t) original_packet_size, (uint64_t)total_length, (int)(total_length), m_bytes.c_str());
+                    else
+                        log->Printf("<%4" PRIu64 "> read packet: %.*s", (uint64_t)total_length, (int)(total_length), m_bytes.c_str());
                 }
             }
 

Modified: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h?rev=240066&r1=240065&r2=240066&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h (original)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h Thu Jun 18 16:46:06 2015
@@ -41,6 +41,15 @@ typedef enum
     eWatchpointReadWrite
 } GDBStoppointType;
 
+enum class CompressionType
+{
+    None = 0,       // no compression
+    ZlibDeflate,    // zlib's deflate compression scheme, requires zlib or Apple's libcompression
+    LZFSE,          // an Apple compression scheme, requires Apple's libcompression
+    LZ4,            // lz compression - called "lz4 raw" in libcompression terms, compat with https://code.google.com/p/lz4/
+    LZMA,           // Lempel–Ziv–Markov chain algorithm
+};
+
 class ProcessGDBRemote;
 
 class GDBRemoteCommunication : public Communication
@@ -296,6 +305,22 @@ protected:
     bool
     WaitForNotRunningPrivate (const TimeValue *timeout_ptr);
 
+    bool
+    CompressionIsEnabled ()
+    {
+        return m_compression_type != CompressionType::None;
+    }
+
+    // If compression is enabled, decompress the packet in m_bytes and update
+    // m_bytes with the uncompressed version.
+    // Returns 'true' packet was decompressed and m_bytes is the now-decompressed text.
+    // Returns 'false' if unable to decompress or if the checksum was invalid.
+    //
+    // NB: Once the packet has been decompressed, checksum cannot be computed based
+    // on m_bytes.  The checksum was for the compressed packet.
+    bool
+    DecompressPacket ();
+
     //------------------------------------------------------------------
     // Classes that inherit from GDBRemoteCommunication can see and modify these
     //------------------------------------------------------------------
@@ -315,6 +340,7 @@ protected:
                         // false if this class represents a debug session for
                         // a single process
     
+    CompressionType m_compression_type;
 
     Error
     StartListenThread (const char *hostname = "127.0.0.1", uint16_t port = 0);

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=240066&r1=240065&r2=240066&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp (original)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp Thu Jun 18 16:46:06 2015
@@ -43,6 +43,10 @@
 #include "ProcessGDBRemoteLog.h"
 #include "lldb/Host/Config.h"
 
+#if defined (HAVE_LIBCOMPRESSION)
+#include <compression.h>
+#endif
+
 using namespace lldb;
 using namespace lldb_private;
 using namespace lldb_private::process_gdb_remote;
@@ -423,6 +427,59 @@ GDBRemoteCommunicationClient::GetRemoteQ
         if (::strstr (response_cstr, "qXfer:features:read+"))
             m_supports_qXfer_features_read = eLazyBoolYes;
 
+
+        // Look for a list of compressions in the features list e.g.
+        // qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-deflate,lzma
+        const char *features_list = ::strstr (response_cstr, "qXfer:features:");
+        if (features_list)
+        {
+            const char *compressions = ::strstr (features_list, "SupportedCompressions=");
+            if (compressions)
+            {
+                std::vector<std::string> supported_compressions;
+                compressions += sizeof ("SupportedCompressions=") - 1;
+                const char *end_of_compressions = strchr (compressions, ';');
+                if (end_of_compressions == NULL)
+                {
+                    end_of_compressions = strchr (compressions, '\0');
+                }
+                const char *current_compression = compressions;
+                while (current_compression < end_of_compressions)
+                {
+                    const char *next_compression_name = strchr (current_compression, ',');
+                    const char *end_of_this_word = next_compression_name;
+                    if (next_compression_name == NULL || end_of_compressions < next_compression_name)
+                    {
+                        end_of_this_word = end_of_compressions;
+                    }
+
+                    if (end_of_this_word)
+                    {
+                        if (end_of_this_word == current_compression)
+                        {
+                            current_compression++;
+                        }
+                        else
+                        {
+                            std::string this_compression (current_compression, end_of_this_word - current_compression);
+                            supported_compressions.push_back (this_compression);
+                            current_compression = end_of_this_word + 1;
+                        }
+                    }
+                    else
+                    {
+                        supported_compressions.push_back (current_compression);
+                        current_compression = end_of_compressions;
+                    }
+                }
+
+                if (supported_compressions.size() > 0)
+                {
+                    MaybeEnableCompression (supported_compressions);
+                }
+            }
+        }
+
         if (::strstr (response_cstr, "qEcho"))
             m_supports_qEcho = eLazyBoolYes;
         else
@@ -1629,6 +1686,105 @@ GDBRemoteCommunicationClient::GetGDBServ
     return m_qGDBServerVersion_is_valid == eLazyBoolYes;
 }
 
+void
+GDBRemoteCommunicationClient::MaybeEnableCompression (std::vector<std::string> supported_compressions)
+{
+    CompressionType avail_type = CompressionType::None;
+    std::string avail_name;
+
+#if defined (HAVE_LIBCOMPRESSION)
+    // libcompression is weak linked so test if compression_decode_buffer() is available
+    if (compression_decode_buffer != NULL && avail_type == CompressionType::None)
+    {
+        for (auto compression : supported_compressions)
+        {
+            if (compression == "lzfse")
+            {
+                avail_type = CompressionType::LZFSE;
+                avail_name = compression;
+                break;
+            }
+        }
+    }
+#endif
+
+#if defined (HAVE_LIBCOMPRESSION)
+    // libcompression is weak linked so test if compression_decode_buffer() is available
+    if (compression_decode_buffer != NULL && avail_type == CompressionType::None)
+    {
+        for (auto compression : supported_compressions)
+        {
+            if (compression == "zlib-deflate")
+            {
+                avail_type = CompressionType::ZlibDeflate;
+                avail_name = compression;
+                break;
+            }
+        }
+    }
+#endif
+
+#if defined (HAVE_LIBZ)
+    if (avail_type == CompressionType::None)
+    {
+        for (auto compression : supported_compressions)
+        {
+            if (compression == "zlib-deflate")
+            {
+                avail_type = CompressionType::ZlibDeflate;
+                avail_name = compression;
+                break;
+            }
+        }
+    }
+#endif
+
+#if defined (HAVE_LIBCOMPRESSION)
+    // libcompression is weak linked so test if compression_decode_buffer() is available
+    if (compression_decode_buffer != NULL && avail_type == CompressionType::None)
+    {
+        for (auto compression : supported_compressions)
+        {
+            if (compression == "lz4")
+            {
+                avail_type = CompressionType::LZ4;
+                avail_name = compression;
+                break;
+            }
+        }
+    }
+#endif
+
+#if defined (HAVE_LIBCOMPRESSION)
+    // libcompression is weak linked so test if compression_decode_buffer() is available
+    if (compression_decode_buffer != NULL && avail_type == CompressionType::None)
+    {
+        for (auto compression : supported_compressions)
+        {
+            if (compression == "lzma")
+            {
+                avail_type = CompressionType::LZMA;
+                avail_name = compression;
+                break;
+            }
+        }
+    }
+#endif
+
+    if (avail_type != CompressionType::None)
+    {
+        StringExtractorGDBRemote response;
+        std::string packet = "QEnableCompression:type:" + avail_name + ";";
+        if (SendPacketAndWaitForResponse (packet.c_str(), response, false) !=  PacketResult::Success)
+            return;
+    
+        if (response.IsOKResponse())
+        {
+            m_compression_type = avail_type;
+        }
+    }
+}
+
 const char *
 GDBRemoteCommunicationClient::GetGDBServerProgramName()
 {

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=240066&r1=240065&r2=240066&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h (original)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h Thu Jun 18 16:46:06 2015
@@ -564,6 +564,11 @@ protected:
     bool
     GetGDBServerVersion();
 
+    // Given the list of compression types that the remote debug stub can support,
+    // possibly enable compression if we find an encoding we can handle.
+    void
+    MaybeEnableCompression (std::vector<std::string> supported_compressions);
+
     //------------------------------------------------------------------
     // Classes that inherit from GDBRemoteCommunicationClient can see and modify these
     //------------------------------------------------------------------
@@ -643,6 +648,7 @@ protected:
     uint32_t m_gdb_server_version; // from reply to qGDBServerVersion, zero if qGDBServerVersion is not supported
     uint32_t m_default_packet_timeout;
     uint64_t m_max_packet_size;  // as returned by qSupported
+
     
     bool
     DecodeProcessInfoResponse (StringExtractorGDBRemote &response, 

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=240066&r1=240065&r2=240066&view=diff
==============================================================================
--- lldb/trunk/tools/debugserver/debugserver.xcodeproj/project.pbxproj (original)
+++ lldb/trunk/tools/debugserver/debugserver.xcodeproj/project.pbxproj Thu Jun 18 16:46:06 2015
@@ -524,7 +524,14 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
+				LLDB_COMPRESSION_CFLAGS = "";
+				"LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+				LLDB_COMPRESSION_LDFLAGS = "";
+				"LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
+				LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+				LLDB_ZLIB_LDFLAGS = "-lz";
 				ONLY_ACTIVE_ARCH = YES;
+				OTHER_CFLAGS = "";
 				STRIP_INSTALLED_PRODUCT = NO;
 				VERSIONING_SYSTEM = "apple-generic";
 				VERSION_INFO_BUILDER = "$(USER)";
@@ -557,7 +564,14 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
+				LLDB_COMPRESSION_CFLAGS = "";
+				"LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+				LLDB_COMPRESSION_LDFLAGS = "";
+				"LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
+				LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+				LLDB_ZLIB_LDFLAGS = "-lz";
 				ONLY_ACTIVE_ARCH = YES;
+				OTHER_CFLAGS = "";
 				STRIPFLAGS = "-x";
 				STRIP_STYLE = debugging;
 				VERSIONING_SYSTEM = "apple-generic";
@@ -591,6 +605,13 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
+				LLDB_COMPRESSION_CFLAGS = "";
+				"LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+				LLDB_COMPRESSION_LDFLAGS = "";
+				"LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
+				LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+				LLDB_ZLIB_LDFLAGS = "-lz";
+				OTHER_CFLAGS = "";
 				STRIPFLAGS = "-x";
 				STRIP_STYLE = debugging;
 				VERSIONING_SYSTEM = "apple-generic";
@@ -627,8 +648,8 @@
 				"LLDB_ENERGY_LFLAGS[sdk=macosx10.10internal]" = "-weak-lpmenergy -weak-lpmsample";
 				MACOSX_DEPLOYMENT_TARGET = 10.9;
 				OTHER_CFLAGS = (
-					"-Wparentheses",
-					"$(LLDB_ENERGY_CFLAGS)",
+					"$(LLDB_COMPRESSION_CFLAGS)",
+					"$(LLDB_ZLIB_CFLAGS)",
 				);
 				"OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
 					"-Wparentheses",
@@ -637,6 +658,7 @@
 					"-DOS_OBJECT_USE_OBJC=0",
 				);
 				"OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+				OTHER_LDFLAGS = "";
 				"OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
 					"-framework",
 					SpringBoardServices,
@@ -645,6 +667,8 @@
 					"-framework",
 					Foundation,
 					"-llockdown",
+					"$(LLDB_COMPRESSION_LDFLAGS)",
+					"$(LLDB_ZLIB_LDFLAGS)",
 				);
 				"OTHER_LDFLAGS[sdk=macosx*]" = (
 					"-sectcreate",
@@ -652,6 +676,8 @@
 					__info_plist,
 					"$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
 					"$(LLDB_ENERGY_LFLAGS)",
+					"$(LLDB_COMPRESSION_LDFLAGS)",
+					"$(LLDB_ZLIB_LDFLAGS)",
 				);
 				OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
 				PRODUCT_NAME = debugserver;
@@ -693,8 +719,8 @@
 				"LLDB_ENERGY_LFLAGS[sdk=macosx10.10internal]" = "-weak-lpmenergy -weak-lpmsample";
 				MACOSX_DEPLOYMENT_TARGET = 10.9;
 				OTHER_CFLAGS = (
-					"-Wparentheses",
-					"$(LLDB_ENERGY_CFLAGS)",
+					"$(LLDB_COMPRESSION_CFLAGS)",
+					"$(LLDB_ZLIB_CFLAGS)",
 				);
 				"OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
 					"-Wparentheses",
@@ -712,6 +738,8 @@
 					"-framework",
 					Foundation,
 					"-llockdown",
+					"$(LLDB_COMPRESSION_LDFLAGS)",
+					"$(LLDB_ZLIB_LDFLAGS)",
 				);
 				"OTHER_LDFLAGS[sdk=macosx*]" = (
 					"-sectcreate",
@@ -719,6 +747,8 @@
 					__info_plist,
 					"$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
 					"$(LLDB_ENERGY_LFLAGS)",
+					"$(LLDB_ZLIB_LDFLAGS)",
+					"$(LLDB_COMPRESSION_LDFLAGS)",
 				);
 				OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
 				PRODUCT_NAME = debugserver;
@@ -760,8 +790,8 @@
 				"LLDB_ENERGY_LFLAGS[sdk=macosx10.10internal]" = "-weak-lpmenergy -weak-lpmsample";
 				MACOSX_DEPLOYMENT_TARGET = 10.9;
 				OTHER_CFLAGS = (
-					"-Wparentheses",
-					"$(LLDB_ENERGY_CFLAGS)",
+					"$(LLDB_COMPRESSION_CFLAGS)",
+					"$(LLDB_ZLIB_CFLAGS)",
 				);
 				"OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
 					"-Wparentheses",
@@ -770,6 +800,7 @@
 					"-DOS_OBJECT_USE_OBJC=0",
 				);
 				"OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+				OTHER_LDFLAGS = "";
 				"OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
 					"-framework",
 					SpringBoardServices,
@@ -778,6 +809,8 @@
 					"-llockdown",
 					"-framework",
 					Foundation,
+					"$(LLDB_COMPRESSION_LDFLAGS)",
+					"$(LLDB_ZLIB_LDFLAGS)",
 				);
 				"OTHER_LDFLAGS[sdk=macosx*]" = (
 					"-sectcreate",
@@ -785,6 +818,8 @@
 					__info_plist,
 					"$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
 					"$(LLDB_ENERGY_LFLAGS)",
+					"$(LLDB_COMPRESSION_LDFLAGS)",
+					"$(LLDB_ZLIB_LDFLAGS)",
 				);
 				OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
 				PRODUCT_NAME = debugserver;
@@ -826,7 +861,14 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
+				LLDB_COMPRESSION_CFLAGS = "";
+				"LLDB_COMPRESSION_CFLAGS[sdk=macosx10.11]" = "-DHAVE_LIBCOMPRESSION=1";
+				LLDB_COMPRESSION_LDFLAGS = "";
+				"LLDB_COMPRESSION_LDFLAGS[sdk=macosx10.11]" = "-weak-lcompression";
+				LLDB_ZLIB_CFLAGS = "-DHAVE_LIBZ=1";
+				LLDB_ZLIB_LDFLAGS = "-lz";
 				ONLY_ACTIVE_ARCH = YES;
+				OTHER_CFLAGS = "";
 				STRIP_INSTALLED_PRODUCT = NO;
 				VERSIONING_SYSTEM = "apple-generic";
 				VERSION_INFO_BUILDER = "$(USER)";
@@ -863,8 +905,8 @@
 				"LLDB_ENERGY_LFLAGS[sdk=macosx10.10internal]" = "-weak-lpmenergy -weak-lpmsample";
 				MACOSX_DEPLOYMENT_TARGET = 10.9;
 				OTHER_CFLAGS = (
-					"-Wparentheses",
-					"$(LLDB_ENERGY_CFLAGS)",
+					"$(LLDB_COMPRESSION_CFLAGS)",
+					"$(LLDB_ZLIB_CFLAGS)",
 				);
 				"OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = (
 					"-Wparentheses",
@@ -873,6 +915,7 @@
 					"-DOS_OBJECT_USE_OBJC=0",
 				);
 				"OTHER_CPLUSPLUSFLAGS[sdk=iphoneos*][arch=*]" = "$(OTHER_CFLAGS)";
+				OTHER_LDFLAGS = "";
 				"OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = (
 					"-framework",
 					SpringBoardServices,
@@ -881,6 +924,8 @@
 					"-llockdown",
 					"-framework",
 					Foundation,
+					"$(LLDB_COMPRESSION_LDFLAGS)",
+					"$(LLDB_ZLIB_LDFLAGS)",
 				);
 				"OTHER_LDFLAGS[sdk=macosx*]" = (
 					"-sectcreate",
@@ -888,6 +933,8 @@
 					__info_plist,
 					"$(PROJECT_DIR)/resources/lldb-debugserver-Info.plist",
 					"$(LLDB_ENERGY_LFLAGS)",
+					"$(LLDB_COMPRESSION_LDFLAGS)",
+					"$(LLDB_ZLIB_LDFLAGS)",
 				);
 				OTHER_MIGFLAGS = "-I$(DERIVED_FILE_DIR)";
 				PRODUCT_NAME = debugserver;

Modified: lldb/trunk/tools/debugserver/source/RNBRemote.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/debugserver/source/RNBRemote.cpp?rev=240066&r1=240065&r2=240066&view=diff
==============================================================================
--- lldb/trunk/tools/debugserver/source/RNBRemote.cpp (original)
+++ lldb/trunk/tools/debugserver/source/RNBRemote.cpp Thu Jun 18 16:46:06 2015
@@ -35,6 +35,14 @@
 #include "Utility/StringExtractor.h"
 #include "MacOSX/Genealogy.h"
 
+#if defined (HAVE_LIBCOMPRESSION)
+#include <compression.h>
+#endif
+
+#if defined (HAVE_LIBZ)
+#include <zlib.h>
+#endif
+
 #include <iomanip>
 #include <sstream>
 #include <unordered_set>
@@ -83,7 +91,10 @@ RNBRemote::RNBRemote () :
     m_extended_mode(false),
     m_noack_mode(false),
     m_thread_suffix_supported (false),
-    m_list_threads_in_stop_reply (false)
+    m_list_threads_in_stop_reply (false),
+    m_compression_minsize (384),
+    m_enable_compression_next_send_packet (false),
+    m_compression_mode (compression_types::none)
 {
     DNBLogThreadedIf (LOG_RNB_REMOTE, "%s", __PRETTY_FUNCTION__);
     CreatePacketTable ();
@@ -207,6 +218,7 @@ RNBRemote::CreatePacketTable  ()
     t.push_back (Packet (memory_region_info,            &RNBRemote::HandlePacket_MemoryRegionInfo, NULL, "qMemoryRegionInfo", "Return size and attributes of a memory region that contains the given address"));
     t.push_back (Packet (get_profile_data,              &RNBRemote::HandlePacket_GetProfileData, NULL, "qGetProfileData", "Return profiling data of the current target."));
     t.push_back (Packet (set_enable_profiling,          &RNBRemote::HandlePacket_SetEnableAsyncProfiling, NULL, "QSetEnableAsyncProfiling", "Enable or disable the profiling of current target."));
+    t.push_back (Packet (enable_compression,            &RNBRemote::HandlePacket_QEnableCompression, NULL, "QEnableCompression:", "Enable compression for the remainder of the connection"));
     t.push_back (Packet (watchpoint_support_info,       &RNBRemote::HandlePacket_WatchpointSupportInfo, NULL, "qWatchpointSupportInfo", "Return the number of supported hardware watchpoints"));
     t.push_back (Packet (set_process_event,             &RNBRemote::HandlePacket_QSetProcessEvent, NULL, "QSetProcessEvent:", "Set a process event, to be passed to the process, can be set before the process is started, or after."));
     t.push_back (Packet (set_detach_on_error,           &RNBRemote::HandlePacket_QSetDetachOnError, NULL, "QSetDetachOnError:", "Set whether debugserver will detach (1) or kill (0) from the process it is controlling if it loses connection to lldb."));
@@ -310,11 +322,146 @@ RNBRemote::SendAsyncProfileDataPacket (c
     return SendPacket(packet);
 }
 
+// Given a std::string packet contents to send, possibly encode/compress it.
+// If compression is enabled, the returned std::string will be in one of two
+// forms:
+// 
+//    N<original packet contents uncompressed>
+//    C<size of original decompressed packet>:<packet compressed with the requested compression scheme>
+//
+// If compression is not requested, the original packet contents are returned
+
+std::string 
+RNBRemote::CompressString (const std::string &orig)
+{
+    std::string compressed;
+    compression_types compression_type = GetCompressionType();
+    if (compression_type != compression_types::none)
+    {
+        bool compress_this_packet = false;
+
+        if (orig.size() > m_compression_minsize)
+        {
+            compress_this_packet = true;
+        }
+
+        if (compress_this_packet)
+        {
+            const size_t encoded_data_buf_size = orig.size() + 128;
+            std::vector<uint8_t> encoded_data (encoded_data_buf_size);
+            size_t compressed_size = 0;
+
+#if defined (HAVE_LIBCOMPRESSION)
+            if (compression_decode_buffer && compression_type == compression_types::lz4)
+            {
+                compressed_size = compression_encode_buffer (encoded_data.data(), 
+                                                             encoded_data_buf_size,
+                                                             (uint8_t*) orig.c_str(),
+                                                             orig.size(),
+                                                             nullptr,
+                                                             COMPRESSION_LZ4_RAW);
+            }
+            if (compression_decode_buffer && compression_type == compression_types::zlib_deflate)
+            {
+                compressed_size = compression_encode_buffer (encoded_data.data(), 
+                                                             encoded_data_buf_size,
+                                                             (uint8_t*) orig.c_str(),
+                                                             orig.size(),
+                                                             nullptr,
+                                                             COMPRESSION_ZLIB);
+            }
+            if (compression_decode_buffer && compression_type == compression_types::lzma)
+            {
+                compressed_size = compression_encode_buffer (encoded_data.data(), 
+                                                             encoded_data_buf_size,
+                                                             (uint8_t*) orig.c_str(),
+                                                             orig.size(),
+                                                             nullptr,
+                                                             COMPRESSION_LZMA);
+            }
+            if (compression_decode_buffer && compression_type == compression_types::lzfse)
+            {
+                compressed_size = compression_encode_buffer (encoded_data.data(), 
+                                                             encoded_data_buf_size,
+                                                             (uint8_t*) orig.c_str(),
+                                                             orig.size(),
+                                                             nullptr,
+                                                             COMPRESSION_LZFSE);
+            }
+#endif
+
+#if defined (HAVE_LIBZ)
+            if (compressed_size == 0 && compression_type == compression_types::zlib_deflate)
+            {
+                z_stream stream;
+                memset (&stream, 0, sizeof (z_stream));
+                stream.next_in = (Bytef *) orig.c_str();
+                stream.avail_in = (uInt) orig.size();
+                stream.next_out = (Bytef *) encoded_data.data();
+                stream.avail_out = (uInt) encoded_data_buf_size;
+                stream.zalloc = Z_NULL;
+                stream.zfree = Z_NULL;
+                stream.opaque = Z_NULL;
+                deflateInit2 (&stream, 5, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
+                int compress_status = deflate (&stream, Z_FINISH);
+                deflateEnd (&stream);
+                if (compress_status == Z_STREAM_END && stream.total_out > 0)
+                {
+                    compressed_size = stream.total_out;
+                }
+            }
+#endif
+
+            if (compressed_size > 0)
+            {
+                compressed.clear ();
+                compressed.reserve (compressed_size);
+                compressed = "C";
+                char numbuf[16];
+                snprintf (numbuf, sizeof (numbuf), "%zu:", orig.size());
+                numbuf[sizeof (numbuf) - 1] = '\0';
+                compressed.append (numbuf);
+
+                for (size_t i = 0; i < compressed_size; i++)
+                {
+                    uint8_t byte = encoded_data[i];
+                    if (byte == '#' || byte == '$' || byte == '}' || byte == '*' || byte == '\0')
+                    {
+                        compressed.push_back (0x7d);
+                        compressed.push_back (byte ^ 0x20);
+                    }
+                    else
+                    {
+                        compressed.push_back (byte);
+                    }
+                }
+            }
+            else
+            {
+                compressed = "N" + orig;
+            }
+        }
+        else
+        {
+            compressed = "N" + orig;
+        }
+    }
+    else
+    {
+        compressed = orig;
+    }
+
+    return compressed;
+}
+
 rnb_err_t
 RNBRemote::SendPacket (const std::string &s)
 {
     DNBLogThreadedIf (LOG_RNB_MAX, "%8d RNBRemote::%s (%s) called", (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__, s.c_str());
-    std::string sendpacket = "$" + s + "#";
+
+    std::string s_compressed = CompressString (s);
+
+    std::string sendpacket = "$" + s_compressed + "#";
     int cksum = 0;
     char hexbuf[5];
 
@@ -324,8 +471,8 @@ RNBRemote::SendPacket (const std::string
     }
     else
     {
-        for (int i = 0; i != s.size(); ++i)
-            cksum += s[i];
+        for (int i = 0; i != s_compressed.size(); ++i)
+            cksum += s_compressed[i];
         snprintf (hexbuf, sizeof hexbuf, "%02x", cksum & 0xff);
         sendpacket += hexbuf;
     }
@@ -3096,8 +3243,39 @@ rnb_err_t
 RNBRemote::HandlePacket_qSupported (const char *p)
 {
     uint32_t max_packet_size = 128 * 1024;  // 128KBytes is a reasonable max packet size--debugger can always use less
-    char buf[64];
+    char buf[256];
     snprintf (buf, sizeof(buf), "qXfer:features:read+;PacketSize=%x;qEcho+", max_packet_size);
+
+    // By default, don't enable compression.  It's only worth doing when we are working
+    // with a low speed communication channel.
+    bool enable_compression = false;
+
+    // Enable compression when debugserver is running on a watchOS device where communication may be over Bluetooth.
+#if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
+    enable_compression = true;
+#endif
+
+#if defined (HAVE_LIBCOMPRESSION)
+    // libcompression is weak linked so test if compression_decode_buffer() is available
+    if (enable_compression && compression_decode_buffer != NULL)
+    {
+        strcat (buf, ";SupportedCompressions=lzfse,zlib-deflate,lz4,lzma;DefaultCompressionMinSize=");
+        char numbuf[16];
+        snprintf (numbuf, sizeof (numbuf), "%zu", m_compression_minsize);
+        numbuf[sizeof (numbuf) - 1] = '\0';
+        strcat (buf, numbuf);
+    }
+#elif defined (HAVE_LIBZ)
+    if (enable_compression)
+    {
+        strcat (buf, ";SupportedCompressions=zlib-deflate;DefaultCompressionMinSize=");
+        char numbuf[16];
+        snprintf (numbuf, sizeof (numbuf), "%zu", m_compression_minsize);
+        numbuf[sizeof (numbuf) - 1] = '\0';
+        strcat (buf, numbuf);
+    }
+#endif
+
     return SendPacket (buf);
 }
 
@@ -3765,6 +3943,72 @@ RNBRemote::HandlePacket_SetEnableAsyncPr
     return SendPacket ("OK");
 }
 
+// QEnableCompression:type:<COMPRESSION-TYPE>;minsize:<MINIMUM PACKET SIZE TO COMPRESS>;
+//
+// type: must be a type previously reported by the qXfer:features: SupportedCompressions list
+//
+// minsize: is optional; by default the qXfer:features: DefaultCompressionMinSize value is used
+// debugserver may have a better idea of what a good minimum packet size to compress is than lldb. 
+
+rnb_err_t
+RNBRemote::HandlePacket_QEnableCompression (const char *p)
+{
+    p += sizeof ("QEnableCompression:") - 1;
+
+    size_t new_compression_minsize = m_compression_minsize;
+    const char *new_compression_minsize_str = strstr (p, "minsize:");
+    if (new_compression_minsize_str)
+    {
+        new_compression_minsize_str += strlen ("minsize:");
+        errno = 0;
+        new_compression_minsize = strtoul (new_compression_minsize_str, NULL, 10);
+        if (errno != 0 || new_compression_minsize == ULONG_MAX)
+        {
+            new_compression_minsize = m_compression_minsize;
+        }
+    }
+
+#if defined (HAVE_LIBCOMPRESSION)
+    if (compression_decode_buffer != NULL)
+    {
+        if (strstr (p, "type:zlib-deflate;") != nullptr)
+        {
+            EnableCompressionNextSendPacket (compression_types::zlib_deflate);
+            m_compression_minsize = new_compression_minsize;
+            return SendPacket ("OK");
+        }
+        else if (strstr (p, "type:lz4;") != nullptr)
+        {
+            EnableCompressionNextSendPacket (compression_types::lz4);
+            m_compression_minsize = new_compression_minsize;
+            return SendPacket ("OK");
+        }
+        else if (strstr (p, "type:lzma;") != nullptr)
+        {
+            EnableCompressionNextSendPacket (compression_types::lzma);
+            m_compression_minsize = new_compression_minsize;
+            return SendPacket ("OK");
+        }
+        else if (strstr (p, "type:lzfse;") != nullptr)
+        {
+            EnableCompressionNextSendPacket (compression_types::lzfse);
+            m_compression_minsize = new_compression_minsize;
+            return SendPacket ("OK");
+        }
+    }
+#endif
+
+#if defined (HAVE_LIBZ)
+    if (strstr (p, "type:zlib-deflate;") != nullptr)
+    {
+        EnableCompressionNextSendPacket (compression_types::zlib_deflate);
+        m_compression_minsize = new_compression_minsize;
+        return SendPacket ("OK");
+    }
+#endif
+
+    return SendPacket ("E88");
+}
 
 rnb_err_t
 RNBRemote::HandlePacket_qSpeedTest (const char *p)
@@ -4450,6 +4694,14 @@ RNBRemote::HandlePacket_qXfer (const cha
                     }
                 }
             }
+            else
+            {
+                SendPacket ("E85");
+            }
+        }
+        else
+        {
+            SendPacket ("E86");
         }
     }
     return SendPacket ("E82");
@@ -4941,3 +5193,26 @@ RNBRemote::HandlePacket_qProcessInfo (co
     return SendPacket (rep.str());
 }
 
+void
+RNBRemote::EnableCompressionNextSendPacket (compression_types type)
+{
+    m_compression_mode = type;
+    m_enable_compression_next_send_packet = true;
+}
+
+compression_types
+RNBRemote::GetCompressionType ()
+{
+    // The first packet we send back to the debugger after a QEnableCompression request
+    // should be uncompressed -- so we can indicate whether the compression was enabled
+    // or not via OK / Enn returns.  After that, all packets sent will be using the
+    // compression protocol.
+
+    if (m_enable_compression_next_send_packet)
+    {
+        // One time, we send back "None" as our compression type
+        m_enable_compression_next_send_packet = false;
+        return compression_types::none;
+    }
+    return m_compression_mode;
+}

Modified: lldb/trunk/tools/debugserver/source/RNBRemote.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/debugserver/source/RNBRemote.h?rev=240066&r1=240065&r2=240066&view=diff
==============================================================================
--- lldb/trunk/tools/debugserver/source/RNBRemote.h (original)
+++ lldb/trunk/tools/debugserver/source/RNBRemote.h Thu Jun 18 16:46:06 2015
@@ -1,4 +1,4 @@
-//===-- RNBRemote.h ---------------------------------------------*- C++ -*-===//
+
 //
 //                     The LLVM Compiler Infrastructure
 //
@@ -30,6 +30,8 @@ class PThreadEvents;
 
 enum event_loop_mode { debug_nub, gdb_remote_protocol, done };
 
+enum class compression_types { zlib_deflate, lz4, lzma, lzfse, none };
+
 class RNBRemote
 {
 public:
@@ -120,6 +122,7 @@ public:
         memory_region_info,             // 'qMemoryRegionInfo:'
         get_profile_data,               // 'qGetProfileData'
         set_enable_profiling,           // 'QSetEnableAsyncProfiling'
+        enable_compression,             // 'QEnableCompression:'
         watchpoint_support_info,        // 'qWatchpointSupportInfo:'
         allocate_memory,                // '_M'
         deallocate_memory,              // '_m'
@@ -235,6 +238,7 @@ public:
     rnb_err_t HandlePacket_MemoryRegionInfo (const char *p);
     rnb_err_t HandlePacket_GetProfileData(const char *p);
     rnb_err_t HandlePacket_SetEnableAsyncProfiling(const char *p);
+    rnb_err_t HandlePacket_QEnableCompression(const char *p);
     rnb_err_t HandlePacket_WatchpointSupportInfo (const char *p);
     rnb_err_t HandlePacket_qSpeedTest (const char *p);
     rnb_err_t HandlePacket_qXfer (const char *p);
@@ -309,6 +313,7 @@ protected:
 
     rnb_err_t       GetPacket (std::string &packet_data, RNBRemote::Packet& packet_info, bool wait);
     rnb_err_t       SendPacket (const std::string &);
+    std::string     CompressString (const std::string &);
 
     void CreatePacketTable ();
     rnb_err_t GetPacketPayload (std::string &);
@@ -316,6 +321,12 @@ protected:
     nub_thread_t
     ExtractThreadIDFromThreadSuffix (const char *p);
 
+    void
+    EnableCompressionNextSendPacket (compression_types);
+
+    compression_types
+    GetCompressionType ();
+
     RNBContext      m_ctx;              // process context
     RNBSocket       m_comm;             // communication port
     std::string     m_arch;
@@ -336,6 +347,11 @@ protected:
                                                                 // "$g;thread:TTTT" instead of "$g"
                                                                 // "$GVVVVVVVVVVVVVV;thread:TTTT;#00 instead of "$GVVVVVVVVVVVVVV"
     bool            m_list_threads_in_stop_reply;
+
+    size_t          m_compression_minsize;                      // only packets larger than this size will be compressed
+    bool            m_enable_compression_next_send_packet;
+
+    compression_types m_compression_mode;
 };
 
 /* We translate the /usr/include/mach/exception_types.h exception types






More information about the lldb-commits mailing list