Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: "Terry Guo" <terry.guo@arm.com>
To: <gdb-patches@sourceware.org>
Cc: "'Jonathan Larmour'" <jifl@ecoscentric.com>
Subject: [Patch] Enable GDB remote protocol to support zlib-compressed xml
Date: Wed, 08 Aug 2012 08:31:00 -0000	[thread overview]
Message-ID: <000401cd7540$59679610$0c36c230$@guo@arm.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 1530 bytes --]

Hi,

This patch intends to implement a new feature discussed in thread
http://sourceware.org/ml/gdb-patches/2012-06/msg00271.html. With this patch,
stub can return host gdb with zlib-compressed object which consumes less
space and communication overhead compared to its uncompressed format.

A minor change from our discussion is that "compressedXML" is replaced with
"compressedXfer" which I think is more representative because we may
transfer something other than XML file.

Please help to review and comment this patch. Thanks in advance.

BR,
Terry

gdb/doc/ChangeLog:

2012-08-07  Terry Guo  <terry.guo@arm.com>

	* gdb.texinfo (General Query Packets): Document new 
	compressedXfer stub feature.
	(General Query Packets): Document new zread packets.


gdb/ChangeLog:

2012-08-07  Terry Guo  <terry.guo@arm.com>

	* defs.h (gdb_zlib_error): Declare.
	* utils.c (gdb_zlib_error): New function.
	* remote.c (struct remote_state): New field has_cmprs_xfer.
	(enum packet_cmprs_xfer): New enum.
	(struct packet_config): New field xfer_status.
	(add_packet_config_cmd): Initialize the field xfer_status.
	(remote_packet_is_cmprs_xfer): New function.
	(remote_compressed_xfer_feature): New function.
	(remote_protocol_features): New feature compressedXfer.
	(remote_read_qxfer): Try zread and fallback to plain read when
	fails.
	* target.c (target_decompress_buf): New function.
	(target_read_stralloc): Decompress buffer first if any.
	* target.h (remote_packet_is_cmprs_xfer): Declare.

[-- Attachment #2: gdb-zlib-compressed-object-v3.patch --]
[-- Type: application/octet-stream, Size: 16903 bytes --]

diff --git a/gdb/defs.h b/gdb/defs.h
index 1c6fa79..1457624 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -291,6 +291,8 @@ extern void set_display_time (int);
 
 extern void set_display_space (int);
 
+extern void gdb_zlib_error (int);
+
 /* Cleanup utilities.  */
 
 #include "cleanups.h"
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index a92df86..c877710 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -36704,6 +36704,11 @@ These are the currently defined stub features and their properties:
 @tab @samp{-}
 @tab No
 
+@item @samp{compressedXfer}
+@tab No
+@tab @samp{-}
+@tab Yes
+
 @end multitable
 
 These are the currently defined stub features, in more detail:
@@ -36858,6 +36863,13 @@ See @ref{Bytecode Descriptions} for details about the bytecode.
 The remote stub supports running a breakpoint's command list itself,
 rather than reporting the hit to @value{GDBN}.
 
+@item compressedXfer
+The remote stub can reply GDB @samp{qXfer:object:zread:annex:offset,length}
+(@pxref{qXfer read}) request with contents compressed in zlib format rather
+than the plain format.  This doesn't mean all the replies will be in compressed
+format.  In the event of this feature, the host GDB will always try @samp{zread}
+first and fall back to plain @samp{read} when fails.
+
 @end table
 
 @item qSymbol::
@@ -36954,6 +36966,7 @@ packets.)
 @xref{Tracepoint Packets}.
 
 @item qXfer:@var{object}:read:@var{annex}:@var{offset},@var{length}
+@item qXfer:@var{object}:zread:@var{annex}:@var{offset},@var{length}
 @cindex read special object, remote request
 @cindex @samp{qXfer} packet
 @anchor{qXfer read}
@@ -36963,6 +36976,12 @@ starting at @var{offset} bytes into the data.  The content and
 encoding of @var{annex} is specific to @var{object}; it can supply
 additional details about what data to access.
 
+The @var{zread} works as the @var{read} except that the transferred objects
+should be zlib-compressed.  This can enable stub to save spaces by
+comprising pre-built contents in zlib format instead of plain format. The
+compressed format can also reduce the communication overhead.  The @var{zread}
+should be used only when the host gdb is built with appropriate zlib library.
+
 Here are the specific requests of this form defined so far.  All
 @samp{qXfer:@var{object}:read:@dots{}} requests use the same reply
 formats, listed below.
