--- amd64-linux-tdep.c | 4 gdbthread.h | 2 i386-linux-tdep.c | 4 i386-tdep.c | 52 ++-- i386-tdep.h | 6 linux-record.c | 7 linux-record.h | 2 record.c | 566 ++++++++++++++++++++++++++++++++++++++++++++--------- record.h | 1 9 files changed, 516 insertions(+), 128 deletions(-) --- a/amd64-linux-tdep.c +++ b/amd64-linux-tdep.c @@ -1155,7 +1155,7 @@ static struct linux_record_tdep amd64_li #define RECORD_ARCH_GET_GS 0x1004 static int -amd64_linux_syscall_record (struct regcache *regcache) +amd64_linux_syscall_record (struct regcache *regcache, CORE_ADDR addr) { int ret; ULONGEST syscall_native; @@ -1205,7 +1205,7 @@ amd64_linux_syscall_record (struct regca } else { - ret = record_linux_system_call (syscall_gdb, regcache, + ret = record_linux_system_call (syscall_gdb, addr, regcache, &amd64_linux_record_tdep); if (ret) return ret; --- a/gdbthread.h +++ b/gdbthread.h @@ -37,6 +37,8 @@ struct thread_info kernel thread id, etc. */ int num; /* Convenient handle (GDB thread id) */ + int record_is_waiting; + /* Non-zero means the thread is executing. Note: this is different from saying that there is an active target and we are stopped at a breakpoint, for instance. This is a real indicator whether the --- a/i386-linux-tdep.c +++ b/i386-linux-tdep.c @@ -411,7 +411,7 @@ i386_canonicalize_syscall (int syscall) static struct linux_record_tdep i386_linux_record_tdep; static int -i386_linux_intx80_sysenter_record (struct regcache *regcache) +i386_linux_intx80_sysenter_record (struct regcache *regcache, CORE_ADDR addr) { int ret; LONGEST syscall_native; @@ -437,7 +437,7 @@ i386_linux_intx80_sysenter_record (struc return 0; } - ret = record_linux_system_call (syscall_gdb, regcache, + ret = record_linux_system_call (syscall_gdb, addr, regcache, &i386_linux_record_tdep); if (ret) return ret; --- a/i386-tdep.c +++ b/i386-tdep.c @@ -3160,10 +3160,11 @@ i386_record_lea_modrm (struct i386_recor if (irp->override >= 0) { - warning (_("Process record ignores the memory change " - "of instruction at address %s because it " - "can't get the value of the segment register."), - paddress (gdbarch, irp->orig_addr)); + if (record_debug) + warning (_("Process record ignores the memory change " + "of instruction at address %s because it " + "can't get the value of the segment register."), + paddress (gdbarch, irp->orig_addr)); return 0; } @@ -4042,11 +4043,12 @@ reswitch: case 0xa3: if (ir.override >= 0) { - warning (_("Process record ignores the memory change " - "of instruction at address %s because " - "it can't get the value of the segment " - "register."), - paddress (gdbarch, ir.orig_addr)); + if (record_debug) + warning (_("Process record ignores the memory change " + "of instruction at address %s because " + "it can't get the value of the segment " + "register."), + paddress (gdbarch, ir.orig_addr)); } else { @@ -4467,11 +4469,12 @@ reswitch: if (ir.aflag && (es != ds)) { /* addr += ((uint32_t) read_register (I386_ES_REGNUM)) << 4; */ - warning (_("Process record ignores the memory " - "change of instruction at address %s " - "because it can't get the value of the " - "ES segment register."), - paddress (gdbarch, ir.orig_addr)); + if (record_debug) + warning (_("Process record ignores the memory " + "change of instruction at address %s " + "because it can't get the value of the " + "ES segment register."), + paddress (gdbarch, ir.orig_addr)); } else { @@ -4873,7 +4876,7 @@ reswitch: ir.addr -= 2; goto no_support; } - ret = gdbarch_tdep (gdbarch)->i386_intx80_record (ir.regcache); + ret = gdbarch_tdep (gdbarch)->i386_intx80_record (ir.regcache, ir.addr); if (ret) return ret; } @@ -4975,7 +4978,8 @@ reswitch: ir.addr -= 2; goto no_support; } - ret = gdbarch_tdep (gdbarch)->i386_sysenter_record (ir.regcache); + ret = gdbarch_tdep (gdbarch)->i386_sysenter_record (ir.regcache, + ir.addr); if (ret) return ret; } @@ -5000,7 +5004,8 @@ reswitch: ir.addr -= 2; goto no_support; } - ret = gdbarch_tdep (gdbarch)->i386_syscall_record (ir.regcache); + ret = gdbarch_tdep (gdbarch)->i386_syscall_record (ir.regcache, + ir.addr); if (ret) return ret; } @@ -5136,12 +5141,13 @@ reswitch: /* sidt */ if (ir.override >= 0) { - warning (_("Process record ignores the memory " - "change of instruction at " - "address %s because it can't get " - "the value of the segment " - "register."), - paddress (gdbarch, ir.orig_addr)); + if (record_debug) + warning (_("Process record ignores the memory " + "change of instruction at " + "address %s because it can't get " + "the value of the segment " + "register."), + paddress (gdbarch, ir.orig_addr)); } else { --- a/i386-tdep.h +++ b/i386-tdep.h @@ -115,11 +115,11 @@ struct gdbarch_tdep in GDB is not same as I386 instructions. */ const int *record_regmap; /* Parse intx80 args. */ - int (*i386_intx80_record) (struct regcache *regcache); + int (*i386_intx80_record) (struct regcache *regcache, CORE_ADDR addr); /* Parse sysenter args. */ - int (*i386_sysenter_record) (struct regcache *regcache); + int (*i386_sysenter_record) (struct regcache *regcache, CORE_ADDR addr); /* Parse syscall args. */ - int (*i386_syscall_record) (struct regcache *regcache); + int (*i386_syscall_record) (struct regcache *regcache, CORE_ADDR addr); }; /* Floating-point registers. */ --- a/linux-record.c +++ b/linux-record.c @@ -222,7 +222,7 @@ record_linux_msghdr (struct regcache *re Return -1 if something wrong. */ int -record_linux_system_call (enum gdb_syscall syscall, +record_linux_system_call (enum gdb_syscall syscall, CORE_ADDR addr, struct regcache *regcache, struct linux_record_tdep *tdep) { @@ -1209,10 +1209,13 @@ record_linux_system_call (enum gdb_sysca case gdb_sys_fsync: case gdb_sys_sigreturn: - case gdb_sys_clone: case gdb_sys_setdomainname: break; + case gdb_sys_clone: + record_step = 0; + break; + case gdb_sys_newuname: regcache_raw_read_unsigned (regcache, tdep->arg1, &tmpulongest); if (record_arch_list_add_mem ((CORE_ADDR) tmpulongest, --- a/linux-record.h +++ b/linux-record.h @@ -534,7 +534,7 @@ enum gdb_syscall { /* Record a linux syscall. */ -extern int record_linux_system_call (enum gdb_syscall num, +extern int record_linux_system_call (enum gdb_syscall num, CORE_ADDR addr, struct regcache *regcache, struct linux_record_tdep *tdep); #endif /* _LINUX_RECORD_H_ */ --- a/record.c +++ b/record.c @@ -30,6 +30,7 @@ #include "record.h" #include "elf-bfd.h" #include "gcore.h" +#include "observer.h" #include @@ -60,6 +61,8 @@ #define RECORD_FILE_MAGIC netorder32(0x20091016) +#define RECORD_THREAD (record_ops.beneath->to_stratum == thread_stratum) + /* These are the core structs of the process record functionality. A record_entry is a record of the value change of a register @@ -106,7 +109,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. @@ -144,6 +148,8 @@ struct record_entry struct record_reg_entry reg; /* mem */ struct record_mem_entry mem; + /* ptid */ + ptid_t ptid; /* end */ struct record_end_entry end; } u; @@ -196,10 +202,18 @@ static int record_insn_num = 0; than count of insns presently in execution log). */ static ULONGEST record_insn_count; +/* In record mode, it's the step of next resume. */ +int record_step; + +/* The current inferior_ptid that is recorded. */ +static ptid_t record_prev_ptid; + /* The target_ops of process record. */ static struct target_ops record_ops; static struct target_ops record_core_ops; +static struct observer *record_threads_observer = NULL; + /* 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, @@ -285,6 +299,27 @@ record_mem_release (struct record_entry xfree (rec); } +/* Alloc a record_ptid record entry. */ + +static inline struct record_entry * +record_ptid_alloc (void) +{ + struct record_entry *rec; + + rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry)); + rec->type = record_ptid; + + return rec; +} + +/* Free a record_ptid record entry. */ + +static inline void +record_ptid_release (struct record_entry *rec) +{ + xfree (rec); +} + /* Alloc a record_end record entry. */ static inline struct record_entry * @@ -321,6 +356,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; @@ -509,6 +547,24 @@ record_arch_list_add_mem (CORE_ADDR addr return 0; } +static void +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 = record_ptid_alloc (); + + rec->u.ptid = *ptid; + + record_arch_list_add (rec); +} + /* Add a record_end type struct record_entry to record_arch_list. */ int @@ -570,7 +626,6 @@ record_arch_list_cleanups (void *ignore) record_arch_list, and add it to record_list. */ struct record_message_args { - struct regcache *regcache; enum target_signal signal; }; @@ -579,15 +634,21 @@ record_message (void *args) { int ret; struct record_message_args *myargs = args; - struct gdbarch *gdbarch = get_regcache_arch (myargs->regcache); + struct regcache *regcache; + struct gdbarch *gdbarch; struct cleanup *old_cleanups = make_cleanup (record_arch_list_cleanups, 0); + record_step = 1; + record_arch_list_head = NULL; record_arch_list_tail = NULL; /* Check record_insn_num. */ record_check_insn_num (1); + regcache = get_current_regcache (); + gdbarch = get_regcache_arch (regcache); + /* If gdb sends a signal value to target_resume, save it in the 'end' field of the previous instruction. @@ -610,20 +671,45 @@ record_message (void *args) 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 */ + if (ptid_equal (record_prev_ptid, inferior_ptid)) { - gdb_assert (record_list->type == record_end); - record_list->u.end.sigval = myargs->signal; + if (myargs->signal != TARGET_SIGNAL_0) + { + gdb_assert (record_list->type == record_end); + record_list->u.end.sigval = myargs->signal; + } + } + else + { + if (myargs->signal != TARGET_SIGNAL_0) + { + struct record_entry *rec; + for (rec = record_list; rec != record_first.next; rec = rec->prev) + { + if (rec->type == record_ptid + && ptid_equal (rec->u.ptid, inferior_ptid)) + { + gdb_assert (rec->prev->type == record_end); + rec->prev->u.end.sigval = myargs->signal; + break; + } + } + } + + /* If the inferior_ptid change (inferior_ptid is not same with + record_prev_ptid), record record_prev_ptid to record list. */ + record_arch_list_add_ptid (&record_prev_ptid); + record_prev_ptid = inferior_ptid; } if (myargs->signal == TARGET_SIGNAL_0 || !gdbarch_process_record_signal_p (gdbarch)) ret = gdbarch_process_record (gdbarch, - myargs->regcache, - regcache_read_pc (myargs->regcache)); + regcache, + regcache_read_pc (regcache)); else ret = gdbarch_process_record_signal (gdbarch, - myargs->regcache, + regcache, myargs->signal); if (ret > 0) @@ -646,12 +732,10 @@ record_message (void *args) } static int -do_record_message (struct regcache *regcache, - enum target_signal signal) +do_record_message (enum target_signal signal) { struct record_message_args args; - args.regcache = regcache; args.signal = signal; return catch_errors (record_message, &args, NULL, RETURN_MASK_ALL); } @@ -678,9 +762,12 @@ record_gdb_operation_disable_set (void) entries and memory entries, followed by an 'end' entry. */ static inline void -record_exec_insn (struct regcache *regcache, struct gdbarch *gdbarch, +record_exec_insn (struct regcache **regcache_p, struct gdbarch **gdbarch_p, struct record_entry *entry) { + struct regcache *regcache = *regcache_p; + struct gdbarch *gdbarch = *gdbarch_p; + switch (entry->type) { case record_reg: /* reg */ @@ -744,9 +831,59 @@ record_exec_insn (struct regcache *regca } } break; + + case record_ptid: /* ptid */ + { + ptid_t tmp_ptid; + + if (record_debug > 1) + fprintf_unfiltered (gdb_stdlog, + "Process record: record_ptid %s to " + "inferior ptid = %s.\n", + host_address_to_string (entry), + target_pid_to_str (entry->u.ptid)); + + if (!ptid_equal (entry->u.ptid, null_ptid)) + { + inferior_ptid = entry->u.ptid; + *regcache_p = get_current_regcache (); + *gdbarch_p = get_regcache_arch (*regcache_p); + } + tmp_ptid = entry->u.ptid; + entry->u.ptid = record_prev_ptid; + record_prev_ptid = tmp_ptid; + } + break; } } +static int record_thread_number; + +static int +record_thread_number_count (struct thread_info *thread, void *unused) +{ + record_thread_number ++; + + return 0; +} + +static void +record_thread_number_reset (void) +{ + record_thread_number = 0; + + if (RECORD_THREAD) + iterate_over_threads (record_thread_number_count, NULL); +} + +static void +record_new_thread_handler (struct thread_info *tp) +{ + tp->record_is_waiting = 0; + + record_thread_number_reset (); +} + static struct target_ops *tmp_to_resume_ops; static void (*tmp_to_resume) (struct target_ops *, ptid_t, int, enum target_signal); @@ -838,6 +975,8 @@ record_open_1 (char *name, int from_tty) error (_("Could not find 'to_remove_breakpoint' method on the target stack.")); push_target (&record_ops); + + record_threads_observer = observer_attach_new_thread (record_new_thread_handler); } /* "to_open" target method. Open the process record target. */ @@ -903,6 +1042,7 @@ record_open (char *name, int from_tty) record_insn_count = 0; record_list = &record_first; record_list->next = NULL; + record_prev_ptid = null_ptid; /* Set the tmp beneath pointers to beneath pointers. */ record_beneath_to_resume_ops = tmp_to_resume_ops; @@ -920,6 +1060,8 @@ record_open (char *name, int from_tty) record_core_open_1 (name, from_tty); else record_open_1 (name, from_tty); + + record_thread_number_reset (); } /* "to_close" target method. Close the process record target. */ @@ -932,6 +1074,12 @@ record_close (int quitting) if (record_debug) fprintf_unfiltered (gdb_stdlog, "Process record: record_close\n"); + if (record_threads_observer) + { + observer_detach_new_thread (record_threads_observer); + record_threads_observer = NULL; + } + record_list_release (record_list); /* Release record_core_regbuf. */ @@ -954,6 +1102,8 @@ record_close (int quitting) } static int record_resume_step = 0; +static ptid_t record_resume_ptid; +static enum target_signal record_resume_signal = TARGET_SIGNAL_0; /* "to_resume" target method. Resume the process record target. */ @@ -965,14 +1115,188 @@ record_resume (struct target_ops *ops, p if (!RECORD_IS_REPLAY) { - struct record_message_args args; + record_resume_ptid = ptid; - args.regcache = get_current_regcache (); - args.signal = signal; - record_message (&args); - record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1, - signal); + if (record_thread_number > 1 && !ptid_equal (ptid, inferior_ptid)) + { + record_resume_signal = signal; + } + else + { + struct record_message_args args; + + args.signal = signal; + record_message (&args); + record_beneath_to_resume (record_beneath_to_resume_ops, ptid, + record_step, signal); + } + } +} + +struct record_thread_wait_args { + ptid_t real_inferior_ptid; + struct thread_info *real_inferior_tp; + struct target_waitstatus real_inferior_status; + + struct target_waitstatus *status; + int options; +}; + +static int +record_thread_reset_callback (struct thread_info *tp, void *data) +{ + tp->record_is_waiting = 0; + + return 0; +} + +static int +record_thread_check_callback (struct thread_info *tp, void *data) +{ + if (tp->record_is_waiting) + return 1; + + return 0; +} + +/* Return 1 if it is not a simple step. + Return 0 if it is a simple step. */ + +static int +record_thread_wait (ptid_t ptid, struct target_waitstatus *status, + int options, ptid_t *ret_ptid) +{ + ptid_t wait_ptid = record_beneath_to_wait (record_beneath_to_wait_ops, + ptid, status, options); + + if (ret_ptid) + *ret_ptid = wait_ptid; + + if (status->kind == TARGET_WAITKIND_IGNORE) + return 0; + + inferior_ptid = wait_ptid; + + /* Is this a SIGTRAP? */ + if (status->kind == TARGET_WAITKIND_STOPPED + && status->value.sig == TARGET_SIGNAL_TRAP) + { + CORE_ADDR tmp_pc; + struct regcache *regcache; + struct gdbarch *gdbarch; + + /* Yes -- check if there is a breakpoint. */ + registers_changed (); + regcache = get_current_regcache (); + gdbarch = get_regcache_arch (regcache); + tmp_pc = regcache_read_pc (regcache); + if (!record_step) + tmp_pc -= gdbarch_decr_pc_after_break (gdbarch); + if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), tmp_pc)) + { + /* There is a breakpoint. GDB will want to stop. */ + if (record_step) + { + CORE_ADDR decr_pc_after_break + = gdbarch_decr_pc_after_break (gdbarch); + if (decr_pc_after_break) + regcache_write_pc (regcache, tmp_pc + decr_pc_after_break); + } + + return 1; + } + + return 0; + } + + return 1; +} + +static int +record_thread_wait_callback (struct thread_info *tp, void *data) +{ + struct record_thread_wait_args *args = data; + enum target_signal sig = TARGET_SIGNAL_0; + int options = args->options; + int ret; + + if (record_debug > 1) + fprintf_unfiltered (gdb_stdlog, + "Process record: record_thread_wait_callback " + "resume %s\n", target_pid_to_str (tp->ptid)); + + inferior_ptid = tp->ptid; + + if (!tp->record_is_waiting) + { + if (record_resume_signal != TARGET_SIGNAL_0 + && ptid_equal (inferior_ptid, args->real_inferior_ptid)) + { + sig = record_resume_signal; + record_resume_signal = TARGET_SIGNAL_0; + } + if (!do_record_message (sig)) + { + args->status->kind = TARGET_WAITKIND_STOPPED; + args->status->value.sig = TARGET_SIGNAL_0; + return 1; + } + + if (record_step == 0) + { + /* The next insn is a sys_clone. */ + if (iterate_over_threads (record_thread_check_callback, NULL)) + target_stop (minus_one_ptid); + non_stop = 0; + } + + record_beneath_to_resume (record_beneath_to_resume_ops, inferior_ptid, + record_step, sig); + } + else + { + if (record_debug > 1) + fprintf_unfiltered (gdb_stdlog, + "Process record: record_thread_wait_callback " + "not resume %s\n", target_pid_to_str (tp->ptid)); + } + + if (record_step) + options |= TARGET_WNOHANG; + ret = record_thread_wait (inferior_ptid, args->status, + options, NULL); + if (args->status->kind == TARGET_WAITKIND_IGNORE) + { + if (!tp->record_is_waiting) + { + tp->record_is_waiting = 1; + if (record_debug > 1) + fprintf_unfiltered (gdb_stdlog, + "Process record: record_thread_wait_callback " + "start waiting %s.\n", + target_pid_to_str (tp->ptid)); + } + } + else + { + if (tp->record_is_waiting) + { + tp->record_is_waiting = 0; + if (record_debug > 1) + fprintf_unfiltered (gdb_stdlog, + "Process record: record_thread_wait_callback " + "stop waiting %s.\n", + target_pid_to_str (tp->ptid)); + } + + if (!args->real_inferior_tp + && ptid_equal (inferior_ptid, args->real_inferior_ptid)) + args->real_inferior_tp = tp; + if (args->real_inferior_tp == tp) + args->real_inferior_status = *args->status; } + + return ret; } static int record_get_sig = 0; @@ -994,7 +1318,13 @@ record_sig_handler (int signo) } static void -record_wait_cleanups (void *ignore) +record_wait_signal_cleanups (void *ignore) +{ + signal (SIGINT, handle_sigint); +} + +static void +record_wait_replay_cleanups (void *ignore) { if (execution_direction == EXEC_REVERSE) { @@ -1005,6 +1335,16 @@ record_wait_cleanups (void *ignore) record_list = record_list->prev; } +static void +record_wait_mthread_cleanups (void *ignore) +{ + /* Stop the threads that still running. */ + if (iterate_over_threads (record_thread_check_callback, NULL)) + target_stop (minus_one_ptid); + + non_stop = 0; +} + /* "to_wait" target method for process record target. In record mode, the target is always run in singlestep mode @@ -1023,7 +1363,10 @@ record_wait (struct target_ops *ops, ptid_t ptid, struct target_waitstatus *status, int options) { + ptid_t ret_ptid; struct cleanup *set_cleanups = record_gdb_operation_disable_set (); + struct cleanup *signal_cleanups + = make_cleanup (record_wait_signal_cleanups, 0); if (record_debug) fprintf_unfiltered (gdb_stdlog, @@ -1031,81 +1374,103 @@ record_wait (struct target_ops *ops, "record_resume_step = %d\n", record_resume_step); + record_get_sig = 0; + signal (SIGINT, record_sig_handler); + if (!RECORD_IS_REPLAY && ops != &record_core_ops) { - if (record_resume_step) - { - /* This is a single step. */ - return record_beneath_to_wait (record_beneath_to_wait_ops, - ptid, status, options); - } - else - { - /* This is not a single step. */ - ptid_t ret; - CORE_ADDR tmp_pc; - - while (1) - { - ret = record_beneath_to_wait (record_beneath_to_wait_ops, - ptid, status, options); + /* Record mode. */ + if (record_thread_number > 1 && !ptid_equal (record_resume_ptid, + inferior_ptid)) + { + /* Multi-threads record. */ + struct record_thread_wait_args args; + struct thread_info *tp; + struct cleanup *mthread_cleanups + = make_cleanup (record_wait_mthread_cleanups, 0); - /* Is this a SIGTRAP? */ - if (status->kind == TARGET_WAITKIND_STOPPED - && status->value.sig == TARGET_SIGNAL_TRAP) - { - struct regcache *regcache; + args.real_inferior_ptid = inferior_ptid; + args.real_inferior_tp = NULL; + args.status = status; + args.options = options; + non_stop = 1; + iterate_over_threads (record_thread_reset_callback, NULL); - /* Yes -- check if there is a breakpoint. */ - registers_changed (); - regcache = get_current_regcache (); - tmp_pc = regcache_read_pc (regcache); - if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), - tmp_pc)) - { - /* There is a breakpoint. GDB will want to stop. */ - struct gdbarch *gdbarch = get_regcache_arch (regcache); - CORE_ADDR decr_pc_after_break - = gdbarch_decr_pc_after_break (gdbarch); - if (decr_pc_after_break) - regcache_write_pc (regcache, - tmp_pc + decr_pc_after_break); - } - else - { - /* There is not a breakpoint, and gdb is not - stepping, therefore gdb will not stop. - Therefore we will not return to gdb. - Record the insn and resume. */ - if (!do_record_message (regcache, TARGET_SIGNAL_0)) - { - status->kind = TARGET_WAITKIND_STOPPED; - status->value.sig = TARGET_SIGNAL_0; - break; - } + while (1) + { + tp = iterate_over_threads (record_thread_wait_callback, &args); + if (tp) + break; + if (record_resume_step + && args.real_inferior_tp + && !args.real_inferior_tp->record_is_waiting) + { + *status = args.real_inferior_status; + inferior_ptid = args.real_inferior_ptid; + break; + } + } - record_beneath_to_resume (record_beneath_to_resume_ops, - ptid, 1, - TARGET_SIGNAL_0); - continue; - } - } + do_cleanups (mthread_cleanups); - /* The inferior is broken by a breakpoint or a signal. */ - break; + ret_ptid = inferior_ptid; + } + else + { + /* Single-thread record. */ + if (record_resume_step) + { + /* This is a single step. */ + ret_ptid = record_beneath_to_wait (record_beneath_to_wait_ops, + ptid, status, options); } + else + { + /* This is not a single step. */ + while (1) + { + if (record_thread_wait (ptid, status, + options, &ret_ptid)) + break; - return ret; - } + if (record_resume_step) + break; + + /* There is not a breakpoint, and gdb is not + stepping, therefore gdb will not stop. + Therefore we will not return to gdb. + Record the insn and resume. */ + if (!do_record_message (TARGET_SIGNAL_0)) + { + ret_ptid = inferior_ptid; + status->kind = TARGET_WAITKIND_STOPPED; + status->value.sig = TARGET_SIGNAL_0; + break; + } + + record_beneath_to_resume (record_beneath_to_resume_ops, + record_resume_ptid, record_step, + TARGET_SIGNAL_0); + } + } + } } else { - struct regcache *regcache = get_current_regcache (); - struct gdbarch *gdbarch = get_regcache_arch (regcache); + /* Replay mode. */ + struct regcache *regcache; + struct gdbarch *gdbarch; int continue_flag = 1; int first_record_end = 1; - struct cleanup *old_cleanups = make_cleanup (record_wait_cleanups, 0); + struct cleanup *old_cleanups = + make_cleanup (record_wait_replay_cleanups, 0); CORE_ADDR tmp_pc; + ptid_t old_inferior_ptid = inferior_ptid; + + if (!ptid_equal (record_prev_ptid, null_ptid)) + inferior_ptid = record_prev_ptid; + regcache = get_current_regcache (); + gdbarch = get_regcache_arch (regcache); status->kind = TARGET_WAITKIND_STOPPED; @@ -1129,8 +1494,6 @@ record_wait (struct target_ops *ops, } } - record_get_sig = 0; - signal (SIGINT, record_sig_handler); /* If GDB is in terminal_inferior mode, it will not get the signal. And in GDB replay mode, GDB doesn't need to be in terminal_inferior mode, because inferior will not executed. @@ -1161,7 +1524,7 @@ record_wait (struct target_ops *ops, break; } - record_exec_insn (regcache, gdbarch, record_list); + record_exec_insn (®cache, &gdbarch, record_list); if (record_list->type == record_end) { @@ -1184,12 +1547,13 @@ record_wait (struct target_ops *ops, In EXEC_FORWARD mode, this is the record_end of current instruction. */ /* step */ - if (record_resume_step) + if (record_resume_step + && ptid_equal (inferior_ptid, old_inferior_ptid)) { if (record_debug > 1) fprintf_unfiltered (gdb_stdlog, "Process record: step.\n"); - continue_flag = 0; + continue_flag = 0; } /* check breakpoint */ @@ -1233,22 +1597,23 @@ record_wait (struct target_ops *ops, } while (continue_flag); - signal (SIGINT, handle_sigint); - replay_out: - if (record_get_sig) - status->value.sig = TARGET_SIGNAL_INT; - else if (record_list->u.end.sigval != TARGET_SIGNAL_0) + if (record_list->u.end.sigval != TARGET_SIGNAL_0) /* FIXME: better way to check */ status->value.sig = record_list->u.end.sigval; else status->value.sig = TARGET_SIGNAL_TRAP; + ret_ptid = inferior_ptid; discard_cleanups (old_cleanups); } + if (record_get_sig) + status->value.sig = TARGET_SIGNAL_INT; + + do_cleanups (signal_cleanups); do_cleanups (set_cleanups); - return inferior_ptid; + return ret_ptid; } /* "to_disconnect" method for process record target. */ @@ -1311,6 +1676,12 @@ record_registers_change (struct regcache record_arch_list_head = NULL; record_arch_list_tail = NULL; + if (!ptid_equal (record_prev_ptid, inferior_ptid)) + { + record_arch_list_add_ptid (&record_prev_ptid); + record_prev_ptid = inferior_ptid; + } + if (regnum < 0) { int i; @@ -1433,6 +1804,11 @@ record_xfer_partial (struct target_ops * /* Record registers change to list as an instruction. */ record_arch_list_head = NULL; record_arch_list_tail = NULL; + if (!ptid_equal (record_prev_ptid, inferior_ptid)) + { + record_arch_list_add_ptid (&record_prev_ptid); + record_prev_ptid = inferior_ptid; + } if (record_arch_list_add_mem (offset, len)) { record_list_release (record_arch_list_tail); @@ -2240,7 +2616,7 @@ cmd_record_save (char *args, int from_tt if (record_list == &record_first) break; - record_exec_insn (regcache, gdbarch, record_list); + record_exec_insn (®cache, &gdbarch, record_list); if (record_list->prev) record_list = record_list->prev; @@ -2367,7 +2743,7 @@ cmd_record_save (char *args, int from_tt } /* Execute entry. */ - record_exec_insn (regcache, gdbarch, record_list); + record_exec_insn (®cache, &gdbarch, record_list); if (record_list->next) record_list = record_list->next; @@ -2382,7 +2758,7 @@ cmd_record_save (char *args, int from_tt if (record_list == cur_record_list) break; - record_exec_insn (regcache, gdbarch, record_list); + record_exec_insn (®cache, &gdbarch, record_list); if (record_list->prev) record_list = record_list->prev; --- a/record.h +++ b/record.h @@ -23,6 +23,7 @@ #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);