From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 14058 invoked by alias); 17 Oct 2009 01:32:59 -0000 Received: (qmail 14047 invoked by uid 22791); 17 Oct 2009 01:32:57 -0000 X-SWARE-Spam-Status: No, hits=-2.4 required=5.0 tests=AWL,BAYES_00 X-Spam-Check-By: sourceware.org Received: from smtp-outbound-2.vmware.com (HELO smtp-outbound-2.vmware.com) (65.115.85.73) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Sat, 17 Oct 2009 01:32:52 +0000 Received: from mailhost3.vmware.com (mailhost3.vmware.com [10.16.27.45]) by smtp-outbound-2.vmware.com (Postfix) with ESMTP id 7D77B2D01A; Fri, 16 Oct 2009 18:32:51 -0700 (PDT) Received: from [10.20.94.141] (msnyder-server.eng.vmware.com [10.20.94.141]) by mailhost3.vmware.com (Postfix) with ESMTP id 6BFB3CDA70; Fri, 16 Oct 2009 18:32:51 -0700 (PDT) Message-ID: <4AD91D72.1030802@vmware.com> Date: Sat, 17 Oct 2009 01:32:00 -0000 From: Michael Snyder User-Agent: Thunderbird 1.5.0.12 (X11/20080411) MIME-Version: 1.0 To: "gdb-patches@sourceware.org" , Hui Zhu Subject: [RFA, 3 of 3] save/restore process record, part 3 (save/restore) Content-Type: multipart/mixed; boundary="------------040405010506070201010605" X-IsSubscribed: yes 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: 2009-10/txt/msg00380.txt.bz2 This is a multi-part message in MIME format. --------------040405010506070201010605 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Content-length: 245 This is part 3 of the sync/update of process record save/restore patch. This part actually adds the save and restore commands, and cleans up a few loose ends. If I need to resubmit the docs, I'll do that separately. (part 4 of 3, I guess...) --------------040405010506070201010605 Content-Type: text/plain; name="tmp9.txt" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="tmp9.txt" Content-length: 19512 2009-10-16 Hui Zhu Michael Snyder * record.c (RECORD_FILE_MAGIC): New constant. (record_arch_list_cleanups): Renamed from record_message_cleanups. (bfdcore_read): New function. (netorder64): New function. (netorder32): New function. (netorder16): New function. (record_restore): New function. Restore a saved record log. (bfdcore_write): New function. (cmd_record_restore): New function. (cmd_record_save): New function. Save a record log to a file. (_initialize_record): Set up commands for save and restore. --- record.2.c 2009-10-16 15:09:49.000000000 -0700 +++ record.9.c 2009-10-16 17:43:40.000000000 -0700 @@ -23,17 +23,25 @@ #include "gdbthread.h" #include "event-top.h" #include "exceptions.h" +#include "completer.h" +#include "arch-utils.h" #include "gdbcore.h" #include "exec.h" #include "record.h" +#include "elf-bfd.h" +#include "gcore.h" +#include #include +#include #define DEFAULT_RECORD_INSN_MAX_NUM 200000 #define RECORD_IS_REPLAY \ (record_list->next || execution_direction == EXEC_REVERSE) +#define RECORD_FILE_MAGIC htonl(0x20091016) + /* These are the core structs of the process record functionality. A record_entry is a record of the value change of a register @@ -482,24 +490,24 @@ record_check_insn_num (int set_terminal) if (q) record_stop_at_limit = 0; else - error (_("Process record: inferior program stopped.")); + error (_("Process record: stopped by user.")); } } } } +static void +record_arch_list_cleanups (void *ignore) +{ + record_list_release (record_arch_list_tail); +} + /* Before inferior step (when GDB record the running message, inferior only can step), GDB will call this function to record the values to record_list. This function will call gdbarch_process_record to record the running message of inferior and set them to record_arch_list, and add it to record_list. */ -static void -record_message_cleanups (void *ignore) -{ - record_list_release (record_arch_list_tail); -} - struct record_message_args { struct regcache *regcache; enum target_signal signal; @@ -511,7 +519,7 @@ record_message (void *args) int ret; struct record_message_args *myargs = args; struct gdbarch *gdbarch = get_regcache_arch (myargs->regcache); - struct cleanup *old_cleanups = make_cleanup (record_message_cleanups, 0); + struct cleanup *old_cleanups = make_cleanup (record_arch_list_cleanups, 0); record_arch_list_head = NULL; record_arch_list_tail = NULL; @@ -676,6 +684,211 @@ record_exec_entry (struct regcache *regc } } +/* bfdcore_read -- read bytes from a core file section. */ + +static void +bfdcore_read (bfd *obfd, asection *osec, void *buf, int len, int *offset) +{ + int ret = bfd_get_section_contents (obfd, osec, buf, *offset, len); + + if (ret) + *offset += len; + else + error (_("Failed to read %d bytes from core file ('%s').\n"), + len, bfd_errmsg (bfd_get_error ())); +} + +static uint64_t +netorder64 (uint64_t fromfile) +{ + return (BYTE_ORDER == LITTLE_ENDIAN) + ? bswap_64 (fromfile) + : fromfile; +} + +static uint32_t +netorder32 (uint32_t fromfile) +{ + return (BYTE_ORDER == LITTLE_ENDIAN) + ? bswap_32 (fromfile) + : fromfile; +} + +static uint16_t +netorder16 (uint16_t fromfile) +{ + return (BYTE_ORDER == LITTLE_ENDIAN) + ? bswap_16 (fromfile) + : fromfile; +} + +/* Restore the execution log from a core_bfd file. */ + +static void +record_restore (void) +{ + uint32_t magic; + struct cleanup *old_cleanups; + struct record_entry *rec; + asection *osec; + uint32_t osec_size; + int bfd_offset = 0; + struct regcache *regcache; + + /* We restore the execution log from the open core bfd, + if there is one. */ + if (core_bfd == NULL) + return; + + /* "record_restore" can only be called when record list is empty. */ + /* FIXME mvs */ + gdb_assert (record_first.next == NULL); + + if (record_debug) + printf_filtered (_("Restoring recording from core file.\n")); + + /* Now need to find our special note section. */ + osec = bfd_get_section_by_name (core_bfd, "null0"); + osec_size = bfd_section_size (core_bfd, osec); + printf_filtered ("Find precord section %s.\n", + osec ? "succeeded" : "failed"); + if (!osec) + return; + if (record_debug) + printf_filtered ("%s", bfd_section_name (core_bfd, osec)); + + /* Check the magic code. */ + if (record_debug) + printf_filtered (_("\ + Reading 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n"), + magic); + bfdcore_read (core_bfd, osec, &magic, sizeof (magic), &bfd_offset); + if (magic != RECORD_FILE_MAGIC) + error (_("Version mis-match or file format error.")); + + /* Restore the entries in recfd into record_arch_list_head and + record_arch_list_tail. */ + record_arch_list_head = NULL; + record_arch_list_tail = NULL; + record_insn_num = 0; + old_cleanups = make_cleanup (record_arch_list_cleanups, 0); + regcache = get_current_regcache (); + + while (1) + { + int ret; + uint8_t tmpu8; + uint32_t regnum, len, signal, count; + uint64_t addr; + + /* We are finished when offset reaches osec_size. */ + if (bfd_offset >= osec_size) + break; + bfdcore_read (core_bfd, osec, &tmpu8, sizeof (tmpu8), &bfd_offset); + + switch (tmpu8) + { + case record_reg: /* reg */ + if (record_debug) + printf_filtered (_("\ + Reading register %d (1 plus %d plus %d bytes)\n"), + rec->u.reg.num, + sizeof (regnum), + rec->u.reg.len); + /* Get register number to regnum. */ + bfdcore_read (core_bfd, osec, ®num, + sizeof (regnum), &bfd_offset); + regnum = netorder32 (regnum); + + rec = record_reg_alloc (regcache, regnum); + + /* Get val. */ + bfdcore_read (core_bfd, osec, record_get_loc (rec), + rec->u.reg.len, &bfd_offset); + break; + + case record_mem: /* mem */ + if (record_debug) + printf_filtered (_("\ + Reading memory %s (1 plus %d plus %d plus %d bytes)\n"), + paddress (get_current_arch (), + rec->u.mem.addr), + sizeof (addr), + sizeof (len), + rec->u.mem.len); + /* Get len. */ + bfdcore_read (core_bfd, osec, &len, + sizeof (len), &bfd_offset); + len = netorder32 (len); + + /* Get addr. */ + bfdcore_read (core_bfd, osec, &addr, + sizeof (addr), &bfd_offset); + addr = netorder64 (addr); + + rec = record_mem_alloc (addr, len); + + /* Get val. */ + bfdcore_read (core_bfd, osec, record_get_loc (rec), + rec->u.mem.len, &bfd_offset); + break; + + case record_end: /* end */ + if (record_debug) + printf_filtered (_("\ + Reading record_end (1 + %d + %d bytes), offset == %s\n"), + sizeof (signal), + sizeof (count), + paddress (get_current_arch (), + bfd_offset)); + rec = record_end_alloc (); + record_insn_num ++; + + /* Get signal value. */ + bfdcore_read (core_bfd, osec, &signal, + sizeof (signal), &bfd_offset); + signal = netorder32 (signal); + rec->u.end.sigval = signal; + + /* Get insn count. */ + bfdcore_read (core_bfd, osec, &count, + sizeof (count), &bfd_offset); + count = netorder32 (count); + rec->u.end.insn_num = count; + record_insn_count = count + 1; + break; + + default: + error (_("Format of core file is not right.")); + break; + } + + /* Add rec to record arch list. */ + record_arch_list_add (rec); + } + + discard_cleanups (old_cleanups); + + /* Add record_arch_list_head to the end of record list. */ + record_first.next = record_arch_list_head; + record_arch_list_head->prev = &record_first; + record_arch_list_tail->next = NULL; + record_list = &record_first; + + /* Update record_insn_max_num. */ + if (record_insn_num > record_insn_max_num) + { + record_insn_max_num = record_insn_num; + warning (_("Auto increase record/replay buffer limit to %d."), + record_insn_max_num); + } + + /* Succeeded. */ + printf_filtered ("Restored records from core file.\n"); + + print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC); +} + static struct target_ops *tmp_to_resume_ops; static void (*tmp_to_resume) (struct target_ops *, ptid_t, int, enum target_signal); @@ -733,9 +946,6 @@ record_open_1 (char *name, int from_tty) { struct target_ops *t; - if (record_debug) - fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n"); - /* check exec */ if (!target_has_execution) error (_("Process record: the program is not being run.")); @@ -842,9 +1052,9 @@ record_open (char *name, int from_tty) record_beneath_to_xfer_partial = tmp_to_xfer_partial; record_beneath_to_insert_breakpoint = tmp_to_insert_breakpoint; record_beneath_to_remove_breakpoint = tmp_to_remove_breakpoint; - - //if (strcmp (current_target.to_shortname, "record_core") == 0) - //record_load (); + /* FIXME why doesn't this go in record_core_open? */ + if (strcmp (current_target.to_shortname, "record_core") == 0) + record_restore (); } static void @@ -1643,6 +1853,283 @@ cmd_record_start (char *args, int from_t execute_command ("target record", from_tty); } +/* Record log save-file format + Version 1 (never released) + + Header: + 4 bytes: magic number htonl(0x20090829). + NOTE: be sure to change whenever this file format changes! + + Records: + record_end: + 1 byte: record type (record_end). + record_reg: + 1 byte: record type (record_reg). + 8 bytes: register id (network byte order). + MAX_REGISTER_SIZE bytes: register value. + record_mem: + 1 byte: record type (record_mem). + 8 bytes: memory length (network byte order). + 8 bytes: memory address (network byte order). + n bytes: memory value (n == memory length). + + Version 2 + 4 bytes: magic number htonl(0x20091016). + NOTE: be sure to change whenever this file format changes! + + Records: + record_end: + 1 byte: record type (record_end). + 4 bytes: signal + 4 bytes: instruction count + record_reg: + 1 byte: record type (record_reg). + 4 bytes: register id (network byte order). + n bytes: register value (n == register size). + record_mem: + 1 byte: record type (record_mem). + 4 bytes: memory length (network byte order). + 8 bytes: memory address (network byte order). + n bytes: memory value (n == memory length). + +*/ + +/* bfdcore_write -- write bytes into a core file section. */ + +static void +bfdcore_write (bfd *obfd, asection *osec, void *buf, int len, int *offset) +{ + int ret = bfd_set_section_contents (obfd, osec, buf, *offset, len); + + if (ret) + *offset += len; + else + error (_("Failed to write %d bytes to core file ('%s').\n"), + len, bfd_errmsg (bfd_get_error ())); +} + +/* Restore the execution log from a file. We use a modified elf + corefile format, with an extra section for our data. */ + +static void +cmd_record_restore (char *args, int from_tty) +{ + core_file_command (args, from_tty); + record_open (args, from_tty); +} + +/* Save the execution log to a file. We use a modified elf corefile + format, with an extra section for our data. */ + +static void +cmd_record_save (char *args, int from_tty) +{ + char *recfilename, recfilename_buffer[40]; + int recfd; + struct record_entry *cur_record_list; + uint32_t magic; + struct regcache *regcache; + struct gdbarch *gdbarch; + struct cleanup *old_cleanups; + struct cleanup *set_cleanups; + bfd *obfd; + int save_size = 0; + asection *osec = NULL; + int bfd_offset = 0; + + if (strcmp (current_target.to_shortname, "record") != 0) + error (_("Process record is not started.\n")); + + if (args && *args) + recfilename = args; + else + { + /* Default recfile name is "gdb_record.PID". */ + snprintf (recfilename_buffer, sizeof (recfilename_buffer), + "gdb_record.%d", PIDGET (inferior_ptid)); + recfilename = recfilename_buffer; + } + + /* Open the save file. */ + if (record_debug) + printf_filtered (_("Saving recording to file '%s'\n"), + recfilename); + /* Open the output file. */ + obfd = create_gcore_bfd (recfilename); + /* Need a cleanup that will close the file (FIXME: delete it?). */ + old_cleanups = make_cleanup_bfd_close (obfd); + + /* Save the current record entry to "cur_record_list". */ + cur_record_list = record_list; + + /* Get the values of regcache and gdbarch. */ + regcache = get_current_regcache (); + gdbarch = get_regcache_arch (regcache); + + /* Disable the GDB operation record. */ + set_cleanups = record_gdb_operation_disable_set (); + + /* Reverse execute to the begin of record list. */ + while (1) + { + /* Check for beginning and end of log. */ + if (record_list == &record_first) + break; + + record_exec_entry (regcache, gdbarch, record_list); + + if (record_list->prev) + record_list = record_list->prev; + } + + /* Compute the size needed for the extra bfd section. */ + save_size = 4; /* magic cookie */ + for (record_list = record_first.next; record_list; + record_list = record_list->next) + switch (record_list->type) + { + case record_end: + save_size += 1 + 4 + 4; + break; + case record_reg: + save_size += 1 + 4 + record_list->u.reg.len; + break; + case record_mem: + save_size += 1 + 4 + 8 + record_list->u.mem.len; + break; + } + + /* Make the new bfd section. */ + osec = bfd_make_section_anyway_with_flags (obfd, "precord", + SEC_HAS_CONTENTS + | SEC_READONLY); + if (osec == NULL) + error (_("Failed to create 'precord' section for corefile: %s"), + bfd_errmsg (bfd_get_error ())); + bfd_set_section_size (obfd, osec, save_size); + bfd_set_section_vma (obfd, osec, 0); + bfd_set_section_alignment (obfd, osec, 0); + bfd_section_lma (obfd, osec) = 0; + + /* Save corefile state. */ + write_gcore_file (obfd); + + /* Write out the record log. */ + /* Write the magic code. */ + magic = RECORD_FILE_MAGIC; + if (record_debug) + printf_filtered (_("\ + Writing 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n"), + magic); + bfdcore_write (obfd, osec, &magic, sizeof (magic), &bfd_offset); + + /* Save the entries to recfd and forward execute to the end of + record list. */ + record_list = &record_first; + while (1) + { + /* Save entry. */ + if (record_list != &record_first) + { + uint8_t type; + uint32_t regnum, len, signal, count; + uint64_t addr; + + type = record_list->type; + bfdcore_write (obfd, osec, &type, sizeof (type), &bfd_offset); + + switch (record_list->type) + { + case record_reg: /* reg */ + if (record_debug) + printf_filtered (_("\ + Writing register %d (1 plus %d plus %d bytes)\n"), + record_list->u.reg.num, + sizeof (regnum), + record_list->u.reg.len); + + /* Write regnum. */ + regnum = netorder32 (record_list->u.reg.num); + bfdcore_write (obfd, osec, ®num, + sizeof (regnum), &bfd_offset); + + /* Write regval. */ + bfdcore_write (obfd, osec, record_get_loc (record_list), + record_list->u.reg.len, &bfd_offset); + break; + + case record_mem: /* mem */ + if (record_debug) + printf_filtered (_("\ + Writing memory %s (1 plus %d plus %d plus %d bytes)\n"), + paddress (gdbarch, + record_list->u.mem.addr), + sizeof (addr), + sizeof (len), + record_list->u.mem.len); + + /* Write memlen. */ + len = netorder32 (record_list->u.mem.len); + bfdcore_write (obfd, osec, &len, sizeof (len), &bfd_offset); + + /* Write memaddr. */ + addr = netorder64 (record_list->u.mem.addr); + bfdcore_write (obfd, osec, &addr, + sizeof (addr), &bfd_offset); + + /* Write memval. */ + bfdcore_write (obfd, osec, record_get_loc (record_list), + record_list->u.mem.len, &bfd_offset); + break; + + case record_end: + if (record_debug) + printf_filtered (_("\ + Writing record_end (1 + %d + %d bytes)\n"), + sizeof (signal), + sizeof (count)); + /* Write signal value. */ + signal = netorder32 (record_list->u.end.sigval); + bfdcore_write (obfd, osec, &signal, + sizeof (signal), &bfd_offset); + + /* Write insn count. */ + count = netorder32 (record_list->u.end.insn_num); + bfdcore_write (obfd, osec, &count, + sizeof (count), &bfd_offset); + break; + } + } + + /* Execute entry. */ + record_exec_entry (regcache, gdbarch, record_list); + + if (record_list->next) + record_list = record_list->next; + else + break; + } + + /* Reverse execute to cur_record_list. */ + while (1) + { + /* Check for beginning and end of log. */ + if (record_list == cur_record_list) + break; + + record_exec_entry (regcache, gdbarch, record_list); + + if (record_list->prev) + record_list = record_list->prev; + } + + do_cleanups (set_cleanups); + do_cleanups (old_cleanups); + + /* Succeeded. */ + printf_filtered (_("Saved recfile %s.\n"), recfilename); +} + /* Truncate the record log from the present point of replay until the end. */ @@ -1750,6 +2237,8 @@ info_record_command (char *args, int fro void _initialize_record (void) { + struct cmd_list_element *c; + /* Init record_first. */ record_first.prev = NULL; record_first.next = NULL; @@ -1768,10 +2257,12 @@ _initialize_record (void) NULL, show_record_debug, &setdebuglist, &showdebuglist); - add_prefix_cmd ("record", class_obscure, cmd_record_start, + c = add_prefix_cmd ("record", class_obscure, cmd_record_start, _("Abbreviated form of \"target record\" command."), &record_cmdlist, "record ", 0, &cmdlist); + set_cmd_completer (c, filename_completer); add_com_alias ("rec", "record", class_obscure, 1); + add_prefix_cmd ("record", class_support, set_record_command, _("Set record options"), &set_record_cmdlist, "set record ", 0, &setlist); @@ -1785,6 +2276,17 @@ _initialize_record (void) "info record ", 0, &infolist); add_alias_cmd ("rec", "record", class_obscure, 1, &infolist); + c = add_cmd ("save", class_obscure, cmd_record_save, + _("Save the execution log to a file.\n\ +Argument is optional filename.\n\ +Default filename is 'gdb_record.'."), + &record_cmdlist); + + c = add_cmd ("restore", class_obscure, cmd_record_restore, + _("Restore the execution log from a file.\n\ +Argument is filename. File must be created with 'record dump'."), + &record_cmdlist); + set_cmd_completer (c, filename_completer); add_cmd ("delete", class_obscure, cmd_record_delete, _("Delete the rest of execution log and start recording it anew."), --------------040405010506070201010605--