From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 26972 invoked by alias); 22 Aug 2009 17:39:44 -0000 Received: (qmail 26955 invoked by uid 22791); 22 Aug 2009 17:39:36 -0000 X-SWARE-Spam-Status: No, hits=-1.7 required=5.0 tests=AWL,BAYES_00,SARE_MSGID_LONG40,SPF_PASS X-Spam-Check-By: sourceware.org Received: from mail-pz0-f198.google.com (HELO mail-pz0-f198.google.com) (209.85.222.198) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Sat, 22 Aug 2009 17:39:26 +0000 Received: by pzk36 with SMTP id 36so360275pzk.12 for ; Sat, 22 Aug 2009 10:39:24 -0700 (PDT) MIME-Version: 1.0 Received: by 10.142.60.4 with SMTP id i4mr246989wfa.273.1250962764106; Sat, 22 Aug 2009 10:39:24 -0700 (PDT) In-Reply-To: References: <4A7B99B3.40407@vmware.com> <4A7B9F49.9030202@vmware.com> <83ws5gm30b.fsf@gnu.org> <4A7C625B.8080005@vmware.com> <4A7F5410.4000400@vmware.com> From: Hui Zhu Date: Sun, 23 Aug 2009 01:14:00 -0000 Message-ID: Subject: Re: [RFA/RFC] Add dump and load command to process record and replay To: Michael Snyder Cc: Eli Zaretskii , "gdb-patches@sourceware.org" Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable 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/msg00353.txt.bz2 And about record load. I keep it because "record" is a prefix_cmd (If it's not, I can let "record" command load file directly). A file name cannot be the argument of this command. So I keep the command record load. Thanks, Hui On Sun, Aug 23, 2009 at 01:34, Hui Zhu wrote: > On Mon, Aug 10, 2009 at 06:56, Michael Snyder wrote: >> Hui Zhu wrote: >>> >>> I think give him a query is very clear. >>> >>> When he load, if there are some record log, it will query to user. =A0He >>> must choice remove the old record log or keep them. =A0He already know >>> what will happen. >> >> This is my opinion. =A0The default should be to remove the old log >> (not to query). =A0I think this will be both the most common case >> and the safest. =A0We can maybe add a command option for those who >> wish not to do that. >> >> Anyone else have an opinion? >> >>> On Sat, Aug 8, 2009 at 01:20, Michael Snyder wrote: >>>> >>>> Eli Zaretskii wrote: >>>>>> >>>>>> From: Hui Zhu >>>>>> Date: Fri, 7 Aug 2009 11:34:20 +0800 >>>>>> Cc: Eli Zaretskii , "gdb-patches@sourceware.org" >>>>>> >>>>>> >>>>>> I think a warning is clear to most of people. >>>>>> >>>>>> And when he get this warning. =A0He can delete the record list and l= oad >>>>>> again. =A0He will lost nothing. >>>>>> >>>>>> If we delete the old record list, maybe he still need old record. = =A0He >>>>>> will lost something. >>>>> >>>>> Instead of a warning, how about asking the user whether to discard the >>>>> old records or keep them? >>>> >>>> My concern is, in most cases keeping them will be the wrong thing to d= o. >>>> It will be very easy to create an internally inconsistent state, and >>>> rather unlikely to create one that is *not* internally inconsistant. >>>> >>>> Think about it -- we will be concatenating two independent sets of >>>> state changes, with no way of knowing that the actual machine state >>>> at the end of one is the same as the machine state at the beginning >>>> of the other. =A0When these are then replayed, their effect may have >>>> little or nothing to do with what the real machine would actually do. >>>> >>>> To actually get this right, you would have to be *sure* that your >>>> target machine is in the exact same state "now" (ie. when you do >>>> the load command) as it was at the *beginning* of the previous >>>> recording/debugging session. >>>> >>>> I would rather either make this a separate, "expert mode" >>>> command, or better still, leave it for a future patch to extend >>>> the basic (and safe) patch that we first accept. >>>> >>>> >>>> >>> >> >> > > Hi Michael, > > I make a new version patch. =A0It 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. > > Thanks, > Hui > > 2009-08-23 =A0Hui Zhu =A0 > > =A0 =A0 =A0 =A0Add dump and load command to process record and replay. > > =A0 =A0 =A0 =A0* record.c (completer.h, arch-utils.h, gdbcore.h, exec.h, > =A0 =A0 =A0 =A0byteswap.h, netinet/in.h): Include files. > =A0 =A0 =A0 =A0(RECORD_FILE_MAGIC): New macro. > =A0 =A0 =A0 =A0(record_core_buf_entry): New struct. > =A0 =A0 =A0 =A0(record_core_ops): New target_ops. > =A0 =A0 =A0 =A0(record_list_release_first_insn): Change function > =A0 =A0 =A0 =A0record_list_release_first to this name. > =A0 =A0 =A0 =A0(record_arch_list_cleanups): New function. > =A0 =A0 =A0 =A0(record_message_cleanups): Removed. > =A0 =A0 =A0 =A0(record_message): Change to call record_arch_list_cleanups > =A0 =A0 =A0 =A0and record_list_release_first_insn. > =A0 =A0 =A0 =A0(record_exec_entry, (record_read_dump, record_fd_cleanups, > =A0 =A0 =A0 =A0record_load, record_core_open_1, record_open_1): New funct= ion. > =A0 =A0 =A0 =A0(record_open): Add support for record_core_ops. > =A0 =A0 =A0 =A0(record_close): Add support for record_core_ops. > =A0 =A0 =A0 =A0(record_wait): Call function 'record_exec_entry' and > =A0 =A0 =A0 =A0add support for target core. > =A0 =A0 =A0 =A0(record_registers_change): Call record_list_release_first_= insn. > =A0 =A0 =A0 =A0(record_core_resume, record_core_resume, record_core_kill, > =A0 =A0 =A0 =A0record_core_fetch_registers, record_core_prepare_to_store, > =A0 =A0 =A0 =A0record_core_store_registers, record_core_xfer_partial, > =A0 =A0 =A0 =A0record_core_insert_breakpoint, record_core_remove_breakpoi= nt, > =A0 =A0 =A0 =A0record_core_has_execution, init_record_core_ops, > =A0 =A0 =A0 =A0cmd_record_load, record_write_dump, > =A0 =A0 =A0 =A0cmd_record_dump): New function. > =A0 =A0 =A0 =A0(cmd_record_stop): Add support for record_core_ops. > =A0 =A0 =A0 =A0(set_record_insn_max_num): Call record_list_release_first_= insn. > =A0 =A0 =A0 =A0(_initialize_record): Add commands "record dump" > =A0 =A0 =A0 =A0and "record load". > > --- > =A0record.c | =A0970 ++++++++++++++++++++++++++++++++++++++++++++++++++++= ++--------- > =A01 file changed, 844 insertions(+), 126 deletions(-) > > --- a/record.c > +++ b/record.c > @@ -23,15 +23,23 @@ > =A0#include "gdbthread.h" > =A0#include "event-top.h" > =A0#include "exceptions.h" > +#include "completer.h" > +#include "arch-utils.h" > +#include "gdbcore.h" > +#include "exec.h" > =A0#include "record.h" > > +#include > =A0#include > +#include > > =A0#define DEFAULT_RECORD_INSN_MAX_NUM =A0 =A0200000 > > =A0#define RECORD_IS_REPLAY \ > =A0 =A0 =A0(record_list->next || execution_direction =3D=3D EXEC_REVERSE) > > +#define RECORD_FILE_MAGIC =A0 =A0 =A0htonl(0x20090726) > + > =A0/* These are the core struct of record function. > > =A0 =A0An record_entry is a record of the value change of a register > @@ -78,9 +86,22 @@ struct record_entry > =A0 } u; > =A0}; > > +struct record_core_buf_entry > +{ > + =A0struct record_core_buf_entry *prev; > + =A0struct target_section *p; > + =A0bfd_byte *buf; > +}; > + > =A0/* This is the debug switch for process record. =A0*/ > =A0int record_debug =3D 0; > > +/* Record buf with core target. =A0*/ > +static gdb_byte *record_core_regbuf =3D 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 =3D NULL; > + > =A0/* These list is for execution log. =A0*/ > =A0static struct record_entry record_first; > =A0static struct record_entry *record_list =3D &record_first; > @@ -94,6 +115,7 @@ static int record_insn_num =3D 0; > > =A0/* The target_ops of process record. =A0*/ > =A0static struct target_ops record_ops; > +static struct target_ops record_core_ops; > > =A0/* The beneath function pointers. =A0*/ > =A0static struct target_ops *record_beneath_to_resume_ops; > @@ -169,7 +191,7 @@ record_list_release_next (void) > =A0} > > =A0static void > -record_list_release_first (void) > +record_list_release_first_insn (void) > =A0{ > =A0 struct record_entry *tmp =3D NULL; > =A0 enum record_type type; > @@ -340,30 +362,30 @@ record_check_insn_num (int set_terminal) > =A0 =A0 =A0 =A0 =A0 =A0 =A0if (q) > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0record_stop_at_limit =3D 0; > =A0 =A0 =A0 =A0 =A0 =A0 =A0else > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 error (_("Process record: inferior program = stopped.")); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 error (_("Process record: stoped by user.")= ); > =A0 =A0 =A0 =A0 =A0 =A0} > =A0 =A0 =A0 =A0} > =A0 =A0 } > =A0} > > +static void > +record_arch_list_cleanups (void *ignore) > +{ > + =A0record_list_release (record_arch_list_tail); > +} > + > =A0/* Before inferior step (when GDB record the running message, inferior > =A0 =A0only can step), GDB will call this function to record the values to > =A0 =A0record_list. =A0This function will call gdbarch_process_record to > =A0 =A0record the running message of inferior and set them to > =A0 =A0record_arch_list, and add it to record_list. =A0*/ > > -static void > -record_message_cleanups (void *ignore) > -{ > - =A0record_list_release (record_arch_list_tail); > -} > - > =A0static int > =A0record_message (void *args) > =A0{ > =A0 int ret; > =A0 struct regcache *regcache =3D args; > - =A0struct cleanup *old_cleanups =3D make_cleanup (record_message_cleanu= ps, 0); > + =A0struct cleanup *old_cleanups =3D make_cleanup (record_arch_list_clea= nups, 0); > > =A0 record_arch_list_head =3D NULL; > =A0 record_arch_list_tail =3D NULL; > @@ -386,7 +408,7 @@ record_message (void *args) > =A0 record_list =3D record_arch_list_tail; > > =A0 if (record_insn_num =3D=3D record_insn_max_num && record_insn_max_num) > - =A0 =A0record_list_release_first (); > + =A0 =A0record_list_release_first_insn (); > =A0 else > =A0 =A0 record_insn_num++; > > @@ -416,13 +438,309 @@ record_gdb_operation_disable_set (void) > =A0 return old_cleanups; > =A0} > > +static inline void > +record_exec_entry (struct regcache *regcache, struct gdbarch *gdbarch, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct record_entry *entry, int cor= e) > +{ > + =A0switch (entry->type) > + =A0 =A0{ > + =A0 =A0case record_reg: /* reg */ > + =A0 =A0 =A0{ > + =A0 =A0 =A0 =A0gdb_byte reg[MAX_REGISTER_SIZE]; > + > + =A0 =A0 =A0 =A0if (record_debug > 1) > + =A0 =A0 =A0 =A0 =A0fprintf_unfiltered (gdb_stdlog, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"Process rec= ord: record_reg %s to " > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"inferior nu= m =3D %d.\n", > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0host_address= _to_string (entry), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0entry->u.reg= .num); > + > + =A0 =A0 =A0 =A0regcache_cooked_read (regcache, entry->u.reg.num, reg); > + =A0 =A0 =A0 =A0regcache_cooked_write (regcache, entry->u.reg.num, entry= ->u.reg.val); > + =A0 =A0 =A0 =A0memcpy (entry->u.reg.val, reg, MAX_REGISTER_SIZE); > + =A0 =A0 =A0} > + =A0 =A0 =A0break; > + > + =A0 =A0case record_mem: /* mem */ > + =A0 =A0 =A0{ > + =A0 =A0 =A0 =A0if (!record_list->u.mem.mem_entry_not_accessible) > + =A0 =A0 =A0 =A0 =A0{ > + =A0 =A0 =A0 =A0 =A0 =A0gdb_byte *mem =3D alloca (entry->u.mem.len); > + > + =A0 =A0 =A0 =A0 =A0 =A0if (record_debug > 1) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0fprintf_unfiltered (gdb_stdlog, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"Pro= cess record: record_mem %s to " > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"inf= erior addr =3D %s len =3D %d.\n", > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0host= _address_to_string (entry), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0padd= ress (gdbarch, entry->u.mem.addr), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0reco= rd_list->u.mem.len); > + > + =A0 =A0 =A0 =A0 =A0 =A0if (target_read_memory (entry->u.mem.addr, mem, = entry->u.mem.len)) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0{ > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if ((execution_direction =3D=3D EXEC_RE= VERSE && !core) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 || (execution_direction !=3D EX= EC_REVERSE && core)) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0{ > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0record_list->u.mem.mem_entry_not= _accessible =3D 1; > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (record_debug) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0warning (_("Process record: = error reading memory at " > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "addr = =3D %s len =3D %d."), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 paddress (g= dbarch, entry->u.mem.addr), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 entry->u.me= m.len); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0} > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0else > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0error (_("Process record: error read= ing memory at " > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "addr =3D %s len = =3D %d."), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 paddress (gdbarch, entr= y->u.mem.addr), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0entry->u.mem.len); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0} > + =A0 =A0 =A0 =A0 =A0 =A0else > + =A0 =A0 =A0 =A0 =A0 =A0 =A0{ > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (target_write_memory (entry->u.mem.ad= dr, entry->u.mem.val, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 entry->u.mem.len)) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0{ > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if ((execution_direction =3D=3D= EXEC_REVERSE && !core) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 || (execution_direction= !=3D EXEC_REVERSE && core)) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0{ > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0record_list->u.mem.mem_e= ntry_not_accessible =3D 1; > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (record_debug) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0warning (_("Process = record: error writing memory at " > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= "addr =3D %s len =3D %d."), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pad= dress (gdbarch, entry->u.mem.addr), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ent= ry->u.mem.len); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0} > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0else > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0error (_("Process record: er= ror writing memory at " > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "addr =3D %= s len =3D %d."), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 paddress (gdbar= ch, entry->u.mem.addr), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0entry->u.mem.len= ); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0} > + =A0 =A0 =A0 =A0 =A0 =A0 =A0} > + > + =A0 =A0 =A0 =A0 =A0 =A0memcpy (entry->u.mem.val, mem, entry->u.mem.len); > + =A0 =A0 =A0 =A0 =A0} > + =A0 =A0 =A0} > + =A0 =A0 =A0break; > + =A0 =A0} > +} > + > +static inline void > +record_read_dump (char *recfilename, int fildes, void *buf, size_t nbyte) > +{ > + =A0if (read (fildes, buf, nbyte) !=3D nbyte) > + =A0 =A0error (_("Failed to read dump of execution records in '%s'."), > + =A0 =A0 =A0 =A0 =A0 recfilename); > +} > + > =A0static void > -record_open (char *name, int from_tty) > +record_fd_cleanups (void *recfdp) > =A0{ > - =A0struct target_ops *t; > + =A0int recfd =3D *(int *) recfdp; > + =A0close (recfd); > +} > > - =A0if (record_debug) > - =A0 =A0fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n"); > +/* Load the execution log from a file. =A0*/ > + > +static void > +record_load (char *name) > +{ > + =A0int recfd; > + =A0uint32_t magic; > + =A0struct cleanup *old_cleanups; > + =A0struct cleanup *old_cleanups2; > + =A0struct record_entry *rec; > + =A0int insn_number =3D 0; > + > + =A0if (!name || (name && !*name)) > + =A0 =A0return; > + > + =A0/* Open the load file. =A0*/ > + =A0recfd =3D open (name, O_RDONLY | O_BINARY); > + =A0if (recfd < 0) > + =A0 =A0error (_("Failed to open '%s' for loading execution records: %s"= ), > + =A0 =A0 =A0 =A0 =A0 name, strerror (errno)); > + =A0old_cleanups =3D make_cleanup (record_fd_cleanups, &recfd); > + > + =A0/* Check the magic code. =A0*/ > + =A0record_read_dump (name, recfd, &magic, 4); > + =A0if (magic !=3D RECORD_FILE_MAGIC) > + =A0 =A0error (_("'%s' is not a valid dump of execution records."), name= ); > + > + =A0/* Load the entries in recfd to the record_arch_list_head and > + =A0 =A0 record_arch_list_tail. =A0*/ > + =A0record_arch_list_head =3D NULL; > + =A0record_arch_list_tail =3D NULL; > + =A0old_cleanups2 =3D make_cleanup (record_arch_list_cleanups, 0); > + > + =A0while (1) > + =A0 =A0{ > + =A0 =A0 =A0int ret; > + =A0 =A0 =A0uint8_t tmpu8; > + =A0 =A0 =A0uint64_t tmpu64; > + > + =A0 =A0 =A0ret =3D read (recfd, &tmpu8, 1); > + =A0 =A0 =A0if (ret < 0) > + =A0 =A0 =A0 =A0error (_("Failed to read dump of execution records in '%= s'."), name); > + =A0 =A0 =A0if (ret =3D=3D 0) > + =A0 =A0 =A0 =A0break; > + > + =A0 =A0 =A0switch (tmpu8) > + =A0 =A0 =A0 =A0{ > + =A0 =A0 =A0 =A0case record_reg: /* reg */ > + =A0 =A0 =A0 =A0 =A0rec =3D (struct record_entry *) xmalloc (sizeof (str= uct record_entry)); > + =A0 =A0 =A0 =A0 =A0rec->u.reg.val =3D (gdb_byte *) xmalloc (MAX_REGISTE= R_SIZE); > + =A0 =A0 =A0 =A0 =A0rec->prev =3D NULL; > + =A0 =A0 =A0 =A0 =A0rec->next =3D NULL; > + =A0 =A0 =A0 =A0 =A0rec->type =3D record_reg; > + > + =A0 =A0 =A0 =A0 =A0/* Get num. =A0*/ > + =A0 =A0 =A0 =A0 =A0record_read_dump (name, recfd, &tmpu64, 8); > + =A0 =A0 =A0 =A0 =A0if (BYTE_ORDER =3D=3D LITTLE_ENDIAN) > + =A0 =A0 =A0 =A0 =A0 =A0tmpu64 =3D bswap_64 (tmpu64); > + =A0 =A0 =A0 =A0 =A0rec->u.reg.num =3D tmpu64; > + > + =A0 =A0 =A0 =A0 =A0/* Get val. =A0*/ > + =A0 =A0 =A0 =A0 =A0record_read_dump (name, recfd, rec->u.reg.val, MAX_R= EGISTER_SIZE); > + > + =A0 =A0 =A0 =A0 =A0if (record_debug) > + =A0 =A0 =A0 =A0 =A0 =A0fprintf_unfiltered (gdb_stdlog, _("\ > +Reading register %d (1 plus 8 plus %d bytes)\n"), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0rec->u.r= eg.num, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0MAX_REGI= STER_SIZE); > + > + =A0 =A0 =A0 =A0 =A0record_arch_list_add (rec); > + =A0 =A0 =A0 =A0 =A0break; > + > + =A0 =A0 =A0 =A0case record_mem: /* mem */ > + =A0 =A0 =A0 =A0 =A0rec =3D (struct record_entry *) xmalloc (sizeof (str= uct record_entry)); > + =A0 =A0 =A0 =A0 =A0rec->prev =3D NULL; > + =A0 =A0 =A0 =A0 =A0rec->next =3D NULL; > + =A0 =A0 =A0 =A0 =A0rec->type =3D record_mem; > + > + =A0 =A0 =A0 =A0 =A0/* Get addr. =A0*/ > + =A0 =A0 =A0 =A0 =A0record_read_dump (name, recfd, &tmpu64, 8); > + =A0 =A0 =A0 =A0 =A0if (BYTE_ORDER =3D=3D LITTLE_ENDIAN) > + =A0 =A0 =A0 =A0 =A0 =A0tmpu64 =3D bswap_64 (tmpu64); > + =A0 =A0 =A0 =A0 =A0rec->u.mem.addr =3D tmpu64; > + > + =A0 =A0 =A0 =A0 =A0/* Get len. =A0*/ > + =A0 =A0 =A0 =A0 =A0record_read_dump (name, recfd, &tmpu64, 8); > + =A0 =A0 =A0 =A0 =A0if (BYTE_ORDER =3D=3D LITTLE_ENDIAN) > + =A0 =A0 =A0 =A0 =A0 =A0tmpu64 =3D bswap_64 (tmpu64); > + =A0 =A0 =A0 =A0 =A0rec->u.mem.len =3D tmpu64; > + =A0 =A0 =A0 =A0 =A0rec->u.mem.mem_entry_not_accessible =3D 0; > + =A0 =A0 =A0 =A0 =A0rec->u.mem.val =3D (gdb_byte *) xmalloc (rec->u.mem.= len); > + > + =A0 =A0 =A0 =A0 =A0/* Get val. =A0*/ > + =A0 =A0 =A0 =A0 =A0record_read_dump (name, recfd, rec->u.mem.val, rec->= u.mem.len); > + > + =A0 =A0 =A0 =A0 =A0if (record_debug) > + =A0 =A0 =A0 =A0 =A0 =A0fprintf_unfiltered (gdb_stdlog, _("\ > +Reading memory %s (1 plus 8 plus 8 bytes plus %d bytes)\n"), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0paddress (get_current_arch (), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0 =A0 =A0 =A0 =A0rec->u.mem.addr), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0rec->u.mem.len); > + > + =A0 =A0 =A0 =A0 =A0record_arch_list_add (rec); > + =A0 =A0 =A0 =A0 =A0break; > + > + =A0 =A0 =A0 =A0case record_end: /* end */ > + =A0 =A0 =A0 =A0 =A0if (record_debug) > + =A0 =A0 =A0 =A0 =A0 =A0fprintf_unfiltered (gdb_stdlog, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0_("Readi= ng record_end (1 byte)\n")); > + > + =A0 =A0 =A0 =A0 =A0rec =3D (struct record_entry *) xmalloc (sizeof (str= uct record_entry)); > + =A0 =A0 =A0 =A0 =A0rec->prev =3D NULL; > + =A0 =A0 =A0 =A0 =A0rec->next =3D NULL; > + =A0 =A0 =A0 =A0 =A0rec->type =3D record_end; > + =A0 =A0 =A0 =A0 =A0record_arch_list_add (rec); > + =A0 =A0 =A0 =A0 =A0insn_number ++; > + =A0 =A0 =A0 =A0 =A0break; > + > + =A0 =A0 =A0 =A0default: > + =A0 =A0 =A0 =A0 =A0error (_("Format of '%s' is not right."), name); > + =A0 =A0 =A0 =A0 =A0break; > + =A0 =A0 =A0 =A0} > + =A0 =A0} > + > + =A0discard_cleanups (old_cleanups2); > + > + =A0/* Add record_arch_list_head to the end of record list. =A0*/ > + =A0for (rec =3D record_list; rec->next; rec =3D rec->next); > + =A0rec->next =3D record_arch_list_head; > + =A0record_arch_list_head->prev =3D rec; > + > + =A0/* Update record_insn_num and record_insn_max_num. =A0*/ > + =A0record_insn_num +=3D insn_number; > + =A0if (record_insn_num > record_insn_max_num) > + =A0 =A0{ > + =A0 =A0 =A0record_insn_max_num =3D record_insn_num; > + =A0 =A0 =A0warning (_("Auto increase record/replay buffer limit to %d."= ), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 record_insn_max_num); > + =A0 =A0} > + > + =A0do_cleanups (old_cleanups); > + > + =A0/* Succeeded. =A0*/ > + =A0fprintf_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, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0enum target_= signal); > +static struct target_ops *tmp_to_wait_ops; > +static ptid_t (*tmp_to_wait) (struct target_ops *, ptid_t, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct targe= t_waitstatus *, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0int); > +static struct target_ops *tmp_to_store_registers_ops; > +static void (*tmp_to_store_registers) (struct target_ops *, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 struct regcache *, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 int regno); > +static struct target_ops *tmp_to_xfer_partial_ops; > +static LONGEST (*tmp_to_xfer_partial) (struct target_ops *ops, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 enum target_object object, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 const char *annex, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 gdb_byte *readbuf, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 const gdb_byte *writebuf, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 ULONGEST offset, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 LONGEST len); > +static int (*tmp_to_insert_breakpoint) (struct gdbarch *, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0struct bp_target_info *); > +static int (*tmp_to_remove_breakpoint) (struct gdbarch *, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0struct bp_target_info *); > + > +static void > +record_core_open_1 (char *name, int from_tty) > +{ > + =A0struct regcache *regcache =3D get_current_regcache (); > + =A0int regnum =3D gdbarch_num_regs (get_regcache_arch (regcache)); > + =A0int i; > + > + =A0if (!name || (name && !*name)) > + =A0 =A0error (_("Argument for gdb record filename required.\n")); > + > + =A0/* Get record_core_regbuf. =A0*/ > + =A0target_fetch_registers (regcache, -1); > + =A0record_core_regbuf =3D xmalloc (MAX_REGISTER_SIZE * regnum); > + =A0for (i =3D 0; i < regnum; i ++) > + =A0 =A0regcache_raw_collect (regcache, i, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0record_core_regbuf += MAX_REGISTER_SIZE * i); > + > + =A0/* Get record_core_start and record_core_end. =A0*/ > + =A0if (build_section_table (core_bfd, &record_core_start, &record_core_= end)) > + =A0 =A0{ > + =A0 =A0 =A0xfree (record_core_regbuf); > + =A0 =A0 =A0record_core_regbuf =3D NULL; > + =A0 =A0 =A0error (_("\"%s\": Can't find sections: %s"), > + =A0 =A0 =A0 =A0 =A0 =A0 bfd_get_filename (core_bfd), bfd_errmsg (bfd_ge= t_error ())); > + =A0 =A0} > + > + =A0push_target (&record_core_ops); > +} > + > +static void > +record_open_1 (char *name, int from_tty) > +{ > + =A0struct target_ops *t; > > =A0 /* check exec */ > =A0 if (!target_has_execution) > @@ -438,6 +756,28 @@ record_open (char *name, int from_tty) > =A0 =A0 error (_("Process record: the current architecture doesn't suppor= t " > =A0 =A0 =A0 =A0 =A0 =A0 "record function.")); > > + =A0if (!tmp_to_resume) > + =A0 =A0error (_("Process record can't get to_resume.")); > + =A0if (!tmp_to_wait) > + =A0 =A0error (_("Process record can't get to_wait.")); > + =A0if (!tmp_to_store_registers) > + =A0 =A0error (_("Process record can't get to_store_registers.")); > + =A0if (!tmp_to_insert_breakpoint) > + =A0 =A0error (_("Process record can't get to_insert_breakpoint.")); > + =A0if (!tmp_to_remove_breakpoint) > + =A0 =A0error (_("Process record can't get to_remove_breakpoint.")); > + > + =A0push_target (&record_ops); > +} > + > +static void > +record_open (char *name, int from_tty) > +{ > + =A0struct target_ops *t; > + > + =A0if (record_debug) > + =A0 =A0fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n"); > + > =A0 /* Check if record target is already running. =A0*/ > =A0 if (current_target.to_stratum =3D=3D record_stratum) > =A0 =A0 { > @@ -447,70 +787,102 @@ record_open (char *name, int from_tty) > =A0 =A0 =A0 =A0return; > =A0 =A0 } > > - =A0/*Reset the beneath function pointers. =A0*/ > - =A0record_beneath_to_resume =3D NULL; > - =A0record_beneath_to_wait =3D NULL; > - =A0record_beneath_to_store_registers =3D NULL; > - =A0record_beneath_to_xfer_partial =3D NULL; > - =A0record_beneath_to_insert_breakpoint =3D NULL; > - =A0record_beneath_to_remove_breakpoint =3D NULL; > + =A0/* Reset the tmp beneath pointers. =A0*/ > + =A0tmp_to_resume_ops =3D NULL; > + =A0tmp_to_resume =3D NULL; > + =A0tmp_to_wait_ops =3D NULL; > + =A0tmp_to_wait =3D NULL; > + =A0tmp_to_store_registers_ops =3D NULL; > + =A0tmp_to_store_registers =3D NULL; > + =A0tmp_to_xfer_partial_ops =3D NULL; > + =A0tmp_to_xfer_partial =3D NULL; > + =A0tmp_to_insert_breakpoint =3D NULL; > + =A0tmp_to_remove_breakpoint =3D NULL; > > =A0 /* Set the beneath function pointers. =A0*/ > =A0 for (t =3D current_target.beneath; t !=3D NULL; t =3D t->beneath) > =A0 =A0 { > - =A0 =A0 =A0if (!record_beneath_to_resume) > + =A0 =A0 =A0if (!tmp_to_resume) > =A0 =A0 =A0 =A0 { > - =A0 =A0 =A0 =A0 record_beneath_to_resume =3D t->to_resume; > - =A0 =A0 =A0 =A0 record_beneath_to_resume_ops =3D t; > + =A0 =A0 =A0 =A0 tmp_to_resume =3D t->to_resume; > + =A0 =A0 =A0 =A0 tmp_to_resume_ops =3D t; > =A0 =A0 =A0 =A0 } > - =A0 =A0 =A0if (!record_beneath_to_wait) > + =A0 =A0 =A0if (!tmp_to_wait) > =A0 =A0 =A0 =A0 { > - =A0 =A0 =A0 =A0 record_beneath_to_wait =3D t->to_wait; > - =A0 =A0 =A0 =A0 record_beneath_to_wait_ops =3D t; > + =A0 =A0 =A0 =A0 tmp_to_wait =3D t->to_wait; > + =A0 =A0 =A0 =A0 tmp_to_wait_ops =3D t; > =A0 =A0 =A0 =A0 } > - =A0 =A0 =A0if (!record_beneath_to_store_registers) > + =A0 =A0 =A0if (!tmp_to_store_registers) > =A0 =A0 =A0 =A0 { > - =A0 =A0 =A0 =A0 record_beneath_to_store_registers =3D t->to_store_regis= ters; > - =A0 =A0 =A0 =A0 record_beneath_to_store_registers_ops =3D t; > + =A0 =A0 =A0 =A0 tmp_to_store_registers =3D t->to_store_registers; > + =A0 =A0 =A0 =A0 tmp_to_store_registers_ops =3D t; > =A0 =A0 =A0 =A0 } > - =A0 =A0 =A0if (!record_beneath_to_xfer_partial) > + =A0 =A0 =A0if (!tmp_to_xfer_partial) > =A0 =A0 =A0 =A0 { > - =A0 =A0 =A0 =A0 record_beneath_to_xfer_partial =3D t->to_xfer_partial; > - =A0 =A0 =A0 =A0 record_beneath_to_xfer_partial_ops =3D t; > + =A0 =A0 =A0 =A0 tmp_to_xfer_partial =3D t->to_xfer_partial; > + =A0 =A0 =A0 =A0 tmp_to_xfer_partial_ops =3D t; > =A0 =A0 =A0 =A0 } > - =A0 =A0 =A0if (!record_beneath_to_insert_breakpoint) > - =A0 =A0 =A0 record_beneath_to_insert_breakpoint =3D t->to_insert_breakp= oint; > - =A0 =A0 =A0if (!record_beneath_to_remove_breakpoint) > - =A0 =A0 =A0 record_beneath_to_remove_breakpoint =3D t->to_remove_breakp= oint; > + =A0 =A0 =A0if (!tmp_to_insert_breakpoint) > + =A0 =A0 =A0 tmp_to_insert_breakpoint =3D t->to_insert_breakpoint; > + =A0 =A0 =A0if (!tmp_to_remove_breakpoint) > + =A0 =A0 =A0 tmp_to_remove_breakpoint =3D t->to_remove_breakpoint; > =A0 =A0 } > - =A0if (!record_beneath_to_resume) > - =A0 =A0error (_("Process record can't get to_resume.")); > - =A0if (!record_beneath_to_wait) > - =A0 =A0error (_("Process record can't get to_wait.")); > - =A0if (!record_beneath_to_store_registers) > - =A0 =A0error (_("Process record can't get to_store_registers.")); > - =A0if (!record_beneath_to_xfer_partial) > + =A0if (!tmp_to_xfer_partial) > =A0 =A0 error (_("Process record can't get to_xfer_partial.")); > - =A0if (!record_beneath_to_insert_breakpoint) > - =A0 =A0error (_("Process record can't get to_insert_breakpoint.")); > - =A0if (!record_beneath_to_remove_breakpoint) > - =A0 =A0error (_("Process record can't get to_remove_breakpoint.")); > > - =A0push_target (&record_ops); > + =A0if (current_target.to_stratum =3D=3D core_stratum) > + =A0 =A0record_core_open_1 (name, from_tty); > + =A0else > + =A0 =A0record_open_1 (name, from_tty); > > =A0 /* Reset */ > =A0 record_insn_num =3D 0; > =A0 record_list =3D &record_first; > =A0 record_list->next =3D NULL; > + > + =A0/* Set the tmp beneath pointers to beneath pointers. =A0*/ > + =A0record_beneath_to_resume_ops =3D tmp_to_resume_ops; > + =A0record_beneath_to_resume =3D tmp_to_resume; > + =A0record_beneath_to_wait_ops =3D tmp_to_wait_ops; > + =A0record_beneath_to_wait =3D tmp_to_wait; > + =A0record_beneath_to_store_registers_ops =3D tmp_to_store_registers_ops; > + =A0record_beneath_to_store_registers =3D tmp_to_store_registers; > + =A0record_beneath_to_xfer_partial_ops =3D tmp_to_xfer_partial_ops; > + =A0record_beneath_to_xfer_partial =3D tmp_to_xfer_partial; > + =A0record_beneath_to_insert_breakpoint =3D tmp_to_insert_breakpoint; > + =A0record_beneath_to_remove_breakpoint =3D tmp_to_remove_breakpoint; > + > + =A0/* Load if there is argument. =A0*/ > + =A0record_load (name); > =A0} > > =A0static void > =A0record_close (int quitting) > =A0{ > + =A0struct record_core_buf_entry *entry; > + > =A0 if (record_debug) > =A0 =A0 fprintf_unfiltered (gdb_stdlog, "Process record: record_close\n"); > > =A0 record_list_release (record_list); > + > + =A0/* Release record_core_regbuf. =A0*/ > + =A0if (record_core_regbuf) > + =A0 =A0{ > + =A0 =A0 =A0xfree (record_core_regbuf); > + =A0 =A0 =A0record_core_regbuf =3D NULL; > + =A0 =A0} > + > + =A0/* Release record_core_buf_list. =A0*/ > + =A0if (record_core_buf_list) > + =A0 =A0{ > + =A0 =A0 =A0for (entry =3D record_core_buf_list->prev; entry; entry =3D = entry->prev) > + =A0 =A0 =A0 =A0{ > + =A0 =A0 =A0 =A0 =A0xfree (record_core_buf_list); > + =A0 =A0 =A0 =A0 =A0record_core_buf_list =3D entry; > + =A0 =A0 =A0 =A0} > + =A0 =A0 =A0record_core_buf_list =3D NULL; > + =A0 =A0} > =A0} > > =A0static int record_resume_step =3D 0; > @@ -584,7 +956,7 @@ record_wait (struct target_ops *ops, > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"record_resume_step =3D %d= \n", > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0record_resume_step); > > - =A0if (!RECORD_IS_REPLAY) > + =A0if (!RECORD_IS_REPLAY && ops !=3D &record_core_ops) > =A0 =A0 { > =A0 =A0 =A0 if (record_resume_error) > =A0 =A0 =A0 =A0{ > @@ -712,76 +1084,10 @@ record_wait (struct target_ops *ops, > =A0 =A0 =A0 =A0 =A0 =A0 =A0break; > =A0 =A0 =A0 =A0 =A0 =A0} > > - =A0 =A0 =A0 =A0 /* Set ptid, register and memory according to record_li= st. =A0*/ > - =A0 =A0 =A0 =A0 if (record_list->type =3D=3D record_reg) > - =A0 =A0 =A0 =A0 =A0 { > - =A0 =A0 =A0 =A0 =A0 =A0 /* reg */ > - =A0 =A0 =A0 =A0 =A0 =A0 gdb_byte reg[MAX_REGISTER_SIZE]; > - =A0 =A0 =A0 =A0 =A0 =A0 if (record_debug > 1) > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 fprintf_unfiltered (gdb_stdlog, > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "Pr= ocess record: record_reg %s to " > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "in= ferior num =3D %d.\n", > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 hos= t_address_to_string (record_list), > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 rec= ord_list->u.reg.num); > - =A0 =A0 =A0 =A0 =A0 =A0 regcache_cooked_read (regcache, record_list->u.= reg.num, reg); > - =A0 =A0 =A0 =A0 =A0 =A0 regcache_cooked_write (regcache, record_list->u= .reg.num, > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= record_list->u.reg.val); > - =A0 =A0 =A0 =A0 =A0 =A0 memcpy (record_list->u.reg.val, reg, MAX_REGIST= ER_SIZE); > - =A0 =A0 =A0 =A0 =A0 } > - =A0 =A0 =A0 =A0 else if (record_list->type =3D=3D record_mem) > - =A0 =A0 =A0 =A0 =A0 { > - =A0 =A0 =A0 =A0 =A0 =A0 /* mem */ > - =A0 =A0 =A0 =A0 =A0 =A0 /* Nothing to do if the entry is flagged not_ac= cessible. =A0*/ > - =A0 =A0 =A0 =A0 =A0 =A0 if (!record_list->u.mem.mem_entry_not_accessibl= e) > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 { > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 gdb_byte *mem =3D alloca (record_list->= u.mem.len); > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (record_debug > 1) > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 fprintf_unfiltered (gdb_stdlog, > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 "Process record: record_mem %s to " > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 "inferior addr =3D %s len =3D %d.\n", > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 host_address_to_string (record_list), > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 paddress (gdbarch, > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0 =A0 =A0 =A0 record_list->u.mem.addr), > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 record_list->u.mem.len); > + =A0 =A0 =A0 =A0 =A0record_exec_entry (regcache, gdbarch, record_list, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (ops =3D=3D &re= cord_core_ops) ? 1 : 0); > > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (target_read_memory (record_list->u.= mem.addr, mem, > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 record_list->u.mem.len)) > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 { > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (execution_direction !=3D EX= EC_REVERSE) > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 error (_("Process record: e= rror reading memory at " > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"addr = =3D %s len =3D %d."), > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0paddress (gd= barch, record_list->u.mem.addr), > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0record_list-= >u.mem.len); > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 else > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Read failed -- > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0flag entry as not_ac= cessible. =A0*/ > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 record_list->u.mem.mem_entr= y_not_accessible =3D 1; > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 } > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 else > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 { > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (target_write_memory (record= _list->u.mem.addr, > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0 =A0 =A0record_list->u.mem.val, > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0 =A0 =A0record_list->u.mem.len)) > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 { > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (execution_direction= !=3D EXEC_REVERSE) > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 error (_("Process r= ecord: error writing memory at " > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= "addr =3D %s len =3D %d."), > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0padd= ress (gdbarch, record_list->u.mem.addr), > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0reco= rd_list->u.mem.len); > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 else > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Write failed -- > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0flag entry a= s not_accessible. =A0*/ > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 record_list->u.mem.= mem_entry_not_accessible =3D 1; > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 } > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 else > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 { > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 memcpy (record_list->u.= mem.val, mem, > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 record_= list->u.mem.len); > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 } > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 } > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 } > - =A0 =A0 =A0 =A0 =A0 } > - =A0 =A0 =A0 =A0 else > + =A0 =A0 =A0 =A0 if (record_list->type =3D=3D record_end) > =A0 =A0 =A0 =A0 =A0 =A0{ > =A0 =A0 =A0 =A0 =A0 =A0 =A0if (record_debug > 1) > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0fprintf_unfiltered (gdb_stdlog, > @@ -901,6 +1207,7 @@ record_kill (struct target_ops *ops) > =A0 =A0 fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n"); > > =A0 unpush_target (&record_ops); > + > =A0 target_kill (); > =A0} > > @@ -945,7 +1252,7 @@ record_registers_change (struct regcache > =A0 record_list =3D record_arch_list_tail; > > =A0 if (record_insn_num =3D=3D record_insn_max_num && record_insn_max_num) > - =A0 =A0record_list_release_first (); > + =A0 =A0record_list_release_first_insn (); > =A0 else > =A0 =A0 record_insn_num++; > =A0} > @@ -1058,7 +1365,7 @@ record_xfer_partial (struct target_ops * > =A0 =A0 =A0 record_list =3D record_arch_list_tail; > > =A0 =A0 =A0 if (record_insn_num =3D=3D record_insn_max_num && record_insn= _max_num) > - =A0 =A0 =A0 record_list_release_first (); > + =A0 =A0 =A0 record_list_release_first_insn (); > =A0 =A0 =A0 else > =A0 =A0 =A0 =A0record_insn_num++; > =A0 =A0 } > @@ -1138,6 +1445,191 @@ init_record_ops (void) > =A0} > > =A0static void > +record_core_resume (struct target_ops *ops, ptid_t ptid, int step, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0enum target_signal siggnal) > +{ > + =A0record_resume_step =3D step; > + =A0record_resume_siggnal =3D siggnal; > +} > + > +static void > +record_core_kill (struct target_ops *ops) > +{ > + =A0if (record_debug) > + =A0 =A0fprintf_unfiltered (gdb_stdlog, "Process record: record_core_kil= l\n"); > + > + =A0unpush_target (&record_core_ops); > +} > + > +static void > +record_core_fetch_registers (struct target_ops *ops, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct regcache= *regcache, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 int regno) > +{ > + =A0if (regno < 0) > + =A0 =A0{ > + =A0 =A0 =A0int num =3D gdbarch_num_regs (get_regcache_arch (regcache)); > + =A0 =A0 =A0int i; > + > + =A0 =A0 =A0for (i =3D 0; i < num; i ++) > + =A0 =A0 =A0 =A0regcache_raw_supply (regcache, i, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 record_core_reg= buf + MAX_REGISTER_SIZE * i); > + =A0 =A0} > + =A0else > + =A0 =A0regcache_raw_supply (regcache, regno, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 record_core_regbuf + MA= X_REGISTER_SIZE * regno); > +} > + > +static void > +record_core_prepare_to_store (struct regcache *regcache) > +{ > +} > + > +static void > +record_core_store_registers (struct target_ops *ops, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct regcache= *regcache, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 int regno) > +{ > + =A0if (record_gdb_operation_disable) > + =A0 =A0regcache_raw_collect (regcache, regno, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0record_core_regbuf += MAX_REGISTER_SIZE * regno); > + =A0else > + =A0 =A0error (_("You can't do that without a process to debug.")); > +} > + > +static LONGEST > +record_core_xfer_partial (struct target_ops *ops, enum target_object obj= ect, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 const char *annex, gdb_= byte *readbuf, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 const gdb_byte *writebu= f, ULONGEST offset, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0LONGEST len) > +{ > + =A0 if (object =3D=3D TARGET_OBJECT_MEMORY) > + =A0 =A0 { > + =A0 =A0 =A0 if (record_gdb_operation_disable || !writebuf) > + =A0 =A0 =A0 =A0 { > + =A0 =A0 =A0 =A0 =A0 struct target_section *p; > + =A0 =A0 =A0 =A0 =A0 for (p =3D record_core_start; p < record_core_end; = p++) > + =A0 =A0 =A0 =A0 =A0 =A0 { > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (offset >=3D p->addr) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 { > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct record_core_buf_entry *entry; > + > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (offset >=3D p->endaddr) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 continue; > + > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (offset + len > p->endaddr) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 len =3D p->endaddr - offset; > + > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 offset -=3D p->addr; > + > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Read readbuf or write writebuf p= , offset, len. =A0*/ > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Check flags. =A0*/ > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (p->the_bfd_section->flags & SEC= _CONSTRUCTOR > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 || (p->the_bfd_section->fla= gs & SEC_HAS_CONTENTS) =3D=3D 0) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 { > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (readbuf) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 memset (readbuf, 0, len= ); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return len; > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 } > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Get record_core_buf_entry. =A0*/ > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 for (entry =3D record_core_buf_list= ; entry; > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0entry =3D entry->prev) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (entry->p =3D=3D p) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (writebuf) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 { > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!entry) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 { > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Add a new entry.= =A0*/ > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 entry > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =3D (struct rec= ord_core_buf_entry *) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 xmalloc > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (si= zeof (struct record_core_buf_entry)); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 entry->p =3D p; > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!bfd_malloc_and= _get_section (p->bfd, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0p->the_bfd_section, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0&entry->buf)) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 { > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 xfree (entr= y); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return 0; > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 } > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 entry->prev =3D rec= ord_core_buf_list; > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 record_core_buf_lis= t =3D entry; > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 } > + > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0memcpy (entry->buf + off= set, writebuf, (size_t) len); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 } > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 else > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 { > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!entry) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return record_beneath_t= o_xfer_partial > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(rec= ord_beneath_to_xfer_partial_ops, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 obj= ect, annex, readbuf, writebuf, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 off= set, len); > + > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 memcpy (readbuf, entry->buf= + offset, (size_t) len); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 } > + > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 return len; > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 } > + =A0 =A0 =A0 =A0 =A0 =A0 } > + > + =A0 =A0 =A0 =A0 =A0 return -1; > + =A0 =A0 =A0 =A0 } > + =A0 =A0 =A0 else > + =A0 =A0 =A0 =A0 error (_("You can't do that without a process to debug.= ")); > + =A0 =A0 } > + > + =A0return record_beneath_to_xfer_partial (record_beneath_to_xfer_partia= l_ops, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 object, annex, readbuf, writebuf, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 offset, len); > +} > + > +static int > +record_core_insert_breakpoint (struct gdbarch *gdbarch, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct bp_ta= rget_info *bp_tgt) > +{ > + =A0return 0; > +} > + > +static int > +record_core_remove_breakpoint (struct gdbarch *gdbarch, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct bp_ta= rget_info *bp_tgt) > +{ > + =A0return 0; > +} > + > +int > +record_core_has_execution (struct target_ops *ops) > +{ > + =A0return 1; > +} > + > +static void > +init_record_core_ops (void) > +{ > + =A0record_core_ops.to_shortname =3D "record_core"; > + =A0record_core_ops.to_longname =3D "Process record and replay target"; > + =A0record_core_ops.to_doc =3D > + =A0 =A0"Log program while executing and replay execution from log."; > + =A0record_core_ops.to_open =3D record_open; > + =A0record_core_ops.to_close =3D record_close; > + =A0record_core_ops.to_resume =3D record_core_resume; > + =A0record_core_ops.to_wait =3D record_wait; > + =A0record_core_ops.to_kill =3D record_core_kill; > + =A0record_core_ops.to_fetch_registers =3D record_core_fetch_registers; > + =A0record_core_ops.to_prepare_to_store =3D record_core_prepare_to_store; > + =A0record_core_ops.to_store_registers =3D record_core_store_registers; > + =A0record_core_ops.to_xfer_partial =3D record_core_xfer_partial; > + =A0record_core_ops.to_insert_breakpoint =3D record_core_insert_breakpoi= nt; > + =A0record_core_ops.to_remove_breakpoint =3D record_core_remove_breakpoi= nt; > + =A0record_core_ops.to_can_execute_reverse =3D record_can_execute_revers= e; > + =A0record_core_ops.to_has_execution =3D record_core_has_execution; > + =A0record_core_ops.to_stratum =3D record_stratum; > + =A0record_core_ops.to_magic =3D OPS_MAGIC; > +} > + > +static void > =A0show_record_debug (struct ui_file *file, int from_tty, > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 struct cmd_list_element *c, const cha= r *value) > =A0{ > @@ -1153,6 +1645,212 @@ cmd_record_start (char *args, int from_t > =A0 execute_command ("target record", from_tty); > =A0} > > +static void > +cmd_record_load (char *args, int from_tty) > +{ > + =A0char buf[512]; > + > + =A0snprintf (buf, 512, "target record %s", args); > + =A0execute_command (buf, from_tty); > +} > + > +static inline void > +record_write_dump (char *recfilename, int fildes, const void *buf, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 size_t nbyte) > +{ > + =A0if (write (fildes, buf, nbyte) !=3D nbyte) > + =A0 =A0error (_("Failed to write dump of execution records to '%s'."), > + =A0 =A0 =A0 =A0 =A0 recfilename); > +} > + > +/* Record log save-file format > + =A0 Version 1 > + > + =A0 Header: > + =A0 =A0 4 bytes: magic number htonl(0x20090726). > + =A0 =A0 =A0 NOTE: be sure to change whenever this file format changes! > + > + =A0 Records: > + =A0 =A0 record_end: > + =A0 =A0 =A0 1 byte: =A0record type (record_end). > + =A0 =A0 record_reg: > + =A0 =A0 =A0 1 byte: =A0record type (record_reg). > + =A0 =A0 =A0 8 bytes: register id (network byte order). > + =A0 =A0 =A0 MAX_REGISTER_SIZE bytes: register value. > + =A0 =A0 record_mem: > + =A0 =A0 =A0 1 byte: =A0record type (record_mem). > + =A0 =A0 =A0 8 bytes: memory address (network byte order). > + =A0 =A0 =A0 8 bytes: memory length (network byte order). > + =A0 =A0 =A0 n bytes: memory value (n =3D=3D memory length). > +*/ > + > +/* Dump the execution log to a file. =A0*/ > + > +static void > +cmd_record_dump (char *args, int from_tty) > +{ > + =A0char *recfilename, recfilename_buffer[40]; > + =A0int recfd; > + =A0struct record_entry *cur_record_list; > + =A0uint32_t magic; > + =A0struct regcache *regcache; > + =A0struct gdbarch *gdbarch; > + =A0struct cleanup *old_cleanups; > + =A0struct cleanup *set_cleanups; > + > + =A0if (strcmp (current_target.to_shortname, "record") !=3D 0) > + =A0 =A0error (_("Process record is not started.\n")); > + > + =A0if (args && *args) > + =A0 =A0recfilename =3D args; > + =A0else > + =A0 =A0{ > + =A0 =A0 =A0/* Default corefile name is "gdb_record.PID". =A0*/ > + =A0 =A0 =A0snprintf (recfilename_buffer, 40, "gdb_record.%d", > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0PIDGET (inferior_ptid)); > + =A0 =A0 =A0recfilename =3D recfilename_buffer; > + =A0 =A0} > + > + =A0/* Open the dump file. =A0*/ > + =A0if (record_debug) > + =A0 =A0fprintf_unfiltered (gdb_stdlog, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0_("Saving recording to f= ile '%s'\n"), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0recfilename); > + =A0recfd =3D open (recfilename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0S_IRUSR | S_IWUSR); > + =A0if (recfd < 0) > + =A0 =A0error (_("Failed to open '%s' for dump execution records: %s"), > + =A0 =A0 =A0 =A0 =A0 recfilename, strerror (errno)); > + =A0old_cleanups =3D make_cleanup (record_fd_cleanups, &recfd); > + > + =A0/* Save the current record entry to "cur_record_list". =A0*/ > + =A0cur_record_list =3D record_list; > + > + =A0/* Get the values of regcache and gdbarch. =A0*/ > + =A0regcache =3D get_current_regcache (); > + =A0gdbarch =3D get_regcache_arch (regcache); > + > + =A0/* Disable the GDB operation record. =A0*/ > + =A0set_cleanups =3D record_gdb_operation_disable_set (); > + > + =A0/* Write the magic code. =A0*/ > + =A0magic =3D RECORD_FILE_MAGIC; > + =A0if (record_debug) > + =A0 =A0fprintf_unfiltered (gdb_stdlog, _("\ > +Writing 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n"), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0magic); > + =A0record_write_dump (recfilename, recfd, &magic, 4); > + > + =A0/* Reverse execute to the begin of record list. =A0*/ > + =A0while (1) > + =A0 =A0{ > + =A0 =A0 =A0/* Check for beginning and end of log. =A0*/ > + =A0 =A0 =A0if (record_list =3D=3D &record_first) > + =A0 =A0 =A0 =A0break; > + > + =A0 =A0 =A0record_exec_entry (regcache, gdbarch, record_list, 0); > + > + =A0 =A0 =A0if (record_list->prev) > + =A0 =A0 =A0 =A0record_list =3D record_list->prev; > + =A0 =A0} > + > + =A0/* Dump the entries to recfd and forward execute to the end of > + =A0 =A0 record list. =A0*/ > + =A0while (1) > + =A0 =A0{ > + =A0 =A0 =A0/* Dump entry. =A0*/ > + =A0 =A0 =A0if (record_list !=3D &record_first) > + =A0 =A0 =A0 =A0{ > + =A0 =A0 =A0 =A0 =A0uint8_t tmpu8; > + =A0 =A0 =A0 =A0 =A0uint64_t tmpu64; > + > + =A0 =A0 =A0 =A0 =A0tmpu8 =3D record_list->type; > + =A0 =A0 =A0 =A0 =A0record_write_dump (recfilename, recfd, &tmpu8, 1); > + > + =A0 =A0 =A0 =A0 =A0switch (record_list->type) > + =A0 =A0 =A0 =A0 =A0 =A0{ > + =A0 =A0 =A0 =A0 =A0 =A0case record_reg: /* reg */ > + =A0 =A0 =A0 =A0 =A0 =A0 =A0if (record_debug) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0fprintf_unfiltered (gdb_stdlog, _("\ > +Writing register %d (1 plus 8 plus %d bytes)\n"), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= record_list->u.reg.num, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= MAX_REGISTER_SIZE); > + > + =A0 =A0 =A0 =A0 =A0 =A0 =A0tmpu64 =3D record_list->u.reg.num; > + =A0 =A0 =A0 =A0 =A0 =A0 =A0if (BYTE_ORDER =3D=3D LITTLE_ENDIAN) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0tmpu64 =3D bswap_64 (tmpu64); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0record_write_dump (recfilename, recfd, &tmpu= 64, 8); > + > + =A0 =A0 =A0 =A0 =A0 =A0 =A0record_write_dump (recfilename, recfd, recor= d_list->u.reg.val, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 MAX_REG= ISTER_SIZE); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0break; > + > + =A0 =A0 =A0 =A0 =A0 =A0case record_mem: /* mem */ > + =A0 =A0 =A0 =A0 =A0 =A0 =A0if (!record_list->u.mem.mem_entry_not_access= ible) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0{ > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (record_debug) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0fprintf_unfiltered (gdb_stdlog, = _("\ > +Writing memory %s (1 plus 8 plus 8 bytes plus %d bytes)\n"), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0paddress (gdbarch, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0 =A0 =A0 =A0 =A0record_list->u.mem.addr), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0record_list->u.mem.len); > + > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0tmpu64 =3D record_list->u.mem.addr; > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (BYTE_ORDER =3D=3D LITTLE_ENDIAN) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0tmpu64 =3D bswap_64 (tmpu64); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0record_write_dump (recfilename, recf= d, &tmpu64, 8); > + > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0tmpu64 =3D record_list->u.mem.len; > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (BYTE_ORDER =3D=3D LITTLE_ENDIAN) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0tmpu64 =3D bswap_64 (tmpu64); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0record_write_dump (recfilename, recf= d, &tmpu64, 8); > + > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0record_write_dump (recfilename, recf= d, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= record_list->u.mem.val, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= record_list->u.mem.len); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0} > + =A0 =A0 =A0 =A0 =A0 =A0 =A0break; > + > + =A0 =A0 =A0 =A0 =A0 =A0 =A0case record_end: > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0/* FIXME: record the contents of record_= end rec. =A0*/ > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (record_debug) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0fprintf_unfiltered (gdb_stdlog, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0_("Writing record_end (1 byte)\n")); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0break; > + =A0 =A0 =A0 =A0 =A0 =A0} > + =A0 =A0 =A0 =A0} > + > + =A0 =A0 =A0/* Execute entry. =A0*/ > + =A0 =A0 =A0record_exec_entry (regcache, gdbarch, record_list, 0); > + > + =A0 =A0 =A0if (record_list->next) > + =A0 =A0 =A0 =A0record_list =3D record_list->next; > + =A0 =A0 =A0else > + =A0 =A0 =A0 =A0break; > + =A0 =A0} > + > + =A0/* Reverse execute to cur_record_list. =A0*/ > + =A0while (1) > + =A0 =A0{ > + =A0 =A0 =A0/* Check for beginning and end of log. =A0*/ > + =A0 =A0 =A0if (record_list =3D=3D cur_record_list) > + =A0 =A0 =A0 =A0break; > + > + =A0 =A0 =A0record_exec_entry (regcache, gdbarch, record_list, 0); > + > + =A0 =A0 =A0if (record_list->prev) > + =A0 =A0 =A0 =A0record_list =3D record_list->prev; > + =A0 =A0} > + > + =A0do_cleanups (set_cleanups); > + =A0do_cleanups (old_cleanups); > + > + =A0/* Succeeded. =A0*/ > + =A0fprintf_filtered (gdb_stdout, _("Saved dump of execution " > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"rec= ords to `%s'.\n"), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0recfilename); > +} > + > =A0/* Truncate the record log from the present point > =A0 =A0of replay until the end. =A0*/ > > @@ -1185,7 +1883,12 @@ cmd_record_stop (char *args, int from_tt > =A0 =A0 { > =A0 =A0 =A0 if (!record_list || !from_tty || query (_("Delete recorded lo= g and " > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0"stop recording?"))) > - =A0 =A0 =A0 unpush_target (&record_ops); > + =A0 =A0 =A0 =A0{ > + =A0 =A0 =A0 =A0 =A0if (strcmp (current_target.to_shortname, "record") = =3D=3D 0) > + =A0 =A0 =A0 =A0 =A0 unpush_target (&record_ops); > + =A0 =A0 =A0 =A0 =A0else > + =A0 =A0 =A0 =A0 =A0 =A0unpush_target (&record_core_ops); > + =A0 =A0 =A0 =A0} > =A0 =A0 } > =A0 else > =A0 =A0 printf_unfiltered (_("Process record is not started.\n")); > @@ -1203,7 +1906,7 @@ set_record_insn_max_num (char *args, int > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "the first ones?\n")); > > =A0 =A0 =A0 while (record_insn_num > record_insn_max_num) > - =A0 =A0 =A0 record_list_release_first (); > + =A0 =A0 =A0 record_list_release_first_insn (); > =A0 =A0 } > =A0} > > @@ -1243,6 +1946,8 @@ info_record_command (char *args, int fro > =A0void > =A0_initialize_record (void) > =A0{ > + =A0struct cmd_list_element *c; > + > =A0 /* Init record_first. =A0*/ > =A0 record_first.prev =3D NULL; > =A0 record_first.next =3D NULL; > @@ -1250,6 +1955,8 @@ _initialize_record (void) > > =A0 init_record_ops (); > =A0 add_target (&record_ops); > + =A0init_record_core_ops (); > + =A0add_target (&record_core_ops); > > =A0 add_setshow_zinteger_cmd ("record", no_class, &record_debug, > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0_("Set debugging o= f record/replay feature."), > @@ -1259,9 +1966,10 @@ _initialize_record (void) > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0NULL, show_record_= debug, &setdebuglist, > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0&showdebuglist); > > - =A0add_prefix_cmd ("record", class_obscure, cmd_record_start, > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 _("Abbreviated form of \"target record\= " command."), > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 &record_cmdlist, "record ", 0, &cmdlist= ); > + =A0c =3D add_prefix_cmd ("record", class_obscure, cmd_record_start, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 _("Abbreviated form of \"target= record\" command."), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 &record_cmdlist, "record ", 0, = &cmdlist); > + =A0set_cmd_completer (c, filename_completer); > =A0 add_com_alias ("rec", "record", class_obscure, 1); > =A0 add_prefix_cmd ("record", class_support, set_record_command, > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0_("Set record options"), &set_record_c= mdlist, > @@ -1276,6 +1984,16 @@ _initialize_record (void) > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"info record ", 0, &infolist); > =A0 add_alias_cmd ("rec", "record", class_obscure, 1, &infolist); > > + =A0c =3D add_cmd ("load", class_obscure, cmd_record_load, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0_("Load previously dumped execution records = from \ > +a file given as argument."), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 &record_cmdlist); > + =A0set_cmd_completer (c, filename_completer); > + =A0c =3D add_cmd ("dump", class_obscure, cmd_record_dump, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0_("Dump the execution records to a file.\n\ > +Argument is optional filename. =A0Default filename is > 'gdb_record.'."), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 &record_cmdlist); > + =A0set_cmd_completer (c, filename_completer); > > =A0 add_cmd ("delete", class_obscure, cmd_record_delete, > =A0 =A0 =A0 =A0 =A0 _("Delete the rest of execution log and start recordi= ng it anew."), >