From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 29599 invoked by alias); 18 Oct 2009 02:23:37 -0000 Received: (qmail 29583 invoked by uid 22791); 18 Oct 2009 02:23:34 -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-px0-f175.google.com (HELO mail-px0-f175.google.com) (209.85.216.175) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Sun, 18 Oct 2009 02:23:27 +0000 Received: by pxi5 with SMTP id 5so1270613pxi.12 for ; Sat, 17 Oct 2009 19:23:26 -0700 (PDT) MIME-Version: 1.0 Received: by 10.143.25.36 with SMTP id c36mr202767wfj.1.1255832606076; Sat, 17 Oct 2009 19:23:26 -0700 (PDT) In-Reply-To: <4ADA4191.4060404@vmware.com> References: <4AD91D72.1030802@vmware.com> <831vl2ifui.fsf@gnu.org> <4ADA0EB3.60104@vmware.com> <83my3phjew.fsf@gnu.org> <4ADA4191.4060404@vmware.com> From: Hui Zhu Date: Sun, 18 Oct 2009 02:23:00 -0000 Message-ID: Subject: Re: [RFA, 3 of 3] save/restore process record, part 3 (save/restore) 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-10/txt/msg00404.txt.bz2 Hi Michael, Thanks for your patch. I cannot find the diff for the gcore.c file. (netorder64): New function. (netorder32): New function. (netorder16): New function. I suggest inline them. Thanks, Hui On Sun, Oct 18, 2009 at 06:13, Michael Snyder wrote: > Eli Zaretskii wrote: >>> >>> Date: Sat, 17 Oct 2009 11:36:35 -0700 >>> From: Michael Snyder >>> CC: "gdb-patches@sourceware.org" , >>> =A0"teawater@gmail.com" >>> >>>>> + =A0 =A0 =A0 NOTE: be sure to change whenever this file format chang= es! >>>>> + >>>>> + =A0 Records: >>>>> + =A0 =A0 record_end: >>>>> + =A0 =A0 =A0 1 byte: =A0record type (record_end). >>>> >>>> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0^^^^^^^= ^^^ >>>> This probably has some integer value, right? =A0Or does this indicate >>>> something other than an integer type? >>> >>> It's an enum. =A0Why? =A0You think I should give its numeric >>> value in the comment, instead of its symbolic value? >> >> Either the numeric value or a more explicit reference to the enum >> (e.g., say that it's an enum defined on such-and-such file). >> >> IOW, imagine that someone reads these comments because they want to >> write a utility that reads these files, and ask yourself what would >> they be missing in this text. > > OK, see revised patch. > >>>>> + =A0 =A0 record_reg: >>>>> + =A0 =A0 =A0 1 byte: =A0record type (record_reg). >>>>> + =A0 =A0 =A0 4 bytes: register id (network byte order). >>>>> + =A0 =A0 =A0 n bytes: register value (n =3D=3D register size). >>>> >>>> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0^^^^^^^^^^^^^ >>>> How does one know what is the correct register size? >>> >>> We get it from the current regcache and regnum. =A0What can I say about >>> it here, that wouldn't be arch-specific? >> >> Well, saying it's the exact size of the register being recorded works >> for me. > > OK. > >> >>> I mean, I could say: >>> >>> =A0 =A0This will generally be 4 bytes for x86, 8 bytes for x86_64. >> >> That's good as an example. >> >>> but that doesn't seem very useful or scalable. =A0Plus it will >>> be different for floating point regs, once we handle them too. >> >> Would it make sense to use here some size that can accommodate all the >> known registers, to make the file format truly architecture >> independent? =A0Or would it be more trouble than it's worth? > > That's what version 1 of the file format did. > I found it wasteful, hence this change. > >> >>>>> + =A0if (strcmp (current_target.to_shortname, "record") !=3D 0) >>>>> + =A0 =A0error (_("Process record is not started.\n")); >>>> >>>> Suggest to add to the message text something that tells the user how >>>> to remedy this situation. =A0E.g., "Invoke FOO command first." >>> >>> OK. =A0It's a target-specific command, that can only be used >>> with target 'record'. =A0How about this? >>> >>> =A0 error (_("This command can only be used with target 'record'.\n")); >> >> That's good, but maybe adding "Use 'record start' first." will be >> better? > > OK. > >> >>> This is the same approach that is used by the "gcore" command. >>> How does "gcore" work with go32, if at all? >> >> It doesn't. =A0DJGPP cannot generate core files. > > Well, save/restore depends on core files, so I guess > it won't work in go32. > > >>> Thanks for all your suggestions, Eli. >>> Please see revised patch attached. >> >> It's fine, except for this: >> >>> + =A0 Header: >>> + =A0 =A0 4 bytes: magic number htonl(0x20090829). >> >> I thought you agreed to replace this with "0x20090829 (network byte >> order)"? > > Ah. =A0Yes -- you're looking at the version 1 header, but I do need > to change the comment for the version 2 header. > > See revised patch below. > > > > 2009-10-16 =A0Hui Zhu =A0 > =A0 =A0 =A0 =A0 =A0 =A0Michael Snyder =A0 > > =A0 =A0 =A0 =A0* record.c (RECORD_FILE_MAGIC): New constant. > =A0 =A0 =A0 =A0(record_arch_list_cleanups): Renamed from record_message_c= leanups. > =A0 =A0 =A0 =A0(bfdcore_read): New function. > =A0 =A0 =A0 =A0(netorder64): New function. > =A0 =A0 =A0 =A0(netorder32): New function. > =A0 =A0 =A0 =A0(netorder16): New function. > =A0 =A0 =A0 =A0(record_restore): New function. =A0Restore a saved record = log. > =A0 =A0 =A0 =A0(bfdcore_write): New function. > =A0 =A0 =A0 =A0(cmd_record_restore): New function. > =A0 =A0 =A0 =A0(cmd_record_save): New function. =A0Save a record log to a= file. > =A0 =A0 =A0 =A0(_initialize_record): Set up commands for save and restore. > > --- record.2b.c 2009-10-17 11:30:35.000000000 -0700 > +++ record.12.c 2009-10-17 15:09:48.000000000 -0700 > @@ -23,10 +23,15 @@ > =A0#include "gdbthread.h" > =A0#include "event-top.h" > =A0#include "exceptions.h" > +#include "completer.h" > +#include "arch-utils.h" > =A0#include "gdbcore.h" > =A0#include "exec.h" > =A0#include "record.h" > +#include "elf-bfd.h" > +#include "gcore.h" > > +#include > =A0#include > > =A0#define DEFAULT_RECORD_INSN_MAX_NUM =A0 =A0200000 > @@ -34,6 +39,8 @@ > =A0#define RECORD_IS_REPLAY \ > =A0 =A0 =A0(record_list->next || execution_direction =3D=3D EXEC_REVERSE) > > +#define RECORD_FILE_MAGIC =A0 =A0 =A0netorder32(0x20091016) > + > =A0/* These are the core structs of the process record functionality. > > =A0 =A0A record_entry is a record of the value change of a register > @@ -482,24 +489,24 @@ 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: stopped 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); > -} > - > =A0struct record_message_args { > =A0 struct regcache *regcache; > =A0 enum target_signal signal; > @@ -511,7 +518,7 @@ record_message (void *args) > =A0 int ret; > =A0 struct record_message_args *myargs =3D args; > =A0 struct gdbarch *gdbarch =3D get_regcache_arch (myargs->regcache); > - =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; > @@ -651,8 +658,8 @@ record_exec_insn (struct regcache *regca > =A0 =A0 =A0 =A0 =A0 =A0 =A0 { > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 entry->u.mem.mem_entry_not_accessible =3D= 1; > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (record_debug) > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0warning (_("Process record: error re= ading memory at " > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "addr =3D %s le= n =3D %d."), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0warning ("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 =A0 paddress (gdbarch, en= try->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 } > @@ -664,8 +671,8 @@ record_exec_insn (struct regcache *regca > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 { > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 entry->u.mem.mem_entry_not_access= ible =3D 1; > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (record_debug) > - =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 "addr = =3D %s len =3D %d."), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0warning ("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 =A0 =A0paddress (= gdbarch, entry->u.mem.addr), > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0entry->u.m= em.len); > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 } > @@ -678,6 +685,215 @@ record_exec_insn (struct regcache *regca > =A0 =A0 } > =A0} > > +/* bfdcore_read -- read bytes from a core file section. =A0*/ > + > +static void > +bfdcore_read (bfd *obfd, asection *osec, void *buf, int len, int *offset) > +{ > + =A0int ret =3D bfd_get_section_contents (obfd, osec, buf, *offset, len); > + > + =A0if (ret) > + =A0 =A0*offset +=3D len; > + =A0else > + =A0 =A0error (_("Failed to read %d bytes from core file %s ('%s').\n"), > + =A0 =A0 =A0 =A0 =A0len, bfd_get_filename (obfd), > + =A0 =A0 =A0 =A0 =A0bfd_errmsg (bfd_get_error ())); > +} > + > +static uint64_t > +netorder64 (uint64_t fromfile) > +{ > + =A0return (BYTE_ORDER =3D=3D LITTLE_ENDIAN) > + =A0 =A0? bswap_64 (fromfile) > + =A0 =A0: fromfile; > +} > + > +static uint32_t > +netorder32 (uint32_t fromfile) > +{ > + =A0return (BYTE_ORDER =3D=3D LITTLE_ENDIAN) > + =A0 =A0? bswap_32 (fromfile) > + =A0 =A0: fromfile; > +} > + > +static uint16_t > +netorder16 (uint16_t fromfile) > +{ > + =A0return (BYTE_ORDER =3D=3D LITTLE_ENDIAN) > + =A0 =A0? bswap_16 (fromfile) > + =A0 =A0: fromfile; > +} > + > +/* Restore the execution log from a core_bfd file. =A0*/ > + > +static void > +record_restore (void) > +{ > + =A0uint32_t magic; > + =A0struct cleanup *old_cleanups; > + =A0struct record_entry *rec; > + =A0asection *osec; > + =A0uint32_t osec_size; > + =A0int bfd_offset =3D 0; > + =A0struct regcache *regcache; > + > + =A0/* We restore the execution log from the open core bfd, > + =A0 =A0 if there is one. =A0*/ > + =A0if (core_bfd =3D=3D NULL) > + =A0 =A0return; > + > + =A0/* "record_restore" can only be called when record list is empty. = =A0*/ > + =A0gdb_assert (record_first.next =3D=3D NULL); > + > + =A0if (record_debug) > + =A0 =A0printf_filtered ("Restoring recording from core file.\n"); > + > + =A0/* Now need to find our special note section. =A0*/ > + =A0osec =3D bfd_get_section_by_name (core_bfd, "null0"); > + =A0osec_size =3D bfd_section_size (core_bfd, osec); > + =A0if (record_debug) > + =A0 =A0printf_filtered ("Find precord section %s.\n", > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0osec ? "succeeded" : "failed"); > + =A0if (!osec) > + =A0 =A0return; > + =A0if (record_debug) > + =A0 =A0printf_filtered ("%s", bfd_section_name (core_bfd, osec)); > + > + =A0/* Check the magic code. =A0*/ > + =A0if (record_debug) > + =A0 =A0printf_filtered ("\ > + =A0Reading 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n", > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0magic); > + =A0bfdcore_read (core_bfd, osec, &magic, sizeof (magic), &bfd_offset); > + =A0if (magic !=3D RECORD_FILE_MAGIC) > + =A0 =A0error (_("Version mis-match or file format error in core file %s= ."), > + =A0 =A0 =A0 =A0 =A0bfd_get_filename (core_bfd)); > + > + =A0/* Restore the entries in recfd into record_arch_list_head and > + =A0 =A0 record_arch_list_tail. =A0*/ > + =A0record_arch_list_head =3D NULL; > + =A0record_arch_list_tail =3D NULL; > + =A0record_insn_num =3D 0; > + =A0old_cleanups =3D make_cleanup (record_arch_list_cleanups, 0); > + =A0regcache =3D get_current_regcache (); > + > + =A0while (1) > + =A0 =A0{ > + =A0 =A0 =A0int ret; > + =A0 =A0 =A0uint8_t tmpu8; > + =A0 =A0 =A0uint32_t regnum, len, signal, count; > + =A0 =A0 =A0uint64_t addr; > + > + =A0 =A0 =A0/* We are finished when offset reaches osec_size. =A0*/ > + =A0 =A0 =A0if (bfd_offset >=3D osec_size) > + =A0 =A0 =A0 break; > + =A0 =A0 =A0bfdcore_read (core_bfd, osec, &tmpu8, sizeof (tmpu8), &bfd_o= ffset); > + > + =A0 =A0 =A0switch (tmpu8) > + =A0 =A0 =A0 =A0{ > + =A0 =A0 =A0 =A0case record_reg: /* reg */ > + =A0 =A0 =A0 =A0 =A0if (record_debug) > + =A0 =A0 =A0 =A0 =A0 =A0printf_filtered ("\ > + =A0Reading register %d (1 plus %d plus %d bytes)\n", > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0rec->u.reg.num, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0sizeof (regnum), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0rec->u.reg.len); > + =A0 =A0 =A0 =A0 =A0/* Get register number to regnum. =A0*/ > + =A0 =A0 =A0 =A0 =A0bfdcore_read (core_bfd, osec, ®num, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 sizeof (regnum), &bfd_offse= t); > + =A0 =A0 =A0 =A0 regnum =3D netorder32 (regnum); > + > + =A0 =A0 =A0 =A0 =A0rec =3D record_reg_alloc (regcache, regnum); > + > + =A0 =A0 =A0 =A0 =A0/* Get val. =A0*/ > + =A0 =A0 =A0 =A0 =A0bfdcore_read (core_bfd, osec, record_get_loc (rec), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 rec->u.reg.len, &bfd_offset= ); > + =A0 =A0 =A0 =A0 =A0break; > + > + =A0 =A0 =A0 =A0case record_mem: /* mem */ > + =A0 =A0 =A0 =A0 =A0if (record_debug) > + =A0 =A0 =A0 =A0 =A0 =A0printf_filtered ("\ > + =A0Reading memory %s (1 plus %d plus %d plus %d bytes)\n", > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0paddress (get_cu= rrent_arch (), > + =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 =A0sizeof (addr), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0sizeof (len), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0rec->u.mem.len); > + =A0 =A0 =A0 =A0 =A0/* Get len. =A0*/ > + =A0 =A0 =A0 =A0 =A0bfdcore_read (core_bfd, osec, &len, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 sizeof (len), &bfd_offset); > + =A0 =A0 =A0 =A0 len =3D netorder32 (len); > + > + =A0 =A0 =A0 =A0 =A0/* Get addr. =A0*/ > + =A0 =A0 =A0 =A0 =A0bfdcore_read (core_bfd, osec, &addr, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 sizeof (addr), &bfd_offset); > + =A0 =A0 =A0 =A0 addr =3D netorder64 (addr); > + > + =A0 =A0 =A0 =A0 =A0rec =3D record_mem_alloc (addr, len); > + > + =A0 =A0 =A0 =A0 =A0/* Get val. =A0*/ > + =A0 =A0 =A0 =A0 =A0bfdcore_read (core_bfd, osec, record_get_loc (rec), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 rec->u.mem.len, &bfd_offset= ); > + =A0 =A0 =A0 =A0 =A0break; > + > + =A0 =A0 =A0 =A0case record_end: /* end */ > + =A0 =A0 =A0 =A0 =A0if (record_debug) > + =A0 =A0 =A0 =A0 =A0 =A0printf_filtered ("\ > + =A0Reading record_end (1 + %d + %d bytes), offset =3D=3D %s\n", > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0sizeof (signal), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0sizeof (count), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0paddress (get_cu= rrent_arch (), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0bfd_offset)); > + =A0 =A0 =A0 =A0 =A0rec =3D record_end_alloc (); > + =A0 =A0 =A0 =A0 =A0record_insn_num ++; > + > + =A0 =A0 =A0 =A0 /* Get signal value. =A0*/ > + =A0 =A0 =A0 =A0 bfdcore_read (core_bfd, osec, &signal, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 sizeof (signal), &bfd_offse= t); > + =A0 =A0 =A0 =A0 signal =3D netorder32 (signal); > + =A0 =A0 =A0 =A0 rec->u.end.sigval =3D signal; > + > + =A0 =A0 =A0 =A0 /* Get insn count. =A0*/ > + =A0 =A0 =A0 =A0 bfdcore_read (core_bfd, osec, &count, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 sizeof (count), &bfd_offset= ); > + =A0 =A0 =A0 =A0 count =3D netorder32 (count); > + =A0 =A0 =A0 =A0 rec->u.end.insn_num =3D count; > + =A0 =A0 =A0 =A0 record_insn_count =3D count + 1; > + =A0 =A0 =A0 =A0 =A0break; > + > + =A0 =A0 =A0 =A0default: > + =A0 =A0 =A0 =A0 =A0error (_("Bad entry type in core file %s."), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0bfd_get_filename (core_bfd)); > + =A0 =A0 =A0 =A0 =A0break; > + =A0 =A0 =A0 =A0} > + > + =A0 =A0 =A0/* Add rec to record arch list. =A0*/ > + =A0 =A0 =A0record_arch_list_add (rec); > + =A0 =A0} > + > + =A0discard_cleanups (old_cleanups); > + > + =A0/* Add record_arch_list_head to the end of record list. =A0*/ > + =A0record_first.next =3D record_arch_list_head; > + =A0record_arch_list_head->prev =3D &record_first; > + =A0record_arch_list_tail->next =3D NULL; > + =A0record_list =3D &record_first; > + > + =A0/* Update record_insn_max_num. =A0*/ > + =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} > + > + =A0/* Succeeded. =A0*/ > + =A0printf_filtered (_("Restored records from core file %s.\n"), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0bfd_get_filename (core_bfd)); > + > + =A0print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC); > +} > + > =A0static struct target_ops *tmp_to_resume_ops; > =A0static 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_si= gnal); > @@ -728,6 +944,7 @@ record_core_open_1 (char *name, int from > =A0 =A0 } > > =A0 push_target (&record_core_ops); > + =A0record_restore (); > =A0} > > =A0static void > @@ -735,9 +952,6 @@ record_open_1 (char *name, int from_tty) > =A0{ > =A0 struct target_ops *t; > > - =A0if (record_debug) > - =A0 =A0fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n"); > - > =A0 /* check exec */ > =A0 if (!target_has_execution) > =A0 =A0 error (_("Process record: the program is not being run.")); > @@ -779,6 +993,12 @@ record_open (char *name, int from_tty) > =A0 =A0 error (_("Process record target already running. =A0Use \"record = stop\" to > " > =A0 =A0 =A0 =A0 =A0 =A0 =A0"stop record target first.")); > > + =A0/* Reset */ > + =A0record_insn_num =3D 0; > + =A0record_insn_count =3D 0; > + =A0record_list =3D &record_first; > + =A0record_list->next =3D NULL; > + > =A0 /* Reset the tmp beneath pointers. =A0*/ > =A0 tmp_to_resume_ops =3D NULL; > =A0 tmp_to_resume =3D NULL; > @@ -822,17 +1042,6 @@ record_open (char *name, int from_tty) > =A0 if (!tmp_to_xfer_partial) > =A0 =A0 error (_("Could not find 'to_xfer_partial' method on the target > stack.")); > > - =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 */ > - =A0record_insn_num =3D 0; > - =A0record_insn_count =3D 0; > - =A0record_list =3D &record_first; > - =A0record_list->next =3D NULL; > - > =A0 /* Set the tmp beneath pointers to beneath pointers. =A0*/ > =A0 record_beneath_to_resume_ops =3D tmp_to_resume_ops; > =A0 record_beneath_to_resume =3D tmp_to_resume; > @@ -844,6 +1053,11 @@ record_open (char *name, int from_tty) > =A0 record_beneath_to_xfer_partial =3D tmp_to_xfer_partial; > =A0 record_beneath_to_insert_breakpoint =3D tmp_to_insert_breakpoint; > =A0 record_beneath_to_remove_breakpoint =3D tmp_to_remove_breakpoint; > + > + =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} > > =A0static void > @@ -1114,8 +1328,7 @@ record_wait (struct target_ops *ops, > =A0 =A0 =A0 =A0 =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 =A0 =A0 =A0fprintf_unfiltered (gdb_st= dlog, > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0 "Process record: break " > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0 "at %s.\n", > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0 "Process record: break at > %s.\n", > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0paddress (gdbarch, tmp_pc)); > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (gdbarch_decr_pc_after_brea= k (gdbarch) > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0&& execution_direction= =3D=3D EXEC_FORWARD > @@ -1190,8 +1403,7 @@ static void > =A0record_mourn_inferior (struct target_ops *ops) > =A0{ > =A0 if (record_debug) > - =A0 =A0fprintf_unfiltered (gdb_stdlog, "Process record: " > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "re= cord_mourn_inferior\n"); > + =A0 =A0fprintf_unfiltered (gdb_stdlog, "Process record: > record_mourn_inferior\n"); > > =A0 unpush_target (&record_ops); > =A0 target_mourn_inferior (); > @@ -1345,8 +1557,8 @@ record_xfer_partial (struct target_ops * > =A0 =A0 =A0 =A0 =A0record_list_release (record_arch_list_tail); > =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 _("Process = record: failed to record " > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "execut= ion log.")); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "Process re= cord: failed to record " > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "execution = log."); > =A0 =A0 =A0 =A0 =A0return -1; > =A0 =A0 =A0 =A0} > =A0 =A0 =A0 if (record_arch_list_add_end ()) > @@ -1354,8 +1566,8 @@ record_xfer_partial (struct target_ops * > =A0 =A0 =A0 =A0 =A0record_list_release (record_arch_list_tail); > =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 _("Process = record: failed to record " > - =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "execut= ion log.")); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "Process re= cord: failed to record " > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "execution = log."); > =A0 =A0 =A0 =A0 =A0return -1; > =A0 =A0 =A0 =A0} > =A0 =A0 =A0 record_list->next =3D record_arch_list_head; > @@ -1642,6 +1854,297 @@ cmd_record_start (char *args, int from_t > =A0 execute_command ("target record", from_tty); > =A0} > > +/* Record log save-file format > + =A0 Version 1 (never released) > + > + =A0 Header: > + =A0 =A0 4 bytes: magic number htonl(0x20090829). > + =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, see enum record_type). > + =A0 =A0 record_reg: > + =A0 =A0 =A0 1 byte: =A0record type (record_reg, see enum record_type). > + =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, see enum record_type). > + =A0 =A0 =A0 8 bytes: memory length (network byte order). > + =A0 =A0 =A0 8 bytes: memory address (network byte order). > + =A0 =A0 =A0 n bytes: memory value (n =3D=3D memory length). > + > + =A0 Version 2 > + =A0 =A0 4 bytes: magic number netorder32(0x20091016). > + =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, see enum record_type). > + =A0 =A0 =A0 4 bytes: signal > + =A0 =A0 =A0 4 bytes: instruction count > + =A0 =A0 record_reg: > + =A0 =A0 =A0 1 byte: =A0record type (record_reg, see enum record_type). > + =A0 =A0 =A0 4 bytes: register id (network byte order). > + =A0 =A0 =A0 n bytes: register value (n =3D=3D actual register size). > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(eg. 4 bytes for x86 general registers). > + =A0 =A0 record_mem: > + =A0 =A0 =A0 1 byte: =A0record type (record_mem, see enum record_type). > + =A0 =A0 =A0 4 bytes: memory length (network byte order). > + =A0 =A0 =A0 8 bytes: memory address (network byte order). > + =A0 =A0 =A0 n bytes: memory value (n =3D=3D memory length). > + > +*/ > + > +/* bfdcore_write -- write bytes into a core file section. =A0*/ > + > +static void > +bfdcore_write (bfd *obfd, asection *osec, void *buf, int len, int *offse= t) > +{ > + =A0int ret =3D bfd_set_section_contents (obfd, osec, buf, *offset, len); > + > + =A0if (ret) > + =A0 =A0*offset +=3D len; > + =A0else > + =A0 =A0error (_("Failed to write %d bytes to core file %s ('%s').\n"), > + =A0 =A0 =A0 =A0 =A0len, bfd_get_filename (obfd), > + =A0 =A0 =A0 =A0 =A0bfd_errmsg (bfd_get_error ())); > +} > + > +/* Restore the execution log from a file. =A0We use a modified elf > + =A0 corefile format, with an extra section for our data. =A0*/ > + > +static void > +cmd_record_restore (char *args, int from_tty) > +{ > + =A0core_file_command (args, from_tty); > + =A0record_open (args, from_tty); > +} > + > +static void > +record_save_cleanups (void *data) > +{ > + =A0bfd *obfd =3D data; > + =A0char *pathname =3D xstrdup (bfd_get_filename (obfd)); > + =A0bfd_close (obfd); > + =A0unlink (pathname); > + =A0xfree (pathname); > +} > + > +/* Save the execution log to a file. =A0We use a modified elf corefile > + =A0 format, with an extra section for our data. =A0*/ > + > +static void > +cmd_record_save (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; > + =A0bfd *obfd; > + =A0int save_size =3D 0; > + =A0asection *osec =3D NULL; > + =A0int bfd_offset =3D 0; > + > + =A0if (strcmp (current_target.to_shortname, "record") !=3D 0) > + =A0 =A0error (_("This command can only be used with target 'record'.\n" > + =A0 =A0 =A0 =A0 =A0 =A0"Use 'target record' first.\n")); > + > + =A0if (args && *args) > + =A0 =A0recfilename =3D args; > + =A0else > + =A0 =A0{ > + =A0 =A0 =A0/* Default recfile name is "gdb_record.PID". =A0*/ > + =A0 =A0 =A0snprintf (recfilename_buffer, sizeof (recfilename_buffer), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"gdb_record.%d", PIDGET (inferior_ptid)); > + =A0 =A0 =A0recfilename =3D recfilename_buffer; > + =A0 =A0} > + > + =A0/* Open the save file. =A0*/ > + =A0if (record_debug) > + =A0 =A0printf_filtered ("Saving execution log to core file '%s'\n", > recfilename); > + > + =A0/* Open the output file. =A0*/ > + =A0obfd =3D create_gcore_bfd (recfilename); > + =A0old_cleanups =3D make_cleanup (record_save_cleanups, obfd); > + > + =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/* 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_insn (regcache, gdbarch, record_list); > + > + =A0 =A0 =A0if (record_list->prev) > + =A0 =A0 =A0 =A0record_list =3D record_list->prev; > + =A0 =A0} > + > + =A0/* Compute the size needed for the extra bfd section. =A0*/ > + =A0save_size =3D 4; =A0 =A0 =A0 /* magic cookie */ > + =A0for (record_list =3D record_first.next; record_list; > + =A0 =A0 =A0 record_list =3D record_list->next) > + =A0 =A0switch (record_list->type) > + =A0 =A0 =A0{ > + =A0 =A0 =A0case record_end: > + =A0 =A0 =A0 save_size +=3D 1 + 4 + 4; > + =A0 =A0 =A0 break; > + =A0 =A0 =A0case record_reg: > + =A0 =A0 =A0 save_size +=3D 1 + 4 + record_list->u.reg.len; > + =A0 =A0 =A0 break; > + =A0 =A0 =A0case record_mem: > + =A0 =A0 =A0 save_size +=3D 1 + 4 + 8 + record_list->u.mem.len; > + =A0 =A0 =A0 break; > + =A0 =A0 =A0} > + > + =A0/* Make the new bfd section. =A0*/ > + =A0osec =3D bfd_make_section_anyway_with_flags (obfd, "precord", > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0 =A0 SEC_HAS_CONTENTS > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0= =A0 =A0 =A0 =A0 | SEC_READONLY); > + =A0if (osec =3D=3D NULL) > + =A0 =A0error (_("Failed to create 'precord' section for corefile %s: %s= "), > + =A0 =A0 =A0 =A0 =A0recfilename, > + =A0 =A0 =A0 =A0 =A0 bfd_errmsg (bfd_get_error ())); > + =A0bfd_set_section_size (obfd, osec, save_size); > + =A0bfd_set_section_vma (obfd, osec, 0); > + =A0bfd_set_section_alignment (obfd, osec, 0); > + =A0bfd_section_lma (obfd, osec) =3D 0; > + > + =A0/* Save corefile state. =A0*/ > + =A0write_gcore_file (obfd); > + > + =A0/* Write out the record log. =A0*/ > + =A0/* Write the magic code. =A0*/ > + =A0magic =3D RECORD_FILE_MAGIC; > + =A0if (record_debug) > + =A0 =A0printf_filtered ("\ > + =A0Writing 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n", > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0magic); > + =A0bfdcore_write (obfd, osec, &magic, sizeof (magic), &bfd_offset); > + > + =A0/* Save the entries to recfd and forward execute to the end of > + =A0 =A0 record list. =A0*/ > + =A0record_list =3D &record_first; > + =A0while (1) > + =A0 =A0{ > + =A0 =A0 =A0/* Save entry. =A0*/ > + =A0 =A0 =A0if (record_list !=3D &record_first) > + =A0 =A0 =A0 =A0{ > + =A0 =A0 =A0 =A0 uint8_t type; > + =A0 =A0 =A0 =A0 uint32_t regnum, len, signal, count; > + =A0 =A0 =A0 =A0 =A0uint64_t addr; > + > + =A0 =A0 =A0 =A0 type =3D record_list->type; > + =A0 =A0 =A0 =A0 =A0bfdcore_write (obfd, osec, &type, sizeof (type), &bf= d_offset); > + > + =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 printf_filtered ("\ > + =A0Writing register %d (1 plus %d plus %d bytes)\n", > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0record_l= ist->u.reg.num, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0sizeof (= regnum), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0record_l= ist->u.reg.len); > + > + =A0 =A0 =A0 =A0 =A0 =A0 =A0/* Write regnum. =A0*/ > + =A0 =A0 =A0 =A0 =A0 =A0 =A0regnum =3D netorder32 (record_list->u.reg.nu= m); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0bfdcore_write (obfd, osec, ®num, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0sizeof (regnum),= &bfd_offset); > + > + =A0 =A0 =A0 =A0 =A0 =A0 =A0/* Write regval. =A0*/ > + =A0 =A0 =A0 =A0 =A0 =A0 =A0bfdcore_write (obfd, osec, record_get_loc (r= ecord_list), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0record_list->u.r= eg.len, &bfd_offset); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0break; > + > + =A0 =A0 =A0 =A0 =A0 =A0case record_mem: /* mem */ > + =A0 =A0 =A0 =A0 =A0 =A0 if (record_debug) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 printf_filtered ("\ > + =A0Writing memory %s (1 plus %d plus %d plus %d bytes)\n", > + =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 =A0record_list->u.mem.addr), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0sizeof (= addr), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0sizeof (= len), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0record_l= ist->u.mem.len); > + > + =A0 =A0 =A0 =A0 =A0 =A0 /* Write memlen. =A0*/ > + =A0 =A0 =A0 =A0 =A0 =A0 len =3D netorder32 (record_list->u.mem.len); > + =A0 =A0 =A0 =A0 =A0 =A0 bfdcore_write (obfd, osec, &len, sizeof (len), = &bfd_offset); > + > + =A0 =A0 =A0 =A0 =A0 =A0 /* Write memaddr. =A0*/ > + =A0 =A0 =A0 =A0 =A0 =A0 addr =3D netorder64 (record_list->u.mem.addr); > + =A0 =A0 =A0 =A0 =A0 =A0 bfdcore_write (obfd, osec, &addr, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0sizeof (addr), &= bfd_offset); > + > + =A0 =A0 =A0 =A0 =A0 =A0 /* Write memval. =A0*/ > + =A0 =A0 =A0 =A0 =A0 =A0 bfdcore_write (obfd, osec, record_get_loc (reco= rd_list), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0record_list->u.m= em.len, &bfd_offset); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0break; > + > + =A0 =A0 =A0 =A0 =A0 =A0 =A0case record_end: > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (record_debug) > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0printf_filtered ("\ > + =A0Writing record_end (1 + %d + %d bytes)\n", > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0size= of (signal), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0size= of (count)); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Write signal value. =A0*/ > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 signal =3D netorder32 (record_list->u.end.s= igval); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 bfdcore_write (obfd, osec, &signal, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0sizeof (sign= al), &bfd_offset); > + > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Write insn count. =A0*/ > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 count =3D netorder32 (record_list->u.end.in= sn_num); > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 bfdcore_write (obfd, osec, &count, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0sizeof (coun= t), &bfd_offset); > + =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_insn (regcache, gdbarch, record_list); > + > + =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_insn (regcache, gdbarch, record_list); > + > + =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*/ > + =A0printf_filtered (_("Saved core file %s with execution log.\n"), > + =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*/ > > @@ -1749,6 +2252,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; > @@ -1767,10 +2272,12 @@ _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, > + =A0c =3D add_prefix_cmd ("record", class_obscure, cmd_record_start, > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0_("Abbreviated form of \"target record= \" command."), > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0&record_cmdlist, "record ", 0, &cmdlis= t); > + =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, > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"set record ", 0, &setlist); > @@ -1784,6 +2291,17 @@ _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 ("save", class_obscure, cmd_record_save, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0_("Save the execution log to a file.\n\ > +Argument is optional filename.\n\ > +Default filename is 'gdb_record.'."), > + =A0 =A0 =A0 =A0 =A0 =A0 =A0&record_cmdlist); > + > + =A0c =3D add_cmd ("restore", class_obscure, cmd_record_restore, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0_("Restore the execution log from a file.\n\ > +Argument is filename. =A0File must be created with 'record save'."), > + =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."), > >