diff --git a/gdb/remote.c b/gdb/remote.c
index 1c9367d..53a5ed8 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -353,6 +353,9 @@ struct remote_state
   /* Nonzero if the user has pressed Ctrl-C, but the target hasn't
      responded to that.  */
   int ctrlc_pending_p;
+
+  /* Nonzero if the stub reports support for zlib-compressed transfer.  */
+  int has_cmprs_xfer;
 };
 
 /* Private data that we'll store in (struct thread_info)->private.  */
@@ -1043,12 +1046,20 @@ enum packet_support
     PACKET_DISABLE
   };
 
+enum packet_cmprs_xfer
+  {
+    CMPRS_XFER_DISABLE = 0,
+    CMPRS_XFER_ENABLE,
+    CMPRS_XFER_UNKNOWN
+  };
+
 struct packet_config
   {
     const char *name;
     const char *title;
     enum auto_boolean detect;
     enum packet_support support;
+    enum packet_cmprs_xfer xfer_status;
   };
 
 /* Analyze a packet's return value and update the packet config
@@ -1122,6 +1133,7 @@ add_packet_config_cmd (struct packet_config *config, const char *name,
   config->title = title;
   config->detect = AUTO_BOOLEAN_AUTO;
   config->support = PACKET_SUPPORT_UNKNOWN;
+  config->xfer_status = CMPRS_XFER_UNKNOWN;
   set_doc = xstrprintf ("Set use of remote protocol `%s' (%s) packet",
 			name, title);
   show_doc = xstrprintf ("Show current use of remote "
@@ -1380,6 +1392,74 @@ show_remote_protocol_Z_packet_cmd (struct ui_file *file, int from_tty,
     }
 }
 
+/* Return to target whether the current obj is transferred in zlib-compressed
+   format.  */
+
+int
+remote_packet_is_cmprs_xfer (enum target_object obj, const char *annex,
+			     int type_read)
+{
+  switch (obj)
+    {
+    case TARGET_OBJECT_SPU:
+      if (type_read)
+	return (remote_protocol_packets[PACKET_qXfer_spu_read].xfer_status
+		== CMPRS_XFER_ENABLE);
+      else
+	return (remote_protocol_packets[PACKET_qXfer_spu_write].xfer_status
+		== CMPRS_XFER_ENABLE);
+    case TARGET_OBJECT_SIGNAL_INFO:
+      if (type_read)
+	return (remote_protocol_packets[PACKET_qXfer_siginfo_read].xfer_status
+		== CMPRS_XFER_ENABLE);
+      else
+	return (remote_protocol_packets[PACKET_qXfer_siginfo_write].xfer_status
+		== CMPRS_XFER_ENABLE);
+    case TARGET_OBJECT_STATIC_TRACE_DATA:
+      if (type_read)
+	return
+	  (remote_protocol_packets[PACKET_qXfer_statictrace_read].xfer_status
+	   == CMPRS_XFER_ENABLE);
+      else
+	return 0;
+    case TARGET_OBJECT_AUXV:
+      return (remote_protocol_packets[PACKET_qXfer_auxv].xfer_status
+	      == CMPRS_XFER_ENABLE);
+    case TARGET_OBJECT_AVAILABLE_FEATURES:
+      return (remote_protocol_packets[PACKET_qXfer_features].xfer_status
+	      == CMPRS_XFER_ENABLE);
+    case TARGET_OBJECT_LIBRARIES:
+      return (remote_protocol_packets[PACKET_qXfer_libraries].xfer_status
+	      == CMPRS_XFER_ENABLE);
+    case TARGET_OBJECT_LIBRARIES_SVR4:
+      return (remote_protocol_packets[PACKET_qXfer_libraries_svr4].xfer_status
+	      == CMPRS_XFER_ENABLE);
+    case TARGET_OBJECT_MEMORY_MAP:
+      return (remote_protocol_packets[PACKET_qXfer_memory_map].xfer_status
+	      == CMPRS_XFER_ENABLE);
+    case TARGET_OBJECT_OSDATA:
+      gdb_assert (remote_desc);
+      return (remote_protocol_packets[PACKET_qXfer_osdata].xfer_status
+	      == CMPRS_XFER_ENABLE);
+    case TARGET_OBJECT_THREADS:
+      gdb_assert (annex == NULL);
+      return (remote_protocol_packets[PACKET_qXfer_threads].xfer_status
+	      == CMPRS_XFER_ENABLE);
+    case TARGET_OBJECT_TRACEFRAME_INFO:
+      gdb_assert (annex == NULL);
+      return (remote_protocol_packets[PACKET_qXfer_traceframe_info].xfer_status
+	      == CMPRS_XFER_ENABLE);
+    case TARGET_OBJECT_FDPIC:
+      return (remote_protocol_packets[PACKET_qXfer_fdpic].xfer_status
+	      == CMPRS_XFER_ENABLE);
+    case TARGET_OBJECT_OPENVMS_UIB:
+      return (remote_protocol_packets[PACKET_qXfer_uib].xfer_status
+	      == CMPRS_XFER_ENABLE);
+    default:
+      return 0;
+    }
+};
+
 /* Should we try the 'ThreadInfo' query packet?
 
    This variable (NOT available to the user: auto-detect only!)
@@ -3877,6 +3957,16 @@ remote_string_tracing_feature (const struct protocol_feature *feature,
   rs->string_tracing = (support == PACKET_ENABLE);
 }
 
+static void
+remote_compressed_xfer_feature (const struct protocol_feature *feature,
+				enum packet_support support,
+				const char *value)
+{
+  struct remote_state *rs = get_remote_state ();
+
+  rs->has_cmprs_xfer = (support != PACKET_DISABLE);
+}
+
 static struct protocol_feature remote_protocol_features[] = {
   { "PacketSize", PACKET_DISABLE, remote_packet_size, -1 },
   { "qXfer:auxv:read", PACKET_DISABLE, remote_supported_packet,
@@ -3944,6 +4034,7 @@ static struct protocol_feature remote_protocol_features[] = {
   { "QAgent", PACKET_DISABLE, remote_supported_packet, PACKET_QAgent},
   { "tracenz", PACKET_DISABLE,
     remote_string_tracing_feature, -1 },
+  { "compressedXfer", PACKET_DISABLE, remote_compressed_xfer_feature, -1 },
 };
 
 static char *remote_support_xml;
@@ -8443,9 +8534,12 @@ remote_read_qxfer (struct target_ops *ops, const char *object_name,
   static char *finished_object;
   static char *finished_annex;
   static ULONGEST finished_offset;
+  int xfer_ok = 1;
+  struct packet_config zpacket = *packet;
+  char *zname = NULL;
 
   struct remote_state *rs = get_remote_state ();
-  LONGEST i, n, packet_len;
+  LONGEST i, n, packet_len = 0;
 
   if (packet->support == PACKET_DISABLE)
     return -1;
@@ -8472,18 +8566,79 @@ remote_read_qxfer (struct target_ops *ops, const char *object_name,
      the target is free to respond with slightly less data.  We subtract
      five to account for the response type and the protocol frame.  */
   n = min (get_remote_packet_size () - 5, len);
