From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 6181 invoked by alias); 21 Oct 2011 18:57:27 -0000 Received: (qmail 6161 invoked by uid 22791); 21 Oct 2011 18:57:25 -0000 X-SWARE-Spam-Status: No, hits=-1.9 required=5.0 tests=AWL,BAYES_00,MSGID_FROM_MTA_HEADER,RP_MATCHES_RCVD,TW_CP,TW_QX X-Spam-Check-By: sourceware.org Received: from mtagate7.uk.ibm.com (HELO mtagate7.uk.ibm.com) (194.196.100.167) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Fri, 21 Oct 2011 18:57:07 +0000 Received: from d06nrmr1707.portsmouth.uk.ibm.com (d06nrmr1707.portsmouth.uk.ibm.com [9.149.39.225]) by mtagate7.uk.ibm.com (8.13.1/8.13.1) with ESMTP id p9LIv5MJ025983 for ; Fri, 21 Oct 2011 18:57:05 GMT Received: from d06av02.portsmouth.uk.ibm.com (d06av02.portsmouth.uk.ibm.com [9.149.37.228]) by d06nrmr1707.portsmouth.uk.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id p9LIv5vS937990 for ; Fri, 21 Oct 2011 19:57:05 +0100 Received: from d06av02.portsmouth.uk.ibm.com (loopback [127.0.0.1]) by d06av02.portsmouth.uk.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id p9LIv5QT013331 for ; Fri, 21 Oct 2011 12:57:05 -0600 Received: from tuxmaker.boeblingen.de.ibm.com (tuxmaker.boeblingen.de.ibm.com [9.152.85.9]) by d06av02.portsmouth.uk.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with SMTP id p9LIv4j0013316 for ; Fri, 21 Oct 2011 12:57:04 -0600 Message-Id: <201110211857.p9LIv4j0013316@d06av02.portsmouth.uk.ibm.com> Received: by tuxmaker.boeblingen.de.ibm.com (sSMTP sendmail emulation); Fri, 21 Oct 2011 20:57:04 +0200 Subject: [rfc][3/3] Remote core file generation: memory map To: gdb-patches@sourceware.org Date: Fri, 21 Oct 2011 19:54:00 -0000 From: "Ulrich Weigand" MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2011-10/txt/msg00606.txt.bz2 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 (¤t_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 #include +#include #include #include #include @@ -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, "\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, + "\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, "\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