[rfc][3/3] Remote core file generation: memory map

Ulrich Weigand uweigand@de.ibm.com
Fri Oct 21 19:54:00 GMT 2011


Hello,

the final remaining piece to support remote core file generation
is the target_find_memory_regions hook.  This is really properly
a target hook, but it is unfortunately not implemented for the
remote target.

The patch below adds support by adding a new TARGET_OBJECT_PROCESS_MAP
xfer type implemented via a qXfer:process-map:read packet.

Note that there already is a qXfer:memory-map:read packet, but this
is not usable as-is to implement target_find_memory_regions, since
it is really intended for a *system* memory map for some naked
embedded targets instead of a per-process virtual address space map.

For example:

- the memory map is read into a single global mem_region list; it is not
  switched for multiple inferiors

- native or gdbserver Linux targets do not have a memory map today,
  and just enabling it changes memory access behaviour in unexpected
  ways, e.g. accesses outside of memory regions in /proc//maps are
  now no longer possible; also caching behaviour is different

- the memory attribute format is insufficient to express properties
  of a virtual memory mapping (e.g. permissions; mapped filename ...)


I guess longer term it might be nicer to always have a memory map,
and also use it for native targets, and then use the same map also
for core file generation ...

I'd appreciate suggestions how to move forward on this; is having a
new qXfer type just for core file generation OK, or should we rather
attempt to move towards an always-active memory map -- if the latter,
how can we get there?

Thanks,
Ulrich


ChangeLog:

	gdb/
	* target.h (TARGET_OBJECT_PROCESS_MAP): New enum.
	* remote.c (PACKET_qXfer_process_map): New packet type.
	(remote_protocol_features): Add support.
	(remote_xfer_partial): Handle TARGET_OBJECT_PROCESS_MAP.

	(struct find_memory_regions_args): New data structure.
	(process_map_start_vma): New function.
	(vma_attributes, process_map_children, process_map_elements):
	New global variables.
	(remote_find_memory_regions): New function.
	(init_remote_ops): Install it.

	* common/buffer.c (buffer_xml_printf): Support %l and %ll
	length modifiers.
	* common/linux-osdata.c: Include "sys/param.h".
	(read_mapping): New function.
	(linux_common_xfer_process_map): Likewise.
	* common/linux-osdata.h (linux_common_xfer_process_map): Add prototype.

	gdb/gdbserver/
	* target.h (struct target_ops): New callback "qxfer_process_map".
	* linux-low.c (linux_qxfer_process_map): New function.
	(linux_target_ops): Install it.
	* server.c (handle_qxfer_process_map): New function.
	(qxfer_packets): Use it to implement "process-map".


Index: gdb/remote.c
===================================================================
RCS file: /cvs/src/src/gdb/remote.c,v
retrieving revision 1.465
diff -u -p -r1.465 remote.c
--- gdb/remote.c	13 Oct 2011 13:15:16 -0000	1.465
+++ gdb/remote.c	21 Oct 2011 16:24:19 -0000
@@ -1237,6 +1237,7 @@ enum {
   PACKET_qXfer_features,
   PACKET_qXfer_libraries,
   PACKET_qXfer_memory_map,
+  PACKET_qXfer_process_map,
   PACKET_qXfer_spu_read,
   PACKET_qXfer_spu_write,
   PACKET_qXfer_osdata,
@@ -2800,6 +2801,102 @@ remote_threads_extra_info (struct thread
       }
   return NULL;
 }