-  snprintf (rs->buf, get_remote_packet_size () - 4, "qXfer:%s:read:%s:%s,%s",
-	    object_name, annex ? annex : "",
-	    phex_nz (offset, sizeof offset),
-	    phex_nz (n, sizeof n));
-  i = putpkt (rs->buf);
-  if (i < 0)
-    return -1;
 
-  rs->buf[0] = '\0';
-  packet_len = getpkt_sane (&rs->buf, &rs->buf_size, 0);
-  if (packet_len < 0 || packet_ok (rs->buf, packet) != PACKET_OK)
-    return -1;
+  /* If compressed object is supported, try it first.  */
+  if (rs->has_cmprs_xfer && packet->xfer_status != CMPRS_XFER_DISABLE)
+    {
+      /* When start to transfer a new object, fake a zread packet to detect
+         whether the remote stub support zread for this object.  */
+      if (packet->xfer_status == CMPRS_XFER_UNKNOWN)
+	{
+	  int zname_len = strlen(object_name) + 13;
+	  zname = (char *)xcalloc (zname_len, 1);
+	  snprintf (zname, zname_len, "qXfer:%s:zread", object_name);
+	  zpacket.name = zname;
+	  zpacket.support = PACKET_SUPPORT_UNKNOWN;
+	}
+
+      snprintf (rs->buf, get_remote_packet_size () - 4, "qXfer:%s:zread:%s:%s,%s",
+		object_name, annex ? annex : "",
+		phex_nz (offset, sizeof offset),
+		phex_nz (n, sizeof n));
+      i = putpkt (rs->buf);
+      if (i >= 0)
+	{
+	  rs->buf[0] = '\0';
+	  packet_len = getpkt_sane (&rs->buf, &rs->buf_size, 0);
+	  if (packet_len < 0 || packet_ok (rs->buf, &zpacket) != PACKET_OK)
+	    xfer_ok = 0;
+	}
+      else
+	xfer_ok = 0;
+
+      if (zname)
+	xfree (zname);
+
+      if (packet->xfer_status == CMPRS_XFER_ENABLE && xfer_ok == 0)
+	{
+	  /* We are in the middle of transferring a large object,
+	     the fail of current transfer will invalid all finished
+	     transfer, so we don't fall back to plain format.  */
+	  return -1;
+	}
+      else if (packet->xfer_status == CMPRS_XFER_UNKNOWN && xfer_ok == 1)
+	{
+	  /* If successfully transferred the first chunk in compressed format,
+	     change xfer_status of packet to tell next transfer that we decided
+	     to use compressed format to transfer remaining chunks, so the next
+	     transfer won't fall back to use plain format when it fails.  */
+	  packet->xfer_status = CMPRS_XFER_ENABLE;
+	}
+      else if (packet->xfer_status == CMPRS_XFER_UNKNOWN && xfer_ok == 0)
+	{
+	  /* If the first attempt fails,
+	     just fall back to use plain format.  */
+	  packet->xfer_status = CMPRS_XFER_DISABLE;
+	}
+    }
+
+  /* The compressed object isn't supported, fall back to use plain format.  */
+  if (rs->has_cmprs_xfer == 0 || packet->xfer_status == CMPRS_XFER_DISABLE)
+    {
+      snprintf (rs->buf, get_remote_packet_size () - 4, "qXfer:%s:read:%s:%s,%s",
+		object_name, annex ? annex : "",
+		phex_nz (offset, sizeof offset),
+		phex_nz (n, sizeof n));
+
+      i = putpkt (rs->buf);
+      if (i < 0)
+	return -1;
+
+      rs->buf[0] = '\0';
+      packet_len = getpkt_sane (&rs->buf, &rs->buf_size, 0);
+      if (packet_len < 0 || packet_ok (rs->buf, packet) != PACKET_OK)
+	return -1;
+    }
 
   if (rs->buf[0] != 'l' && rs->buf[0] != 'm')
     error (_("Unknown remote qXfer reply: %s"), rs->buf);
