--- 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 +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);