This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.
Index Nav: | [Date Index] [Subject Index] [Author Index] [Thread Index] | |
---|---|---|
Message Nav: | [Date Prev] [Date Next] | [Thread Prev] [Thread Next] |
Other format: | [Raw text] |
On Wed, Aug 5, 2009 at 04:00, Michael Snyder<msnyder@vmware.com> wrote: > Eli Zaretskii wrote: >>> >>> From: Hui Zhu <teawater@gmail.com> >>> Date: Tue, 4 Aug 2009 10:07:11 +0800 >>> Cc: msnyder@vmware.com, gdb-patches@sourceware.org >>> >>>> By the way, does it make sense to make the dump file portable, so that >>>> another host that supports debugging the same target could then use >>>> it? >>>> >>> Yes, the dump file is portable like core file. >> >> Well, maybe I'm missing something important, but isn't the dump file a >> series of struct's defined as this: >> >> ? ?struct record_entry >> ? ?{ >> ? ? ?struct record_entry *prev; >> ? ? ?struct record_entry *next; >> ? ? ?enum record_type type; >> ? ? ?union >> ? ? ?{ >> ? ? ? ?/* reg */ >> ? ? ? ?struct record_reg_entry reg; >> ? ? ? ?/* mem */ >> ? ? ? ?struct record_mem_entry mem; >> ? ? ?} u; >> ? ?}; >> >> ? ?If so, then the dump file uses host's native pointers, so it is not >> portable to a different host. ?Right? > > No. ?If you look at the code that dumps the file (and I had > to add a bunch of printfs and stuff to figure it out), you'll > see that the dump file looks like this: > > 4 byte magic number > Series of the following: > 1) 1 byte tag (record_end), or > 2) 1 byte tag (record_rec) followed by > ? 8 byte number (register id), followed by > ? MAX_REGISTER_SIZE byte value (register value): or > 3) 1 byte tag (record_mem) followed by > ? 8 byte number (memory address) followed by > ? 8 byte number (memory length) followed by > ? N byte buffer (memory value). > > If you look for the #if (BYTE_ORDER == BIG_ENDIAN), this is > where Teawater is making the byte orders host-independent. > > I was going to mention the ifdefs eventually. ? ;-) > Cool. It's very clear. :) Thanks Michael. I make a new version patch to follow the cvs head. And removed the ifdef. Please help me review it. Thanks, Hui 2009-08-05 Hui Zhu <teawater@gmail.com> Add dump and load command to process record and replay. * record.c (completer.h, arch-utils.h, gdbcore.h, exec.h, byteswap.h, netinet/in.h): Include files. (RECORD_IS_REPLAY): Return true if record_core is true. (RECORD_FILE_MAGIC): New macro. (record_core_buf_entry): New struct. (record_core, record_core_regbuf, record_core_start, record_core_end, record_core_buf_list, record_beneath_to_fetch_registers_ops, record_beneath_to_fetch_registers, record_beneath_to_store_registers_ops, record_beneath_to_store_registers, record_beneath_to_has_execution_ops, record_beneath_to_has_execution, record_beneath_to_prepare_to_store): New variables. (record_list_release_first_insn): Change function record_list_release_first to this name. (record_arch_list_cleanups): New function. (record_message_cleanups): Removed. (record_message): Change to call record_arch_list_cleanups and record_list_release_first_insn. (record_exec_entry): New function. (record_open): Add support for target core. (record_close): Add support for target core. (record_wait): Call function 'record_exec_entry'. (record_kill): Add support for target core. (record_registers_change): Call record_list_release_first_insn. (record_fetch_registers): New function. (record_prepare_to_store): New function. (record_store_registers): Add support for target core. (record_xfer_partial): Add support for target core. (record_has_execution): New function. (init_record_ops): Set record_ops.to_fetch_registers, record_ops.to_prepare_to_store and record_ops.to_has_execution. (cmd_record_fd_cleanups): New function. (cmd_record_dump): New function. (cmd_record_load): New function. (set_record_insn_max_num): Call record_list_release_first_insn. (_initialize_record): Add commands "record dump" and "record load". --- record.c | 731 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 640 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 <byteswap.h> #include <signal.h> +#include <netinet/in.h> #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,294 @@ 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); +} + +/* 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. */ + 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; + 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 */ + 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) + { + 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; + } + } + + /* 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); + 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); + record_arch_list_add (rec); + break; + + case record_end: /* end */ + 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 +1741,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 +1781,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 +1816,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.<process_id>'."), + &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."),
Attachment:
prec-dump.txt
Description: Text document
Index Nav: | [Date Index] [Subject Index] [Author Index] [Thread Index] | |
---|---|---|
Message Nav: | [Date Prev] [Date Next] | [Thread Prev] [Thread Next] |