[Discussion/Prec] The record speed up plan (Make speed like without prec)
Hui Zhu
teawater@gmail.com
Wed May 19 07:19:00 GMT 2010
Hi,
This is a demo.
Still not support segment register, system call and some others.
Thanks,
Hui
On Fri, Apr 30, 2010 at 21:23, Hui Zhu <teawater@gmail.com> wrote:
> Hello,
>
> I think the record speed is the biggest trouble of prec.
> After I did a long think and a lot of test around with it. I got a
> idea. Actually, I have began the code work.
>
> I found that the big trouble is prec let the inferior just step. It
> make inferior speed very low. Because the setp need a lot of context
> works.
> So I think let inferior continue can make it speed up. But How to
> record the change of each step?
>
> Some physicists said all the things in the world have execution rules.
> So use the current stat of this thing, we will get what will happen
> in the future. Looks most of rules are still not found. :)
>
> But lucky for us that insns exec rules we know. So most of insns
> (There a some special, I will talk it later), if we have the a
> inferior value(memory and reg), we can get the each value of next
> insn.
> So if we can record the all the value of a inferior A(or all the value
> that will be change, but to get it will need parse the insns that will
> be exec, this is not easy.) , we can let the inferior exec without
> step. If the user want reverse exec, get the each step value from A.
> Then the record speed will very faster than before.
>
> But this way have a 2 question.
> 1. How to record all the status of a inferior? For the linux,
> checkpoint already use fork to record the inferior. So prec will use
> it too.
> And when we want get the old status of inferior step by step, we can
> let the forked process step by step. That will easy by parse the insn
> and know what will happen.
>
> 2. How to handle special insns that we will not know what will happen
> after it exec?
> The first type of this insns is system call. Linux have catchpoint
> that make inferior stop before and after syscall. Then we can record
> the change after the system call.
> The other insn is like rdtsc, I don't know howto get the feature value
> of this type. My current idea with them is give up all the record
> after this insn.
> If user need, insert special breakpoint for it. Next time, inferior
> will stop on this insn, then prec can record the value after it exec.
>
> BTW, I call this new function pre_record, I agree with you if you said
> this name is ugly. :)
>
> Please tell me your opinions about my idea. That will help me a lot. Thanks.
>
> Thanks,
> Hui
>
-------------- next part --------------
---
infrun.c | 2 +-
record.c | 38 ++++++++++++++++++++------------------
record.h | 2 +-
3 files changed, 22 insertions(+), 20 deletions(-)
--- a/infrun.c
+++ b/infrun.c
@@ -2731,7 +2731,7 @@ adjust_pc_after_break (struct execution_
struct cleanup *old_cleanups = NULL;
if (RECORD_IS_USED)
- old_cleanups = record_gdb_operation_disable_set ();
+ old_cleanups = record_disable_set ();
/* When using hardware single-step, a SIGTRAP is reported for both
a completed single-step and a software breakpoint. Need to
--- a/record.c
+++ b/record.c
@@ -671,16 +671,16 @@ record_message_wrapper_safe (struct regc
/* Set to 1 if record_store_registers and record_xfer_partial
doesn't need record. */
-static int record_gdb_operation_disable = 0;
+static int record_disable = 0;
struct cleanup *
-record_gdb_operation_disable_set (void)
+record_disable_set (void)
{
struct cleanup *old_cleanups = NULL;
old_cleanups =
- make_cleanup_restore_integer (&record_gdb_operation_disable);
- record_gdb_operation_disable = 1;
+ make_cleanup_restore_integer (&record_disable);
+ record_disable = 1;
return old_cleanups;
}
@@ -1007,7 +1007,8 @@ record_resume (struct target_ops *ops, p
if (!RECORD_IS_REPLAY)
{
- record_message (get_current_regcache (), signal);
+ if (!record_disable)
+ record_message (get_current_regcache (), signal);
record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1,
signal);
}
@@ -1061,8 +1062,6 @@ record_wait (struct target_ops *ops,
ptid_t ptid, struct target_waitstatus *status,
int options)
{
- struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
-
if (record_debug)
fprintf_unfiltered (gdb_stdlog,
"Process record: record_wait "
@@ -1071,9 +1070,9 @@ record_wait (struct target_ops *ops,
if (!RECORD_IS_REPLAY && ops != &record_core_ops)
{
- if (record_resume_step)
+ if (record_resume_step || record_disable)
{
- /* This is a single step. */
+ /* This is a single step or record is disabled. */
return record_beneath_to_wait (record_beneath_to_wait_ops,
ptid, status, options);
}
@@ -1082,6 +1081,7 @@ record_wait (struct target_ops *ops,
/* This is not a single step. */
ptid_t ret;
CORE_ADDR tmp_pc;
+ struct cleanup *set_cleanups = record_disable_set ();
while (1)
{
@@ -1145,6 +1145,7 @@ record_wait (struct target_ops *ops,
break;
}
+ do_cleanups (set_cleanups);
return ret;
}
}
@@ -1157,6 +1158,7 @@ record_wait (struct target_ops *ops,
int first_record_end = 1;
struct cleanup *old_cleanups = make_cleanup (record_wait_cleanups, 0);
CORE_ADDR tmp_pc;
+ struct cleanup *set_cleanups = record_disable_set ();
record_hw_watchpoint = 0;
status->kind = TARGET_WAITKIND_STOPPED;
@@ -1309,10 +1311,10 @@ replay_out:
else
status->value.sig = TARGET_SIGNAL_TRAP;
+ do_cleanups (set_cleanups);
discard_cleanups (old_cleanups);
}
- do_cleanups (set_cleanups);
return inferior_ptid;
}
@@ -1436,7 +1438,7 @@ static void
record_store_registers (struct target_ops *ops, struct regcache *regcache,
int regno)
{
- if (!record_gdb_operation_disable)
+ if (!record_disable)
{
if (RECORD_IS_REPLAY)
{
@@ -1495,7 +1497,7 @@ record_xfer_partial (struct target_ops *
const char *annex, gdb_byte *readbuf,
const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
{
- if (!record_gdb_operation_disable
+ if (!record_disable
&& (object == TARGET_OBJECT_MEMORY
|| object == TARGET_OBJECT_RAW_MEMORY) && writebuf)
{
@@ -1561,7 +1563,7 @@ record_insert_breakpoint (struct gdbarch
{
if (!RECORD_IS_REPLAY)
{
- struct cleanup *old_cleanups = record_gdb_operation_disable_set ();
+ struct cleanup *old_cleanups = record_disable_set ();
int ret = record_beneath_to_insert_breakpoint (gdbarch, bp_tgt);
do_cleanups (old_cleanups);
@@ -1580,7 +1582,7 @@ record_remove_breakpoint (struct gdbarch
{
if (!RECORD_IS_REPLAY)
{
- struct cleanup *old_cleanups = record_gdb_operation_disable_set ();
+ struct cleanup *old_cleanups = record_disable_set ();
int ret = record_beneath_to_remove_breakpoint (gdbarch, bp_tgt);
do_cleanups (old_cleanups);
@@ -1735,7 +1737,7 @@ record_core_store_registers (struct targ
struct regcache *regcache,
int regno)
{
- if (record_gdb_operation_disable)
+ if (record_disable)
regcache_raw_collect (regcache, regno,
record_core_regbuf + MAX_REGISTER_SIZE * regno);
else
@@ -1752,7 +1754,7 @@ record_core_xfer_partial (struct target_
{
if (object == TARGET_OBJECT_MEMORY)
{
- if (record_gdb_operation_disable || !writebuf)
+ if (record_disable || !writebuf)
{
struct target_section *p;
@@ -2375,7 +2377,7 @@ cmd_record_save (char *args, int from_tt
gdbarch = get_regcache_arch (regcache);
/* Disable the GDB operation record. */
- set_cleanups = record_gdb_operation_disable_set ();
+ set_cleanups = record_disable_set ();
/* Reverse execute to the begin of record list. */
while (1)
@@ -2549,7 +2551,7 @@ static void
record_goto_insn (struct record_entry *entry,
enum exec_direction_kind dir)
{
- struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
+ struct cleanup *set_cleanups = record_disable_set ();
struct regcache *regcache = get_current_regcache ();
struct gdbarch *gdbarch = get_regcache_arch (regcache);
--- a/record.h
+++ b/record.h
@@ -27,6 +27,6 @@ extern int record_debug;
extern int record_arch_list_add_reg (struct regcache *regcache, int num);
extern int record_arch_list_add_mem (CORE_ADDR addr, int len);
extern int record_arch_list_add_end (void);
-extern struct cleanup *record_gdb_operation_disable_set (void);
+extern struct cleanup *record_disable_set (void);
#endif /* _RECORD_H_ */
-------------- next part --------------
---
gdbarch.c | 33 ++++++
gdbarch.h | 8 +
gdbarch.sh | 3
i386-linux-tdep.c | 1
linux-nat.c | 17 ++-
linux-record.c | 68 +++++++++++++
linux-record.h | 2
record.c | 267 ++++++++++++++++++++++++++++++++++++++++++++++++++----
record.h | 2
9 files changed, 378 insertions(+), 23 deletions(-)
--- a/gdbarch.c
+++ b/gdbarch.c
@@ -251,6 +251,7 @@ struct gdbarch
int sofun_address_maybe_missing;
gdbarch_process_record_ftype *process_record;
gdbarch_process_record_signal_ftype *process_record_signal;
+ gdbarch_pre_process_record_ftype *pre_process_record;
gdbarch_target_signal_from_host_ftype *target_signal_from_host;
gdbarch_target_signal_to_host_ftype *target_signal_to_host;
gdbarch_get_siginfo_type_ftype *get_siginfo_type;
@@ -398,6 +399,7 @@ struct gdbarch startup_gdbarch =
0, /* sofun_address_maybe_missing */
0, /* process_record */
0, /* process_record_signal */
+ 0, /* pre_process_record */
default_target_signal_from_host, /* target_signal_from_host */
default_target_signal_to_host, /* target_signal_to_host */
0, /* get_siginfo_type */
@@ -673,6 +675,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
/* Skip verify of sofun_address_maybe_missing, invalid_p == 0 */
/* Skip verify of process_record, has predicate */
/* Skip verify of process_record_signal, has predicate */
+ /* Skip verify of pre_process_record, has predicate */
/* Skip verify of target_signal_from_host, invalid_p == 0 */
/* Skip verify of target_signal_to_host, invalid_p == 0 */
/* Skip verify of get_siginfo_type, has predicate */
@@ -1009,6 +1012,12 @@ gdbarch_dump (struct gdbarch *gdbarch, s
"gdbarch_dump: pointer_to_address = <%s>\n",
host_address_to_string (gdbarch->pointer_to_address));
fprintf_unfiltered (file,
+ "gdbarch_dump: gdbarch_pre_process_record_p() = %d\n",
+ gdbarch_pre_process_record_p (gdbarch));
+ fprintf_unfiltered (file,
+ "gdbarch_dump: pre_process_record = <%s>\n",
+ host_address_to_string (gdbarch->pre_process_record));
+ fprintf_unfiltered (file,
"gdbarch_dump: gdbarch_print_float_info_p() = %d\n",
gdbarch_print_float_info_p (gdbarch));
fprintf_unfiltered (file,
@@ -3439,6 +3448,30 @@ set_gdbarch_process_record_signal (struc
gdbarch->process_record_signal = process_record_signal;
}
+int
+gdbarch_pre_process_record_p (struct gdbarch *gdbarch)
+{
+ gdb_assert (gdbarch != NULL);
+ return gdbarch->pre_process_record != NULL;
+}
+
+int
+gdbarch_pre_process_record (struct gdbarch *gdbarch)
+{
+ gdb_assert (gdbarch != NULL);
+ gdb_assert (gdbarch->pre_process_record != NULL);
+ if (gdbarch_debug >= 2)
+ fprintf_unfiltered (gdb_stdlog, "gdbarch_pre_process_record called\n");
+ return gdbarch->pre_process_record (gdbarch);
+}
+
+void
+set_gdbarch_pre_process_record (struct gdbarch *gdbarch,
+ gdbarch_pre_process_record_ftype pre_process_record)
+{
+ gdbarch->pre_process_record = pre_process_record;
+}
+
enum target_signal
gdbarch_target_signal_from_host (struct gdbarch *gdbarch, int signo)
{
--- a/gdbarch.h
+++ b/gdbarch.h
@@ -853,6 +853,14 @@ typedef int (gdbarch_process_record_sign
extern int gdbarch_process_record_signal (struct gdbarch *gdbarch, struct regcache *regcache, enum target_signal signal);
extern void set_gdbarch_process_record_signal (struct gdbarch *gdbarch, gdbarch_process_record_signal_ftype *process_record_signal);
+/* Pre process record */
+
+extern int gdbarch_pre_process_record_p (struct gdbarch *gdbarch);
+
+typedef int (gdbarch_pre_process_record_ftype) (struct gdbarch *gdbarch);
+extern int gdbarch_pre_process_record (struct gdbarch *gdbarch);
+extern void set_gdbarch_pre_process_record (struct gdbarch *gdbarch, gdbarch_pre_process_record_ftype *pre_process_record);
+
/* Signal translation: translate inferior's signal (host's) number into
GDB's representation. */
--- a/gdbarch.sh
+++ b/gdbarch.sh
@@ -728,6 +728,9 @@ M:int:process_record:struct regcache *re
# Return -1 if something goes wrong, 0 otherwise.
M:int:process_record_signal:struct regcache *regcache, enum target_signal signal:regcache, signal
+# Pre process record
+M:int:pre_process_record:void:
+
# Signal translation: translate inferior's signal (host's) number into
# GDB's representation.
m:enum target_signal:target_signal_from_host:int signo:signo::default_target_signal_from_host::0
--- a/i386-linux-tdep.c
+++ b/i386-linux-tdep.c
@@ -693,6 +693,7 @@ i386_linux_init_abi (struct gdbarch_info
tdep->xsave_xcr0_offset = I386_LINUX_XSAVE_XCR0_OFFSET;
set_gdbarch_process_record (gdbarch, i386_process_record);
+ set_gdbarch_pre_process_record (gdbarch, linux_pre_record);
set_gdbarch_process_record_signal (gdbarch, i386_linux_record_signal);
/* Initialize the i386_linux_record_tdep. */
--- a/linux-nat.c
+++ b/linux-nat.c
@@ -2143,6 +2143,8 @@ linux_handle_syscall_trap (struct lwp_in
event should be ignored and we should wait again. If STOPPING is
true, the new LWP remains stopped, otherwise it is continued. */
+extern int linux_pre_recording;
+
static int
linux_handle_extended_wait (struct lwp_info *lp, int status,
int stopping)
@@ -2180,7 +2182,8 @@ linux_handle_extended_wait (struct lwp_i
ourstatus->value.related_pid = ptid_build (new_pid, new_pid, 0);
if (event == PTRACE_EVENT_FORK
- && linux_fork_checkpointing_p (GET_PID (lp->ptid)))
+ && (linux_fork_checkpointing_p (GET_PID (lp->ptid))
+ || linux_pre_recording))
{
struct fork_info *fp;
@@ -2192,10 +2195,14 @@ linux_handle_extended_wait (struct lwp_i
physically remove the breakpoints from the child. */
detach_breakpoints (new_pid);
- /* Retain child fork in ptrace (stopped) state. */
- fp = find_fork_pid (new_pid);
- if (!fp)
- fp = add_fork (new_pid);
+ /* Retain child fork in ptrace (stopped) state if this is
+ checkpoint. */
+ if (!linux_pre_recording)
+ {
+ fp = find_fork_pid (new_pid);
+ if (!fp)
+ fp = add_fork (new_pid);
+ }
/* Report as spurious, so that infrun doesn't want to follow
this fork. We're actually doing an infcall in
--- a/linux-record.c
+++ b/linux-record.c
@@ -21,9 +21,21 @@
#include "target.h"
#include "gdbtypes.h"
#include "regcache.h"
+#include "inferior.h"
+#include "infcall.h"
+#include "objfiles.h"
#include "record.h"
#include "linux-record.h"
+//#include "arch-utils.h"
+//
+//#include "gdbcmd.h"
+//
+//
+//#include "gdb_assert.h"
+//#include "gdb_string.h"
+#include "linux-fork.h"
+
/* These macros are the values of the first argument of system call
"sys_ptrace". The values of these macros were obtained from Linux
Kernel source. */
@@ -2243,3 +2255,59 @@ record_linux_system_call (enum gdb_sysca
return 0;
}
+
+int linux_pre_recording = 0;
+
+int
+linux_pre_record (struct gdbarch *gdbarch)
+{
+ struct objfile *fork_objf;
+ struct value *fork_fn = NULL, *ret;
+ struct cleanup *old_cleanups;
+ struct cleanup *self_cleanups;
+ pid_t pid;
+ ptid_t ptid, record_ptid;
+ struct regcache *record_regcache;
+ extern void linux_nat_switch_fork (ptid_t new_ptid);
+
+ /* Get the fork_fn. */
+ if (lookup_minimal_symbol ("fork", NULL, NULL) != NULL)
+ fork_fn = find_function_in_inferior ("fork", &fork_objf);
+ if (!fork_fn)
+ if (lookup_minimal_symbol ("_fork", NULL, NULL) != NULL)
+ fork_fn = find_function_in_inferior ("fork", &fork_objf);
+ if (!fork_fn)
+ return -1;
+ ret = value_from_longest (builtin_type (gdbarch)->builtin_int, 0);
+
+ /* Tell record.c that the following inferior change doesn't need record. */
+ old_cleanups = record_disable_set ();
+
+ /* Tell target that this is linux pre-record. */
+ self_cleanups = make_cleanup_restore_integer (&linux_pre_recording);
+ linux_pre_recording = 1;
+
+ ret = call_function_by_hand (fork_fn, 0, &ret);
+
+ do_cleanups (self_cleanups);
+
+ pid = value_as_long (ret);
+ if (pid < 0)
+ return -1;
+ ptid = ptid_build (pid, pid, 0);
+
+ /* Set the current regcache to ptid. */
+ record_ptid = inferior_ptid;
+ record_regcache = regcache_dup (get_current_regcache ());
+ linux_nat_switch_fork (ptid);
+ regcache_cpy (get_current_regcache (), record_regcache);
+ linux_nat_switch_fork (record_ptid);
+ regcache_xfree (record_regcache);
+
+ do_cleanups (old_cleanups);
+
+ record_arch_list_add_ptid (ptid);
+ record_arch_list_add_end ();
+
+ return 0;
+}
--- a/linux-record.h
+++ b/linux-record.h
@@ -537,4 +537,6 @@ enum gdb_syscall {
extern int record_linux_system_call (enum gdb_syscall num,
struct regcache *regcache,
struct linux_record_tdep *tdep);
+
+extern int linux_pre_record (struct gdbarch *gdbarch);
#endif /* _LINUX_RECORD_H_ */
--- a/record.c
+++ b/record.c
@@ -106,7 +106,8 @@ enum record_type
{
record_end = 0,
record_reg,
- record_mem
+ record_mem,
+ record_ptid,
};
/* This is the data structure that makes up the execution log.
@@ -146,6 +147,8 @@ struct record_entry
struct record_mem_entry mem;
/* end */
struct record_end_entry end;
+ /* ptid */
+ ptid_t ptid;
} u;
};
@@ -159,6 +162,14 @@ struct record_core_buf_entry
bfd_byte *buf;
};
+/* The arch level interface choice inferior stepi or continue. */
+
+int record_step;
+
+/* The step of record_resume. */
+
+static int record_resume_step = 0;
+
/* Record buf with core target. */
static gdb_byte *record_core_regbuf = NULL;
static struct target_section *record_core_start;
@@ -200,6 +211,9 @@ static ULONGEST record_insn_count;
static struct target_ops record_ops;
static struct target_ops record_core_ops;
+/* The swith of record pre. */
+static int record_pre = 1;
+
/* The beneath function pointers. */
static struct target_ops *record_beneath_to_resume_ops;
static void (*record_beneath_to_resume) (struct target_ops *, ptid_t, int,
@@ -288,6 +302,16 @@ record_mem_release (struct record_entry
xfree (rec);
}
+/* XXX: Free a record_ptid record entry. */
+#include <sys/ptrace.h>
+static inline void
+record_ptid_release (struct record_entry *rec)
+{
+ gdb_assert (rec->type == record_ptid);
+ ptrace (PTRACE_KILL, PIDGET (rec->u.ptid), 0, 0);
+ xfree (rec);
+}
+
/* Alloc a record_end record entry. */
static inline struct record_entry *
@@ -324,6 +348,9 @@ record_entry_release (struct record_entr
case record_mem:
record_mem_release (rec);
break;
+ case record_ptid:
+ record_ptid_release (rec);
+ break;
case record_end:
record_end_release (rec);
break;
@@ -512,6 +539,28 @@ record_arch_list_add_mem (CORE_ADDR addr
return 0;
}
+/* Record the value of a ptid to record_arch_list. */
+
+int
+record_arch_list_add_ptid (ptid_t ptid)
+{
+ struct record_entry *rec;
+
+ if (record_debug > 1)
+ fprintf_unfiltered (gdb_stdlog,
+ "Process record: add ptid = %s to "
+ "record list.\n",
+ target_pid_to_str (ptid));
+
+ rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry));
+ rec->type = record_ptid;
+ rec->u.ptid = ptid;
+
+ record_arch_list_add (rec);
+
+ return 0;
+}
+
/* Add a record_end type struct record_entry to record_arch_list. */
int
@@ -576,7 +625,7 @@ record_arch_list_cleanups (void *ignore)
static int
record_message (struct regcache *regcache, enum target_signal signal)
{
- int ret;
+ int ret = -1;
struct gdbarch *gdbarch = get_regcache_arch (regcache);
struct cleanup *old_cleanups = make_cleanup (record_arch_list_cleanups, 0);
@@ -586,6 +635,8 @@ record_message (struct regcache *regcach
/* Check record_insn_num. */
record_check_insn_num (1);
+ record_step = 1;
+
/* If gdb sends a signal value to target_resume,
save it in the 'end' field of the previous instruction.
@@ -608,11 +659,20 @@ record_message (struct regcache *regcach
But we should still deliver the signal to gdb during the replay,
if we delivered it during the recording. Therefore we should
record the signal during record_wait, not record_resume. */
- if (record_list != &record_first) /* FIXME better way to check */
- {
- gdb_assert (record_list->type == record_end);
- record_list->u.end.sigval = signal;
- }
+ {
+ struct record_entry *rec;
+
+ if (RECORD_IS_REPLAY)
+ rec = record_list->prev;
+ else
+ rec = record_list;
+
+ if (rec != &record_first) /* FIXME better way to check */
+ {
+ gdb_assert (rec->type == record_end);
+ rec->u.end.sigval = signal;
+ }
+ }
if (signal == TARGET_SIGNAL_0
|| !gdbarch_process_record_signal_p (gdbarch))
@@ -631,9 +691,19 @@ record_message (struct regcache *regcach
discard_cleanups (old_cleanups);
- record_list->next = record_arch_list_head;
- record_arch_list_head->prev = record_list;
- record_list = record_arch_list_tail;
+ if (RECORD_IS_REPLAY)
+ {
+ record_list->prev->next = record_arch_list_head;
+ record_arch_list_head->prev = record_list->prev;
+ record_list->prev = record_arch_list_tail;
+ record_arch_list_tail->next = record_list;
+ }
+ else
+ {
+ record_list->next = record_arch_list_head;
+ record_arch_list_head->prev = record_list;
+ record_list = record_arch_list_tail;
+ }
if (record_insn_num == record_insn_max_num && record_insn_max_num)
record_list_release_first ();
@@ -774,6 +844,129 @@ record_exec_insn (struct regcache *regca
}
}
break;
+
+ case record_ptid:
+ {
+ struct frame_info *fi;
+ int i;
+ CORE_ADDR current_stop_pc = stop_pc;
+ ptid_t current_ptid = inferior_ptid;
+ struct regcache *current_reg;
+ int current_frame_count = 0;
+ struct frame_id *current_frame_id;
+ struct regcache *record_reg;
+ extern void linux_nat_switch_fork (ptid_t new_ptid);
+ extern void nullify_last_target_wait_ptid ();
+
+ set_executing (inferior_ptid, 0);
+
+ /* Get current_reg. */
+ current_reg = regcache_dup (get_current_regcache ());
+ /* Get current_frame_count. */
+ for (fi = get_current_frame (); fi; fi = get_prev_frame (fi))
+ current_frame_count ++;
+ /* Get current_frame_reg. */
+ current_frame_id = alloca (current_frame_count
+ * sizeof (struct frame_id));
+ i = 0;
+ for (fi = get_current_frame (); fi; fi = get_prev_frame (fi))
+ {
+ current_frame_id[i] = get_frame_id (fi);
+ i ++;
+ }
+
+ /* Switch to record_ptid. */
+ linux_nat_switch_fork (record_list->u.ptid);
+ registers_changed ();
+ reinit_frame_cache ();
+ record_reg = get_current_regcache ();
+ stop_pc = regcache_read_pc (record_reg);
+ nullify_last_target_wait_ptid ();
+
+ while (1)
+ {
+ struct target_waitstatus status;
+
+ /* Check if the record_ptid is in the same place of
+ current_ptid. */
+ if (stop_pc == current_stop_pc)
+ {
+ int not_same = 0;
+ struct regcache *record_reg = get_current_regcache ();
+ gdb_byte current[MAX_REGISTER_SIZE], record[MAX_REGISTER_SIZE];
+
+ /* Check current_reg. */
+ for (i = gdbarch_num_regs (gdbarch) - 1; i >= 0; i --)
+ {
+ if (!regcache_valid_p (current_reg,i))
+ continue;
+
+ memset (current, 0, MAX_REGISTER_SIZE);
+ memset (record, 0, MAX_REGISTER_SIZE);
+
+ regcache_raw_read (current_reg, i, current);
+ regcache_raw_read (record_reg, i, record);
+ if (memcmp (current, record, MAX_REGISTER_SIZE))
+ {
+ not_same = 1;
+ break;
+ }
+ }
+ if (not_same)
+ goto keep_exec;
+
+ /* Check current_frame_id. */
+ i = 0;
+ for (fi = get_current_frame (); fi; fi = get_prev_frame (fi))
+ {
+ if (i >= current_frame_count)
+ {
+ not_same = 1;
+ break;
+ }
+
+ if (!frame_id_eq (get_frame_id (fi), current_frame_id[i]))
+ {
+ not_same = 1;
+ break;
+ }
+
+ i ++;
+ }
+ if (not_same)
+ goto keep_exec;
+
+ break;
+ }
+
+keep_exec:
+ if (!record_message_wrapper_safe (record_reg, TARGET_SIGNAL_0))
+ break;
+ record_beneath_to_resume (record_beneath_to_resume_ops,
+ record_list->u.ptid, 1,
+ TARGET_SIGNAL_0);
+ record_beneath_to_wait (record_beneath_to_wait_ops,
+ record_list->u.ptid, &status, 0);
+
+ /* Update stop_pc. */
+ registers_changed ();
+ record_reg = get_current_regcache ();
+ stop_pc = regcache_read_pc (record_reg);
+ }
+
+ /* Release. */
+ regcache_xfree (current_reg);
+
+ /* Switch to current_ptid. */
+ linux_nat_switch_fork (current_ptid);
+ registers_changed ();
+ reinit_frame_cache ();
+ stop_pc = regcache_read_pc (get_current_regcache ());
+ nullify_last_target_wait_ptid ();
+
+ set_executing (inferior_ptid, 1);
+ }
+ break;
}
}
@@ -872,6 +1065,31 @@ record_open_1 (char *name, int from_tty)
error (_("Could not find 'to_stopped_data_address' method on the target stack."));
push_target (&record_ops);
+
+ /* Do pre record. */
+ if (record_pre)
+ {
+ int ret;
+ struct gdbarch *gdbarch = get_current_arch ();
+
+ record_arch_list_head = NULL;
+ record_arch_list_tail = NULL;
+
+ if (gdbarch_pre_process_record_p (gdbarch))
+ ret = gdbarch_pre_process_record (gdbarch);
+ if (ret == 0)
+ {
+ record_list->next = record_arch_list_head;
+ record_arch_list_head->prev = record_list;
+ record_list = record_arch_list_tail;
+ }
+ else
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ _("Auto close the record pre.\n"));
+ record_pre = 0;
+ }
+ }
}
/* "to_open" target method. Open the process record target. */
@@ -879,6 +1097,7 @@ record_open_1 (char *name, int from_tty)
static void
record_open (char *name, int from_tty)
{
+ int ret = -1;
struct target_ops *t;
if (record_debug)
@@ -995,8 +1214,6 @@ record_close (int quitting)
}
}
-static int record_resume_step = 0;
-
/* "to_resume" target method. Resume the process record target. */
static void
@@ -1007,10 +1224,12 @@ record_resume (struct target_ops *ops, p
if (!RECORD_IS_REPLAY)
{
- if (!record_disable)
+ if (record_disable || record_pre)
+ record_step = step;
+ else
record_message (get_current_regcache (), signal);
- record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1,
- signal);
+ record_beneath_to_resume (record_beneath_to_resume_ops, ptid,
+ record_step, signal);
}
}
@@ -1070,7 +1289,8 @@ record_wait (struct target_ops *ops,
if (!RECORD_IS_REPLAY && ops != &record_core_ops)
{
- if (record_resume_step || record_disable)
+ if (record_resume_step || record_disable
+ || record_pre)
{
/* This is a single step or record is disabled. */
return record_beneath_to_wait (record_beneath_to_wait_ops,
@@ -1135,7 +1355,7 @@ record_wait (struct target_ops *ops,
}
record_beneath_to_resume (record_beneath_to_resume_ops,
- ptid, 1,
+ ptid, record_step,
TARGET_SIGNAL_0);
continue;
}
@@ -1219,7 +1439,8 @@ record_wait (struct target_ops *ops,
record_exec_insn (regcache, gdbarch, record_list);
- if (record_list->type == record_end)
+ if (record_list->type == record_end
+ && record_list->prev->type != record_ptid)
{
if (record_debug > 1)
fprintf_unfiltered (gdb_stdlog,
@@ -2724,6 +2945,16 @@ record/replay buffer. Zero means unlimi
set_record_insn_max_num,
NULL, &set_record_cmdlist, &show_record_cmdlist);
+ add_setshow_boolean_cmd ("pre", no_class,
+ &record_pre, _("\
+Set whether record/replay pre work."), _("\
+Show whether record/replay pre work."), _("\
+Default is ON.\n\
+When ON, if the record/replay pre will work.\n\
+When OFF, if the record/replay pre will not work."),
+ NULL, NULL,
+ &set_record_cmdlist, &show_record_cmdlist);
+
add_cmd ("goto", class_obscure, cmd_record_goto, _("\
Restore the program to its state at instruction number N.\n\
Argument is instruction number, as shown by 'info record'."),
--- a/record.h
+++ b/record.h
@@ -23,9 +23,11 @@
#define RECORD_IS_USED (current_target.to_stratum == record_stratum)
extern int record_debug;
+extern int record_step;
extern int record_arch_list_add_reg (struct regcache *regcache, int num);
extern int record_arch_list_add_mem (CORE_ADDR addr, int len);
+extern int record_arch_list_add_ptid (ptid_t ptid);
extern int record_arch_list_add_end (void);
extern struct cleanup *record_disable_set (void);
More information about the Gdb
mailing list