summaryrefslogtreecommitdiffstats
path: root/lldb
diff options
context:
space:
mode:
Diffstat (limited to 'lldb')
-rw-r--r--lldb/docs/lldb-gdb-remote.txt63
-rw-r--r--lldb/lldb.xcodeproj/project.pbxproj96
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp254
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h26
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp156
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h6
-rw-r--r--lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj63
-rw-r--r--lldb/tools/debugserver/source/RNBRemote.cpp285
-rw-r--r--lldb/tools/debugserver/source/RNBRemote.h18
9 files changed, 948 insertions, 19 deletions
diff --git a/lldb/docs/lldb-gdb-remote.txt b/lldb/docs/lldb-gdb-remote.txt
index b471b245f85..d8f22fe604a 100644
--- a/lldb/docs/lldb-gdb-remote.txt
+++ b/lldb/docs/lldb-gdb-remote.txt
@@ -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.
+//----------------------------------------------------------------------
diff --git a/lldb/lldb.xcodeproj/project.pbxproj b/lldb/lldb.xcodeproj/project.pbxproj
index 7e839152d1e..aa0d397e25c 100644
--- a/lldb/lldb.xcodeproj/project.pbxproj
+++ b/lldb/lldb.xcodeproj/project.pbxproj
@@ -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",
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
index da04f46c25a..1af3947a75f 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
@@ -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::GDBRemoteCommunication(const char *comm_name,
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::WaitForPacketWithTimeoutMicroSecondsNoLock (StringExtrac
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 (const uint8_t *src, size_t src_len, Stri
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 (const uint8_t *src, size_t src_len, Stri
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 (const uint8_t *src, size_t src_len, Stri
{
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 (const uint8_t *src, size_t src_len, Stri
}
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());
}
}
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
index c13352781b3..7379bb3aa09 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
@@ -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);
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index 21a538ced17..d2a15b3152e 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -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::GetRemoteQSupported ()
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::GetGDBServerVersion()
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()
{
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index 726bc577a1b..ba34a313090 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -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,
diff --git a/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj b/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj
index 42565ef4cd0..f7cbc1338fb 100644
--- a/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj
+++ b/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj
@@ -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;
diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp
index a82179dcb78..2385246a200 100644
--- a/lldb/tools/debugserver/source/RNBRemote.cpp
+++ b/lldb/tools/debugserver/source/RNBRemote.cpp
@@ -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 (char *buf, nub_size_t buf_size)
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 &s)
}
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_SetEnableAsyncProfiling (const char *p)
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 char *command)
}
}
}
+ else
+ {
+ SendPacket ("E85");
+ }
+ }
+ else
+ {
+ SendPacket ("E86");
}
}
return SendPacket ("E82");
@@ -4941,3 +5193,26 @@ RNBRemote::HandlePacket_qProcessInfo (const char *p)
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;
+}
diff --git a/lldb/tools/debugserver/source/RNBRemote.h b/lldb/tools/debugserver/source/RNBRemote.h
index c769a045cdd..1f4883ab9e0 100644
--- a/lldb/tools/debugserver/source/RNBRemote.h
+++ b/lldb/tools/debugserver/source/RNBRemote.h
@@ -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
OpenPOWER on IntegriCloud