--- record.c | 283 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- record.h | 6 + 2 files changed, 277 insertions(+), 12 deletions(-) --- a/record.c +++ b/record.c @@ -18,6 +18,7 @@ along with this program. If not, see . */ #include "defs.h" +#include "arch-utils.h" #include "gdbcmd.h" #include "regcache.h" #include "gdbthread.h" @@ -78,6 +79,16 @@ struct record_entry } u; }; +struct record_skip_entry +{ + int id; + struct record_skip_entry *prev; + const char *name; + CORE_ADDR addr; + struct breakpoint *bp; + int (*record) (struct regcache *regcache); +}; + /* This is the debug switch for process record. */ int record_debug = 0; @@ -92,6 +103,10 @@ static int record_stop_at_limit = 1; static int record_insn_max_num = DEFAULT_RECORD_INSN_MAX_NUM; static int record_insn_num = 0; +/* Skip record if true. */ +static int record_skip = 0; +static struct record_skip_entry *record_skip_list = NULL; + /* The target_ops of process record. */ static struct target_ops record_ops; @@ -498,10 +513,14 @@ record_open (char *name, int from_tty) push_target (&record_ops); + if (gdbarch_process_record_open_p (target_gdbarch)) + gdbarch_process_record_open (target_gdbarch); + /* Reset */ record_insn_num = 0; record_list = &record_first; record_list->next = NULL; + record_skip = 0; } static void @@ -511,6 +530,19 @@ record_close (int quitting) fprintf_unfiltered (gdb_stdlog, "Process record: record_close\n"); record_list_release (record_list); + + /* Release record_skip_list. */ + if (record_skip_list) + { + struct record_skip_entry *entry; + for (entry = record_skip_list->prev; entry; entry = entry->prev) + { + xfree (record_skip_list); + record_skip_list = entry; + } + record_skip_list = NULL; + } + remove_record_skip_breakpoint (0, 1); } static int record_resume_step = 0; @@ -526,17 +558,24 @@ record_resume (struct target_ops *ops, p if (!RECORD_IS_REPLAY) { - if (do_record_message (get_current_regcache ())) - { - record_resume_error = 0; - } + if (record_skip) + record_beneath_to_resume (record_beneath_to_resume_ops, ptid, step, + siggnal); else { - record_resume_error = 1; - return; + if (do_record_message (get_current_regcache ())) + { + record_resume_error = 0; + } + else + { + record_resume_error = 1; + return; + } + record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1, + siggnal); } - record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1, - siggnal); + } } @@ -594,9 +633,10 @@ record_wait (struct target_ops *ops, return inferior_ptid; } - if (record_resume_step) + if (record_resume_step || record_skip) { - /* This is a single step. */ + /* This is a single step or record skip function is + enable. */ return record_beneath_to_wait (record_beneath_to_wait_ops, ptid, status, 0); } @@ -1137,6 +1177,132 @@ init_record_ops (void) record_ops.to_magic = OPS_MAGIC; } +void +record_skip_entry_create (struct gdbarch *gdbarch, const char *name, + int (*record) (struct regcache *regcache)) +{ + struct minimal_symbol *msym; + struct record_skip_entry *entry; + + gdb_assert (record != NULL); + + /* Get msym. */ + msym = lookup_minimal_symbol (name, NULL, NULL); + if (!msym) + { + warning (_("Process record: failed to create record skip " + "breakpoint for '%s'."), name); + return; + } + + /* Alloc entry. */ + entry = xmalloc (sizeof (struct record_skip_entry)); + + /* Set entry->addr. */ + entry->addr = SYMBOL_VALUE_ADDRESS (msym); + + /* Set entry->bp. */ + entry->bp = create_record_skip_breakpoint (gdbarch, + entry->addr, + bp_record_skip_start); + + /* Set entry->name. */ + entry->name = name; + + /* Set entry->record. */ + entry->record = record; + + /* Set entry->id and add entry to record_skip_list. */ + if (record_skip_list) + { + entry->id = record_skip_list->id + 1; + entry->prev = record_skip_list; + } + else + { + entry->id = 1; + entry->prev = NULL; + } + record_skip_list = entry; +} + +void +record_skip_breakpoint_handler (struct frame_info *frame, ptid_t ptid, + int skip_type) +{ + if (RECORD_IS_REPLAY) + return; + + gdb_assert (skip_type == bp_record_skip_start + || skip_type == bp_record_skip_stop); + + if (skip_type == bp_record_skip_start) + { + /* Skip start. */ + struct regcache *regcache = get_thread_regcache (ptid); + struct gdbarch *gdbarch = get_regcache_arch (regcache); + CORE_ADDR caller_pc; + struct record_skip_entry *entry; + struct cleanup *old_cleanups = make_cleanup (record_message_cleanups, 0); + + gdb_assert (record_skip == 0); + + /* Get record_skip_entry. */ + for (entry = record_skip_list; entry; entry = entry->prev) + if (entry->addr == stop_pc) + break; + if (entry == NULL) + { + warning (_("Failed to find record skip entry in address %s."), + paddress (gdbarch, stop_pc)); + return; + } + + /* Record the behavior of this function. */ + record_arch_list_head = NULL; + record_arch_list_tail = NULL; + record_check_insn_num (0); + if (entry->record (regcache)) + { + warning (_("Process record: failed to record execution log.")); + record_list_release (record_arch_list_tail); + return; + } + gdb_assert (record_arch_list_head != NULL); + 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 (); + else + record_insn_num++; + + /* Insert a bp_record_skip_stop to stop the skip. */ + caller_pc = gdbarch_addr_bits_remove (gdbarch, + frame_unwind_caller_pc(frame)); + create_record_skip_breakpoint (gdbarch, caller_pc, bp_record_skip_stop); + + record_skip = 1; + + if (record_debug) + fprintf_unfiltered (gdb_stdlog, + _("Process record skip start in '%s'.\n"), + entry->name); + } + else + { + /* Skip stop. */ + /* Remove this bp_record_skip_stop. */ + remove_record_skip_breakpoint (stop_pc, 0); + + record_skip = 0; + + if (record_debug) + fprintf_unfiltered (gdb_stdlog, + _("Process record skip stop.\n")); + } +} + static void show_record_debug (struct ui_file *file, int from_tty, struct cmd_list_element *c, const char *value) @@ -1191,6 +1357,89 @@ cmd_record_stop (char *args, int from_tt printf_unfiltered (_("Process record is not started.\n")); } +static void +cmd_record_skip (char *args, int from_tty) +{ + if (record_skip) + printf_unfiltered (_("Process record is skip.\n")); + else + printf_unfiltered (_("Process record is not skip.\n")); + + if (record_skip_list) + { + int i; + struct record_skip_entry *entry; + + printf_unfiltered (_("Id\tEnb\tAddress\t\tWhat\n")); + for (i = 1; 1; i++) + { + for (entry = record_skip_list; entry; entry = entry->prev) + if (entry->id == i) + break; + if (entry) + printf_unfiltered (_("%d\t%s\t%s\t%s\n"), entry->id, + (entry->bp->enable_state == bp_enabled) + ? "y" : "n", + paddress (get_current_arch (), entry->addr), + entry->name); + else + break; + } + } + else + printf_unfiltered (_("No record skip.\n")); +} + +static void +cmd_record_skip_enable (char *args, int from_tty) +{ + struct record_skip_entry *entry; + + if (args && *args) + { + int id = atoi (args); + + for (entry = record_skip_list; entry; entry = entry->prev) + if (entry->id == id) + break; + + if (entry) + enable_breakpoint (entry->bp); + else + printf_unfiltered (_("No record skip id %s.\n"), args); + } + else + { + for (entry = record_skip_list; entry; entry = entry->prev) + enable_breakpoint (entry->bp); + } +} + +static void +cmd_record_skip_disable (char *args, int from_tty) +{ + struct record_skip_entry *entry; + + if (args && *args) + { + int id = atoi (args); + + for (entry = record_skip_list; entry; entry = entry->prev) + if (entry->id == id) + break; + + if (entry) + disable_breakpoint (entry->bp); + else + printf_unfiltered (_("No record skip id %s.\n"), args); + } + else + { + for (entry = record_skip_list; entry; entry = entry->prev) + disable_breakpoint (entry->bp); + } +} + /* Set upper limit of record log size. */ static void @@ -1218,7 +1467,8 @@ show_record_insn_number (char *ignore, i } static struct cmd_list_element *record_cmdlist, *set_record_cmdlist, - *show_record_cmdlist, *info_record_cmdlist; + *show_record_cmdlist, *info_record_cmdlist, + *record_skip_cmdlist; static void set_record_command (char *args, int from_tty) @@ -1276,7 +1526,6 @@ _initialize_record (void) "info record ", 0, &infolist); add_alias_cmd ("rec", "record", class_obscure, 1, &infolist); - add_cmd ("delete", class_obscure, cmd_record_delete, _("Delete the rest of execution log and start recording it anew."), &record_cmdlist); @@ -1288,6 +1537,16 @@ _initialize_record (void) &record_cmdlist); add_alias_cmd ("s", "stop", class_obscure, 1, &record_cmdlist); + add_prefix_cmd ("skip", class_obscure, cmd_record_skip, + _("Show the record skip status."), + &record_skip_cmdlist, "record ", 0, &record_cmdlist); + add_cmd ("enable", class_obscure, cmd_record_skip_enable, + _("Enable some record skip."), + &record_skip_cmdlist); + add_cmd ("disable", class_obscure, cmd_record_skip_disable, + _("Disable some record skip."), + &record_skip_cmdlist); + /* Record instructions number limit command. */ add_setshow_boolean_cmd ("stop-at-limit", no_class, &record_stop_at_limit, _("\ --- a/record.h +++ b/record.h @@ -28,5 +28,11 @@ extern int record_arch_list_add_reg (str 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 void record_skip_entry_create (struct gdbarch *gdbarch, + const char *name, + int (*record) (struct regcache *regcache)); +extern void record_skip_breakpoint_handler (struct frame_info *frame, + ptid_t ptid, + int skip_type); #endif /* _RECORD_H_ */