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> 

llvm-svn: 240066
This commit is contained in:
Jason Molenda 2015-06-18 21:46:06 +00:00
parent 905c802f5b
commit 91ffe0a570
9 changed files with 948 additions and 19 deletions

View File

@ -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.
//----------------------------------------------------------------------

View File

@ -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",

View File

@ -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());
}
}

View File

@ -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, // LempelZivMarkov 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);

View File

@ -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()
{

View File

@ -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,

View File

@ -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;

View File

@ -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;
}

View File

@ -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