--- record.c | 734 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- record.h | 1 2 files changed, 631 insertions(+), 104 deletions(-) --- 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 (current_target.beneath->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,19 @@ 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_new_thread_observer = NULL; +static struct observer *record_thread_exit_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, @@ -288,6 +303,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 * @@ -324,6 +360,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; @@ -331,6 +370,16 @@ record_entry_release (struct record_entr return type; } +/* Remove one record entry from record_list. */ + +static void +record_entry_remove_from_list (struct record_entry *rec) +{ + if (rec->next) + rec->next->prev = rec->prev; + rec->prev->next = rec->next; +} + /* Free all record entries in list pointed to by REC. */ static void @@ -512,6 +561,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 @@ -573,7 +640,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; }; @@ -582,15 +648,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. @@ -613,20 +685,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) @@ -649,12 +746,10 @@ record_message (void *args) } static int -do_record_message (struct regcache *regcache, - enum target_signal signal, int catch) +do_record_message (enum target_signal signal, int catch) { struct record_message_args args; - args.regcache = regcache; args.signal = signal; if (catch) @@ -688,9 +783,14 @@ static int record_hw_watchpoint = 0; 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 address_space **aspace_p, struct record_entry *entry) { + struct regcache *regcache = *regcache_p; + struct gdbarch *gdbarch = *gdbarch_p; + switch (entry->type) { case record_reg: /* reg */ @@ -769,6 +869,110 @@ 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); + *aspace_p = get_regcache_aspace (*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) +{ + if (thread->state_ != THREAD_EXITED) + 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 void +record_thread_exit_handler (struct thread_info *tp, int silent) +{ + struct record_entry *rec; + ptid_t cur_ptid = record_prev_ptid; + + if (!record_first.next) + return; + + tp->record_is_waiting = 0; + + if (tp->state_ != THREAD_EXITED) + { + gdb_assert (record_thread_number > 0); + record_thread_number --; + } + + /* Delete all the record_reg and record_ptid for tp->ptid + from record list. */ + for (rec = record_list; rec != &record_first;) + { + struct record_entry *tmp = rec; + rec = rec->prev; + + switch (tmp->type) { + case record_reg: + /* If this record_reg is for tp->ptid, delte it. */ + if (ptid_equal (cur_ptid, tp->ptid)) + { + record_entry_remove_from_list (tmp); + record_entry_release (tmp); + } + break; + case record_ptid: + /* If this record_ptid is for tp->ptid, delte it. */ + cur_ptid = tmp->u.ptid; + if (ptid_equal (record_prev_ptid, tp->ptid)) + { + record_prev_ptid = cur_ptid; + record_entry_remove_from_list (tmp); + record_entry_release (tmp); + } + else if (ptid_equal (cur_ptid, tp->ptid)) + { + record_entry_remove_from_list (tmp); + record_entry_release (tmp); + } + break; + } } } @@ -865,6 +1069,9 @@ 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_new_thread_observer = observer_attach_new_thread (record_new_thread_handler); + record_thread_exit_observer = observer_attach_thread_exit (record_thread_exit_handler); } /* "to_open" target method. Open the process record target. */ @@ -934,6 +1141,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; @@ -953,6 +1161,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. */ @@ -965,6 +1175,17 @@ record_close (int quitting) if (record_debug) fprintf_unfiltered (gdb_stdlog, "Process record: record_close\n"); + if (record_new_thread_observer) + { + observer_detach_new_thread (record_new_thread_observer); + record_new_thread_observer = NULL; + } + if (record_thread_exit_observer) + { + observer_detach_thread_exit (record_thread_exit_observer); + record_thread_exit_observer = NULL; + } + record_list_release (record_list); /* Release record_core_regbuf. */ @@ -987,6 +1208,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. */ @@ -998,10 +1221,201 @@ record_resume (struct target_ops *ops, p if (!RECORD_IS_REPLAY) { - do_record_message (get_current_regcache (), signal, 0); - record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1, - signal); + record_resume_ptid = ptid; + if (record_thread_number > 1 && !ptid_equal (ptid, inferior_ptid)) + { + record_resume_signal = signal; + } + else + { + do_record_message (signal, 0); + 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_stop_callback (struct thread_info *tp, void *data) +{ + if (tp->state_ != THREAD_EXITED && tp->record_is_waiting) + target_stop (tp->ptid); + + 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; + struct address_space *aspace; + CORE_ADDR decr_pc_after_break; + + /* Yes -- this is likely our single-step finishing, + but check if there's any reason the core would be + interested in the event. */ + + registers_changed (); + regcache = get_current_regcache (); + gdbarch = get_regcache_arch (regcache); + tmp_pc = regcache_read_pc (regcache); + aspace = get_regcache_aspace (regcache); + decr_pc_after_break = gdbarch_decr_pc_after_break (gdbarch); + + /* To check if this is breakpoint need the right pc, if this is not + a single step. So we need adjust tmp_pc to make it right. */ + if (!record_step) + tmp_pc -= decr_pc_after_break; + + if (target_stopped_by_watchpoint ()) + { + /* Always interested in watchpoints. */ + return 1; + } + else if (breakpoint_inserted_here_p (aspace, tmp_pc)) + { + /* There is a breakpoint here. Let the core handle it. */ + if (record_step + && software_breakpoint_inserted_here_p (aspace, tmp_pc)) + { + 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 (tp->state_ == THREAD_EXITED) + return 0; + + 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, 1)) + { + 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. */ + iterate_over_threads (record_thread_stop_callback, NULL); + 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; @@ -1023,7 +1437,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) { @@ -1034,6 +1454,17 @@ record_wait_cleanups (void *ignore) record_list = record_list->prev; } +static void +record_wait_mthread_cleanups (void *ignore) +{ + non_stop = 1; + + /* Stop the threads that still running. */ + iterate_over_threads (record_thread_stop_callback, NULL); + + non_stop = 0; +} + /* "to_wait" target method for process record target. In record mode, the target is always run in singlestep mode @@ -1052,7 +1483,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, @@ -1060,93 +1494,112 @@ 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; + /* 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); - while (1) - { - ret = record_beneath_to_wait (record_beneath_to_wait_ops, - ptid, status, options); + 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); - /* Is this a SIGTRAP? */ - if (status->kind == TARGET_WAITKIND_STOPPED - && status->value.sig == TARGET_SIGNAL_TRAP) - { - struct regcache *regcache; - struct address_space *aspace; + 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; + } + } - /* Yes -- this is likely our single-step finishing, - but check if there's any reason the core would be - interested in the event. */ + do_cleanups (mthread_cleanups); - registers_changed (); - regcache = get_current_regcache (); - tmp_pc = regcache_read_pc (regcache); - aspace = get_regcache_aspace (regcache); + if (tp && tp->state_ != THREAD_EXITED) + { + inferior_ptid = tp->ptid; + } + ret_ptid = inferior_ptid; + } + else + { + /* Single-thread record. */ + if (record_thread_number > 1) + ptid = record_resume_ptid; - if (target_stopped_by_watchpoint ()) - { - /* Always interested in watchpoints. */ - } - else if (breakpoint_inserted_here_p (aspace, tmp_pc)) - { - /* There is a breakpoint here. Let the core - handle it. */ - if (software_breakpoint_inserted_here_p (aspace, tmp_pc)) - { - 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 - { - /* This must be a single-step trap. Record the - insn and issue another step. */ - if (!do_record_message (regcache, TARGET_SIGNAL_0, 1)) - { - status->kind = TARGET_WAITKIND_STOPPED; - status->value.sig = TARGET_SIGNAL_0; - break; - } + 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; - record_beneath_to_resume (record_beneath_to_resume_ops, - ptid, 1, - TARGET_SIGNAL_0); - continue; - } - } + if (record_resume_step) + break; - /* The inferior is broken by a breakpoint or a signal. */ - 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, 1)) + { + ret_ptid = inferior_ptid; + status->kind = TARGET_WAITKIND_STOPPED; + status->value.sig = TARGET_SIGNAL_0; + break; + } - return ret; - } + 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); - struct address_space *aspace = get_regcache_aspace (regcache); + /* Replay mode. */ + struct regcache *regcache; + struct gdbarch *gdbarch; + struct address_space *aspace; 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); + aspace = get_regcache_aspace (regcache); record_hw_watchpoint = 0; status->kind = TARGET_WAITKIND_STOPPED; @@ -1173,8 +1626,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. @@ -1205,7 +1656,7 @@ record_wait (struct target_ops *ops, break; } - record_exec_insn (regcache, gdbarch, record_list); + record_exec_insn (®cache, &gdbarch, &aspace, record_list); if (record_list->type == record_end) { @@ -1228,19 +1679,20 @@ 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 */ tmp_pc = regcache_read_pc (regcache); if (breakpoint_inserted_here_p (aspace, tmp_pc)) { - int decr_pc_after_break + CORE_ADDR decr_pc_after_break = gdbarch_decr_pc_after_break (gdbarch); if (record_debug) @@ -1288,22 +1740,23 @@ Process record: hit hw watchpoint.\n"); } 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; } static int @@ -1384,6 +1837,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; @@ -1506,6 +1965,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); @@ -2061,6 +2525,17 @@ info_record_command (char *args, int fro 4 bytes: memory length (network byte order). 8 bytes: memory address (network byte order). n bytes: memory value (n == memory length). + record_mem: + 1 byte: record type (record_mem, see enum record_type). + 4 bytes: memory length (network byte order). + 8 bytes: memory address (network byte order). + n bytes: memory value (n == memory length). + record_ptid: + 1 byte: record type (record_ptid, see enum record_type). + 8 bytes: process id (network byte order). + It must same with CORELOW_PID (1). + 8 bytes: lightweight process id (network byte order). + 8 bytes: thread id (network byte order). */ @@ -2222,6 +2697,32 @@ record_restore (void) rec->u.mem.len); break; + case record_ptid: /* ptid */ + rec = record_ptid_alloc (); + + /* Get Process id. */ + bfdcore_read (core_bfd, osec, &addr, + sizeof (addr), &bfd_offset); + rec->u.ptid.pid = (int) netorder64 (addr); + + /* Get Lightweight process id. */ + bfdcore_read (core_bfd, osec, &addr, + sizeof (addr), &bfd_offset); + rec->u.ptid.lwp = (long) netorder64 (addr); + + /* Get Thread id. */ + bfdcore_read (core_bfd, osec, &addr, + sizeof (addr), &bfd_offset); + rec->u.ptid.tid = (long) netorder64 (addr); + + if (record_debug) + fprintf_unfiltered (gdb_stdlog, "\ + Reading ptid %s (1 + 8 + 8 + 8 bytes), offset == %s\n", + target_pid_to_str (record_list->u.ptid), + paddress (get_current_arch (), + bfd_offset)); + break; + case record_end: /* end */ rec = record_end_alloc (); record_insn_num ++; @@ -2327,6 +2828,7 @@ cmd_record_save (char *args, int from_tt uint32_t magic; struct regcache *regcache; struct gdbarch *gdbarch; + struct address_space *aspace; struct cleanup *old_cleanups; struct cleanup *set_cleanups; bfd *obfd; @@ -2363,6 +2865,7 @@ cmd_record_save (char *args, int from_tt /* Get the values of regcache and gdbarch. */ regcache = get_current_regcache (); gdbarch = get_regcache_arch (regcache); + aspace = get_regcache_aspace (regcache); /* Disable the GDB operation record. */ set_cleanups = record_gdb_operation_disable_set (); @@ -2374,7 +2877,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, &aspace, record_list); if (record_list->prev) record_list = record_list->prev; @@ -2395,6 +2898,9 @@ cmd_record_save (char *args, int from_tt case record_mem: save_size += 1 + 4 + 8 + record_list->u.mem.len; break; + case record_ptid: + save_size += 1 + 8 + 8 + 8; + break; } /* Make the new bfd section. */ @@ -2481,6 +2987,25 @@ cmd_record_save (char *args, int from_tt record_list->u.mem.len, &bfd_offset); break; + case record_ptid: /* ptid */ + if (record_debug) + fprintf_unfiltered (gdb_stdlog, "\ + Writing ptid %s (1 plus 8 plus 8 plus 8 bytes)\n", + target_pid_to_str (record_list->u.ptid)); + + /* Write Process id. */ + addr = netorder64 ((uint64_t) 1); + bfdcore_write (obfd, osec, &addr, sizeof (addr), &bfd_offset); + + /* Write Lightweight process id. */ + addr = netorder64 ((uint64_t) record_list->u.ptid.lwp); + bfdcore_write (obfd, osec, &addr, sizeof (addr), &bfd_offset); + + /* Write Thread id. */ + addr = netorder64 ((uint64_t) record_list->u.ptid.tid); + bfdcore_write (obfd, osec, &addr, sizeof (addr), &bfd_offset); + break; + case record_end: if (record_debug) fprintf_unfiltered (gdb_stdlog, "\ @@ -2501,7 +3026,7 @@ cmd_record_save (char *args, int from_tt } /* Execute entry. */ - record_exec_insn (regcache, gdbarch, record_list); + record_exec_insn (®cache, &gdbarch, &aspace, record_list); if (record_list->next) record_list = record_list->next; @@ -2516,7 +3041,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, &aspace, record_list); if (record_list->prev) record_list = record_list->prev; @@ -2542,6 +3067,7 @@ record_goto_insn (struct record_entry *e struct cleanup *set_cleanups = record_gdb_operation_disable_set (); struct regcache *regcache = get_current_regcache (); struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct address_space *aspace = get_regcache_aspace (regcache); /* Assume everything is valid: we will hit the entry, and we will not hit the end of the recording. */ @@ -2551,7 +3077,7 @@ record_goto_insn (struct record_entry *e do { - record_exec_insn (regcache, gdbarch, record_list); + record_exec_insn (®cache, &gdbarch, &aspace, record_list); if (dir == EXEC_REVERSE) record_list = record_list->prev; else --- 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);