+
+
+#if defined(HAVE_LIBEXPAT)
+
+struct find_memory_regions_args
+{
+  find_memory_region_ftype func;
+  void *obfd;
+};
+
+static void
+process_map_start_vma (struct gdb_xml_parser *parser,
+		       const struct gdb_xml_element *element,
+		       void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+  struct find_memory_regions_args *data = user_data;
+  struct gdb_xml_value *attr;
+
+  CORE_ADDR start;
+  unsigned long length;
+  int may_read, may_write, may_exec;
+
+  attr = xml_find_attribute (attributes, "start");
+  start = *(ULONGEST *) attr->value;
+
+  attr = xml_find_attribute (attributes, "length");
+  length = *(ULONGEST *) attr->value;
+
+  attr = xml_find_attribute (attributes, "may_read");
+  may_read = attr && *(ULONGEST *) attr->value;
+
+  attr = xml_find_attribute (attributes, "may_write");
+  may_write = attr && *(ULONGEST *) attr->value;
+
+  attr = xml_find_attribute (attributes, "may_exec");
+  may_exec = attr && *(ULONGEST *) attr->value;
+
+  data->func (start, length, may_read, may_write, may_exec, data->obfd);
+}
+
+const struct gdb_xml_attribute vma_attributes[] = {
+  { "start", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "length", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "may_read", GDB_XML_AF_OPTIONAL,
+    gdb_xml_parse_attr_enum, gdb_xml_enums_boolean },
+  { "may_write", GDB_XML_AF_OPTIONAL,
+    gdb_xml_parse_attr_enum, gdb_xml_enums_boolean },
+  { "may_exec", GDB_XML_AF_OPTIONAL,
+    gdb_xml_parse_attr_enum, gdb_xml_enums_boolean },
+  { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element process_map_children[] = {
+  { "vma", vma_attributes, NULL, GDB_XML_EF_REPEATABLE,
+    process_map_start_vma, NULL },
+  { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element process_map_elements[] = {
+  { "process-map", NULL, process_map_children, GDB_XML_EF_NONE,
+    NULL, NULL },
+  { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+#endif
+
+static int
+remote_find_memory_regions (find_memory_region_ftype func, void *obfd)
+{
+#if defined(HAVE_LIBEXPAT)
+  if (remote_protocol_packets[PACKET_qXfer_process_map].support
+      == PACKET_ENABLE)
+    {
+      int ret = -1;
+      char *xml = target_read_stralloc (&current_target,
+					TARGET_OBJECT_PROCESS_MAP, NULL);
+
+      struct cleanup *back_to = make_cleanup (xfree, xml);
+
+      if (xml && *xml)
+	{
+	  struct find_memory_regions_args args;
+	  args.func = func;
+	  args.obfd = obfd;
+
+	  ret = gdb_xml_parse_quick (_("process-map"), NULL,
+				     process_map_elements, xml, &args);
+	}
+
+      do_cleanups (back_to);
+      return ret;
+    }
+#endif
+
+  return -1;
+}
 
 
 static int
@@ -3722,6 +3819,8 @@ static struct protocol_feature remote_pr
     PACKET_qXfer_libraries },
   { "qXfer:memory-map:read", PACKET_DISABLE, remote_supported_packet,
     PACKET_qXfer_memory_map },
+  { "qXfer:process-map:read", PACKET_DISABLE, remote_supported_packet,
+    PACKET_qXfer_process_map },
   { "qXfer:spu:read", PACKET_DISABLE, remote_supported_packet,
     PACKET_qXfer_spu_read },
   { "qXfer:spu:write", PACKET_DISABLE, remote_supported_packet,
@@ -8321,6 +8420,11 @@ remote_xfer_partial (struct target_ops *
       return remote_read_qxfer (ops, "memory-map", annex, readbuf, offset, len,
 				&remote_protocol_packets[PACKET_qXfer_memory_map]);
 
+    case TARGET_OBJECT_PROCESS_MAP:
+      gdb_assert (annex == NULL);
+      return remote_read_qxfer (ops, "process-map", annex, readbuf, offset, len,
+				&remote_protocol_packets[PACKET_qXfer_process_map]);
+
     case TARGET_OBJECT_OSDATA:
       /* Should only get here if we're connected.  */
       gdb_assert (remote_desc);
@@ -10444,6 +10548,7 @@ Specify the serial device it is connecte
   remote_ops.to_has_thread_control = tc_schedlock;    /* can lock scheduler */
   remote_ops.to_can_execute_reverse = remote_can_execute_reverse;
   remote_ops.to_magic = OPS_MAGIC;
+  remote_ops.to_find_memory_regions = remote_find_memory_regions;
   remote_ops.to_memory_map = remote_memory_map;
   remote_ops.to_flash_erase = remote_flash_erase;
   remote_ops.to_flash_done = remote_flash_done;
Index: gdb/target.h
===================================================================
RCS file: /cvs/src/src/gdb/target.h,v
retrieving revision 1.213
diff -u -p -r1.213 target.h
--- gdb/target.h	7 Oct 2011 12:06:46 -0000	1.213
+++ gdb/target.h	21 Oct 2011 16:24:20 -0000
@@ -242,6 +242,8 @@ enum target_object
   TARGET_OBJECT_WCOOKIE,
   /* Target memory map in XML format.  */
   TARGET_OBJECT_MEMORY_MAP,
+  /* Target process memory map in XML format.  */
+  TARGET_OBJECT_PROCESS_MAP,
   /* Flash memory.  This object can be used to write contents to
      a previously erased flash memory.  Using it without erasing
      flash can have unexpected results.  Addresses are physical
Index: gdb/common/buffer.c
===================================================================
RCS file: /cvs/src/src/gdb/common/buffer.c,v
retrieving revision 1.1
diff -u -p -r1.1 buffer.c
--- gdb/common/buffer.c	21 Jul 2011 23:46:09 -0000	1.1
+++ gdb/common/buffer.c	21 Oct 2011 16:24:20 -0000
@@ -89,7 +89,8 @@ buffer_xml_printf (struct buffer *buffer
   va_list ap;
   const char *f;
   const char *prev;
-  int percent = 0;
+  const char *percent = NULL;
+  int length = 0;
 
   va_start (ap, format);
 
@@ -108,17 +109,40 @@ buffer_xml_printf (struct buffer *buffer
 	      str = va_arg (ap, char *);
 	      break;
 	    case 'd':
-	      sprintf (str, "%d", va_arg (ap, int));
+	      if (length == 2)
+		sprintf (str, "%lld", va_arg (ap, long long));
+	      else if (length == 1)
+		sprintf (str, "%ld", va_arg (ap, long));
+	      else
+		sprintf (str, "%d", va_arg (ap, int));
 	      break;
 	    case 'u':
-	      sprintf (str, "%u", va_arg (ap, unsigned int));
+	      if (length == 2)
+		sprintf (str, "%llu", va_arg (ap, unsigned long long));
+	      else if (length == 1)
+		sprintf (str, "%lu", va_arg (ap, unsigned long));
+	      else
+		sprintf (str, "%u", va_arg (ap, unsigned int));
 	      break;
 	    case 'x':
-	      sprintf (str, "%x", va_arg (ap, unsigned int));
+	      if (length == 2)
+		sprintf (str, "%llx", va_arg (ap, unsigned long long));
+	      else if (length == 1)
+		sprintf (str, "%lx", va_arg (ap, unsigned long));
+	      else
+		sprintf (str, "%x", va_arg (ap, unsigned int));
 	      break;
 	    case 'o':
-	      sprintf (str, "%o", va_arg (ap, unsigned int));
-	      break;
+	      if (length == 2)
+		sprintf (str, "%llo", va_arg (ap, unsigned long long));
+	      else if (length == 1)
+		sprintf (str, "%lo", va_arg (ap, unsigned long));
+	      else
+		sprintf (str, "%o", va_arg (ap, unsigned int));
+	      break;
+	    case 'l':
+	      length++;
+	      continue;
 	    default:
 	      str = 0;
 	      break;
@@ -126,16 +150,17 @@ buffer_xml_printf (struct buffer *buffer
 	  
 	  if (str)
 	    {
-	      buffer_grow (buffer, prev, f - prev - 1);
+	      buffer_grow (buffer, prev, percent - prev);
 	      p = xml_escape_text (str);
 	      buffer_grow_str (buffer, p);
 	      xfree (p);
 	      prev = f + 1;
 	    }
 	  percent = 0;
+	  length = 0;
 	}
       else if (*f == '%')
-	percent = 1;
+	percent = f;
     }
 
   buffer_grow_str (buffer, prev);
Index: gdb/common/linux-osdata.c
===================================================================
RCS file: /cvs/src/src/gdb/common/linux-osdata.c,v
retrieving revision 1.2
diff -u -p -r1.2 linux-osdata.c
--- gdb/common/linux-osdata.c	26 Aug 2011 18:58:04 -0000	1.2
+++ gdb/common/linux-osdata.c	21 Oct 2011 16:24:21 -0000
@@ -27,6 +27,7 @@
 
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/param.h>
 #include <dirent.h>
 #include <ctype.h>
 #include <stdlib.h>
@@ -586,3 +587,109 @@ linux_common_xfer_osdata (const char *an
     }
 }
 
+static int
+read_mapping (FILE *mapfile,
+	      long long *addr,
+	      long long *endaddr,
+	      char *permissions,
+	      long long *offset,
+	      char *device, long long *inode, char *filename)
+{
+  int ret = fscanf (mapfile, "%llx-%llx %s %llx %s %llx",
+                    addr, endaddr, permissions, offset, device, inode);
+
+  filename[0] = '\0';
+  if (ret > 0 && ret != EOF)
+    {
+      /* Eat everything up to EOL for the filename.  This will prevent
+         weird filenames (such as one with embedded whitespace) from
+         confusing this code.  It also makes this code more robust in
+         respect to annotations the kernel may add after the filename.
+
+         Note the filename is used for informational purposes
+         only.  */
+      ret += fscanf (mapfile, "%[^\n]\n", filename);
+    }
+
+  return (ret != 0 && ret != EOF);
+}
+
+LONGEST
+linux_common_xfer_process_map (int pid, gdb_byte *readbuf,
+			       ULONGEST offset, LONGEST len)
+{
+  /* We make the process map snapshot when the object starts to be read.  */
+  static int cached_pid;
+  static const char *buf;
+  static LONGEST len_avail;
+  static struct buffer buffer;
+
+  if (cached_pid != pid)
+    {
+      buffer_free (&buffer);
+      len_avail = 0;
+      cached_pid = pid;
+    }
+
+  if (offset == 0)
+    {
+      char *maps_path;
+      FILE *maps_file;
+
+      if (len_avail != 0)
+	buffer_free (&buffer);
+      len_avail = 0;
+      buf = NULL;
+      buffer_init (&buffer);
+      buffer_grow_str (&buffer, "<process-map>\n");
+
+      maps_path = xstrprintf ("/proc/%d/maps", cached_pid); 
+      maps_file = fopen (maps_path, "r");
+      if (maps_file)
+	{
+	  long long addr, endaddr, offset, inode;
+	  char permissions[8], device[8], filename[MAXPATHLEN];
+
+	  while (read_mapping (maps_file, &addr, &endaddr, &permissions[0],
+			       &offset, &device[0], &inode, &filename[0]))
+	    {
+	      int may_read = (strchr (permissions, 'r') != 0);
+	      int may_write = (strchr (permissions, 'w') != 0);
+	      int may_exec = (strchr (permissions, 'x') != 0);
+
+	      buffer_xml_printf (&buffer,
+				 "<vma start=\"%lld\" length=\"%lld\" "
+				 "may_read=\"%s\" may_write=\"%s\" "
+				 "may_exec=\"%s\"/>\n",
+				 addr, endaddr - addr,
+				 may_read? "yes" : "no",
+				 may_write? "yes" : "no",
+				 may_exec? "yes" : "no");
+	    }
+
+	  fclose (maps_file);
+	}
+
+      xfree (maps_path);
+
+      buffer_grow_str0 (&buffer, "</process-map>\n");
+      buf = buffer_finish (&buffer);
+      len_avail = strlen (buf);
+    }
+
+  if (offset >= len_avail)
+    {
+      /* Done.  Get rid of the buffer.  */
+      buffer_free (&buffer);
+      buf = NULL;
+      len_avail = 0;
+      return 0;
+    }
+
+  if (len > len_avail - offset)
+    len = len_avail - offset;
+  memcpy (readbuf, buf + offset, len);
+
+  return len;
+}
+
Index: gdb/common/linux-osdata.h
===================================================================
RCS file: /cvs/src/src/gdb/common/linux-osdata.h,v
retrieving revision 1.1
diff -u -p -r1.1 linux-osdata.h
--- gdb/common/linux-osdata.h	21 Jul 2011 23:46:09 -0000	1.1
+++ gdb/common/linux-osdata.h	21 Oct 2011 16:24:21 -0000
@@ -26,4 +26,7 @@ extern int linux_common_core_of_thread (
 extern LONGEST linux_common_xfer_osdata (const char *annex, gdb_byte *readbuf,
 					 ULONGEST offset, LONGEST len);
 
+extern LONGEST linux_common_xfer_process_map (int pid, gdb_byte *readbuf,
+					      ULONGEST offset, LONGEST len);
+
 #endif
Index: gdb/gdbserver/linux-low.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/linux-low.c,v
retrieving revision 1.178
diff -u -p -r1.178 linux-low.c
--- gdb/gdbserver/linux-low.c	7 Oct 2011 12:06:48 -0000	1.178
+++ gdb/gdbserver/linux-low.c	21 Oct 2011 16:24:21 -0000
@@ -4505,6 +4505,13 @@ linux_qxfer_osdata (const char *annex,
   return linux_common_xfer_osdata (annex, readbuf, offset, len);
 }
 
+static int
+linux_qxfer_process_map (unsigned char *readbuf, CORE_ADDR offset, int len)
+{
+  int pid = pid_of (get_thread_lwp (current_inferior));
+  return linux_common_xfer_process_map (pid, readbuf, offset, len);
+}
+
 /* Convert a native/host siginfo object, into/from the siginfo in the
    layout of the inferiors' architecture.  */
 
@@ -5014,6 +5021,7 @@ static struct target_ops linux_target_op
   linux_install_fast_tracepoint_jump_pad,
   linux_emit_ops,
   linux_supports_disable_randomization,
+  linux_qxfer_process_map,
 };
 
 static void
Index: gdb/gdbserver/server.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/server.c,v
retrieving revision 1.149
diff -u -p -r1.149 server.c
--- gdb/gdbserver/server.c	7 Oct 2011 12:06:48 -0000	1.149
+++ gdb/gdbserver/server.c	21 Oct 2011 16:24:22 -0000
@@ -992,6 +992,22 @@ handle_qxfer_libraries (const char *anne
   return len;
 }
 
+/* Handle qXfer:process-map:read.  */
+
+static int
+handle_qxfer_process_map (const char *annex,
+			 gdb_byte *readbuf, const gdb_byte *writebuf,
+			 ULONGEST offset, LONGEST len)
+{
+  if (the_target->qxfer_process_map == NULL || writebuf != NULL)
+    return -2;
+
+  if (annex[0] != '\0' || !target_running ())
+    return -1;
+
+  return (*the_target->qxfer_process_map) (readbuf, offset, len);
+}
+
 /* Handle qXfer:osadata:read.  */
 
 static int
@@ -1216,6 +1232,7 @@ static const struct qxfer qxfer_packets[
     { "fdpic", handle_qxfer_fdpic},
     { "features", handle_qxfer_features },
     { "libraries", handle_qxfer_libraries },
+    { "process-map", handle_qxfer_process_map },
     { "osdata", handle_qxfer_osdata },
     { "siginfo", handle_qxfer_siginfo },
     { "spu", handle_qxfer_spu },
Index: gdb/gdbserver/target.h
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/target.h,v
retrieving revision 1.57
diff -u -p -r1.57 target.h
--- gdb/gdbserver/target.h	7 Oct 2011 12:06:48 -0000	1.57
+++ gdb/gdbserver/target.h	21 Oct 2011 16:24:22 -0000
@@ -380,6 +380,9 @@ struct target_ops
 
   /* Returns true if the target supports disabling randomization.  */
   int (*supports_disable_randomization) (void);
+
+  /* Read process map using qXfer packets.  */
+  int (*qxfer_process_map) (unsigned char *readbuf, CORE_ADDR offset, int len);
 };
 
 extern struct target_ops *the_target;
-- 
  Dr. Ulrich Weigand
  GNU Toolchain for Linux on System z and Cell BE
  Ulrich.Weigand@de.ibm.com



More information about the Gdb-patches mailing list