diff --git a/gdb/target.c b/gdb/target.c
index bb8eae8..245cb9e 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -43,6 +43,9 @@
 #include "tracepoint.h"
 #include "gdb/fileio.h"
 #include "agent.h"
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
 
 static void target_info (char *, int);
 
@@ -1998,6 +2001,80 @@ target_write_partial (struct target_ops *ops,
   return target_xfer_partial (ops, object, annex, NULL, buf, offset, len);
 }
 
+/* Decompress the zlib-compressed buffer buf_p.
+   Inside the buf_p, the length of received compressed data is data_size.
+   If decompress successfully, free the buffer pointed by buf_p and redirect
+   buf_p to the new decompressed data buffer.
+   The size of decompressed data will be returned.
+   Otherwise the buf_p won't be touched and simply return data_size.  */
+
+static unsigned int
+target_decompress_buf (gdb_byte **buf_p, size_t data_size)
+{
+#ifndef HAVE_ZLIB_H
+  error (_("Support for zlib-compressed data is disabled in this copy of GDB"));
+#else
+  size_t ret, uncompressed_size, compressed_size, i;
+  gdb_byte *buf;
+
+  if (remote_debug)
+    {
+      int i;
+
+      fprintf_unfiltered (gdb_stdlog,
+			  "Zlib-compressed packet received with size %d bytes: ",
+			  data_size);
+
+      for (i = 0; i < data_size; i++)
+	fprintf_unfiltered (gdb_stdlog, "%02x", (unsigned char)(*buf_p)[i]);
+
+      fprintf_unfiltered (gdb_stdlog, "\n");
+    }
+
+  /* For zlib-based compressed buffer, we assume that the first 8 bytes are
+     used to encode the length of uncompressed data, and the following 4 bytes
+     encode the zlib head, so the total length should be bigger than 12.  */
+  if (data_size <= 12)
+    error (_("Zlib Error: the data size %d is too short to decompress"),
+	   data_size);
+
+  uncompressed_size =  (*buf_p)[0];
+  uncompressed_size += (*buf_p)[1]<<4;
+  uncompressed_size += (*buf_p)[2]<<8;
+  uncompressed_size += (*buf_p)[3]<<12;
+  uncompressed_size += (*buf_p)[4]<<16;
+  uncompressed_size += (*buf_p)[5]<<20;
+  uncompressed_size += (*buf_p)[6]<<24;
+  uncompressed_size += (*buf_p)[7]<<28;
+
+  compressed_size = data_size - 8;
+
+  /* The extra 1 byte is to cope with the existing alloc strategy which always
+     alloc more spaces and have code like buffer[len] = 0.  */
+  buf = xmalloc (uncompressed_size + 1);
+
+  ret = uncompress ((Bytef *)buf, (uLongf *)&uncompressed_size,
+		    (const Bytef *)((*buf_p) + 8), (uLong)compressed_size);
+
+  if (ret == Z_OK)
+    {
+      if (remote_debug)
+	fprintf_unfiltered (gdb_stdlog, "Packet decompressed in size %d bytes as: %s\n",
+			    uncompressed_size, (char *)buf);
+
+      xfree (*buf_p);
+      *buf_p = buf;
+      return uncompressed_size;
+    }
+  else
+    {
+      xfree (buf);
+      gdb_zlib_error (ret);
+      return data_size;
+    }
+#endif
+}
+
 /* Wrappers to perform the full transfer.  */
 
 /* For docs on target_read see target.h.  */
