From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 21398 invoked by alias); 24 Aug 2009 17:56:14 -0000 Received: (qmail 21370 invoked by uid 22791); 24 Aug 2009 17:56:02 -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-1.vmware.com (HELO smtp-outbound-1.vmware.com) (65.115.85.69) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Mon, 24 Aug 2009 17:55:51 +0000 Received: from mailhost4.vmware.com (mailhost4.vmware.com [10.16.67.124]) by smtp-outbound-1.vmware.com (Postfix) with ESMTP id 61A9213048; Mon, 24 Aug 2009 10:55:48 -0700 (PDT) Received: from [10.20.94.141] (msnyder-server.eng.vmware.com [10.20.94.141]) by mailhost4.vmware.com (Postfix) with ESMTP id 555CDC9A1C; Mon, 24 Aug 2009 10:55:48 -0700 (PDT) Message-ID: <4A92D317.9010002@vmware.com> Date: Mon, 24 Aug 2009 18:32:00 -0000 From: Michael Snyder User-Agent: Thunderbird 1.5.0.12 (X11/20080411) MIME-Version: 1.0 To: Hui Zhu CC: Eli Zaretskii , "gdb-patches@sourceware.org" Subject: Re: [RFA/RFC] Add dump and load command to process record and replay References: <4A7B9F49.9030202@vmware.com> <83ws5gm30b.fsf@gnu.org> <4A7C625B.8080005@vmware.com> <4A7F5410.4000400@vmware.com> <4A91CEEC.5000802@vmware.com> In-Reply-To: Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit 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-08/txt/msg00389.txt.bz2 Hui Zhu wrote: > On Mon, Aug 24, 2009 at 07:21, Michael Snyder wrote: >> Hui Zhu wrote: >> >>> Hi Michael, >>> >>> I make a new version patch. It has a lot of changes. >>> Remove record_core and add a new target record_core for core target to >>> make the code more clear. >>> Make the load together with record_open. >>> >>> Please help me review it. >> Hi Hui, >> >> In this review, I'm going to comment only on the parts of the >> patch that relate to the record_core_ops (ie. pushing the >> record stratum on top of the core file stratum). >> >> Those parts of the patch are much improved. I like this >> version a lot better. Thanks for reworking it. >> > > > Hi Michael, > > Thanks for your review. I make a new patch that update the memset code to: Way cool, thanks! Now I can comment on the dump/load part of the patch. It does not seem as if previous issues have been addressed: 1) Core file and record file are separate, no way to verify that they correspond to each other. 2) Previous log not cleared when loading a new log. Have you considered combining this patch with the idea that I proposed -- storing the record log in the core file? Seems like that would be very easy now -- gdb already has the core file open, and there is a global pointer to the BFD. See gdbcore.h:extern bfd* core_bfd; In fact, just for fun, I've merged the two patches in my local tree. It seems to work quite well. I can just post it here if you like, or here's a hint to show you how it goes: static void record_load (void) { int recfd; uint32_t magic; struct cleanup *old_cleanups2; struct record_entry *rec; int insn_number = 0; asection *osec; void nullify_last_target_wait_ptid (void); /* We load the execution log from the open core bfd, if there is one. */ if (core_bfd == NULL) return; if (record_debug) fprintf_filtered (gdb_stdlog, _("Restoring recording from core file.\n")); /* Now need to find our special note section. */ osec = bfd_get_section_by_name (core_bfd, "null0"); > if (target_read_memory (entry->u.mem.addr, mem, entry->u.mem.len)) > { > record_list->u.mem.mem_entry_not_accessible = 1; > if (record_debug) > warning (_("Process record: error reading memory at " > "addr = %s len = %d."), > paddress (gdbarch, entry->u.mem.addr), > entry->u.mem.len); > } > else > { > if (target_write_memory (entry->u.mem.addr, entry->u.mem.val, > entry->u.mem.len)) > { > record_list->u.mem.mem_entry_not_accessible = 1; > if (record_debug) > warning (_("Process record: error writing memory at " > "addr = %s len = %d."), > paddress (gdbarch, entry->u.mem.addr), > entry->u.mem.len); > } > else > memcpy (entry->u.mem.val, mem, entry->u.mem.len); > } > > Please help me review it. > > Thanks, > Hui > > --- > record.c | 951 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- > 1 file changed, 825 insertions(+), 126 deletions(-) > > --- a/record.c > +++ b/record.c > @@ -23,15 +23,23 @@ > #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 > #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(0x20090726) > + > /* These are the core struct of record function. > > An record_entry is a record of the value change of a register > @@ -78,9 +86,22 @@ struct record_entry > } u; > }; > > +struct record_core_buf_entry > +{ > + struct record_core_buf_entry *prev; > + struct target_section *p; > + bfd_byte *buf; > +}; > + > /* This is the debug switch for process record. */ > int record_debug = 0; > > +/* Record buf with core target. */ > +static gdb_byte *record_core_regbuf = NULL; > +static struct target_section *record_core_start; > +static struct target_section *record_core_end; > +static struct record_core_buf_entry *record_core_buf_list = NULL; > + > /* These list is for execution log. */ > static struct record_entry record_first; > static struct record_entry *record_list = &record_first; > @@ -94,6 +115,7 @@ static int record_insn_num = 0; > > /* The target_ops of process record. */ > static struct target_ops record_ops; > +static struct target_ops record_core_ops; > > /* The beneath function pointers. */ > static struct target_ops *record_beneath_to_resume_ops; > @@ -169,7 +191,7 @@ record_list_release_next (void) > } > > static void > -record_list_release_first (void) > +record_list_release_first_insn (void) > { > struct record_entry *tmp = NULL; > enum record_type type; > @@ -340,30 +362,30 @@ record_check_insn_num (int set_terminal) > if (q) > record_stop_at_limit = 0; > else > - error (_("Process record: inferior program stopped.")); > + error (_("Process record: stoped 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); > -} > - > static int > record_message (void *args) > { > int ret; > struct regcache *regcache = args; > - 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; > @@ -386,7 +408,7 @@ record_message (void *args) > record_list = record_arch_list_tail; > > if (record_insn_num == record_insn_max_num && record_insn_max_num) > - record_list_release_first (); > + record_list_release_first_insn (); > else > record_insn_num++; > > @@ -416,13 +438,291 @@ record_gdb_operation_disable_set (void) > return old_cleanups; > } > > +static inline void > +record_exec_entry (struct regcache *regcache, struct gdbarch *gdbarch, > + struct record_entry *entry) > +{ > + switch (entry->type) > + { > + case record_reg: /* reg */ > + { > + gdb_byte reg[MAX_REGISTER_SIZE]; > + > + if (record_debug > 1) > + fprintf_unfiltered (gdb_stdlog, > + "Process record: record_reg %s to " > + "inferior num = %d.\n", > + host_address_to_string (entry), > + entry->u.reg.num); > + > + regcache_cooked_read (regcache, entry->u.reg.num, reg); > + regcache_cooked_write (regcache, entry->u.reg.num, entry->u.reg.val); > + memcpy (entry->u.reg.val, reg, MAX_REGISTER_SIZE); > + } > + break; > + > + case record_mem: /* mem */ > + { > + if (!record_list->u.mem.mem_entry_not_accessible) > + { > + gdb_byte *mem = alloca (entry->u.mem.len); > + > + if (record_debug > 1) > + fprintf_unfiltered (gdb_stdlog, > + "Process record: record_mem %s to " > + "inferior addr = %s len = %d.\n", > + host_address_to_string (entry), > + paddress (gdbarch, entry->u.mem.addr), > + record_list->u.mem.len); > + > + if (target_read_memory (entry->u.mem.addr, mem, entry->u.mem.len)) > + { > + record_list->u.mem.mem_entry_not_accessible = 1; > + if (record_debug) > + warning (_("Process record: error reading memory at " > + "addr = %s len = %d."), > + paddress (gdbarch, entry->u.mem.addr), > + entry->u.mem.len); > + } > + else > + { > + if (target_write_memory (entry->u.mem.addr, entry->u.mem.val, > + entry->u.mem.len)) > + { > + record_list->u.mem.mem_entry_not_accessible = 1; > + if (record_debug) > + warning (_("Process record: error writing memory at " > + "addr = %s len = %d."), > + paddress (gdbarch, entry->u.mem.addr), > + entry->u.mem.len); > + } > + else > + memcpy (entry->u.mem.val, mem, entry->u.mem.len); > + } > + } > + } > + break; > + } > +} > + > +static inline void > +record_read_dump (char *recfilename, int fildes, void *buf, size_t nbyte) > +{ > + if (read (fildes, buf, nbyte) != nbyte) > + error (_("Failed to read dump of execution records in '%s'."), > + recfilename); > +} > + > static void > -record_open (char *name, int from_tty) > +record_fd_cleanups (void *recfdp) > { > - struct target_ops *t; > + int recfd = *(int *) recfdp; > + close (recfd); > +} > > - if (record_debug) > - fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n"); > +/* Load the execution log from a file. */ > + > +static void > +record_load (char *name) > +{ > + int recfd; > + uint32_t magic; > + struct cleanup *old_cleanups; > + struct cleanup *old_cleanups2; > + struct record_entry *rec; > + int insn_number = 0; > + > + if (!name || (name && !*name)) > + return; > + > + /* Open the load file. */ > + recfd = open (name, O_RDONLY | O_BINARY); > + if (recfd < 0) > + error (_("Failed to open '%s' for loading execution records: %s"), > + name, strerror (errno)); > + old_cleanups = make_cleanup (record_fd_cleanups, &recfd); > + > + /* Check the magic code. */ > + record_read_dump (name, recfd, &magic, 4); > + if (magic != RECORD_FILE_MAGIC) > + error (_("'%s' is not a valid dump of execution records."), name); > + > + /* Load the entries in recfd to the record_arch_list_head and > + record_arch_list_tail. */ > + record_arch_list_head = NULL; > + record_arch_list_tail = NULL; > + old_cleanups2 = make_cleanup (record_arch_list_cleanups, 0); > + > + while (1) > + { > + int ret; > + uint8_t tmpu8; > + uint64_t tmpu64; > + > + ret = read (recfd, &tmpu8, 1); > + if (ret < 0) > + error (_("Failed to read dump of execution records in '%s'."), name); > + if (ret == 0) > + break; > + > + switch (tmpu8) > + { > + case record_reg: /* reg */ > + rec = (struct record_entry *) xmalloc (sizeof (struct record_entry)); > + rec->u.reg.val = (gdb_byte *) xmalloc (MAX_REGISTER_SIZE); > + rec->prev = NULL; > + rec->next = NULL; > + rec->type = record_reg; > + > + /* Get num. */ > + record_read_dump (name, recfd, &tmpu64, 8); > + if (BYTE_ORDER == LITTLE_ENDIAN) > + tmpu64 = bswap_64 (tmpu64); > + rec->u.reg.num = tmpu64; > + > + /* Get val. */ > + record_read_dump (name, recfd, rec->u.reg.val, MAX_REGISTER_SIZE); > + > + if (record_debug) > + fprintf_unfiltered (gdb_stdlog, _("\ > +Reading register %d (1 plus 8 plus %d bytes)\n"), > + rec->u.reg.num, > + MAX_REGISTER_SIZE); > + > + record_arch_list_add (rec); > + break; > + > + case record_mem: /* mem */ > + rec = (struct record_entry *) xmalloc (sizeof (struct record_entry)); > + rec->prev = NULL; > + rec->next = NULL; > + rec->type = record_mem; > + > + /* Get addr. */ > + record_read_dump (name, recfd, &tmpu64, 8); > + if (BYTE_ORDER == LITTLE_ENDIAN) > + tmpu64 = bswap_64 (tmpu64); > + rec->u.mem.addr = tmpu64; > + > + /* Get len. */ > + record_read_dump (name, recfd, &tmpu64, 8); > + if (BYTE_ORDER == LITTLE_ENDIAN) > + tmpu64 = bswap_64 (tmpu64); > + rec->u.mem.len = tmpu64; > + rec->u.mem.mem_entry_not_accessible = 0; > + rec->u.mem.val = (gdb_byte *) xmalloc (rec->u.mem.len); > + > + /* Get val. */ > + record_read_dump (name, recfd, rec->u.mem.val, rec->u.mem.len); > + > + if (record_debug) > + fprintf_unfiltered (gdb_stdlog, _("\ > +Reading memory %s (1 plus 8 plus 8 bytes plus %d bytes)\n"), > + paddress (get_current_arch (), > + rec->u.mem.addr), > + rec->u.mem.len); > + > + record_arch_list_add (rec); > + break; > + > + case record_end: /* end */ > + if (record_debug) > + fprintf_unfiltered (gdb_stdlog, > + _("Reading record_end (1 byte)\n")); > + > + rec = (struct record_entry *) xmalloc (sizeof (struct record_entry)); > + rec->prev = NULL; > + rec->next = NULL; > + rec->type = record_end; > + record_arch_list_add (rec); > + insn_number ++; > + break; > + > + default: > + error (_("Format of '%s' is not right."), name); > + break; > + } > + } > + > + discard_cleanups (old_cleanups2); > + > + /* Add record_arch_list_head to the end of record list. */ > + for (rec = record_list; rec->next; rec = rec->next); > + rec->next = record_arch_list_head; > + record_arch_list_head->prev = rec; > + > + /* Update record_insn_num and record_insn_max_num. */ > + record_insn_num += insn_number; > + 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); > + } > + > + do_cleanups (old_cleanups); > + > + /* Succeeded. */ > + fprintf_filtered (gdb_stdout, "Loaded recfile %s.\n", name); > +} > + > +static struct target_ops *tmp_to_resume_ops; > +static void (*tmp_to_resume) (struct target_ops *, ptid_t, int, > + enum target_signal); > +static struct target_ops *tmp_to_wait_ops; > +static ptid_t (*tmp_to_wait) (struct target_ops *, ptid_t, > + struct target_waitstatus *, > + int); > +static struct target_ops *tmp_to_store_registers_ops; > +static void (*tmp_to_store_registers) (struct target_ops *, > + struct regcache *, > + int regno); > +static struct target_ops *tmp_to_xfer_partial_ops; > +static LONGEST (*tmp_to_xfer_partial) (struct target_ops *ops, > + enum target_object object, > + const char *annex, > + gdb_byte *readbuf, > + const gdb_byte *writebuf, > + ULONGEST offset, > + LONGEST len); > +static int (*tmp_to_insert_breakpoint) (struct gdbarch *, > + struct bp_target_info *); > +static int (*tmp_to_remove_breakpoint) (struct gdbarch *, > + struct bp_target_info *); > + > +static void > +record_core_open_1 (char *name, int from_tty) > +{ > + struct regcache *regcache = get_current_regcache (); > + int regnum = gdbarch_num_regs (get_regcache_arch (regcache)); > + int i; > + > + if (!name || (name && !*name)) > + error (_("Argument for gdb record filename required.\n")); > + > + /* Get record_core_regbuf. */ > + target_fetch_registers (regcache, -1); > + record_core_regbuf = xmalloc (MAX_REGISTER_SIZE * regnum); > + for (i = 0; i < regnum; i ++) > + regcache_raw_collect (regcache, i, > + record_core_regbuf + MAX_REGISTER_SIZE * i); > + > + /* Get record_core_start and record_core_end. */ > + if (build_section_table (core_bfd, &record_core_start, &record_core_end)) > + { > + xfree (record_core_regbuf); > + record_core_regbuf = NULL; > + error (_("\"%s\": Can't find sections: %s"), > + bfd_get_filename (core_bfd), bfd_errmsg (bfd_get_error ())); > + } > + > + push_target (&record_core_ops); > +} > + > +static void > +record_open_1 (char *name, int from_tty) > +{ > + struct target_ops *t; > > /* check exec */ > if (!target_has_execution) > @@ -438,6 +738,28 @@ record_open (char *name, int from_tty) > error (_("Process record: the current architecture doesn't support " > "record function.")); > > + if (!tmp_to_resume) > + error (_("Process record can't get to_resume.")); > + if (!tmp_to_wait) > + error (_("Process record can't get to_wait.")); > + if (!tmp_to_store_registers) > + error (_("Process record can't get to_store_registers.")); > + if (!tmp_to_insert_breakpoint) > + error (_("Process record can't get to_insert_breakpoint.")); > + if (!tmp_to_remove_breakpoint) > + error (_("Process record can't get to_remove_breakpoint.")); > + > + push_target (&record_ops); > +} > + > +static void > +record_open (char *name, int from_tty) > +{ > + struct target_ops *t; > + > + if (record_debug) > + fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n"); > + > /* Check if record target is already running. */ > if (current_target.to_stratum == record_stratum) > { > @@ -447,70 +769,102 @@ record_open (char *name, int from_tty) > return; > } > > - /*Reset the beneath function pointers. */ > - record_beneath_to_resume = NULL; > - record_beneath_to_wait = NULL; > - record_beneath_to_store_registers = NULL; > - record_beneath_to_xfer_partial = NULL; > - record_beneath_to_insert_breakpoint = NULL; > - record_beneath_to_remove_breakpoint = NULL; > + /* Reset the tmp beneath pointers. */ > + tmp_to_resume_ops = NULL; > + tmp_to_resume = NULL; > + tmp_to_wait_ops = NULL; > + tmp_to_wait = NULL; > + tmp_to_store_registers_ops = NULL; > + tmp_to_store_registers = NULL; > + tmp_to_xfer_partial_ops = NULL; > + tmp_to_xfer_partial = NULL; > + tmp_to_insert_breakpoint = NULL; > + tmp_to_remove_breakpoint = NULL; > > /* Set the beneath function pointers. */ > for (t = current_target.beneath; t != NULL; t = t->beneath) > { > - if (!record_beneath_to_resume) > + if (!tmp_to_resume) > { > - record_beneath_to_resume = t->to_resume; > - record_beneath_to_resume_ops = t; > + tmp_to_resume = t->to_resume; > + tmp_to_resume_ops = t; > } > - if (!record_beneath_to_wait) > + if (!tmp_to_wait) > { > - record_beneath_to_wait = t->to_wait; > - record_beneath_to_wait_ops = t; > + tmp_to_wait = t->to_wait; > + tmp_to_wait_ops = t; > } > - if (!record_beneath_to_store_registers) > + if (!tmp_to_store_registers) > { > - record_beneath_to_store_registers = t->to_store_registers; > - record_beneath_to_store_registers_ops = t; > + tmp_to_store_registers = t->to_store_registers; > + tmp_to_store_registers_ops = t; > } > - if (!record_beneath_to_xfer_partial) > + if (!tmp_to_xfer_partial) > { > - record_beneath_to_xfer_partial = t->to_xfer_partial; > - record_beneath_to_xfer_partial_ops = t; > + tmp_to_xfer_partial = t->to_xfer_partial; > + tmp_to_xfer_partial_ops = t; > } > - if (!record_beneath_to_insert_breakpoint) > - record_beneath_to_insert_breakpoint = t->to_insert_breakpoint; > - if (!record_beneath_to_remove_breakpoint) > - record_beneath_to_remove_breakpoint = t->to_remove_breakpoint; > + if (!tmp_to_insert_breakpoint) > + tmp_to_insert_breakpoint = t->to_insert_breakpoint; > + if (!tmp_to_remove_breakpoint) > + tmp_to_remove_breakpoint = t->to_remove_breakpoint; > } > - if (!record_beneath_to_resume) > - error (_("Process record can't get to_resume.")); > - if (!record_beneath_to_wait) > - error (_("Process record can't get to_wait.")); > - if (!record_beneath_to_store_registers) > - error (_("Process record can't get to_store_registers.")); > - if (!record_beneath_to_xfer_partial) > + if (!tmp_to_xfer_partial) > error (_("Process record can't get to_xfer_partial.")); > - if (!record_beneath_to_insert_breakpoint) > - error (_("Process record can't get to_insert_breakpoint.")); > - if (!record_beneath_to_remove_breakpoint) > - error (_("Process record can't get to_remove_breakpoint.")); > > - push_target (&record_ops); > + if (current_target.to_stratum == core_stratum) > + record_core_open_1 (name, from_tty); > + else > + record_open_1 (name, from_tty); > > /* Reset */ > record_insn_num = 0; > record_list = &record_first; > record_list->next = NULL; > + > + /* Set the tmp beneath pointers to beneath pointers. */ > + record_beneath_to_resume_ops = tmp_to_resume_ops; > + record_beneath_to_resume = tmp_to_resume; > + record_beneath_to_wait_ops = tmp_to_wait_ops; > + record_beneath_to_wait = tmp_to_wait; > + record_beneath_to_store_registers_ops = tmp_to_store_registers_ops; > + record_beneath_to_store_registers = tmp_to_store_registers; > + record_beneath_to_xfer_partial_ops = tmp_to_xfer_partial_ops; > + 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; > + > + /* Load if there is argument. */ > + record_load (name); > } > > static void > record_close (int quitting) > { > + struct record_core_buf_entry *entry; > + > if (record_debug) > fprintf_unfiltered (gdb_stdlog, "Process record: record_close\n"); > > record_list_release (record_list); > + > + /* Release record_core_regbuf. */ > + if (record_core_regbuf) > + { > + xfree (record_core_regbuf); > + record_core_regbuf = NULL; > + } > + > + /* Release record_core_buf_list. */ > + if (record_core_buf_list) > + { > + for (entry = record_core_buf_list->prev; entry; entry = entry->prev) > + { > + xfree (record_core_buf_list); > + record_core_buf_list = entry; > + } > + record_core_buf_list = NULL; > + } > } > > static int record_resume_step = 0; > @@ -584,7 +938,7 @@ record_wait (struct target_ops *ops, > "record_resume_step = %d\n", > record_resume_step); > > - if (!RECORD_IS_REPLAY) > + if (!RECORD_IS_REPLAY && ops != &record_core_ops) > { > if (record_resume_error) > { > @@ -712,76 +1066,9 @@ record_wait (struct target_ops *ops, > break; > } > > - /* Set ptid, register and memory according to record_list. */ > - if (record_list->type == record_reg) > - { > - /* reg */ > - gdb_byte reg[MAX_REGISTER_SIZE]; > - if (record_debug > 1) > - fprintf_unfiltered (gdb_stdlog, > - "Process record: record_reg %s to " > - "inferior num = %d.\n", > - host_address_to_string (record_list), > - record_list->u.reg.num); > - regcache_cooked_read (regcache, record_list->u.reg.num, reg); > - regcache_cooked_write (regcache, record_list->u.reg.num, > - record_list->u.reg.val); > - memcpy (record_list->u.reg.val, reg, MAX_REGISTER_SIZE); > - } > - else if (record_list->type == record_mem) > - { > - /* mem */ > - /* Nothing to do if the entry is flagged not_accessible. */ > - if (!record_list->u.mem.mem_entry_not_accessible) > - { > - gdb_byte *mem = alloca (record_list->u.mem.len); > - if (record_debug > 1) > - fprintf_unfiltered (gdb_stdlog, > - "Process record: record_mem %s to " > - "inferior addr = %s len = %d.\n", > - host_address_to_string (record_list), > - paddress (gdbarch, > - record_list->u.mem.addr), > - record_list->u.mem.len); > + record_exec_entry (regcache, gdbarch, record_list); > > - if (target_read_memory (record_list->u.mem.addr, mem, > - record_list->u.mem.len)) > - { > - if (execution_direction != EXEC_REVERSE) > - error (_("Process record: error reading memory at " > - "addr = %s len = %d."), > - paddress (gdbarch, record_list->u.mem.addr), > - record_list->u.mem.len); > - else > - /* Read failed -- > - flag entry as not_accessible. */ > - record_list->u.mem.mem_entry_not_accessible = 1; > - } > - else > - { > - if (target_write_memory (record_list->u.mem.addr, > - record_list->u.mem.val, > - record_list->u.mem.len)) > - { > - if (execution_direction != EXEC_REVERSE) > - error (_("Process record: error writing memory at " > - "addr = %s len = %d."), > - paddress (gdbarch, record_list->u.mem.addr), > - record_list->u.mem.len); > - else > - /* Write failed -- > - flag entry as not_accessible. */ > - record_list->u.mem.mem_entry_not_accessible = 1; > - } > - else > - { > - memcpy (record_list->u.mem.val, mem, > - record_list->u.mem.len); > - } > - } > - } > - } > - else > + if (record_list->type == record_end) > { > if (record_debug > 1) > fprintf_unfiltered (gdb_stdlog, > @@ -901,6 +1188,7 @@ record_kill (struct target_ops *ops) > fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n"); > > unpush_target (&record_ops); > + > target_kill (); > } > > @@ -945,7 +1233,7 @@ record_registers_change (struct regcache > record_list = record_arch_list_tail; > > if (record_insn_num == record_insn_max_num && record_insn_max_num) > - record_list_release_first (); > + record_list_release_first_insn (); > else > record_insn_num++; > } > @@ -1058,7 +1346,7 @@ record_xfer_partial (struct target_ops * > record_list = record_arch_list_tail; > > if (record_insn_num == record_insn_max_num && record_insn_max_num) > - record_list_release_first (); > + record_list_release_first_insn (); > else > record_insn_num++; > } > @@ -1138,6 +1426,191 @@ init_record_ops (void) > } > > static void > +record_core_resume (struct target_ops *ops, ptid_t ptid, int step, > + enum target_signal siggnal) > +{ > + record_resume_step = step; > + record_resume_siggnal = siggnal; > +} > + > +static void > +record_core_kill (struct target_ops *ops) > +{ > + if (record_debug) > + fprintf_unfiltered (gdb_stdlog, "Process record: record_core_kill\n"); > + > + unpush_target (&record_core_ops); > +} > + > +static void > +record_core_fetch_registers (struct target_ops *ops, > + struct regcache *regcache, > + int regno) > +{ > + if (regno < 0) > + { > + int num = gdbarch_num_regs (get_regcache_arch (regcache)); > + int i; > + > + for (i = 0; i < num; i ++) > + regcache_raw_supply (regcache, i, > + record_core_regbuf + MAX_REGISTER_SIZE * i); > + } > + else > + regcache_raw_supply (regcache, regno, > + record_core_regbuf + MAX_REGISTER_SIZE * regno); > +} > + > +static void > +record_core_prepare_to_store (struct regcache *regcache) > +{ > +} > + > +static void > +record_core_store_registers (struct target_ops *ops, > + struct regcache *regcache, > + int regno) > +{ > + if (record_gdb_operation_disable) > + regcache_raw_collect (regcache, regno, > + record_core_regbuf + MAX_REGISTER_SIZE * regno); > + else > + error (_("You can't do that without a process to debug.")); > +} > + > +static LONGEST > +record_core_xfer_partial (struct target_ops *ops, enum target_object object, > + const char *annex, gdb_byte *readbuf, > + const gdb_byte *writebuf, ULONGEST offset, > + LONGEST len) > +{ > + if (object == TARGET_OBJECT_MEMORY) > + { > + if (record_gdb_operation_disable || !writebuf) > + { > + struct target_section *p; > + for (p = record_core_start; p < record_core_end; p++) > + { > + if (offset >= p->addr) > + { > + struct record_core_buf_entry *entry; > + > + if (offset >= p->endaddr) > + continue; > + > + if (offset + len > p->endaddr) > + len = p->endaddr - offset; > + > + offset -= p->addr; > + > + /* Read readbuf or write writebuf p, offset, len. */ > + /* Check flags. */ > + if (p->the_bfd_section->flags & SEC_CONSTRUCTOR > + || (p->the_bfd_section->flags & SEC_HAS_CONTENTS) == 0) > + { > + if (readbuf) > + memset (readbuf, 0, len); > + return len; > + } > + /* Get record_core_buf_entry. */ > + for (entry = record_core_buf_list; entry; > + entry = entry->prev) > + if (entry->p == p) > + break; > + if (writebuf) > + { > + if (!entry) > + { > + /* Add a new entry. */ > + entry > + = (struct record_core_buf_entry *) > + xmalloc > + (sizeof (struct record_core_buf_entry)); > + entry->p = p; > + if (!bfd_malloc_and_get_section (p->bfd, > + p->the_bfd_section, > + &entry->buf)) > + { > + xfree (entry); > + return 0; > + } > + entry->prev = record_core_buf_list; > + record_core_buf_list = entry; > + } > + > + memcpy (entry->buf + offset, writebuf, (size_t) len); > + } > + else > + { > + if (!entry) > + return record_beneath_to_xfer_partial > + (record_beneath_to_xfer_partial_ops, > + object, annex, readbuf, writebuf, > + offset, len); > + > + memcpy (readbuf, entry->buf + offset, (size_t) len); > + } > + > + return len; > + } > + } > + > + return -1; > + } > + else > + error (_("You can't do that without a process to debug.")); > + } > + > + return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops, > + object, annex, readbuf, writebuf, > + offset, len); > +} > + > +static int > +record_core_insert_breakpoint (struct gdbarch *gdbarch, > + struct bp_target_info *bp_tgt) > +{ > + return 0; > +} > + > +static int > +record_core_remove_breakpoint (struct gdbarch *gdbarch, > + struct bp_target_info *bp_tgt) > +{ > + return 0; > +} > + > +int > +record_core_has_execution (struct target_ops *ops) > +{ > + return 1; > +} > + > +static void > +init_record_core_ops (void) > +{ > + record_core_ops.to_shortname = "record_core"; > + record_core_ops.to_longname = "Process record and replay target"; > + record_core_ops.to_doc = > + "Log program while executing and replay execution from log."; > + record_core_ops.to_open = record_open; > + record_core_ops.to_close = record_close; > + record_core_ops.to_resume = record_core_resume; > + record_core_ops.to_wait = record_wait; > + record_core_ops.to_kill = record_core_kill; > + record_core_ops.to_fetch_registers = record_core_fetch_registers; > + record_core_ops.to_prepare_to_store = record_core_prepare_to_store; > + record_core_ops.to_store_registers = record_core_store_registers; > + record_core_ops.to_xfer_partial = record_core_xfer_partial; > + record_core_ops.to_insert_breakpoint = record_core_insert_breakpoint; > + record_core_ops.to_remove_breakpoint = record_core_remove_breakpoint; > + record_core_ops.to_can_execute_reverse = record_can_execute_reverse; > + record_core_ops.to_has_execution = record_core_has_execution; > + record_core_ops.to_stratum = record_stratum; > + record_core_ops.to_magic = OPS_MAGIC; > +} > + > +static void > show_record_debug (struct ui_file *file, int from_tty, > struct cmd_list_element *c, const char *value) > { > @@ -1153,6 +1626,212 @@ cmd_record_start (char *args, int from_t > execute_command ("target record", from_tty); > } > > +static void > +cmd_record_load (char *args, int from_tty) > +{ > + char buf[512]; > + > + snprintf (buf, 512, "target record %s", args); > + execute_command (buf, from_tty); > +} > + > +static inline void > +record_write_dump (char *recfilename, int fildes, const void *buf, > + size_t nbyte) > +{ > + if (write (fildes, buf, nbyte) != nbyte) > + error (_("Failed to write dump of execution records to '%s'."), > + recfilename); > +} > + > +/* Record log save-file format > + Version 1 > + > + Header: > + 4 bytes: magic number htonl(0x20090726). > + 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 address (network byte order). > + 8 bytes: memory length (network byte order). > + n bytes: memory value (n == memory length). > +*/ > + > +/* Dump the execution log to a file. */ > + > +static void > +cmd_record_dump (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; > + > + if (strcmp (current_target.to_shortname, "record") != 0) > + error (_("Process record is not started.\n")); > + > + if (args && *args) > + recfilename = args; > + else > + { > + /* Default corefile name is "gdb_record.PID". */ > + snprintf (recfilename_buffer, 40, "gdb_record.%d", > + PIDGET (inferior_ptid)); > + recfilename = recfilename_buffer; > + } > + > + /* Open the dump file. */ > + if (record_debug) > + fprintf_unfiltered (gdb_stdlog, > + _("Saving recording to file '%s'\n"), > + recfilename); > + recfd = open (recfilename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, > + S_IRUSR | S_IWUSR); > + if (recfd < 0) > + error (_("Failed to open '%s' for dump execution records: %s"), > + recfilename, strerror (errno)); > + old_cleanups = make_cleanup (record_fd_cleanups, &recfd); > + > + /* 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 (); > + > + /* Write the magic code. */ > + magic = RECORD_FILE_MAGIC; > + if (record_debug) > + fprintf_unfiltered (gdb_stdlog, _("\ > +Writing 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n"), > + magic); > + record_write_dump (recfilename, recfd, &magic, 4); > + > + /* 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; > + } > + > + /* Dump the entries to recfd and forward execute to the end of > + record list. */ > + while (1) > + { > + /* Dump entry. */ > + if (record_list != &record_first) > + { > + uint8_t tmpu8; > + uint64_t tmpu64; > + > + tmpu8 = record_list->type; > + record_write_dump (recfilename, recfd, &tmpu8, 1); > + > + switch (record_list->type) > + { > + case record_reg: /* reg */ > + if (record_debug) > + fprintf_unfiltered (gdb_stdlog, _("\ > +Writing register %d (1 plus 8 plus %d bytes)\n"), > + record_list->u.reg.num, > + MAX_REGISTER_SIZE); > + > + tmpu64 = record_list->u.reg.num; > + if (BYTE_ORDER == LITTLE_ENDIAN) > + tmpu64 = bswap_64 (tmpu64); > + record_write_dump (recfilename, recfd, &tmpu64, 8); > + > + record_write_dump (recfilename, recfd, record_list->u.reg.val, > + MAX_REGISTER_SIZE); > + break; > + > + case record_mem: /* mem */ > + if (!record_list->u.mem.mem_entry_not_accessible) > + { > + if (record_debug) > + fprintf_unfiltered (gdb_stdlog, _("\ > +Writing memory %s (1 plus 8 plus 8 bytes plus %d bytes)\n"), > + paddress (gdbarch, > + record_list->u.mem.addr), > + record_list->u.mem.len); > + > + tmpu64 = record_list->u.mem.addr; > + if (BYTE_ORDER == LITTLE_ENDIAN) > + tmpu64 = bswap_64 (tmpu64); > + record_write_dump (recfilename, recfd, &tmpu64, 8); > + > + tmpu64 = record_list->u.mem.len; > + if (BYTE_ORDER == LITTLE_ENDIAN) > + tmpu64 = bswap_64 (tmpu64); > + record_write_dump (recfilename, recfd, &tmpu64, 8); > + > + record_write_dump (recfilename, recfd, > + record_list->u.mem.val, > + record_list->u.mem.len); > + } > + break; > + > + case record_end: > + /* FIXME: record the contents of record_end rec. */ > + if (record_debug) > + fprintf_unfiltered (gdb_stdlog, > + _("Writing record_end (1 byte)\n")); > + 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. */ > + fprintf_filtered (gdb_stdout, _("Saved dump of execution " > + "records to `%s'.\n"), > + recfilename); > +} > + > /* Truncate the record log from the present point > of replay until the end. */ > > @@ -1185,7 +1864,12 @@ cmd_record_stop (char *args, int from_tt > { > if (!record_list || !from_tty || query (_("Delete recorded log and " > "stop recording?"))) > - unpush_target (&record_ops); > + { > + if (strcmp (current_target.to_shortname, "record") == 0) > + unpush_target (&record_ops); > + else > + unpush_target (&record_core_ops); > + } > } > else > printf_unfiltered (_("Process record is not started.\n")); > @@ -1203,7 +1887,7 @@ set_record_insn_max_num (char *args, int > "the first ones?\n")); > > while (record_insn_num > record_insn_max_num) > - record_list_release_first (); > + record_list_release_first_insn (); > } > } > > @@ -1243,6 +1927,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; > @@ -1250,6 +1936,8 @@ _initialize_record (void) > > init_record_ops (); > add_target (&record_ops); > + init_record_core_ops (); > + add_target (&record_core_ops); > > add_setshow_zinteger_cmd ("record", no_class, &record_debug, > _("Set debugging of record/replay feature."), > @@ -1259,9 +1947,10 @@ _initialize_record (void) > NULL, show_record_debug, &setdebuglist, > &showdebuglist); > > - add_prefix_cmd ("record", class_obscure, cmd_record_start, > - _("Abbreviated form of \"target record\" command."), > - &record_cmdlist, "record ", 0, &cmdlist); > + 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, > @@ -1276,6 +1965,16 @@ _initialize_record (void) > "info record ", 0, &infolist); > add_alias_cmd ("rec", "record", class_obscure, 1, &infolist); > > + c = add_cmd ("load", class_obscure, cmd_record_load, > + _("Load previously dumped execution records from \ > +a file given as argument."), > + &record_cmdlist); > + set_cmd_completer (c, filename_completer); > + c = add_cmd ("dump", class_obscure, cmd_record_dump, > + _("Dump the execution records to a file.\n\ > +Argument is optional filename. Default filename is > 'gdb_record.'."), > + &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."),