--- record.c | 806 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 715 insertions(+), 91 deletions(-) --- a/record.c +++ b/record.c @@ -23,14 +23,22 @@ #include "gdbthread.h" #include "event-top.h" #include "exceptions.h" +#include "completer.h" +#include "arch-utils.h" +#include "gdbcore.h" +#include "exec.h" #include "record.h" +#include #include +#include #define DEFAULT_RECORD_INSN_MAX_NUM 200000 #define RECORD_IS_REPLAY \ - (record_list->next || execution_direction == EXEC_REVERSE) + (record_list->next || execution_direction == EXEC_REVERSE || record_core) + +#define RECORD_FILE_MAGIC htonl(0x20090726) /* These are the core struct of record function. @@ -78,9 +86,23 @@ struct record_entry } u; }; +struct record_core_buf_entry +{ + struct record_core_buf_entry *prev; + struct target_section *p; + bfd_byte *buf; +}; + /* This is the debug switch for process record. */ int record_debug = 0; +/* Record with core target. */ +static int record_core = 0; +static gdb_byte *record_core_regbuf; +static struct target_section *record_core_start; +static struct target_section *record_core_end; +static struct record_core_buf_entry *record_core_buf_list = NULL; + /* These list is for execution log. */ static struct record_entry record_first; static struct record_entry *record_list = &record_first; @@ -103,6 +125,14 @@ static struct target_ops *record_beneath static ptid_t (*record_beneath_to_wait) (struct target_ops *, ptid_t, struct target_waitstatus *, int); +static struct target_ops *record_beneath_to_fetch_registers_ops; +static void (*record_beneath_to_fetch_registers) (struct target_ops *, + struct regcache *, + int regno); +static struct target_ops *record_beneath_to_store_registers_ops; +static void (*record_beneath_to_store_registers) (struct target_ops *, + struct regcache *, + int regno); static struct target_ops *record_beneath_to_store_registers_ops; static void (*record_beneath_to_store_registers) (struct target_ops *, struct regcache *, @@ -119,6 +149,9 @@ static int (*record_beneath_to_insert_br struct bp_target_info *); static int (*record_beneath_to_remove_breakpoint) (struct gdbarch *, struct bp_target_info *); +static struct target_ops *record_beneath_to_has_execution_ops; +static int (*record_beneath_to_has_execution) (struct target_ops *ops); +static void (*record_beneath_to_prepare_to_store) (struct regcache *regcache); static void record_list_release (struct record_entry *rec) @@ -169,7 +202,7 @@ record_list_release_next (void) } static void -record_list_release_first (void) +record_list_release_first_insn (void) { struct record_entry *tmp = NULL; enum record_type type; @@ -340,30 +373,30 @@ record_check_insn_num (int set_terminal) if (q) record_stop_at_limit = 0; else - error (_("Process record: inferior program stopped.")); + error (_("Process record: stoped by user.")); } } } } +static void +record_arch_list_cleanups (void *ignore) +{ + record_list_release (record_arch_list_tail); +} + /* Before inferior step (when GDB record the running message, inferior only can step), GDB will call this function to record the values to record_list. This function will call gdbarch_process_record to record the running message of inferior and set them to record_arch_list, and add it to record_list. */ -static void -record_message_cleanups (void *ignore) -{ - record_list_release (record_arch_list_tail); -} - static int record_message (void *args) { int ret; struct regcache *regcache = args; - struct cleanup *old_cleanups = make_cleanup (record_message_cleanups, 0); + struct cleanup *old_cleanups = make_cleanup (record_arch_list_cleanups, 0); record_arch_list_head = NULL; record_arch_list_tail = NULL; @@ -386,7 +419,7 @@ record_message (void *args) record_list = record_arch_list_tail; if (record_insn_num == record_insn_max_num && record_insn_max_num) - record_list_release_first (); + record_list_release_first_insn (); else record_insn_num++; @@ -416,6 +449,91 @@ record_gdb_operation_disable_set (void) return old_cleanups; } +static inline void +record_exec_entry (struct regcache *regcache, struct gdbarch *gdbarch, + struct record_entry *entry) +{ + switch (entry->type) + { + case record_reg: /* reg */ + { + gdb_byte reg[MAX_REGISTER_SIZE]; + + if (record_debug > 1) + fprintf_unfiltered (gdb_stdlog, + "Process record: record_reg %s to " + "inferior num = %d.\n", + host_address_to_string (entry), + entry->u.reg.num); + + regcache_cooked_read (regcache, entry->u.reg.num, reg); + regcache_cooked_write (regcache, entry->u.reg.num, entry->u.reg.val); + memcpy (entry->u.reg.val, reg, MAX_REGISTER_SIZE); + } + break; + + case record_mem: /* mem */ + { + if (!record_list->u.mem.mem_entry_not_accessible) + { + gdb_byte *mem = alloca (entry->u.mem.len); + + if (record_debug > 1) + fprintf_unfiltered (gdb_stdlog, + "Process record: record_mem %s to " + "inferior addr = %s len = %d.\n", + host_address_to_string (entry), + paddress (gdbarch, entry->u.mem.addr), + record_list->u.mem.len); + + if (target_read_memory (entry->u.mem.addr, mem, entry->u.mem.len)) + { + if ((execution_direction == EXEC_REVERSE && !record_core) + || (execution_direction != EXEC_REVERSE && record_core)) + { + record_list->u.mem.mem_entry_not_accessible = 1; + if (record_debug) + warning (_("Process record: error reading memory at " + "addr = %s len = %d."), + paddress (gdbarch, entry->u.mem.addr), + entry->u.mem.len); + } + else + error (_("Process record: error reading memory at " + "addr = %s len = %d."), + paddress (gdbarch, entry->u.mem.addr), + entry->u.mem.len); + } + else + { + if (target_write_memory (entry->u.mem.addr, entry->u.mem.val, + entry->u.mem.len)) + { + if ((execution_direction == EXEC_REVERSE && !record_core) + || (execution_direction != EXEC_REVERSE && record_core)) + { + record_list->u.mem.mem_entry_not_accessible = 1; + if (record_debug) + warning (_("Process record: error writing memory at " + "addr = %s len = %d."), + paddress (gdbarch, entry->u.mem.addr), + entry->u.mem.len); + } + else + error (_("Process record: error writing memory at " + "addr = %s len = %d."), + paddress (gdbarch, entry->u.mem.addr), + entry->u.mem.len); + } + } + + memcpy (entry->u.mem.val, mem, entry->u.mem.len); + } + } + break; + } +} + static void record_open (char *name, int from_tty) { @@ -424,8 +542,13 @@ record_open (char *name, int from_tty) if (record_debug) fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n"); + if (!strcmp (current_target.to_shortname, "core")) + record_core = 1; + else + record_core = 0; + /* check exec */ - if (!target_has_execution) + if (!target_has_execution && !record_core) error (_("Process record: the program is not being run.")); if (non_stop) error (_("Process record target can't debug inferior in non-stop mode " @@ -454,6 +577,8 @@ record_open (char *name, int from_tty) record_beneath_to_xfer_partial = NULL; record_beneath_to_insert_breakpoint = NULL; record_beneath_to_remove_breakpoint = NULL; + record_beneath_to_has_execution = NULL; + record_beneath_to_prepare_to_store = NULL; /* Set the beneath function pointers. */ for (t = current_target.beneath; t != NULL; t = t->beneath) @@ -468,6 +593,11 @@ record_open (char *name, int from_tty) record_beneath_to_wait = t->to_wait; record_beneath_to_wait_ops = t; } + if (!record_beneath_to_fetch_registers) + { + record_beneath_to_fetch_registers = t->to_fetch_registers; + record_beneath_to_fetch_registers_ops = t; + } if (!record_beneath_to_store_registers) { record_beneath_to_store_registers = t->to_store_registers; @@ -482,19 +612,51 @@ record_open (char *name, int from_tty) record_beneath_to_insert_breakpoint = t->to_insert_breakpoint; if (!record_beneath_to_remove_breakpoint) record_beneath_to_remove_breakpoint = t->to_remove_breakpoint; + if (!record_beneath_to_has_execution) + { + record_beneath_to_has_execution_ops = t; + record_beneath_to_has_execution = t->to_has_execution; + } + if (!record_beneath_to_prepare_to_store) + record_beneath_to_prepare_to_store = t->to_prepare_to_store; } - if (!record_beneath_to_resume) + if (!record_beneath_to_resume && !record_core) error (_("Process record can't get to_resume.")); - if (!record_beneath_to_wait) + if (!record_beneath_to_wait && !record_core) error (_("Process record can't get to_wait.")); - if (!record_beneath_to_store_registers) + if (!record_beneath_to_fetch_registers) + error (_("Process record can't get to_fetch_registers.")); + if (!record_beneath_to_store_registers && !record_core) error (_("Process record can't get to_store_registers.")); if (!record_beneath_to_xfer_partial) error (_("Process record can't get to_xfer_partial.")); - if (!record_beneath_to_insert_breakpoint) + if (!record_beneath_to_insert_breakpoint && !record_core) error (_("Process record can't get to_insert_breakpoint.")); - if (!record_beneath_to_remove_breakpoint) + if (!record_beneath_to_remove_breakpoint && !record_core) error (_("Process record can't get to_remove_breakpoint.")); + if (!record_beneath_to_has_execution && !record_core) + error (_("Process record can't get to_has_execution.")); + if (!record_beneath_to_prepare_to_store && !record_core) + error (_("Process record can't get to_prepare_to_store.")); + + if (record_core) + { + /* Get record_core_regbuf. */ + struct regcache *regcache = get_current_regcache (); + int regnum = gdbarch_num_regs (get_regcache_arch (regcache)); + int i; + + target_fetch_registers (regcache, -1); + record_core_regbuf = xmalloc (MAX_REGISTER_SIZE * regnum); + for (i = 0; i < regnum; i ++) + regcache_raw_collect (regcache, i, + record_core_regbuf + MAX_REGISTER_SIZE * i); + + /* Get record_core_start and record_core_end. */ + if (build_section_table (core_bfd, &record_core_start, &record_core_end)) + error (_("\"%s\": Can't find sections: %s"), + bfd_get_filename (core_bfd), bfd_errmsg (bfd_get_error ())); + } push_target (&record_ops); @@ -507,10 +669,26 @@ record_open (char *name, int from_tty) static void record_close (int quitting) { + struct record_core_buf_entry *entry; + if (record_debug) fprintf_unfiltered (gdb_stdlog, "Process record: record_close\n"); record_list_release (record_list); + + /* Release record_core_regbuf. */ + xfree (record_core_regbuf); + + /* Release record_core_buf_list. */ + if (record_core_buf_list) + { + for (entry = record_core_buf_list->prev; entry; entry = entry->prev) + { + xfree (record_core_buf_list); + record_core_buf_list = entry; + } + record_core_buf_list = NULL; + } } static int record_resume_step = 0; @@ -712,76 +890,9 @@ record_wait (struct target_ops *ops, break; } - /* Set ptid, register and memory according to record_list. */ - if (record_list->type == record_reg) - { - /* reg */ - gdb_byte reg[MAX_REGISTER_SIZE]; - if (record_debug > 1) - fprintf_unfiltered (gdb_stdlog, - "Process record: record_reg %s to " - "inferior num = %d.\n", - host_address_to_string (record_list), - record_list->u.reg.num); - regcache_cooked_read (regcache, record_list->u.reg.num, reg); - regcache_cooked_write (regcache, record_list->u.reg.num, - record_list->u.reg.val); - memcpy (record_list->u.reg.val, reg, MAX_REGISTER_SIZE); - } - else if (record_list->type == record_mem) - { - /* mem */ - /* Nothing to do if the entry is flagged not_accessible. */ - if (!record_list->u.mem.mem_entry_not_accessible) - { - gdb_byte *mem = alloca (record_list->u.mem.len); - if (record_debug > 1) - fprintf_unfiltered (gdb_stdlog, - "Process record: record_mem %s to " - "inferior addr = %s len = %d.\n", - host_address_to_string (record_list), - paddress (gdbarch, - record_list->u.mem.addr), - record_list->u.mem.len); + record_exec_entry (regcache, gdbarch, record_list); - if (target_read_memory (record_list->u.mem.addr, mem, - record_list->u.mem.len)) - { - if (execution_direction != EXEC_REVERSE) - error (_("Process record: error reading memory at " - "addr = %s len = %d."), - paddress (gdbarch, record_list->u.mem.addr), - record_list->u.mem.len); - else - /* Read failed -- - flag entry as not_accessible. */ - record_list->u.mem.mem_entry_not_accessible = 1; - } - else - { - if (target_write_memory (record_list->u.mem.addr, - record_list->u.mem.val, - record_list->u.mem.len)) - { - if (execution_direction != EXEC_REVERSE) - error (_("Process record: error writing memory at " - "addr = %s len = %d."), - paddress (gdbarch, record_list->u.mem.addr), - record_list->u.mem.len); - else - /* Write failed -- - flag entry as not_accessible. */ - record_list->u.mem.mem_entry_not_accessible = 1; - } - else - { - memcpy (record_list->u.mem.val, mem, - record_list->u.mem.len); - } - } - } - } - else + if (record_list->type == record_end) { if (record_debug > 1) fprintf_unfiltered (gdb_stdlog, @@ -901,7 +1012,9 @@ record_kill (struct target_ops *ops) fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n"); unpush_target (&record_ops); - target_kill (); + + if (!record_core) + target_kill (); } /* Record registers change (by user or by GDB) to list as an instruction. */ @@ -945,15 +1058,58 @@ record_registers_change (struct regcache record_list = record_arch_list_tail; if (record_insn_num == record_insn_max_num && record_insn_max_num) - record_list_release_first (); + record_list_release_first_insn (); else record_insn_num++; } static void +record_fetch_registers (struct target_ops *ops, struct regcache *regcache, + int regno) +{ + if (record_core) + { + if (regno < 0) + { + int num = gdbarch_num_regs (get_regcache_arch (regcache)); + int i; + + for (i = 0; i < num; i ++) + regcache_raw_supply (regcache, i, + record_core_regbuf + MAX_REGISTER_SIZE * i); + } + else + regcache_raw_supply (regcache, regno, + record_core_regbuf + MAX_REGISTER_SIZE * regno); + } + else + record_beneath_to_fetch_registers (record_beneath_to_store_registers_ops, + regcache, regno); +} + +static void +record_prepare_to_store (struct regcache *regcache) +{ + if (!record_core) + record_beneath_to_prepare_to_store (regcache); +} + +static void record_store_registers (struct target_ops *ops, struct regcache *regcache, int regno) { + if (record_core) + { + /* Debug with core. */ + if (record_gdb_operation_disable) + regcache_raw_collect (regcache, regno, + record_core_regbuf + MAX_REGISTER_SIZE * regno); + else + error (_("You can't do that without a process to debug.")); + + return; + } + if (!record_gdb_operation_disable) { if (RECORD_IS_REPLAY) @@ -1000,6 +1156,7 @@ record_store_registers (struct target_op record_registers_change (regcache, regno); } + record_beneath_to_store_registers (record_beneath_to_store_registers_ops, regcache, regno); } @@ -1015,7 +1172,7 @@ record_xfer_partial (struct target_ops * { if (!record_gdb_operation_disable && (object == TARGET_OBJECT_MEMORY - || object == TARGET_OBJECT_RAW_MEMORY) && writebuf) + || object == TARGET_OBJECT_RAW_MEMORY) && writebuf && !record_core) { if (RECORD_IS_REPLAY) { @@ -1059,11 +1216,91 @@ record_xfer_partial (struct target_ops * record_list = record_arch_list_tail; if (record_insn_num == record_insn_max_num && record_insn_max_num) - record_list_release_first (); + record_list_release_first_insn (); else record_insn_num++; } + if (record_core && object == TARGET_OBJECT_MEMORY) + { + /* Debug with core. */ + if (record_gdb_operation_disable || !writebuf) + { + struct target_section *p; + for (p = record_core_start; p < record_core_end; p++) + { + if (offset >= p->addr) + { + struct record_core_buf_entry *entry; + + if (offset >= p->endaddr) + continue; + + if (offset + len > p->endaddr) + len = p->endaddr - offset; + + offset -= p->addr; + + /* Read readbuf or write writebuf p, offset, len. */ + /* Check flags. */ + if (p->the_bfd_section->flags & SEC_CONSTRUCTOR + || (p->the_bfd_section->flags & SEC_HAS_CONTENTS) == 0) + { + if (readbuf) + memset (readbuf, 0, len); + return len; + } + /* Get record_core_buf_entry. */ + for (entry = record_core_buf_list; entry; + entry = entry->prev) + if (entry->p == p) + break; + if (writebuf) + { + if (!entry) + { + /* Add a new entry. */ + entry + = (struct record_core_buf_entry *) + xmalloc + (sizeof (struct record_core_buf_entry)); + entry->p = p; + if (!bfd_malloc_and_get_section (p->bfd, + p->the_bfd_section, + &entry->buf)) + { + xfree (entry); + return 0; + } + entry->prev = record_core_buf_list; + record_core_buf_list = entry; + } + + memcpy (entry->buf + offset, writebuf, (size_t) len); + } + else + { + if (!entry) + return record_beneath_to_xfer_partial + (record_beneath_to_xfer_partial_ops, + object, annex, readbuf, writebuf, + offset, len); + + memcpy (readbuf, entry->buf + offset, (size_t) len); + } + + return len; + } + } + + return 0; + } + else + error (_("You can't do that without a process to debug.")); + + return 0; + } + return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops, object, annex, readbuf, writebuf, offset, len); @@ -1113,6 +1350,15 @@ record_can_execute_reverse (void) return 1; } +int +record_has_execution (struct target_ops *ops) +{ + if (record_core) + return 1; + + return record_beneath_to_has_execution (ops); +} + static void init_record_ops (void) { @@ -1129,11 +1375,14 @@ init_record_ops (void) record_ops.to_mourn_inferior = record_mourn_inferior; record_ops.to_kill = record_kill; record_ops.to_create_inferior = find_default_create_inferior; + record_ops.to_fetch_registers = record_fetch_registers; + record_ops.to_prepare_to_store = record_prepare_to_store; record_ops.to_store_registers = record_store_registers; record_ops.to_xfer_partial = record_xfer_partial; record_ops.to_insert_breakpoint = record_insert_breakpoint; record_ops.to_remove_breakpoint = record_remove_breakpoint; record_ops.to_can_execute_reverse = record_can_execute_reverse; + record_ops.to_has_execution = record_has_execution; record_ops.to_stratum = record_stratum; record_ops.to_magic = OPS_MAGIC; } @@ -1154,6 +1403,369 @@ cmd_record_start (char *args, int from_t execute_command ("target record", from_tty); } +static void +cmd_record_fd_cleanups (void *recfdp) +{ + int recfd = *(int *) recfdp; + close (recfd); +} + +static inline void +record_read_dump (char *recfilename, int fildes, void *buf, size_t nbyte) +{ + if (read (fildes, buf, nbyte) != nbyte) + error (_("Failed to read dump of execution records in '%s'."), + recfilename); +} + +static inline void +record_write_dump (char *recfilename, int fildes, const void *buf, + size_t nbyte) +{ + if (write (fildes, buf, nbyte) != nbyte) + error (_("Failed to write dump of execution records to '%s'."), + recfilename); +} + +/* Record log save-file format + Version 1 + + Header: + 4 bytes: magic number htonl(0x20090726). + NOTE: be sure to change whenever this file format changes! + + Records: + record_end: + 1 byte: record type (record_end). + record_reg: + 1 byte: record type (record_reg). + 8 bytes: register id (network byte order). + MAX_REGISTER_SIZE bytes: register value. + record_mem: + 1 byte: record type (record_mem). + 8 bytes: memory address (network byte order). + 8 bytes: memory length (network byte order). + n bytes: memory value (n == memory length). +*/ + +/* Dump the execution log to a file. */ + +static void +cmd_record_dump (char *args, int from_tty) +{ + char *recfilename, recfilename_buffer[40]; + int recfd; + struct record_entry *cur_record_list; + uint32_t magic; + struct regcache *regcache; + struct gdbarch *gdbarch; + struct cleanup *old_cleanups; + struct cleanup *set_cleanups; + + if (current_target.to_stratum != record_stratum) + error (_("Process record is not started.\n")); + + if (args && *args) + recfilename = args; + else + { + /* Default corefile name is "gdb_record.PID". */ + sprintf (recfilename_buffer, "gdb_record.%d", PIDGET (inferior_ptid)); + recfilename = recfilename_buffer; + } + + /* Open the dump file. */ + if (record_debug) + fprintf_unfiltered (gdb_stdlog, + _("Saving recording to file '%s'\n"), + recfilename); + recfd = open (recfilename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, + S_IRUSR | S_IWUSR); + if (recfd < 0) + error (_("Failed to open '%s' for dump execution records: %s"), + recfilename, strerror (errno)); + old_cleanups = make_cleanup (cmd_record_fd_cleanups, &recfd); + + /* Save the current record entry to "cur_record_list". */ + cur_record_list = record_list; + + /* Get the values of regcache and gdbarch. */ + regcache = get_current_regcache (); + gdbarch = get_regcache_arch (regcache); + + /* Disable the GDB operation record. */ + set_cleanups = record_gdb_operation_disable_set (); + + /* Write the magic code. */ + magic = RECORD_FILE_MAGIC; + if (record_debug) + fprintf_unfiltered (gdb_stdlog, _("\ +Writing 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n"), + magic); + record_write_dump (recfilename, recfd, &magic, 4); + + /* Reverse execute to the begin of record list. */ + while (1) + { + /* Check for beginning and end of log. */ + if (record_list == &record_first) + break; + + record_exec_entry (regcache, gdbarch, record_list); + + if (record_list->prev) + record_list = record_list->prev; + } + + /* Dump the entries to recfd and forward execute to the end of + record list. */ + while (1) + { + /* Dump entry. */ + if (record_list != &record_first) + { + uint8_t tmpu8; + uint64_t tmpu64; + + tmpu8 = record_list->type; + record_write_dump (recfilename, recfd, &tmpu8, 1); + + switch (record_list->type) + { + case record_reg: /* reg */ + if (record_debug) + fprintf_unfiltered (gdb_stdlog, _("\ +Writing register %d (1 plus 8 plus %d bytes)\n"), + record_list->u.reg.num, + MAX_REGISTER_SIZE); + + tmpu64 = record_list->u.reg.num; + if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); + record_write_dump (recfilename, recfd, &tmpu64, 8); + + record_write_dump (recfilename, recfd, record_list->u.reg.val, + MAX_REGISTER_SIZE); + break; + + case record_mem: /* mem */ + if (!record_list->u.mem.mem_entry_not_accessible) + { + if (record_debug) + fprintf_unfiltered (gdb_stdlog, _("\ +Writing memory %s (1 plus 8 plus 8 bytes plus %d bytes)\n"), + paddress (gdbarch, + record_list->u.mem.addr), + record_list->u.mem.len); + + tmpu64 = record_list->u.mem.addr; + if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); + record_write_dump (recfilename, recfd, &tmpu64, 8); + + tmpu64 = record_list->u.mem.len; + if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); + record_write_dump (recfilename, recfd, &tmpu64, 8); + + record_write_dump (recfilename, recfd, + record_list->u.mem.val, + record_list->u.mem.len); + } + break; + + case record_end: + /* FIXME: record the contents of record_end rec. */ + if (record_debug) + fprintf_unfiltered (gdb_stdlog, + _("Writing record_end (1 byte)\n")); + break; + } + } + + /* Execute entry. */ + record_exec_entry (regcache, gdbarch, record_list); + + if (record_list->next) + record_list = record_list->next; + else + break; + } + + /* Reverse execute to cur_record_list. */ + while (1) + { + /* Check for beginning and end of log. */ + if (record_list == cur_record_list) + break; + + record_exec_entry (regcache, gdbarch, record_list); + + if (record_list->prev) + record_list = record_list->prev; + } + + do_cleanups (set_cleanups); + do_cleanups (old_cleanups); + + /* Succeeded. */ + fprintf_filtered (gdb_stdout, _("Saved dump of execution " + "records to `%s'.\n"), + recfilename); +} + +/* Load the execution log from a file. */ + +static void +cmd_record_load (char *args, int from_tty) +{ + int recfd; + uint32_t magic; + struct cleanup *old_cleanups; + struct cleanup *old_cleanups2; + struct record_entry *rec; + int insn_number = 0; + + if (current_target.to_stratum != record_stratum) + { + cmd_record_start (NULL, from_tty); + printf_unfiltered (_("Auto start process record.\n")); + } + + if (!args || (args && !*args)) + error (_("Argument for filename required.\n")); + + /* Open the load file. */ + recfd = open (args, O_RDONLY | O_BINARY); + if (recfd < 0) + error (_("Failed to open '%s' for loading execution records: %s"), + args, strerror (errno)); + old_cleanups = make_cleanup (cmd_record_fd_cleanups, &recfd); + + /* Check the magic code. */ + record_read_dump (args, recfd, &magic, 4); + if (magic != RECORD_FILE_MAGIC) + error (_("'%s' is not a valid dump of execution records."), args); + + /* Load the entries in recfd to the record_arch_list_head and + record_arch_list_tail. */ + record_arch_list_head = NULL; + record_arch_list_tail = NULL; + old_cleanups2 = make_cleanup (record_arch_list_cleanups, 0); + + while (1) + { + int ret; + uint8_t tmpu8; + uint64_t tmpu64; + + ret = read (recfd, &tmpu8, 1); + if (ret < 0) + error (_("Failed to read dump of execution records in '%s'."), args); + if (ret == 0) + break; + + switch (tmpu8) + { + case record_reg: /* reg */ + rec = (struct record_entry *) xmalloc (sizeof (struct record_entry)); + rec->u.reg.val = (gdb_byte *) xmalloc (MAX_REGISTER_SIZE); + rec->prev = NULL; + rec->next = NULL; + rec->type = record_reg; + + /* Get num. */ + record_read_dump (args, recfd, &tmpu64, 8); + if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); + rec->u.reg.num = tmpu64; + + /* Get val. */ + record_read_dump (args, recfd, rec->u.reg.val, MAX_REGISTER_SIZE); + + if (record_debug) + fprintf_unfiltered (gdb_stdlog, _("\ +Reading register %d (1 plus 8 plus %d bytes)\n"), + rec->u.reg.num, + MAX_REGISTER_SIZE); + + record_arch_list_add (rec); + break; + + case record_mem: /* mem */ + rec = (struct record_entry *) xmalloc (sizeof (struct record_entry)); + rec->prev = NULL; + rec->next = NULL; + rec->type = record_mem; + + /* Get addr. */ + record_read_dump (args, recfd, &tmpu64, 8); + if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); + rec->u.mem.addr = tmpu64; + + /* Get len. */ + record_read_dump (args, recfd, &tmpu64, 8); + if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); + rec->u.mem.len = tmpu64; + rec->u.mem.mem_entry_not_accessible = 0; + rec->u.mem.val = (gdb_byte *) xmalloc (rec->u.mem.len); + + /* Get val. */ + record_read_dump (args, recfd, rec->u.mem.val, rec->u.mem.len); + + if (record_debug) + fprintf_unfiltered (gdb_stdlog, _("\ +Reading memory %s (1 plus 8 plus 8 bytes plus %d bytes)\n"), + paddress (get_current_arch (), + rec->u.mem.addr), + rec->u.mem.len); + + record_arch_list_add (rec); + break; + + case record_end: /* end */ + if (record_debug) + fprintf_unfiltered (gdb_stdlog, + _("Reading record_end (1 byte)\n")); + + rec = (struct record_entry *) xmalloc (sizeof (struct record_entry)); + rec->prev = NULL; + rec->next = NULL; + rec->type = record_end; + record_arch_list_add (rec); + insn_number ++; + break; + + default: + error (_("Format of '%s' is not right."), args); + break; + } + } + + discard_cleanups (old_cleanups2); + + /* Add record_arch_list_head to the end of record list. */ + for (rec = record_list; rec->next; rec = rec->next); + rec->next = record_arch_list_head; + record_arch_list_head->prev = rec; + + /* Update record_insn_num and record_insn_max_num. */ + record_insn_num += insn_number; + if (record_insn_num > record_insn_max_num) + { + record_insn_max_num = record_insn_num; + warning (_("Auto increase record/replay buffer limit to %d."), + record_insn_max_num); + } + + do_cleanups (old_cleanups); + + /* Succeeded. */ + fprintf_filtered (gdb_stdout, "Loaded recfile %s.\n", args); +} + /* Truncate the record log from the present point of replay until the end. */ @@ -1204,7 +1816,7 @@ set_record_insn_max_num (char *args, int "the first ones?\n")); while (record_insn_num > record_insn_max_num) - record_list_release_first (); + record_list_release_first_insn (); } } @@ -1244,6 +1856,8 @@ info_record_command (char *args, int fro void _initialize_record (void) { + struct cmd_list_element *c; + /* Init record_first. */ record_first.prev = NULL; record_first.next = NULL; @@ -1277,6 +1891,16 @@ _initialize_record (void) "info record ", 0, &infolist); add_alias_cmd ("rec", "record", class_obscure, 1, &infolist); + c = add_cmd ("dump", class_obscure, cmd_record_dump, + _("Dump the execution records to a file.\n\ +Argument is optional filename. Default filename is 'gdb_record.'."), + &record_cmdlist); + set_cmd_completer (c, filename_completer); + c = add_cmd ("load", class_obscure, cmd_record_load, + _("Load previously dumped execution records from \ +a file given as argument."), + &record_cmdlist); + set_cmd_completer (c, filename_completer); add_cmd ("delete", class_obscure, cmd_record_delete, _("Delete the rest of execution log and start recording it anew."),