From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 9371 invoked by alias); 10 Mar 2009 17:03:05 -0000 Received: (qmail 7720 invoked by uid 22791); 10 Mar 2009 17:02:59 -0000 X-SWARE-Spam-Status: No, hits=-1.5 required=5.0 tests=AWL,BAYES_00,SARE_MSGID_LONG40,SPF_PASS X-Spam-Check-By: sourceware.org Received: from ti-out-0910.google.com (HELO ti-out-0910.google.com) (209.85.142.189) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Tue, 10 Mar 2009 17:02:50 +0000 Received: by ti-out-0910.google.com with SMTP id y8so1376707tia.12 for ; Tue, 10 Mar 2009 10:02:46 -0700 (PDT) MIME-Version: 1.0 Received: by 10.110.57.5 with SMTP id f5mr11162499tia.49.1236704566802; Tue, 10 Mar 2009 10:02:46 -0700 (PDT) In-Reply-To: <200903092034.57367.pedro@codesourcery.com> References: <200903092034.57367.pedro@codesourcery.com> Date: Tue, 10 Mar 2009 17:32:00 -0000 Message-ID: Subject: Re: [RFA] Submit process record and replay third time, 3/9 From: teawater To: Pedro Alves Cc: gdb-patches@sourceware.org, Michael Snyder 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-03/txt/msg00143.txt.bz2 Hi Pedro, On Tue, Mar 10, 2009 at 04:34, Pedro Alves wrote: > On Monday 23 February 2009 09:20:13, teawater wrote: >> This is the new version patches that follow cvs-head. > > Thanks. > >> >> =A0gdb/Makefile.in | =A0 =A04 >> =A0gdb/record.c =A0 =A0| 1283 ++++++++++++++++++++++++++++++++++++++++++= ++++++++++++++ >> =A0gdb/record.h =A0 =A0| =A0 87 +++ >> =A03 files changed, 1372 insertions(+), 2 deletions(-) >> >> Index: src/gdb/Makefile.in >> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >> --- src.orig/gdb/Makefile.in =A02009-02-21 15:53:27.000000000 +0000 >> +++ src/gdb/Makefile.in =A0 =A0 =A0 2009-02-28 20:23:20.000000000 +0000 >> @@ -662,7 +662,7 @@ SFILES =3D ada-exp.y ada-lang.c ada-typepr >> =A0 =A0 =A0 valarith.c valops.c valprint.c value.c varobj.c vec.c \ >> =A0 =A0 =A0 wrapper.c \ >> =A0 =A0 =A0 xml-tdesc.c xml-support.c \ >> - =A0 =A0 inferior.c >> + =A0 =A0 inferior.c record.c >> >> =A0LINTFILES =3D $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c >> >> @@ -813,7 +813,7 @@ COMMON_OBS =3D $(DEPFILES) $(CONFIG_OBS) $ >> =A0 =A0 =A0 solib.o solib-null.o \ >> =A0 =A0 =A0 prologue-value.o memory-map.o xml-support.o \ >> =A0 =A0 =A0 target-descriptions.o target-memory.o xml-tdesc.o xml-builti= n.o \ >> - =A0 =A0 inferior.o osdata.o >> + =A0 =A0 inferior.o osdata.o record.o >> >> =A0TSOBS =3D inflow.o >> >> Index: src/gdb/record.c >> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >> --- /dev/null 1970-01-01 00:00:00.000000000 +0000 >> +++ src/gdb/record.c =A02009-02-28 20:23:20.000000000 +0000 >> @@ -0,0 +1,1283 @@ >> +/* Process record and replay target for GDB, the GNU debugger. >> + >> + =A0 Copyright (C) 2008 Free Software Foundation, Inc. > > Oops, you'll have to update this. I will. :) > >> + >> + =A0 This file is part of GDB. >> + >> + =A0 This program is free software; you can redistribute it and/or modi= fy >> + =A0 it under the terms of the GNU General Public License as published = by >> + =A0 the Free Software Foundation; either version 3 of the License, or >> + =A0 (at your option) any later version. >> + >> + =A0 This program is distributed in the hope that it will be useful, >> + =A0 but WITHOUT ANY WARRANTY; without even the implied warranty of >> + =A0 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =A0See the >> + =A0 GNU General Public License for more details. >> + >> + =A0 You should have received a copy of the GNU General Public License >> + =A0 along with this program. =A0If not, see . =A0*/ >> + >> +#include "defs.h" >> +#include "target.h" >> +#include "gdbcmd.h" >> +#include "regcache.h" >> +#include "inferior.h" >> +#include "gdbthread.h" >> +#include "event-top.h" >> +#include "annotate.h" >> +#include "observer.h" >> +#include "record.h" > > Why did you need annotate.h? =A0Can you check which headers are > really needed here? OK. I will check all of them. > >> + >> +#include >> + >> +#define DEFAULT_RECORD_INSN_MAX_NUM =A0200000 >> + >> +int record_debug =3D 0; >> + >> +record_t record_first; >> +record_t *record_list =3D &record_first; >> +record_t *record_arch_list_head =3D NULL; >> +record_t *record_arch_list_tail =3D NULL; >> +struct regcache *record_regcache =3D NULL; >> + >> +/* 1 ask user. 0 auto delete the last record_t. =A0*/ > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 ^ double-space please. > >> +static int record_stop_at_limit =3D 1; >> +static int record_insn_max_num =3D DEFAULT_RECORD_INSN_MAX_NUM; >> +static int record_insn_num =3D 0; >> + >> +struct target_ops record_ops; >> +static int record_resume_step =3D 0; >> +static enum target_signal record_resume_siggnal; >> +static int record_get_sig =3D 0; >> +static sigset_t record_maskall; >> +static int record_gdb_operation_disable =3D 0; >> +int record_will_store_registers =3D 0; > > You've got a bunch of magic variables here. =A0Could you > add some comments explaining what they're for? OK. I will. > >> + >> +extern struct bp_location *bp_location_chain; > > This is not right. =A0You're accessing a breakpoints.c > internal implementation detail. =A0We need to come up with interfaces > to replace this hack. If I got a interface, I will remove it. > >> + >> +/* The beneath function pointers. =A0*/ >> +static struct target_ops *record_beneath_to_resume_ops =3D NULL; >> +static void (*record_beneath_to_resume) (struct target_ops *, ptid_t, i= nt, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 enum target_signal) =3D NULL; >> +static struct target_ops *record_beneath_to_wait_ops =3D NULL; >> +static ptid_t (*record_beneath_to_wait) (struct target_ops *, ptid_t, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0struct target_waitstatus *) =3D NULL; >> +static struct target_ops *record_beneath_to_store_registers_ops =3D NUL= L; >> +static void (*record_beneath_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 =A0 =A0 =A0 =A0 =A0 =A0struct regcache *, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 int regno) =3D NULL; >> +static struct target_ops *record_beneath_to_xfer_partial_ops =3D NULL; >> +static LONGEST (*record_beneath_to_xfer_partial) (struct target_ops * o= ps, >> + =A0 =A0 =A0 =A0 =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 =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 =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 =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 =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 =A0 =A0 =A0 =A0 LONGEST len) =3D NULL; >> +static int (*record_beneath_to_insert_breakpoint) (struct bp_target_inf= o *) =3D >> + =A0NULL; >> +static int (*record_beneath_to_remove_breakpoint) (struct bp_target_inf= o *) =3D >> + =A0NULL; > > I can't say I'm happy with this mechanism yet, but it is much > better than the previous version of making target.c aware of record.c's c= allbacks. > I had tried my best on it. > >> + >> +static void >> +record_list_release (record_t * rec) > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0^ no s= pace after *, please. > >> +{ >> + =A0record_t *tmp; >> + >> + =A0if (!rec) >> + =A0 =A0return; >> + >> + =A0while (rec->next) >> + =A0 =A0{ >> + =A0 =A0 =A0rec =3D rec->next; >> + =A0 =A0} >> + >> + =A0while (rec->prev) >> + =A0 =A0{ >> + =A0 =A0 =A0tmp =3D rec; >> + =A0 =A0 =A0rec =3D rec->prev; >> + =A0 =A0 =A0if (tmp->type =3D=3D record_reg) >> + =A0 =A0 xfree (tmp->u.reg.val); >> + =A0 =A0 =A0else if (tmp->type =3D=3D record_mem) >> + =A0 =A0 xfree (tmp->u.mem.val); >> + =A0 =A0 =A0xfree (tmp); >> + =A0 =A0} >> + >> + =A0if (rec !=3D &record_first) >> + =A0 =A0xfree (rec); >> +} >> + >> +static void >> +record_list_release_next (void) >> +{ >> + =A0record_t *rec =3D record_list; >> + =A0record_t *tmp =3D rec->next; >> + =A0rec->next =3D NULL; >> + =A0while (tmp) >> + =A0 =A0{ >> + =A0 =A0 =A0rec =3D tmp->next; >> + =A0 =A0 =A0if (tmp->type =3D=3D record_reg) >> + =A0 =A0 record_insn_num--; >> + =A0 =A0 =A0else if (tmp->type =3D=3D record_reg) >> + =A0 =A0 xfree (tmp->u.reg.val); >> + =A0 =A0 =A0else if (tmp->type =3D=3D record_mem) >> + =A0 =A0 xfree (tmp->u.mem.val); >> + =A0 =A0 =A0xfree (tmp); >> + =A0 =A0 =A0tmp =3D rec; >> + =A0 =A0} >> +} >> + >> +static void >> +record_list_release_first (void) >> +{ >> + =A0record_t *tmp =3D NULL; >> + =A0enum record_type type; >> + >> + =A0if (!record_first.next) >> + =A0 =A0return; >> + >> + =A0while (1) >> + =A0 =A0{ >> + =A0 =A0 =A0type =3D record_first.next->type; >> + >> + =A0 =A0 =A0if (type =3D=3D record_reg) >> + =A0 =A0 xfree (record_first.next->u.reg.val); >> + =A0 =A0 =A0else if (type =3D=3D record_mem) >> + =A0 =A0 xfree (record_first.next->u.mem.val); >> + =A0 =A0 =A0tmp =3D record_first.next; >> + =A0 =A0 =A0record_first.next =3D tmp->next; >> + =A0 =A0 =A0xfree (tmp); >> + >> + =A0 =A0 =A0if (!record_first.next) >> + =A0 =A0 { >> + =A0 =A0 =A0 gdb_assert (record_insn_num =3D=3D 1); >> + =A0 =A0 =A0 break; >> + =A0 =A0 } >> + >> + =A0 =A0 =A0record_first.next->prev =3D &record_first; >> + >> + =A0 =A0 =A0if (type =3D=3D record_end) >> + =A0 =A0 break; >> + =A0 =A0} >> + >> + =A0record_insn_num--; >> +} >> + >> +/* Add a record_t to record_arch_list. =A0*/ >> +static void >> +record_arch_list_add (record_t * rec) >> +{ >> + =A0if (record_debug > 1) >> + =A0 =A0fprintf_unfiltered (gdb_stdlog, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "Process record: record_arch_l= ist_add %s.\n", >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 host_address_to_string (rec)); >> + >> + =A0if (record_arch_list_tail) >> + =A0 =A0{ >> + =A0 =A0 =A0record_arch_list_tail->next =3D rec; >> + =A0 =A0 =A0rec->prev =3D record_arch_list_tail; >> + =A0 =A0 =A0record_arch_list_tail =3D rec; >> + =A0 =A0} >> + =A0else >> + =A0 =A0{ >> + =A0 =A0 =A0record_arch_list_head =3D rec; >> + =A0 =A0 =A0record_arch_list_tail =3D rec; >> + =A0 =A0} >> +} >> + >> +/* Record the value of a register ("num") to record_arch_list. =A0*/ > > When we want to mention the value of a parameter, we mentions it in > uppercase, like so: > > /* Record the value of register NUM to record_arch_list. =A0*/ > > Also, there should be an empty line between the comment and > the function itself. =A0Here and elsewhere. > >> +int >> +record_arch_list_add_reg (int num) >> +{ >> + =A0record_t *rec; >> + >> + =A0if (record_debug > 1) >> + =A0 =A0fprintf_unfiltered (gdb_stdlog, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "Process record: add register = num =3D %d to " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "record list.\n", >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 num); >> + >> + =A0rec =3D (record_t *) xmalloc (sizeof (record_t)); >> + =A0rec->u.reg.val =3D (gdb_byte *) xmalloc (MAX_REGISTER_SIZE); >> + =A0rec->prev =3D NULL; >> + =A0rec->next =3D NULL; >> + =A0rec->type =3D record_reg; >> + =A0rec->u.reg.num =3D num; >> + >> + =A0regcache_raw_read (record_regcache, num, rec->u.reg.val); >> + >> + =A0record_arch_list_add (rec); >> + >> + =A0return 0; >> +} >> + >> +/* Record the value of a region of memory whose address is "addr" and >> + =A0 length is "len" to record_arch_list. =A0*/ > > /* Record the value of a region of memory whose address is ADDR and > =A0 length is LEN to record_arch_list. =A0*/ > OK. I will fix all of them. >> + >> +int >> +record_arch_list_add_mem (CORE_ADDR addr, int len) >> +{ >> + =A0record_t *rec; >> + >> + =A0if (record_debug > 1) >> + =A0 =A0fprintf_unfiltered (gdb_stdlog, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "Process record: add mem addr = =3D 0x%s len =3D %d to " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "record list.\n", >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 paddr_nz (addr), len); >> + > >> + =A0if (!addr) >> + =A0 =A0return 0; > > Why is this check for addr =3D=3D 0 necessary here? Sometime, most time is system call. It will send a NULL address to Linux kernel. Process record will try to record the change of this address. So it need check if addr is 0. > >> + >> + =A0rec =3D (record_t *) xmalloc (sizeof (record_t)); >> + =A0rec->u.mem.val =3D (gdb_byte *) xmalloc (len); >> + =A0rec->prev =3D NULL; >> + =A0rec->next =3D NULL; >> + =A0rec->type =3D record_mem; >> + =A0rec->u.mem.addr =3D addr; >> + =A0rec->u.mem.len =3D len; >> + >> + =A0if (target_read_memory (addr, rec->u.mem.val, len)) >> + =A0 =A0{ >> + =A0 =A0 =A0if (record_debug) >> + =A0 =A0 fprintf_unfiltered (gdb_stdlog, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "Process record: error= reading memory at " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "addr =3D 0x%s len =3D= %d.\n", >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 paddr_nz (addr), len); >> + =A0 =A0 =A0xfree (rec->u.mem.val); >> + =A0 =A0 =A0xfree (rec); >> + =A0 =A0 =A0return -1; >> + =A0 =A0} >> + >> + =A0record_arch_list_add (rec); >> + >> + =A0return 0; >> +} >> + >> +/* Add a record_end type record_t to record_arch_list. =A0*/ >> +int >> +record_arch_list_add_end (int need_dasm) >> +{ >> + =A0record_t *rec; >> + >> + =A0if (record_debug > 1) >> + =A0 =A0fprintf_unfiltered (gdb_stdlog, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "Process record: add end need_= dasm =3D %d to " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "arch list.\n", >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 need_dasm); >> + >> + =A0rec =3D (record_t *) xmalloc (sizeof (record_t)); >> + =A0rec->prev =3D NULL; >> + =A0rec->next =3D NULL; >> + =A0rec->type =3D record_end; >> + >> + =A0rec->u.need_dasm =3D need_dasm; >> + >> + =A0record_arch_list_add (rec); >> + >> + =A0return 0; >> +} >> + >> +static void >> +record_check_insn_num (int set_terminal) >> +{ >> + =A0if (record_insn_max_num) >> + =A0 =A0{ >> + =A0 =A0 =A0gdb_assert (record_insn_num <=3D record_insn_max_num); >> + =A0 =A0 =A0if (record_insn_num =3D=3D record_insn_max_num) >> + =A0 =A0 { >> + =A0 =A0 =A0 /* Ask user what to do. =A0*/ >> + =A0 =A0 =A0 if (record_stop_at_limit) >> + =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 int q; >> + =A0 =A0 =A0 =A0 =A0 if (set_terminal) >> + =A0 =A0 =A0 =A0 =A0 =A0 target_terminal_ours (); >> + =A0 =A0 =A0 =A0 =A0 q =3D yquery (_("Do you want to auto delete previo= us execution " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "log entries when reco= rd/replay buffer becomes " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "full (record-stop-at-= limit)?")); >> + =A0 =A0 =A0 =A0 =A0 if (set_terminal) >> + =A0 =A0 =A0 =A0 =A0 =A0 target_terminal_inferior (); >> + =A0 =A0 =A0 =A0 =A0 if (q) >> + =A0 =A0 =A0 =A0 =A0 =A0 record_stop_at_limit =3D 0; >> + =A0 =A0 =A0 =A0 =A0 else >> + =A0 =A0 =A0 =A0 =A0 =A0 error (_("Process record: inferior program sto= pped.")); >> + =A0 =A0 =A0 =A0 } >> + =A0 =A0 } >> + =A0 =A0} >> +} >> + >> +static void >> +record_normal_stop (void) >> +{ >> + =A0finish_thread_state (minus_one_ptid); >> + >> + =A0if (!breakpoints_always_inserted_mode () && target_has_execution) >> + =A0 =A0remove_breakpoints (); >> + >> + =A0target_terminal_ours (); >> + >> + =A0if (target_has_stack && !stop_stack_dummy) >> + =A0 =A0set_current_sal_from_frame (get_current_frame (), 1); >> + >> + =A0select_frame (get_current_frame ()); >> + >> + =A0annotate_stopped (); >> + =A0if (!suppress_stop_observer) >> + =A0 =A0{ >> + =A0 =A0 =A0if (!ptid_equal (inferior_ptid, null_ptid)) >> + =A0 =A0 observer_notify_normal_stop (inferior_thread ()->stop_bpstat, = 0); >> + =A0 =A0 =A0else >> + =A0 =A0 observer_notify_normal_stop (NULL, 0); >> + =A0 =A0} >> +} > > Nope sorry, this is worse than before. =A0When I asked to not call > normal_stop, I didn't mean that you should inline chunks of > it here. =A0This is papering over a different problem. =A0Why > is it that record.c needs to do this, while other targets do not? > If we need to do something like this, it should be needed by all > targets that can throw from target_wait (which, is documented > as something you shouldn't do anyway). =A0Actually, do you still need > any of this in current head? I will try to make record_message throw out error in record_wait. Then I can remove this normail_stop code. > >> + >> +/* Before inferior step (when GDB record the running message, inferior >> + =A0 only can step), GDB will call this function to record the values to >> + =A0 record_list. =A0This function will call gdbarch_process_record to >> + =A0 record the running message of inferior and set them to >> + =A0 record_arch_list, and add it to record_list. =A0*/ >> + >> +static void >> +record_message_cleanups (void *ignore) >> +{ >> + =A0record_list_release (record_arch_list_tail); >> + =A0set_executing (inferior_ptid, 0); >> + =A0record_normal_stop (); >> +} > > Same as above. =A0This isn't a record.c problem. > >> + >> +void >> +record_message (struct gdbarch *gdbarch) >> +{ >> + =A0int ret; >> + =A0struct cleanup *old_cleanups =3D make_cleanup (record_message_clean= ups, 0); >> + >> + =A0record_arch_list_head =3D NULL; >> + =A0record_arch_list_tail =3D NULL; >> + >> + =A0/* Check record_insn_num. =A0*/ >> + =A0record_check_insn_num (1); >> + >> + =A0record_regcache =3D get_current_regcache (); >> + >> + =A0ret =3D gdbarch_process_record (gdbarch, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 regcache_read_= pc (record_regcache)); >> + =A0if (ret > 0) >> + =A0 =A0error (_("Process record: inferior program stopped.")); >> + =A0if (ret < 0) >> + =A0 =A0error (_("Process record: failed to record execution log.")); >> + >> + =A0discard_cleanups (old_cleanups); >> + >> + =A0record_list->next =3D record_arch_list_head; >> + =A0record_arch_list_head->prev =3D record_list; >> + =A0record_list =3D record_arch_list_tail; >> + >> + =A0if (record_insn_num =3D=3D record_insn_max_num && record_insn_max_n= um) >> + =A0 =A0record_list_release_first (); >> + =A0else >> + =A0 =A0record_insn_num++; >> +} >> + > > >> +/* Things to clean up if we error or QUIT out of function that set >> + =A0 record_gdb_operation_disable (ie. command that caused target_wait = to >> + =A0 be called). =A0*/ >> +static void >> +record_gdb_operation_disable_cleanups (void *ignore) >> +{ >> + =A0record_gdb_operation_disable =3D 0; >> +} >> + >> +struct cleanup * >> +record_gdb_operation_disable_set (void) >> +{ >> + =A0struct cleanup *old_cleanups =3D >> + =A0 =A0make_cleanup (record_gdb_operation_disable_cleanups, 0); >> + =A0record_gdb_operation_disable =3D 1; >> + >> + =A0return old_cleanups; >> +} > > This is make_cleanup_restore_integer. I will change this code to use make_cleanup_restore_integer. > >> + >> +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 exec */ > > and core. I will change it. > >> + =A0if (!target_has_execution) >> + =A0 =A0error (_("Process record: the program is not being run.")); >> + =A0if (non_stop) >> + =A0 =A0error (_("Process record target can't debug inferior in non-sto= p mode " >> + =A0 =A0 =A0 =A0 =A0"(non-stop).")); >> + =A0if (target_async_permitted) >> + =A0 =A0error (_("Process record target can't debug inferior in asynchr= onous " >> + =A0 =A0 =A0 =A0 =A0"mode (target-async).")); >> + >> + =A0if (!gdbarch_process_record_p (current_gdbarch)) >> + =A0 =A0error (_("Process record: the current architecture doesn't supp= ort " >> + =A0 =A0 =A0 =A0 =A0"record function.")); >> + >> + =A0/* Check if record target is already running. =A0*/ >> + =A0if (TARGET_IS_PROCESS_RECORD) >> + =A0 =A0{ >> + =A0 =A0 =A0if (!nquery >> + =A0 =A0 =A0 (_("Process record target already running, do you want to = delete " >> + =A0 =A0 =A0 =A0 =A0"the old record log?"))) >> + =A0 =A0 return; >> + =A0 =A0} >> + >> + =A0/* Set the beneath function pointers. =A0*/ >> + =A0for (t =3D current_target.beneath; t !=3D NULL; t =3D t->beneath) >> + =A0 =A0{ >> + =A0 =A0 =A0if (!record_beneath_to_resume) >> + =A0 =A0 =A0 =A0{ >> + =A0 =A0 =A0 record_beneath_to_resume =3D t->to_resume; >> + =A0 =A0 =A0 record_beneath_to_resume_ops =3D t; >> + =A0 =A0 =A0 =A0} >> + =A0 =A0 =A0if (!record_beneath_to_wait) >> + =A0 =A0 =A0 =A0{ >> + =A0 =A0 =A0 record_beneath_to_wait =3D t->to_wait; >> + =A0 =A0 =A0 record_beneath_to_wait_ops =3D t; >> + =A0 =A0 =A0 =A0} >> + =A0 =A0 =A0if (!record_beneath_to_store_registers) >> + =A0 =A0 =A0 =A0{ >> + =A0 =A0 =A0 record_beneath_to_store_registers =3D t->to_store_register= s; >> + =A0 =A0 =A0 record_beneath_to_store_registers_ops =3D t; >> + =A0 =A0 =A0 =A0} >> + =A0 =A0 =A0if (!record_beneath_to_xfer_partial) >> + =A0 =A0 =A0 =A0{ >> + =A0 =A0 =A0 record_beneath_to_xfer_partial =3D t->to_xfer_partial; >> + =A0 =A0 =A0 record_beneath_to_xfer_partial_ops =3D t; >> + =A0 =A0 =A0 =A0} >> + =A0 =A0 =A0if (!record_beneath_to_insert_breakpoint) >> + =A0 =A0 record_beneath_to_insert_breakpoint =3D t->to_insert_breakpoin= t; >> + =A0 =A0 =A0if (!record_beneath_to_remove_breakpoint) >> + =A0 =A0 record_beneath_to_remove_breakpoint =3D t->to_remove_breakpoin= t; >> + =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) >> + =A0 =A0error (_("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.")); > > As I said above, this is better than making target.c aware of > these pointers, but, it still isn't ideal. =A0For one thing, if > some other layer/target is pushed after the record target is opened, > these will be wrong. =A0I would prefer that this beneath lookup > would be done at each callback implementation (record_resume, etc.), > but, I'm happy enough with this for now. =A0It is now contained, so we can > clean this up afterwards... > >> + >> + =A0push_target (&record_ops); >> + >> + =A0/* Reset */ >> + =A0record_insn_num =3D 0; >> + =A0record_list =3D &record_first; >> + =A0record_list->next =3D NULL; >> +} >> + >> +static void >> +record_close (int quitting) >> +{ >> + =A0if (record_debug) >> + =A0 =A0fprintf_unfiltered (gdb_stdlog, "Process record: record_close\n= "); >> + >> + =A0record_list_release (record_list); >> +} > > Shouldn't this clear the record_beneath_* pointers as well? Oops, I forget it. I will add a reset code to record_open. > >> + >> +static void >> +record_resume (struct target_ops *ops, ptid_t ptid, int step, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 enum target_signal siggnal) >> +{ >> + =A0record_resume_step =3D step; >> + =A0record_resume_siggnal =3D siggnal; >> + >> + =A0if (!RECORD_IS_REPLAY) >> + =A0 =A0{ >> + =A0 =A0 =A0record_message (current_gdbarch); >> + =A0 =A0 =A0record_beneath_to_resume (record_beneath_to_resume_ops, pti= d, 1, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0siggnal= ); >> + =A0 =A0} >> +} >> + >> +static void >> +record_sig_handler (int signo) >> +{ >> + =A0if (record_debug) >> + =A0 =A0fprintf_unfiltered (gdb_stdlog, "Process record: get a signal\n= "); >> + >> + =A0record_resume_step =3D 1; >> + =A0record_get_sig =3D 1; >> +} > > This handler is magical. =A0Why is it setting resume_step, for instance? > It would definitelly benefic from some comments. =A0In fact, most of the > file is undercommented. I will add comment to there. > >> + >> +static void >> +record_wait_cleanups (void *ignore) >> +{ >> + =A0if (execution_direction =3D=3D EXEC_REVERSE) >> + =A0 =A0{ >> + =A0 =A0 =A0if (record_list->next) >> + =A0 =A0 record_list =3D record_list->next; >> + =A0 =A0} >> + =A0else >> + =A0 =A0record_list =3D record_list->prev; >> + >> + =A0set_executing (inferior_ptid, 0); >> + =A0record_normal_stop (); >> +} > > See comments about record_normal_stop above. I will try it too. > >> + >> +/* record_wait > > Please remove the function name from the comment. =A0It's redundant. > >> + =A0 In replay mode, this function examines the recorded log and >> + =A0 determines where to stop. =A0*/ OK. I will. >> + >> +static ptid_t >> +record_wait (struct target_ops *ops, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0ptid_t ptid, struct target_waitstatus *stat= us) >> +{ >> + =A0struct cleanup *set_cleanups =3D record_gdb_operation_disable_set (= ); >> + >> + =A0if (record_debug) >> + =A0 =A0fprintf_unfiltered (gdb_stdlog, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "Process record: record_wait " >> + =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 record_resume_step); >> + >> + =A0if (!RECORD_IS_REPLAY) >> + =A0 =A0{ >> + =A0 =A0 =A0if (record_resume_step) >> + =A0 =A0 { >> + =A0 =A0 =A0 /* This is a single step. =A0*/ >> + =A0 =A0 =A0 return record_beneath_to_wait (record_beneath_to_wait_ops, >> + =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 =A0ptid, status); >> + =A0 =A0 } >> + =A0 =A0 =A0else >> + =A0 =A0 { >> + =A0 =A0 =A0 if (record_resume_step) >> + =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 /* This is a single step. =A0*/ >> + =A0 =A0 =A0 =A0 =A0 return record_beneath_to_wait (record_beneath_to_w= ait_ops, >> + =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 =A0ptid, status); >> + =A0 =A0 =A0 =A0 } >> + =A0 =A0 =A0 else >> + =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 /* This is not a single step. =A0*/ >> + =A0 =A0 =A0 =A0 =A0 ptid_t ret; >> + =A0 =A0 =A0 =A0 =A0 int is_breakpoint =3D 1; >> + =A0 =A0 =A0 =A0 =A0 CORE_ADDR pc =3D 0; >> + =A0 =A0 =A0 =A0 =A0 int pc_is_read =3D 0; >> + =A0 =A0 =A0 =A0 =A0 struct bp_location *bl; >> + =A0 =A0 =A0 =A0 =A0 struct breakpoint *b; >> + >> + =A0 =A0 =A0 =A0 =A0 do >> + =A0 =A0 =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 ret =3D record_beneath_to_wait (record_ben= eath_to_wait_ops, >> + =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 =A0ptid, status); >> + >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (status->kind =3D=3D TARGET_WAITKIND_ST= OPPED >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 && status->value.sig =3D=3D TARGET= _SIGNAL_TRAP) >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Check if there is a breakpoint.= =A0*/ >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pc_is_read =3D 0; >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 registers_changed (); >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 for (bl =3D bp_location_chain; bl;= bl =3D bl->global_next) > > This will need to be fixed. =A0Can you use the breakpoint_here-like funct= ions > exported by breakpoint.h instead of referencing bp_location_chain directl= y? I will try to change it. > >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 b =3D bl->owner; >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 gdb_assert (b); >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (b->enable_state !=3D b= p_enabled >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 && b->enable_state= !=3D bp_permanent) >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 continue; >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!pc_is_read) >> + =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 pc =3D >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 regcache_read_= pc (get_thread_regcache (ret)); >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pc_is_read =3D 1; >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 } >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 switch (b->type) >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 default: >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (bl->address = =3D=3D pc) >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto breakpoin= t; >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; >> + >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 case bp_watchpoint: >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* XXX teawater: I= still not very clear how to >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0deal with i= t. =A0*/ >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto breakpoint; >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; >> + >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 case bp_catchpoint: >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 gdb_assert (b->ops= !=3D NULL >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 && b->ops->breakpoint_hit !=3D NULL); >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (b->ops->breakp= oint_hit (b)) >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto breakpoin= t; >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; >> + >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 case bp_hardware_watch= point: >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 case bp_read_watchpoin= t: >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 case bp_access_watchpo= int: >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (STOPPED_BY_WAT= CHPOINT (0)) >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 goto breakpoin= t; >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break; >> + =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 /* There is not a breakpoint. =A0*/ >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 record_message (current_gdbarch); >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 record_beneath_to_resume (record_b= eneath_to_resume_ops, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 =A0 =A0ptid, 1, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0 record_resume_siggnal); >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 continue; >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 } >> + >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 is_breakpoint =3D 0; >> + >> + =A0 =A0 =A0 =A0 =A0 =A0 breakpoint: >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* Add gdbarch_decr_pc_after_break to pc b= ecause gdb will >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0expect the pc to be at address plus= decr_pc_after_break >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0when the inferior stops at a breakp= oint. =A0*/ >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (is_breakpoint) >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 CORE_ADDR decr_pc_after_break =3D >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 gdbarch_decr_pc_after_break (c= urrent_gdbarch); >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (decr_pc_after_break) >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (!pc_is_read) >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 pc =3D >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 regcache_read_pc (= get_thread_regcache (ret)); >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 regcache_write_pc (get_thr= ead_regcache (ret), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0pc + decr_pc_after_break); >> + =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 break; >> + =A0 =A0 =A0 =A0 =A0 =A0 } >> + =A0 =A0 =A0 =A0 =A0 while (1); >> + >> + =A0 =A0 =A0 =A0 =A0 return ret; >> + =A0 =A0 =A0 =A0 } >> + =A0 =A0 } >> + =A0 =A0} >> + =A0else >> + =A0 =A0{ >> + =A0 =A0 =A0int need_dasm =3D 0; >> + =A0 =A0 =A0struct regcache *regcache =3D get_current_regcache (); >> + =A0 =A0 =A0int continue_flag =3D 1; >> + =A0 =A0 =A0int first_record_end =3D 1; >> + =A0 =A0 =A0struct cleanup *old_cleanups =3D make_cleanup (record_wait_= cleanups, 0); >> + =A0 =A0 =A0CORE_ADDR tmp_pc; >> + >> + =A0 =A0 =A0status->kind =3D TARGET_WAITKIND_STOPPED; >> + >> + =A0 =A0 =A0/* Check breakpoint when forward execute. =A0*/ >> + =A0 =A0 =A0if (execution_direction =3D=3D EXEC_FORWARD) >> + =A0 =A0 { >> + =A0 =A0 =A0 tmp_pc =3D regcache_read_pc (regcache); >> + =A0 =A0 =A0 if (breakpoint_inserted_here_p (tmp_pc)) >> + =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 if (record_debug) >> + =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 "Proce= ss record: break at 0x%s.\n", >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 paddr_= nz (tmp_pc)); >> + =A0 =A0 =A0 =A0 =A0 if (gdbarch_decr_pc_after_break (get_regcache_arch= (regcache)) >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 && !record_resume_step) >> + =A0 =A0 =A0 =A0 =A0 =A0 regcache_write_pc (regcache, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0tmp_pc + >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0gdbarch= _decr_pc_after_break >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(get_re= gcache_arch (regcache))); >> + =A0 =A0 =A0 =A0 =A0 goto replay_out; >> + =A0 =A0 =A0 =A0 } >> + =A0 =A0 } >> + >> + =A0 =A0 =A0record_get_sig =3D 0; >> + =A0 =A0 =A0signal (SIGINT, record_sig_handler); >> + =A0 =A0 =A0/* If GDB is in terminal_inferior mode, it will not get the= signal. >> + =A0 =A0 =A0 =A0 And in GDB replay mode, GDB doesn't need to be in term= inal_inferior >> + =A0 =A0 =A0 =A0 mode, because inferior will not executed. >> + =A0 =A0 =A0 =A0 Then set it to terminal_ours to make GDB get the signa= l. =A0*/ >> + =A0 =A0 =A0target_terminal_ours (); >> + >> + =A0 =A0 =A0/* In EXEC_FORWARD mode, record_list points to the tail of = prev >> + =A0 =A0 =A0 =A0 instruction. =A0*/ >> + =A0 =A0 =A0if (execution_direction =3D=3D EXEC_FORWARD && record_list-= >next) >> + =A0 =A0 record_list =3D record_list->next; >> + >> + =A0 =A0 =A0/* Loop over the record_list, looking for the next place to >> + =A0 =A0 =A0stop. =A0*/ >> + =A0 =A0 =A0do >> + =A0 =A0 { >> + =A0 =A0 =A0 /* Check for beginning and end of log. =A0*/ >> + =A0 =A0 =A0 if (execution_direction =3D=3D EXEC_REVERSE >> + =A0 =A0 =A0 =A0 =A0 && record_list =3D=3D &record_first) >> + =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 /* Hit beginning of record log in reverse. =A0*/ >> + =A0 =A0 =A0 =A0 =A0 status->kind =3D TARGET_WAITKIND_NO_HISTORY; >> + =A0 =A0 =A0 =A0 =A0 break; >> + =A0 =A0 =A0 =A0 } >> + =A0 =A0 =A0 if (execution_direction !=3D EXEC_REVERSE && !record_list-= >next) >> + =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 /* Hit end of record log going forward. =A0*/ >> + =A0 =A0 =A0 =A0 =A0 status->kind =3D TARGET_WAITKIND_NO_HISTORY; >> + =A0 =A0 =A0 =A0 =A0 break; >> + =A0 =A0 =A0 =A0 } >> + >> + =A0 =A0 =A0 /* Set ptid, register and memory according to record_list.= =A0*/ >> + =A0 =A0 =A0 if (record_list->type =3D=3D record_reg) >> + =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 /* reg */ >> + =A0 =A0 =A0 =A0 =A0 gdb_byte reg[MAX_REGISTER_SIZE]; >> + =A0 =A0 =A0 =A0 =A0 if (record_debug > 1) >> + =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 "Proce= ss record: record_reg %s to " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "infer= ior num =3D %d.\n", >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 host_a= ddress_to_string (record_list), >> + =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 regcache_cooked_read (regcache, record_list->u.reg= .num, reg); >> + =A0 =A0 =A0 =A0 =A0 regcache_cooked_write (regcache, record_list->u.re= g.num, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0rec= ord_list->u.reg.val); >> + =A0 =A0 =A0 =A0 =A0 memcpy (record_list->u.reg.val, reg, MAX_REGISTER_= SIZE); >> + =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 /* mem */ >> + =A0 =A0 =A0 =A0 =A0 gdb_byte *mem =3D alloca (record_list->u.mem.len); >> + =A0 =A0 =A0 =A0 =A0 if (record_debug > 1) >> + =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 "Proce= ss record: record_mem %s to " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "infer= ior addr =3D 0x%s len =3D %d.\n", >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 host_a= ddress_to_string (record_list), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 paddr_= nz (record_list->u.mem.addr), >> + =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 if (target_read_memory >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 (record_list->u.mem.addr, mem, record_list= ->u.mem.len)) >> + =A0 =A0 =A0 =A0 =A0 =A0 error (_("Process record: error reading memory= at " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"addr =3D 0x%s len =3D %d."= ), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0paddr_nz (record_list->u.mem.ad= dr), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0record_list->u.mem.len); >> + >> + =A0 =A0 =A0 =A0 =A0 if (target_write_memory >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 (record_list->u.mem.addr, record_list->u.m= em.val, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0record_list->u.mem.len)) >> + =A0 =A0 =A0 =A0 =A0 =A0 error (_ >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0("Process record: error writing= memory at " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "addr =3D 0x%s len =3D %d."), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0paddr_nz (record_list->u.mem.ad= dr), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0record_list->u.mem.len); >> + >> + =A0 =A0 =A0 =A0 =A0 memcpy (record_list->u.mem.val, mem, record_list->= u.mem.len); >> + =A0 =A0 =A0 =A0 } >> + =A0 =A0 =A0 else >> + =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 if (record_debug > 1) >> + =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 "Proce= ss record: record_end %s to " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "infer= ior need_dasm =3D %d.\n", >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 host_a= ddress_to_string (record_list), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 record= _list->u.need_dasm); >> + >> + =A0 =A0 =A0 =A0 =A0 if (execution_direction =3D=3D EXEC_FORWARD) >> + =A0 =A0 =A0 =A0 =A0 =A0 need_dasm =3D record_list->u.need_dasm; >> + =A0 =A0 =A0 =A0 =A0 if (need_dasm) >> + =A0 =A0 =A0 =A0 =A0 =A0 gdbarch_process_record_dasm (current_gdbarch); >> + >> + =A0 =A0 =A0 =A0 =A0 if (first_record_end && execution_direction =3D=3D= EXEC_REVERSE) >> + =A0 =A0 =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* When reverse excute, the first record_e= nd is the part of >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0current instruction. =A0*/ >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 first_record_end =3D 0; >> + =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 /* In EXEC_REVERSE mode, this is the recor= d_end of prev >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0instruction. >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0In EXEC_FORWARD mode, this is the r= ecord_end of current >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0instruction. =A0*/ >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* step */ >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (record_resume_step) >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (record_debug > 1) >> + =A0 =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 =A0 "Process record: step.\n"); >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 continue_flag =3D 0; >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 } >> + >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 /* check breakpoint */ >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 tmp_pc =3D regcache_read_pc (regcache); >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (breakpoint_inserted_here_p (tmp_pc)) >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (record_debug) >> + =A0 =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 =A0 "Process record: break " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 "at 0x%s.\n", >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 paddr_nz (tmp_pc)); >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (gdbarch_decr_pc_after_break (g= et_regcache_arch (regcache)) >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 && execution_direction =3D= =3D EXEC_FORWARD >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 && !record_resume_step) >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 regcache_write_pc (regcache, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0tmp_pc + >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0gdbarch_decr_pc_after_break >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0(get_regcache_arch (regcache))); >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 continue_flag =3D 0; >> + =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) >> + =A0 =A0 =A0 =A0 =A0 =A0 need_dasm =3D record_list->u.need_dasm; >> + =A0 =A0 =A0 =A0 } >> + >> +next: >> + =A0 =A0 =A0 if (continue_flag) >> + =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 if (execution_direction =3D=3D EXEC_REVERSE) >> + =A0 =A0 =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 if (record_list->prev) >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 record_list =3D record_list->prev; >> + =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 if (record_list->next) >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 record_list =3D record_list->next; >> + =A0 =A0 =A0 =A0 =A0 =A0 } >> + =A0 =A0 =A0 =A0 } >> + =A0 =A0 } >> + =A0 =A0 =A0while (continue_flag); >> + >> + =A0 =A0 =A0signal (SIGINT, handle_sigint); >> + >> +replay_out: >> + =A0 =A0 =A0if (record_get_sig) >> + =A0 =A0 status->value.sig =3D TARGET_SIGNAL_INT; >> + =A0 =A0 =A0else >> + =A0 =A0 status->value.sig =3D TARGET_SIGNAL_TRAP; >> + >> + =A0 =A0 =A0discard_cleanups (old_cleanups); >> + =A0 =A0} >> + >> + =A0do_cleanups (set_cleanups); >> + =A0return inferior_ptid; >> +} > > I have to say that I find that function confusing, due to > the use of gotos, and excessive nesting. =A0Personally, I much prefer > code that does: > > =A0if (foo) > =A0 =A0{ > =A0 =A0 =A0/* something */ > =A0 =A0 =A0return; > =A0 =A0} > > =A0if (bar) > =A0 =A0{ > =A0 =A0 =A0/* something */ > =A0 =A0 =A0return; > =A0 =A0} > > =A0if (lala) > =A0 =A0{ > =A0 =A0 =A0/* something */ > =A0 =A0 =A0return; > =A0 =A0} > > Over: > > =A0if (foo) > =A0 =A0{ > =A0 =A0 =A0/* something */ > =A0 =A0 =A0return; > =A0 =A0} > =A0else > =A0 { > =A0 =A0 if (bar) > =A0 =A0 =A0 { > =A0 =A0 =A0 =A0 /* something */ > =A0 =A0 =A0 =A0 return; > =A0 =A0 =A0 } > =A0 =A0 else > =A0 =A0 =A0{ > =A0 =A0 =A0 =A0 if (lala) > =A0 =A0 =A0 =A0 =A0 { > =A0 =A0 =A0 =A0 =A0 =A0 /* something */ > =A0 =A0 =A0 =A0 =A0 =A0 return; > =A0 =A0 =A0 =A0 =A0 } > =A0 =A0 =A0} > =A0 } > In record_wait, all the code in else part's return need do same thing: signal (SIGINT, handle_sigint); if (record_get_sig) status->value.sig =3D TARGET_SIGNAL_INT; else status->value.sig =3D TARGET_SIGNAL_TRAP; discard_cleanups (old_cleanups); BTW, after remove a lot of part of this function, this function must be clear. :) > >> + >> +static void >> +record_disconnect (struct target_ops *target, char *args, int from_tty) >> +{ >> + =A0if (record_debug) >> + =A0 =A0fprintf_unfiltered (gdb_stdlog, "Process record: record_disconn= ect\n"); >> + >> + =A0unpush_target (&record_ops); >> + =A0target_disconnect (args, from_tty); >> +} >> + >> +static void >> +record_detach (struct target_ops *ops, char *args, int from_tty) >> +{ >> + =A0if (record_debug) >> + =A0 =A0fprintf_unfiltered (gdb_stdlog, "Process record: record_detach\= n"); >> + >> + =A0unpush_target (&record_ops); >> + =A0target_detach (args, from_tty); >> +} > > This trick you're using happens to work, but, could you try > this instead? =A0Here and elsewhere similarly. > > =A0struct target_ops *beneath =3D find_target_beaneath (ops); > =A0unpush_target (ops); > =A0beneath->to_detach (args, from_tty); OK. I will. > >> + >> +static void >> +record_mourn_inferior (struct target_ops *ops) >> +{ >> + =A0if (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 "recor= d_mourn_inferior\n"); >> + >> + =A0unpush_target (&record_ops); >> + =A0target_mourn_inferior (); >> +} >> + >> +/* Close process record target before killing the inferior process. =A0= */ >> +static void >> +record_kill (void) >> +{ >> + =A0if (record_debug) >> + =A0 =A0fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n"= ); >> + >> + =A0unpush_target (&record_ops); >> + =A0target_kill (); >> +} >> + >> +/* Record registers change (by user or by GDB) to list as an instructio= n. =A0*/ >> +static void >> +record_registers_change (struct regcache *regcache, int regnum) >> +{ >> + =A0/* Check record_insn_num. =A0*/ >> + =A0record_check_insn_num (0); >> + >> + =A0record_arch_list_head =3D NULL; >> + =A0record_arch_list_tail =3D NULL; >> + >> + =A0record_regcache =3D get_current_regcache (); >> + >> + =A0if (regnum < 0) >> + =A0 =A0{ >> + =A0 =A0 =A0int i; >> + =A0 =A0 =A0for (i =3D 0; i < gdbarch_num_regs (get_regcache_arch (regc= ache)); i++) >> + =A0 =A0 { >> + =A0 =A0 =A0 if (record_arch_list_add_reg (i)) >> + =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 record_list_release (record_arch_list_tail); >> + =A0 =A0 =A0 =A0 =A0 error (_("Process record: failed to record executi= on log.")); >> + =A0 =A0 =A0 =A0 } >> + =A0 =A0 } >> + =A0 =A0} >> + =A0else >> + =A0 =A0{ >> + =A0 =A0 =A0if (record_arch_list_add_reg (regnum)) >> + =A0 =A0 { >> + =A0 =A0 =A0 record_list_release (record_arch_list_tail); >> + =A0 =A0 =A0 error (_("Process record: failed to record execution log."= )); >> + =A0 =A0 } >> + =A0 =A0} >> + =A0if (record_arch_list_add_end (0)) >> + =A0 =A0{ >> + =A0 =A0 =A0record_list_release (record_arch_list_tail); >> + =A0 =A0 =A0error (_("Process record: failed to record execution log.")= ); >> + =A0 =A0} >> + =A0record_list->next =3D record_arch_list_head; >> + =A0record_arch_list_head->prev =3D record_list; >> + =A0record_list =3D record_arch_list_tail; >> + >> + =A0if (record_insn_num =3D=3D record_insn_max_num && record_insn_max_n= um) >> + =A0 =A0record_list_release_first (); >> + =A0else >> + =A0 =A0record_insn_num++; >> +} >> + >> +static void >> +record_store_registers (struct target_ops *ops, struct regcache *regcac= he, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0int regno) >> +{ >> + =A0if (!record_gdb_operation_disable) >> + =A0 =A0{ >> + =A0 =A0 =A0if (RECORD_IS_REPLAY) >> + =A0 =A0 { >> + =A0 =A0 =A0 int n; >> + =A0 =A0 =A0 struct cleanup *old_cleanups; >> + >> + =A0 =A0 =A0 /* Let user choose if he wants to write register or not. = =A0*/ >> + =A0 =A0 =A0 if (regno < 0) >> + =A0 =A0 =A0 =A0 n =3D >> + =A0 =A0 =A0 =A0 =A0 nquery (_("Because GDB is in replay mode, changing= the " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "value of a register will make= the execution " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "log unusable from this point = onward. =A0" >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "Change all registers?")); >> + =A0 =A0 =A0 else >> + =A0 =A0 =A0 =A0 n =3D >> + =A0 =A0 =A0 =A0 =A0 nquery (_("Because GDB is in replay mode, changing= the value " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "of a register will make the e= xecution log unusable " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "from this point onward. =A0Ch= ange register %s?"), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 gdbarch_register_name (get_regcach= e_arch (regcache), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 =A0 =A0 =A0 =A0regno)); >> + >> + =A0 =A0 =A0 if (!n) >> + =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 /* Invalidate the value of regcache that was set i= n function >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0"regcache_raw_write". =A0*/ >> + =A0 =A0 =A0 =A0 =A0 if (regno < 0) >> + =A0 =A0 =A0 =A0 =A0 =A0 { >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 int i; >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 for (i =3D 0; >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0i < gdbarch_num_regs (get_regca= che_arch (regcache)); >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0i++) >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 regcache_invalidate (regcache, i); >> + =A0 =A0 =A0 =A0 =A0 =A0 } >> + =A0 =A0 =A0 =A0 =A0 else >> + =A0 =A0 =A0 =A0 =A0 =A0 regcache_invalidate (regcache, regno); >> + >> + =A0 =A0 =A0 =A0 =A0 error (_("Process record canceled the operation.")= ); >> + =A0 =A0 =A0 =A0 } >> + >> + =A0 =A0 =A0 /* Destroy the record from here forward. =A0*/ >> + =A0 =A0 =A0 record_list_release_next (); >> + =A0 =A0 } >> + >> + =A0 =A0 =A0record_registers_change (regcache, regno); >> + =A0 =A0} >> + =A0record_beneath_to_store_registers (record_beneath_to_store_register= s_ops, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 = =A0 regcache, regno); >> +} >> + >> +/* record_xfer_partial -- behavior is conditional on RECORD_IS_REPLAY. >> + =A0 In replay mode, we cannot write memory unles we are willing to >> + =A0 invalidate the record/replay log from this point forward. =A0*/ >> + >> +static LONGEST >> +record_xfer_partial (struct target_ops *ops, enum target_object object, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0const char *annex, gdb_byte * readb= uf, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0const gdb_byte * writebuf, ULONGEST= offset, LONGEST len) >> +{ >> + =A0if (!record_gdb_operation_disable >> + =A0 =A0 =A0&& (object =3D=3D TARGET_OBJECT_MEMORY >> + =A0 =A0 =A0 || object =3D=3D TARGET_OBJECT_RAW_MEMORY) && writebuf) >> + =A0 =A0{ >> + =A0 =A0 =A0if (RECORD_IS_REPLAY) >> + =A0 =A0 { >> + =A0 =A0 =A0 /* Let user choose if he wants to write memory or not. =A0= */ >> + =A0 =A0 =A0 if (!nquery (_("Because GDB is in replay mode, writing to = memory " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"will make the execution lo= g unusable from this " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"point onward. =A0Write mem= ory at address 0x%s?"), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0paddr_nz (offset))) >> + =A0 =A0 =A0 =A0 return -1; >> + >> + =A0 =A0 =A0 /* Destroy the record from here forward. =A0*/ >> + =A0 =A0 =A0 record_list_release_next (); >> + =A0 =A0 } >> + >> + =A0 =A0 =A0/* Check record_insn_num */ >> + =A0 =A0 =A0record_check_insn_num (0); >> + >> + =A0 =A0 =A0/* Record registers change to list as an instruction. =A0*/ >> + =A0 =A0 =A0record_arch_list_head =3D NULL; >> + =A0 =A0 =A0record_arch_list_tail =3D NULL; >> + =A0 =A0 =A0if (record_arch_list_add_mem (offset, len)) >> + =A0 =A0 { >> + =A0 =A0 =A0 record_list_release (record_arch_list_tail); >> + =A0 =A0 =A0 if (record_debug) >> + =A0 =A0 =A0 =A0 fprintf_unfiltered (gdb_stdlog, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 _("Process rec= ord: failed to record " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "execution= log.")); >> + =A0 =A0 =A0 return -1; >> + =A0 =A0 } >> + =A0 =A0 =A0if (record_arch_list_add_end (0)) >> + =A0 =A0 { >> + =A0 =A0 =A0 record_list_release (record_arch_list_tail); >> + =A0 =A0 =A0 if (record_debug) >> + =A0 =A0 =A0 =A0 fprintf_unfiltered (gdb_stdlog, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 _("Process rec= ord: failed to record " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "execution= log.")); >> + =A0 =A0 =A0 return -1; >> + =A0 =A0 } >> + =A0 =A0 =A0record_list->next =3D record_arch_list_head; >> + =A0 =A0 =A0record_arch_list_head->prev =3D record_list; >> + =A0 =A0 =A0record_list =3D record_arch_list_tail; >> + >> + =A0 =A0 =A0if (record_insn_num =3D=3D record_insn_max_num && record_in= sn_max_num) >> + =A0 =A0 record_list_release_first (); >> + =A0 =A0 =A0else >> + =A0 =A0 record_insn_num++; >> + =A0 =A0} >> + >> + =A0return record_beneath_to_xfer_partial (record_beneath_to_xfer_parti= al_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); >> +} >> + >> +/* record_insert_breakpoint >> + =A0 record_remove_breakpoint >> + =A0 Behavior is conditional on RECORD_IS_REPLAY. >> + =A0 We will not actually insert or remove breakpoints when replaying, >> + =A0 nor when recording. =A0*/ >> + >> +static int >> +record_insert_breakpoint (struct bp_target_info *bp_tgt) >> +{ >> + =A0if (!RECORD_IS_REPLAY) >> + =A0 =A0{ >> + =A0 =A0 =A0struct cleanup *old_cleanups =3D record_gdb_operation_disab= le_set (); >> + =A0 =A0 =A0int ret =3D record_beneath_to_insert_breakpoint (bp_tgt); >> + >> + =A0 =A0 =A0do_cleanups (old_cleanups); >> + >> + =A0 =A0 =A0return ret; >> + =A0 =A0} >> + >> + =A0return 0; >> +} >> + >> +static int >> +record_remove_breakpoint (struct bp_target_info *bp_tgt) >> +{ >> + =A0if (!RECORD_IS_REPLAY) >> + =A0 =A0{ >> + =A0 =A0 =A0struct cleanup *old_cleanups =3D record_gdb_operation_disab= le_set (); >> + =A0 =A0 =A0int ret =3D record_beneath_to_remove_breakpoint (bp_tgt); >> + >> + =A0 =A0 =A0do_cleanups (old_cleanups); >> + >> + =A0 =A0 =A0return ret; >> + =A0 =A0} >> + >> + =A0return 0; >> +} >> + >> +static int >> +record_can_execute_reverse (void) >> +{ >> + =A0return 1; >> +} >> + >> +static void >> +init_record_ops (void) >> +{ >> + =A0record_ops.to_shortname =3D "record"; >> + =A0record_ops.to_longname =3D "Process record and replay target"; >> + =A0record_ops.to_doc =3D >> + =A0 =A0"Log program while executing and replay execution from log."; >> + =A0record_ops.to_open =3D record_open; >> + =A0record_ops.to_close =3D record_close; >> + =A0record_ops.to_resume =3D record_resume; >> + =A0record_ops.to_wait =3D record_wait; >> + =A0record_ops.to_disconnect =3D record_disconnect; >> + =A0record_ops.to_detach =3D record_detach; >> + =A0record_ops.to_mourn_inferior =3D record_mourn_inferior; >> + =A0record_ops.to_kill =3D record_kill; >> + =A0record_ops.to_create_inferior =3D find_default_create_inferior; >> + =A0record_ops.to_store_registers =3D record_store_registers; >> + =A0record_ops.to_xfer_partial =3D record_xfer_partial; >> + =A0record_ops.to_insert_breakpoint =3D record_insert_breakpoint; >> + =A0record_ops.to_remove_breakpoint =3D record_remove_breakpoint; >> + =A0record_ops.to_can_execute_reverse =3D record_can_execute_reverse; >> + =A0record_ops.to_stratum =3D record_stratum; >> + =A0record_ops.to_magic =3D OPS_MAGIC; >> +} >> + >> +static void >> +show_record_debug (struct ui_file *file, int from_tty, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0struct cmd_list_element *c, const char = *value) >> +{ >> + =A0fprintf_filtered (file, _("Debugging of process record target is %s= .\n"), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 value); >> +} >> + >> +/* cmd_record_start -- alias for "target record". =A0*/ >> + >> +static void >> +cmd_record_start (char *args, int from_tty) >> +{ >> + =A0execute_command ("target record", from_tty); >> +} >> + >> +/* cmd_record_delete -- truncate the record log from the present point >> + =A0 of replay until the end. =A0*/ >> + >> +static void >> +cmd_record_delete (char *args, int from_tty) >> +{ >> + =A0if (TARGET_IS_PROCESS_RECORD) >> + =A0 =A0{ >> + =A0 =A0 =A0if (RECORD_IS_REPLAY) >> + =A0 =A0 { >> + =A0 =A0 =A0 if (!from_tty || query (_("Delete the log from this point = forward " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "and b= egin to record the running message " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "at cu= rrent PC?"))) >> + =A0 =A0 =A0 =A0 record_list_release_next (); >> + =A0 =A0 } >> + =A0 =A0 =A0else >> + =A0 =A0 =A0 printf_unfiltered (_("Already at end of record list.\n")); >> + >> + =A0 =A0} >> + =A0else >> + =A0 =A0printf_unfiltered (_("Process record is not started.\n")); >> +} >> + >> +/* cmd_record_stop -- implement the "stoprecord" command. =A0*/ >> + >> +static void >> +cmd_record_stop (char *args, int from_tty) >> +{ >> + =A0if (TARGET_IS_PROCESS_RECORD) >> + =A0 =A0{ >> + =A0 =A0 =A0if (!record_list || !from_tty || query (_("Delete recorded = log and " >> + =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 unpush_target (&record_ops); >> + =A0 =A0} >> + =A0else >> + =A0 =A0printf_unfiltered (_("Process record is not started.\n")); >> +} >> + >> +/* set_record_insn_max_num -- set upper limit of record log size. =A0*/ >> + >> +static void >> +set_record_insn_max_num (char *args, int from_tty, struct cmd_list_elem= ent *c) >> +{ >> + =A0if (record_insn_num > record_insn_max_num && record_insn_max_num) >> + =A0 =A0{ >> + =A0 =A0 =A0printf_unfiltered (_("Record instructions number is bigger = than " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"record instructions ma= x number. =A0Auto delete " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"the first ones?\n")); >> + >> + =A0 =A0 =A0while (record_insn_num > record_insn_max_num) >> + =A0 =A0 record_list_release_first (); >> + =A0 =A0} >> +} >> + >> +/* show_record_insn_number -- print the current index >> + =A0 into the record log (number of insns recorded so far). =A0*/ >> + >> +static void >> +show_record_insn_number (char *ignore, int from_tty) >> +{ >> + =A0printf_unfiltered (_("Record instruction number is %d.\n"), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0record_insn_num); >> +} >> + >> +void >> +_initialize_record (void) >> +{ >> + =A0/* Init record_maskall. =A0*/ >> + =A0if (sigfillset (&record_maskall) =3D=3D -1) >> + =A0 =A0perror_with_name (_("Process record: sigfillset failed")); > > This will not build on all hosts. =A0Is it still needed? =A0I can't > find any other reference to this variable in this patch. I forget it. I will remove it. > >> + >> + =A0/* Init record_first. =A0*/ >> + =A0record_first.prev =3D NULL; >> + =A0record_first.next =3D NULL; >> + =A0record_first.type =3D record_end; >> + =A0record_first.u.need_dasm =3D 0; >> + >> + =A0init_record_ops (); >> + =A0add_target (&record_ops); >> + >> + =A0add_setshow_zinteger_cmd ("record", no_class, &record_debug, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 _("Set debugging of re= cord/replay feature."), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 _("Show debugging of r= ecord/replay feature."), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 _("When enabled, debug= ging output for " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "record/replay fea= ture is displayed."), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 NULL, show_record_debu= g, &setdebuglist, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 &showdebuglist); >> + >> + =A0add_com ("record", class_obscure, cmd_record_start, >> + =A0 =A0 =A0 =A0_("Abbreviated form of \"target record\" command.")); >> + >> + =A0add_com_alias ("rec", "record", class_obscure, 1); >> + >> + =A0/* XXX: I try to use some simple commands such as "disconnect" and >> + =A0 =A0 "detach" to support this functions. =A0But these commands all = have >> + =A0 =A0 other affect to GDB such as call function "no_shared_libraries= ". >> + =A0 =A0 So I add special commands to GDB. =A0*/ >> + =A0add_com ("delrecord", class_obscure, cmd_record_delete, >> + =A0 =A0 =A0 =A0_("Delete the rest of execution log and start recording= it anew.")); >> + =A0add_com_alias ("dr", "delrecord", class_obscure, 1); >> + =A0add_com ("stoprecord", class_obscure, cmd_record_stop, >> + =A0 =A0 =A0 =A0_("Stop the record/replay target.")); >> + =A0add_com_alias ("sr", "stoprecord", class_obscure, 1); >> + >> + =A0/* Record instructions number limit command. =A0*/ >> + =A0add_setshow_boolean_cmd ("record-stop-at-limit", no_class, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 &record_stop_at_limit, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 _("Set whether record/= replay stop when " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "record/replay buf= fer becomes full."), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 _("Show whether record= /replay stop when " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "record/replay buf= fer becomes full."), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 _("Enable is default v= alue.\n" >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "When enabled, if = the record/replay buffer " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "becomes full,\n" >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"ask user w= hat to do.\n" >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"When disab= led, if the record/replay buffer " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "becomes full,\n" >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"delete it = and start new recording."), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 NULL, NULL, &setlist, = &showlist); >> + =A0add_setshow_zinteger_cmd ("record-insn-number-max", no_class, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 &record_insn_max_num, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 _("Set record/replay b= uffer limit."), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 _("Show record/replay = buffer limit."), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 _("Set the maximum num= ber of instructions to be " >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"stored in = the\n" >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"record/rep= lay buffer. =A0" >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"Zero means= unlimited (default 200000)."), >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 set_record_insn_max_nu= m, >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 NULL, &setlist, &showl= ist); >> + =A0add_info ("record-insn-number", show_record_insn_number, >> + =A0 =A0 =A0 =A0 _("Show the current number of instructions in the " >> + =A0 =A0 =A0 =A0 =A0 "record/replay buffer.")); >> +} >> Index: src/gdb/record.h >> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >> --- /dev/null 1970-01-01 00:00:00.000000000 +0000 >> +++ src/gdb/record.h =A02009-02-28 20:23:20.000000000 +0000 >> @@ -0,0 +1,87 @@ >> +/* Process record and replay target for GDB, the GNU debugger. >> + >> + =A0 Copyright (C) 2008 Free Software Foundation, Inc. >> + >> + =A0 This file is part of GDB. >> + >> + =A0 This program is free software; you can redistribute it and/or modi= fy >> + =A0 it under the terms of the GNU General Public License as published = by >> + =A0 the Free Software Foundation; either version 3 of the License, or >> + =A0 (at your option) any later version. >> + >> + =A0 This program is distributed in the hope that it will be useful, >> + =A0 but WITHOUT ANY WARRANTY; without even the implied warranty of >> + =A0 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =A0See the >> + =A0 GNU General Public License for more details. >> + >> + =A0 You should have received a copy of the GNU General Public License >> + =A0 along with this program. =A0If not, see . =A0*/ >> + >> +#ifndef _RECORD_H_ >> +#define _RECORD_H_ >> + >> +#define TARGET_IS_PROCESS_RECORD =A0 \ >> + =A0 =A0 (current_target.beneath =3D=3D &record_ops) > > Sorry, but I repeat the request I've made several times already. =A0This = is > not the right way to do this. =A0You need to add a new target_ops method = or > property that the core of GDB checks on. =A0It is not correct that make > the core of GDB reference record_ops directly. =A0To come up with > the target callback's name, at each call site of TARGET_IS_PROCESS_RECORD, > consider what is the property of the current target that GDB needs to > know about the current target. =A0Is it something like: > > =A0target_is_recording () ? > =A0target_is_replaying () ? > =A0target_is_read_only () ? > > etc. > I forget a process record has special strata "record_stratum". What about delete "TARGET_IS_PROCESS_RECORD" and add #define target_is_record (target) (target->to_stratum =3D=3D record_stratum) to target.h? >> +#define RECORD_IS_REPLAY \ >> + =A0 =A0 (record_list->next || execution_direction =3D=3D EXEC_REVERSE) > > AFAICS, this macro is not used outside of record.c. =A0It should move > there, along with anything that isn't used outside of record.c. OK. I will put it to record.c. > >> + >> +typedef struct record_reg_s >> +{ >> + =A0int num; >> + =A0gdb_byte *val; >> +} record_reg_t; >> + >> +typedef struct record_mem_s >> +{ >> + =A0CORE_ADDR addr; >> + =A0int len; >> + =A0gdb_byte *val; >> +} record_mem_t; >> + >> +enum record_type >> +{ >> + =A0record_end =3D 0, >> + =A0record_reg, >> + =A0record_mem >> +}; >> + >> +/* This is the core struct of record function. >> + >> + =A0 An entity of record_t is a record of the value change of a register >> + =A0 ("record_reg") or a part of memory ("record_mem"). =A0And each >> + =A0 instruction must has a record_t ("record_end") that points out this >> + =A0 is the last record_t of this instruction. >> + >> + =A0 Each record_t is linked to "record_list" by "prev" and "next". >> + */ >> +typedef struct record_s >> +{ >> + =A0struct record_s *prev; >> + =A0struct record_s *next; >> + =A0enum record_type type; >> + =A0union >> + =A0{ >> + =A0 =A0/* reg */ >> + =A0 =A0record_reg_t reg; >> + =A0 =A0/* mem */ >> + =A0 =A0record_mem_t mem; >> + =A0 =A0/* end */ >> + =A0 =A0int need_dasm; >> + =A0} u; >> +} record_t; >> + >> +extern int record_debug; >> +extern record_t *record_list; >> +extern record_t *record_arch_list_head; >> +extern record_t *record_arch_list_tail; >> +extern struct regcache *record_regcache; > > Most of these things don't appear to be used anywhere else other > than in record.c. =A0Can you remove these declarations from the > public header, and make them static in record.c? I will move them to record.c if just record.c use them. > >> + >> +extern struct target_ops record_ops; > > Once you get rid of TARGET_IS_PROCESS_RECORD this doesn't > need to be public anymore. I will. Thanks for you help. Hui