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] |
Hi, This patch add command record dump and record load to make prec can dump execution log to a file and load it later. And load can work with core file. And I include "mem_entry_not_accessible"(not_replay) patch to this patch because when replay with core file, some address in lib cannot be read and write. For example: ./gdb ./a.out GNU gdb (GDB) 6.8.50.20090801-cvs Copyright (C) 2009 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "i686-pc-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... Setting up the environment for debugging gdb. Function "internal_error" not defined. Make breakpoint pending on future shared library load? (y or [n]) [answered N; input not from terminal] Function "info_command" not defined. Make breakpoint pending on future shared library load? (y or [n]) [answered N; input not from terminal] /home/teawater/gdb/bgdbno/gdb/.gdbinit:8: Error in sourced command file: No breakpoint number 0. (gdb) start Temporary breakpoint 1 at 0x80483c1: file 1.c, line 20. Starting program: /home/teawater/gdb/bgdbno/gdb/a.out Temporary breakpoint 1, main () at 1.c:20 20 int b = 0; (gdb) gcore Saved corefile core.11945 (gdb) record (gdb) n During symbol reading, incomplete CFI data; unspecified registers (e.g., eax) at 0x80483be. 21 int c = 1; (gdb) 24 printf ("a = %d b = %d c = %d\n", a, b, c); (gdb) a = 0 b = 0 c = 1 25 b = cool (); (gdb) a = 3 26 printf ("a = %d b = %d c = %d\n", a, b, c); (gdb) a = 3 b = 3 c = 1 29 c += 1; (gdb) 30 printf ("a = %d b = %d c = %d\n", a, b, c); (gdb) a = 3 b = 3 c = 2 31 a -= 2; (gdb) 32 printf ("a = %d b = %d c = %d\n", a, b, c); (gdb) a = 1 b = 3 c = 2 35 return (0); (gdb) 36 } (gdb) rc Continuing. No more reverse-execution history. main () at 1.c:20 20 int b = 0; (gdb) record dump Saved recfile rec.11945. (gdb) quit After that, you get core.11945 and rec.11945. Then you can replay 11945 execution log with them. ./gdb ./a.out ./core.11945 GNU gdb (GDB) 6.8.50.20090801-cvs Copyright (C) 2009 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "i686-pc-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... Reading symbols from /lib/tls/i686/cmov/libc.so.6...done. Loaded symbols for /lib/tls/i686/cmov/libc.so.6 Reading symbols from /lib/ld-linux.so.2...done. Loaded symbols for /lib/ld-linux.so.2 Core was generated by `/home/teawater/gdb/bgdbno/gdb/a.out'. Program terminated with signal 5, Trace/breakpoint trap. #0 main () at 1.c:20 20 int b = 0; Setting up the environment for debugging gdb. Function "internal_error" not defined. Make breakpoint pending on future shared library load? (y or [n]) [answered N; input not from terminal] Function "info_command" not defined. Make breakpoint pending on future shared library load? (y or [n]) [answered N; input not from terminal] /home/teawater/gdb/bgdbno/gdb/.gdbinit:8: Error in sourced command file: No breakpoint number 0. (gdb) record load ./rec.11945 Auto start process record. Loaded recfile ./rec.11945. (gdb) p b During symbol reading, incomplete CFI data; unspecified registers (e.g., eax) at 0x80483be. $1 = -1208017488 (gdb) n 21 int c = 1; (gdb) p b $2 = 0 (gdb) n 24 printf ("a = %d b = %d c = %d\n", a, b, c); (gdb) 25 b = cool (); (gdb) p b $3 = 0 (gdb) n 26 printf ("a = %d b = %d c = %d\n", a, b, c); (gdb) 29 c += 1; (gdb) p c $4 = 1 (gdb) n 30 printf ("a = %d b = %d c = %d\n", a, b, c); (gdb) p c $5 = 2 (gdb) c Continuing. No more reverse-execution history. main () at 1.c:36 36 } (gdb) rc Continuing. No more reverse-execution history. main () at 1.c:20 20 int b = 0; (gdb) p c $6 = 134513849 (gdb) quit The program is running. Quit anyway (and kill it)? (y or n) y Please send your comments about it. Thanks a lot. Hui 2009-08-01 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_mem_entry): New field 'mem_entry_not_accessible'. (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_add_mem): Initialize mem_entry_not_accessible to 0. (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 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 | 709 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 641 insertions(+), 68 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. @@ -51,6 +59,7 @@ struct record_mem_entry { CORE_ADDR addr; int len; + int mem_entry_not_accessible; gdb_byte *val; }; @@ -75,9 +84,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; @@ -100,6 +123,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 *, @@ -116,6 +147,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) @@ -166,7 +200,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; @@ -275,6 +309,7 @@ record_arch_list_add_mem (CORE_ADDR addr rec->type = record_mem; rec->u.mem.addr = addr; rec->u.mem.len = len; + rec->u.mem.mem_entry_not_accessible = 0; if (target_read_memory (addr, rec->u.mem.val, len)) { @@ -336,30 +371,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; @@ -382,7 +417,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++; @@ -412,6 +447,92 @@ 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) + record_list->u.mem.mem_entry_not_accessible = 0; + else + { + 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) { @@ -420,8 +541,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 " @@ -450,6 +576,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) @@ -464,6 +592,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; @@ -478,19 +611,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); @@ -503,10 +668,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; @@ -708,53 +889,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 */ - 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); - - if (target_read_memory - (record_list->u.mem.addr, mem, record_list->u.mem.len)) - error (_("Process record: error reading memory at " - "addr = %s len = %d."), - paddress (gdbarch, record_list->u.mem.addr), - record_list->u.mem.len); - - if (target_write_memory - (record_list->u.mem.addr, record_list->u.mem.val, - record_list->u.mem.len)) - error (_ - ("Process record: error writing memory at " - "addr = %s len = %d."), - paddress (gdbarch, record_list->u.mem.addr), - record_list->u.mem.len); + record_exec_entry (regcache, gdbarch, record_list); - 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, @@ -874,7 +1011,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. */ @@ -918,15 +1057,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) @@ -973,6 +1155,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); } @@ -988,7 +1171,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) { @@ -1032,11 +1215,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); @@ -1086,6 +1349,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) { @@ -1102,11 +1374,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; } @@ -1127,6 +1402,293 @@ 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); +} + +/* 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 "rec.PID". */ + sprintf (recfilename_buffer, "rec.%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."), recfilename); + 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 (write (recfd, &magic, 4) != 4) + error (_("Failed to write '%s' for dump."), recfilename); + + /* 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; + if (write (recfd, &tmpu8, 1) != 1) + error (_("Failed to write '%s' for dump."), recfilename); + + switch (record_list->type) + { + case record_reg: /* reg */ + tmpu64 = record_list->u.reg.num; +#if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); +#endif + if (write (recfd, &tmpu64, 8) != 8) + error (_("Failed to write '%s' for dump."), recfilename); + + if (write (recfd, record_list->u.reg.val, + MAX_REGISTER_SIZE) != MAX_REGISTER_SIZE) + error (_("Failed to write '%s' for dump."), recfilename); + 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); +#endif + if (write (recfd, &tmpu64, 8) != 8) + error (_("Failed to write '%s' for dump."), recfilename); + + tmpu64 = record_list->u.mem.len; +#if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); +#endif + if (write (recfd, &tmpu64, 8) != 8) + error (_("Failed to write '%s' for dump."), recfilename); + + if (write (recfd, record_list->u.mem.val, + record_list->u.mem.len) != record_list->u.mem.len) + error (_("Failed to write '%s' for dump."), recfilename); + } + 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 recfile %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 load."), args); + old_cleanups = make_cleanup (cmd_record_fd_cleanups, &recfd); + + /* Check the magic code. */ + if (read (recfd, &magic, 4) != 4) + error (_("Failed to read '%s' for load."), args); + if (magic != RECORD_FILE_MAGIC) + error (_("'%s' is not a record dump."), 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 '%s' for load."), 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. */ + if (read (recfd, &tmpu64, 8) != 8) + error (_("Failed to read '%s' for load."), args); +#if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); +#endif + rec->u.reg.num = tmpu64; + /* Get val. */ + if (read (recfd, rec->u.reg.val, + MAX_REGISTER_SIZE) != MAX_REGISTER_SIZE) + error (_("Failed to read '%s' for load."), args); + 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. */ + if (read (recfd, &tmpu64, 8) != 8) + error (_("Failed to read '%s' for load."), args); +#if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); +#endif + rec->u.mem.addr = tmpu64; + /* Get len. */ + if (read (recfd, &tmpu64, 8) != 8) + error (_("Failed to read '%s' for load."), args); +#if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); +#endif + 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. */ + if (read (recfd, rec->u.mem.val, + rec->u.mem.len) != rec->u.mem.len) + error (_("Failed to read '%s' for load."), args); + 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. */ @@ -1177,7 +1739,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 (); } } @@ -1217,6 +1779,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; @@ -1250,6 +1814,15 @@ _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 log to a file.\n\ +Argument is optional filename. Default filename is 'rec.<process_id>'."), + &record_cmdlist); + set_cmd_completer (c, filename_completer); + c = add_cmd ("load", class_obscure, cmd_record_load, + _("Load the execution log from a file. Argument is filename."), + &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."), 2009-08-01 Hui Zhu <teawater@gmail.com> * gdb.texinfo (Process Record and Replay): Document the "record dump" and "record dump" commands. --- doc/gdb.texinfo | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) --- a/doc/gdb.texinfo +++ b/doc/gdb.texinfo @@ -5190,6 +5190,22 @@ When record target runs in replay mode ( subsequent execution log and begin to record a new execution log starting from the current address. This means you will abandon the previously recorded ``future'' and begin recording a new ``future''. + +@kindex record dump +@kindex rec dump +@item record dump [@var{file}] +@itemx rec dump [@var{file}] +Produce a record execution log of the inferior process. The optional +argument @var{file} specifies the file name where to put the record dump. +If not specified, the file name defaults to @file{rec.@var{pid}}, where +@var{pid} is the inferior process ID. + +@kindex record load +@kindex rec load +@item record load @var{file} +@itemx rec dump @var{file} +Load process record execution log from @var{file}. +It can work with @code{core-file}. @end table
Attachment:
prec-dump.txt
Description: Text document
Attachment:
prec-dump-doc.txt
Description: Text document
Index Nav: | [Date Index] [Subject Index] [Author Index] [Thread Index] | |
---|---|---|
Message Nav: | [Date Prev] [Date Next] | [Thread Prev] [Thread Next] |