@@ -2372,6 +2449,13 @@ target_read_stralloc (struct target_ops *ops, enum target_object object,
   if (transferred == 0)
     return xstrdup ("");
 
+  /* If it is compressed object, need to decompress it.
+     The first 8 bytes encode the size of uncompressed data.
+     The following 4 bytes encode the head of zlib-based compressed
+     buffer.  */
+  if (transferred > 12 && remote_packet_is_cmprs_xfer (object, annex, 1))
+    transferred = target_decompress_buf (&buffer, transferred);
+
   buffer[transferred] = 0;
 
   /* Check for embedded NUL bytes; but allow trailing NULs.  */
diff --git a/gdb/target.h b/gdb/target.h
index 54c58d6..cf07f38 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -1861,6 +1861,10 @@ extern struct target_ops *find_target_beneath (struct target_ops *);
 
 extern char *target_get_osdata (const char *type);
 
+/* From remote.c */
+
+extern int remote_packet_is_cmprs_xfer (enum target_object, const char *, int);
+
 \f
 /* Stuff that should be shared among the various remote targets.  */
 
diff --git a/gdb/utils.c b/gdb/utils.c
index 5566149..3703607 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -78,6 +78,10 @@
 #include "interps.h"
 #include "gdb_regex.h"
 
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
 #if !HAVE_DECL_MALLOC
 extern PTR malloc ();		/* ARI: PTR */
 #endif
@@ -3893,3 +3897,39 @@ _initialize_utils (void)
   add_internal_problem_command (&internal_error_problem);
   add_internal_problem_command (&internal_warning_problem);
 }
+
+/* Report zlib error message.  */
+
+void
+gdb_zlib_error (int ret_code)
+{
+#ifndef HAVE_ZLIB_H
+  error (_("Zlib support is disabled in this copy of GDB"));
+#else
+  switch (ret_code)
+    {
+    case Z_OK:
+      break;
+    case Z_ERRNO:
+      if (ferror(stdin))
+	error (_("Zlib Error: error reading stdin"));
+      if (ferror(stdout))
+	error (_("Zlib Error: error writing stdout"));
+      break;
+    case Z_STREAM_ERROR:
+      error (_("Zlib Error: invalid compression level"));
+      break;
+    case Z_DATA_ERROR:
+      error (_("Zlib Error: invalid or incomplete deflate data"));
+      break;
+    case Z_MEM_ERROR:
+      error (_("Zlib Error: out of memory"));
+      break;
+    case Z_VERSION_ERROR:
+      error (_("Zlib Error: zlib version mismatch"));
+      break;
+    default:
+      error (_("Zlib Error: unknown error code"));
+    }
+#endif
+}

             reply	other threads:[~2012-08-08  8:31 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-08-08  8:31 Terry Guo [this message]
2012-08-09  6:16 ` Jonathan Larmour
2012-08-09  8:11   ` Terry Guo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='000401cd7540$59679610$0c36c230$@guo@arm.com' \
    --to=terry.guo@arm.com \
    --cc=gdb-patches@sourceware.org \
    --cc=jifl@ecoscentric.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox