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 26, 2009 at 09:35, Michael Snyder<msnyder@vmware.com> wrote: > Michael Snyder wrote: >> >> Hui Zhu wrote: >>> >>> On Tue, Aug 25, 2009 at 01:51, Michael Snyder<msnyder@vmware.com> wrote: >>>> >>>> Have you considered combining this patch with the idea that I >>>> proposed -- storing the record log in the core file? ?Seems like >>>> that would be very easy now -- gdb already has the core file open, >>>> and there is a global pointer to the BFD. >>>> See gdbcore.h:extern bfd* core_bfd; >>> >>> Cool. ?If we just want record_core_ops support load, we can remove the >>> cmd "record load". ?:) >>> >>>> In fact, just for fun, I've merged the two patches in my >>>> local tree. ?It seems to work quite well. ?I can just post >>>> it here if you like, or here's a hint to show you how it goes: >>> >>> Why not? ?Please post it. ?I can not wait to try it. ?:) >> >> Great -- attached. > > This is "quick and dirty", by the way. > It's not meant to be finished! > > Hi Michael, I make a new patch that make the patches together. Prec can load the record log from a core file and debug it with core target. To use it, Call "record" after load the gdb_record.xxx file. Please help me review it. Thanks, Hui 2009-08-29 Michael Snyder <msnyder@vmware.com> 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, elf-bfd.h, gcore.h, byteswap.h, netinet/in.h): Include files. (RECORD_FILE_MAGIC): New macro. (record_core_buf_entry): New struct. (record_core_ops): New target_ops. (record_list_release_first_insn): Change function record_list_release_first to this name. (record_reg_alloc, record_mem_alloc, record_end_alloc): New functions. (record_arch_list_add_reg): Call record_reg_alloc. (record_arch_list_add_mem): Call record_mem_alloc. (record_arch_list_add_end): Call record_end_alloc. (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, (bfdcore_read, record_load, record_core_open_1, record_open_1): New function. (record_open): Add support for record_core_ops. (record_close): Add support for record_core_ops. (record_wait): Call function 'record_exec_entry' and add support for target core. (record_registers_change): Call record_list_release_first_insn. (record_core_resume, record_core_resume, record_core_kill, record_core_fetch_registers, record_core_prepare_to_store, record_core_store_registers, record_core_xfer_partial, record_core_insert_breakpoint, record_core_remove_breakpoint, record_core_has_execution, init_record_core_ops, cmd_record_load, bfdcore_write, cmd_record_dump): New function. (cmd_record_stop): Add support for record_core_ops. (set_record_insn_max_num): Call record_list_release_first_insn. (_initialize_record): Add commands "record dump". * gcore.c (create_gcore_bfd): New function, abstracted from gcore_command for export. (write_gcore_file): New function, abstracted from gcore_command for export. (gcore_command): Call helper functions (above). (call_target_sbrk): New function, abstracted from derive_heap_segment. (derive_heap_segment): Call helper function (above). (load_core_segments): New function. (load_corefile): New function. * gcore.h: New file. 2009-08-29 Hui Zhu <teawater@gmail.com> * gdb.texinfo (Process Record and Replay): Document the "record dump" commands. --- gcore.c | 309 ++++++++++++++---- gcore.h | 23 + record.c | 1072 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 1199 insertions(+), 205 deletions(-) --- a/gcore.c +++ b/gcore.c @@ -25,10 +25,14 @@ #include "gdbcore.h" #include "objfiles.h" #include "symfile.h" - +#include "arch-utils.h" +#include "completer.h" +#include "gcore.h" #include "cli/cli-decode.h" - #include "gdb_assert.h" +#include <fcntl.h> +#include "regcache.h" +#include "regset.h" /* The largest amount of memory to read from the target at once. We must throttle it to limit the amount of memory used by GDB during @@ -40,45 +44,27 @@ static enum bfd_architecture default_gco static unsigned long default_gcore_mach (void); static int gcore_memory_sections (bfd *); -/* Generate a core file from the inferior process. */ +/* create_gcore_bfd -- helper for gcore_command (exported). */ -static void -gcore_command (char *args, int from_tty) +bfd * +create_gcore_bfd (char *filename) { - struct cleanup *old_chain; - char *corefilename, corefilename_buffer[40]; - asection *note_sec = NULL; - bfd *obfd; - void *note_data = NULL; - int note_size = 0; - - /* No use generating a corefile without a target process. */ - if (!target_has_execution) - noprocess (); - - if (args && *args) - corefilename = args; - else - { - /* Default corefile name is "core.PID". */ - sprintf (corefilename_buffer, "core.%d", PIDGET (inferior_ptid)); - corefilename = corefilename_buffer; - } - - if (info_verbose) - fprintf_filtered (gdb_stdout, - "Opening corefile '%s' for output.\n", corefilename); - - /* Open the output file. */ - obfd = bfd_openw (corefilename, default_gcore_target ()); + bfd *obfd = bfd_openw (filename, default_gcore_target ()); if (!obfd) - error (_("Failed to open '%s' for output."), corefilename); - - /* Need a cleanup that will close the file (FIXME: delete it?). */ - old_chain = make_cleanup_bfd_close (obfd); - + error (_("Failed to open '%s' for output."), filename); bfd_set_format (obfd, bfd_core); bfd_set_arch_mach (obfd, default_gcore_arch (), default_gcore_mach ()); + return obfd; +} + +/* write_gcore_file -- helper for gcore_command (exported). */ + +void +write_gcore_file (bfd *obfd) +{ + void *note_data = NULL; + int note_size = 0; + asection *note_sec = NULL; /* An external target method must build the notes section. */ note_data = target_make_corefile_notes (obfd, ¬e_size); @@ -107,8 +93,46 @@ gcore_command (char *args, int from_tty) if (note_data != NULL && note_size != 0) { if (!bfd_set_section_contents (obfd, note_sec, note_data, 0, note_size)) - warning (_("writing note section (%s)"), bfd_errmsg (bfd_get_error ())); + warning (_("writing note section (%s)"), + bfd_errmsg (bfd_get_error ())); } +} + +/* gcore_command -- implements the 'gcore' command. + Generate a core file from the inferior process. */ + +static void +gcore_command (char *args, int from_tty) +{ + struct cleanup *old_chain; + char *corefilename, corefilename_buffer[40]; + bfd *obfd; + + /* No use generating a corefile without a target process. */ + if (!target_has_execution) + noprocess (); + + if (args && *args) + corefilename = args; + else + { + /* Default corefile name is "core.PID". */ + sprintf (corefilename_buffer, "core.%d", PIDGET (inferior_ptid)); + corefilename = corefilename_buffer; + } + + if (info_verbose) + fprintf_filtered (gdb_stdout, + "Opening corefile '%s' for output.\n", corefilename); + + /* Open the output file. */ + obfd = create_gcore_bfd (corefilename); + + /* Need a cleanup that will close the file (FIXME: delete it?). */ + old_chain = make_cleanup_bfd_close (obfd); + + /* Call worker function. */ + write_gcore_file (obfd); /* Succeeded. */ fprintf_filtered (gdb_stdout, "Saved corefile %s\n", corefilename); @@ -212,6 +236,50 @@ derive_stack_segment (bfd_vma *bottom, b return 1; } +/* call_target_sbrk -- + helper function for derive_heap_segment and load_core_segment. */ + +static bfd_vma +call_target_sbrk (int sbrk_arg) +{ + struct objfile *sbrk_objf; + struct gdbarch *gdbarch; + bfd_vma top_of_heap; + struct value *target_sbrk_arg; + struct value *sbrk_fn, *ret; + bfd_vma tmp; + + if (lookup_minimal_symbol ("sbrk", NULL, NULL) != NULL) + { + sbrk_fn = find_function_in_inferior ("sbrk", &sbrk_objf); + if (sbrk_fn == NULL) + return (bfd_vma) 0; + } + else if (lookup_minimal_symbol ("_sbrk", NULL, NULL) != NULL) + { + sbrk_fn = find_function_in_inferior ("_sbrk", &sbrk_objf); + if (sbrk_fn == NULL) + return (bfd_vma) 0; + } + else + return (bfd_vma) 0; + + gdbarch = get_objfile_arch (sbrk_objf); + target_sbrk_arg = value_from_longest (builtin_type (gdbarch)->builtin_int, + sbrk_arg); + gdb_assert (target_sbrk_arg); + ret = call_function_by_hand (sbrk_fn, 1, &target_sbrk_arg); + if (ret == NULL) + return (bfd_vma) 0; + + tmp = value_as_long (ret); + if ((LONGEST) tmp <= 0 || (LONGEST) tmp == 0xffffffff) + return (bfd_vma) 0; + + top_of_heap = tmp; + return top_of_heap; +} + /* Derive a reasonable heap segment for ABFD by looking at sbrk and the static data sections. Store its limits in *BOTTOM and *TOP. Return non-zero if successful. */ @@ -219,12 +287,10 @@ derive_stack_segment (bfd_vma *bottom, b static int derive_heap_segment (bfd *abfd, bfd_vma *bottom, bfd_vma *top) { - struct objfile *sbrk_objf; struct gdbarch *gdbarch; bfd_vma top_of_data_memory = 0; bfd_vma top_of_heap = 0; bfd_size_type sec_size; - struct value *zero, *sbrk; bfd_vma sec_vaddr; asection *sec; @@ -259,29 +325,9 @@ derive_heap_segment (bfd *abfd, bfd_vma } } - /* Now get the top-of-heap by calling sbrk in the inferior. */ - if (lookup_minimal_symbol ("sbrk", NULL, NULL) != NULL) - { - sbrk = find_function_in_inferior ("sbrk", &sbrk_objf); - if (sbrk == NULL) - return 0; - } - else if (lookup_minimal_symbol ("_sbrk", NULL, NULL) != NULL) - { - sbrk = find_function_in_inferior ("_sbrk", &sbrk_objf); - if (sbrk == NULL) - return 0; - } - else - return 0; - - gdbarch = get_objfile_arch (sbrk_objf); - zero = value_from_longest (builtin_type (gdbarch)->builtin_int, 0); - gdb_assert (zero); - sbrk = call_function_by_hand (sbrk, 1, &zero); - if (sbrk == NULL) + top_of_heap = call_target_sbrk (0); + if (top_of_heap == (bfd_vma) 0) return 0; - top_of_heap = value_as_long (sbrk); /* Return results. */ if (top_of_heap > top_of_data_memory) @@ -299,13 +345,15 @@ static void make_output_phdrs (bfd *obfd, asection *osec, void *ignored) { int p_flags = 0; - int p_type; + int p_type = 0; /* FIXME: these constants may only be applicable for ELF. */ if (strncmp (bfd_section_name (obfd, osec), "load", 4) == 0) p_type = PT_LOAD; - else + else if (strncmp (bfd_section_name (obfd, osec), "note", 4) == 0) p_type = PT_NOTE; + else + p_type = PT_NULL; p_flags |= PF_R; /* Segment is readable. */ if (!(bfd_get_section_flags (obfd, osec) & SEC_READONLY)) @@ -516,6 +564,141 @@ gcore_memory_sections (bfd *obfd) return 1; } +struct load_core_args_params { + int from_tty; + bfd_vma top_of_heap; +}; + +/* load_core_segments -- iterator function for bfd_map_over_sections. */ + +static void +load_core_segment (bfd *abfd, asection *asect, void *arg) +{ + struct load_core_args_params *params = arg; + struct cleanup *old_chain; + char *memhunk; + int ret; + + if ((bfd_section_size (abfd, asect) > 0) && + (bfd_get_section_flags (abfd, asect) & SEC_LOAD) && + !(bfd_get_section_flags (abfd, asect) & SEC_READONLY)) + { + if (info_verbose && params->from_tty) + { + printf_filtered (_("Load core section %s"), + bfd_section_name (abfd, asect)); + printf_filtered (_(", vma 0x%08lx to 0x%08lx"), + (unsigned long) bfd_section_vma (abfd, asect), + (unsigned long) bfd_section_vma (abfd, asect) + + (int) bfd_section_size (abfd, asect)); + printf_filtered (_(", size = %d"), + (int) bfd_section_size (abfd, asect)); + printf_filtered (_(".\n")); + } + /* Fixme cleanup? */ + memhunk = xmalloc (bfd_section_size (abfd, asect)); + bfd_get_section_contents (abfd, asect, memhunk, 0, + bfd_section_size (abfd, asect)); + if ((ret = target_write_memory (bfd_section_vma (abfd, asect), + memhunk, + bfd_section_size (abfd, asect))) != 0) + { + print_sys_errmsg ("load_core_segment", ret); + if ((LONGEST) params->top_of_heap < + (LONGEST) bfd_section_vma (abfd, asect) + + (LONGEST) bfd_section_size (abfd, asect)) + { + int increment = bfd_section_vma (abfd, asect) + + bfd_section_size (abfd, asect) - params->top_of_heap; + + params->top_of_heap = call_target_sbrk (increment); + if (params->top_of_heap == 0) + error ("sbrk failed, TOH = 0x%08lx", params->top_of_heap); + else + printf_filtered ("Increase TOH to 0x%08lx and retry.\n", + (unsigned long) params->top_of_heap); + if (target_write_memory (bfd_section_vma (abfd, asect), + memhunk, + bfd_section_size (abfd, asect)) != 0) + { + error ("Nope, still failed."); + } + } + } + xfree (memhunk); + } +} + +/* load_corefile -- reads a corefile, copies its memory sections + into target memory, and its registers into target regcache. */ + +bfd * +load_corefile (char *filename, int from_tty) +{ + struct load_core_args_params params; + struct bfd_section *regsect; + const struct regset *regset; + struct regcache *regcache; + struct cleanup *old_chain; + struct gdbarch *gdbarch; + char *scratch_path; + int scratch_chan; + char *contents; + bfd *core_bfd; + int size; + + scratch_chan = openp (getenv ("PATH"), OPF_TRY_CWD_FIRST, filename, + O_BINARY | O_RDONLY | O_LARGEFILE, &scratch_path); + if (scratch_chan < 0) + perror_with_name (filename); + + core_bfd = bfd_fdopenr (scratch_path, gnutarget, scratch_chan); + old_chain = make_cleanup_bfd_close (core_bfd); + if (!core_bfd) + perror_with_name (scratch_path); + + if (!bfd_check_format (core_bfd, bfd_core)) + error (_("\"%s\" is not a core file: %s"), + filename, bfd_errmsg (bfd_get_error ())); + + params.from_tty = from_tty; + params.top_of_heap = call_target_sbrk (0); + if (params.top_of_heap == 0) + error (_("Couldn't get sbrk.")); + + bfd_map_over_sections (core_bfd, load_core_segment, (void *) ¶ms); + /* Now need to get/set registers. */ + regsect = bfd_get_section_by_name (core_bfd, ".reg"); + + if (!regsect) + error (_("Couldn't find .reg section.")); + + size = bfd_section_size (core_bfd, regsect); + contents = alloca (size); + bfd_get_section_contents (core_bfd, regsect, contents, 0, size); + + /* See FIXME kettenis/20031023 comment in corelow.c */ + gdbarch = gdbarch_from_bfd (core_bfd); + + if (gdbarch && gdbarch_regset_from_core_section_p (gdbarch)) + { + regset = gdbarch_regset_from_core_section (gdbarch, ".reg", size); + if (!regset) + error (_("Failed to allocate regset.")); + + registers_changed (); + regcache = get_current_regcache (); + regset->supply_regset (regset, regcache, -1, contents, size); + reinit_frame_cache (); + target_store_registers (regcache, -1); + } + else + error (_("Failed to get regset from core section")); + + discard_cleanups (old_chain); + return core_bfd; +} + /* Provide a prototype to silence -Wmissing-prototypes. */ extern initialize_file_ftype _initialize_gcore; --- /dev/null +++ b/gcore.h @@ -0,0 +1,23 @@ +/* Support for reading/writing gcore files. + + Copyright (C) 2009, Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +extern bfd *create_gcore_bfd (char *filename); +extern void write_gcore_file (bfd *obfd); +extern bfd *load_corefile (char *filename, int from_tty); + --- a/record.c +++ b/record.c @@ -23,15 +23,25 @@ #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 "elf-bfd.h" +#include "gcore.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) +#define RECORD_FILE_MAGIC htonl(0x20090829) + /* These are the core struct of record function. An record_entry is a record of the value change of a register @@ -78,9 +88,22 @@ 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 buf with core target. */ +static gdb_byte *record_core_regbuf = NULL; +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; @@ -94,6 +117,7 @@ static int record_insn_num = 0; /* The target_ops of process record. */ static struct target_ops record_ops; +static struct target_ops record_core_ops; /* The beneath function pointers. */ static struct target_ops *record_beneath_to_resume_ops; @@ -169,7 +193,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; @@ -227,6 +251,56 @@ record_arch_list_add (struct record_entr } } +/* Alloc a record_reg record entry. */ + +static inline struct record_entry * +record_reg_alloc (int num) +{ + struct record_entry *rec; + + 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; + rec->u.reg.num = num; + + return rec; +} + +/* Alloc a record_mem record entry. */ + +static inline struct record_entry * +record_mem_alloc (int len) +{ + struct record_entry *rec; + + rec = (struct record_entry *) xmalloc (sizeof (struct record_entry)); + rec->u.mem.val = (gdb_byte *) xmalloc (len); + rec->prev = NULL; + rec->next = NULL; + rec->type = record_mem; + rec->u.mem.len = len; + rec->u.mem.mem_entry_not_accessible = 0; + + return rec; +} + +/* Alloc a record_mem record entry. */ + +static inline struct record_entry * +record_end_alloc (void) +{ + struct record_entry *rec; + + rec = (struct record_entry *) xmalloc (sizeof (struct record_entry)); + rec->prev = NULL; + rec->next = NULL; + rec->type = record_end; + + return rec; +} + /* Record the value of a register NUM to record_arch_list. */ int @@ -240,12 +314,7 @@ record_arch_list_add_reg (struct regcach "record list.\n", num); - 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; - rec->u.reg.num = num; + rec = record_reg_alloc (num); regcache_raw_read (regcache, num, rec->u.reg.val); @@ -271,14 +340,8 @@ record_arch_list_add_mem (CORE_ADDR addr if (!addr) return 0; - rec = (struct record_entry *) xmalloc (sizeof (struct record_entry)); - rec->u.mem.val = (gdb_byte *) xmalloc (len); - rec->prev = NULL; - rec->next = NULL; - rec->type = record_mem; + rec = record_mem_alloc (len); 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)) { @@ -308,10 +371,7 @@ record_arch_list_add_end (void) fprintf_unfiltered (gdb_stdlog, "Process record: add end to arch list.\n"); - rec = (struct record_entry *) xmalloc (sizeof (struct record_entry)); - rec->prev = NULL; - rec->next = NULL; - rec->type = record_end; + rec = record_end_alloc (); record_arch_list_add (rec); @@ -340,30 +400,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 +446,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,13 +476,297 @@ 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)) + { + 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 + { + if (target_write_memory (entry->u.mem.addr, entry->u.mem.val, + entry->u.mem.len)) + { + 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 + memcpy (entry->u.mem.val, mem, entry->u.mem.len); + } + } + } + break; + } +} + +/* Load the execution log from core_bfd. */ + +static int +bfdcore_read (bfd *obfd, asection *osec, void *buf, int len, int *offset) +{ + int ret = bfd_get_section_contents (obfd, osec, buf, *offset, len); + + if (ret) + *offset += len; + return ret; +} + static void -record_open (char *name, int from_tty) +record_load (void) { - struct target_ops *t; + uint32_t magic; + struct cleanup *old_cleanups; + struct record_entry *rec; + asection *osec; + int bfd_offset = 0; + + /* We load the execution log from the open core bfd, + if there is one. */ + if (core_bfd == NULL) + return; + + /* "record_load" just can be called when record list is empty. */ + gdb_assert (record_first.next == NULL); if (record_debug) - fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n"); + fprintf_filtered (gdb_stdlog, + _("Restoring recording from core file.\n")); + + /* Now need to find our special note section. */ + osec = bfd_get_section_by_name (core_bfd, "null0"); + printf_filtered ("Find precord section %s.\n", + osec ? "succeeded" : "failed"); + if (!osec) + return; + if (record_debug) + fprintf_filtered (gdb_stdlog, "osec name = '%s'\n", + bfd_section_name (core_bfd, osec)); + + /* Check the magic code. */ + if (!bfdcore_read (core_bfd, osec, &magic, sizeof (magic), &bfd_offset)) + error (_("Failed to read 'magic' from core file (%s)"), + bfd_errmsg (bfd_get_error ())); + if (magic != RECORD_FILE_MAGIC) + error (_("Version mis-match or file format error.")); + if (record_debug) + fprintf_filtered (gdb_stdlog, _("\ +Reading 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n"), + magic); + + /* 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; + record_insn_num = 0; + old_cleanups = make_cleanup (record_arch_list_cleanups, 0); + + while (1) + { + int ret; + uint8_t tmpu8; + uint64_t tmpu64; + + /* FIXME: Check offset for end-of-section. */ + if (!bfdcore_read (core_bfd, osec, &tmpu8, + sizeof (tmpu8), &bfd_offset)) + break; + + switch (tmpu8) + { + case record_reg: /* reg */ + /* Get num to tmpu64. */ + if (!bfdcore_read (core_bfd, osec, &tmpu64, + sizeof (tmpu64), &bfd_offset)) + error (_("Failed to read regnum from core file (%s)"), + bfd_errmsg (bfd_get_error ())); + if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); + + rec = record_reg_alloc ((int) tmpu64); + + /* Get val. */ + if (!bfdcore_read (core_bfd, osec, rec->u.reg.val, + MAX_REGISTER_SIZE, &bfd_offset)) + error (_("Failed to read regval from core file (%s)"), + bfd_errmsg (bfd_get_error ())); + + if (record_debug) + fprintf_unfiltered (gdb_stdlog, _("\ +Reading register %d (1 plus 8 plus %d bytes)\n"), + rec->u.reg.num, + MAX_REGISTER_SIZE); + break; + + case record_mem: /* mem */ + /* Get len. */ + if (!bfdcore_read (core_bfd, osec, &tmpu64, + sizeof (tmpu64), &bfd_offset)) + error (_("Failed to read memlen from core file (%s)"), + bfd_errmsg (bfd_get_error ())); + if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); + + rec = record_mem_alloc ((int) tmpu64); + + /* Get addr. */ + if (!bfdcore_read (core_bfd, osec, &tmpu64, + sizeof (tmpu64), &bfd_offset)) + error (_("Failed to read memaddr from core file (%s)"), + bfd_errmsg (bfd_get_error ())); + if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); + rec->u.mem.addr = tmpu64; + + /* Get val. */ + if (!bfdcore_read (core_bfd, osec, rec->u.mem.val, + rec->u.mem.len, &bfd_offset)) + error (_("Failed to read memval from core file (%s)"), + bfd_errmsg (bfd_get_error ())); + + 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); + break; + + case record_end: /* end */ + rec = record_end_alloc (); + record_insn_num ++; + + if (record_debug) + fprintf_unfiltered (gdb_stdlog, + _("Reading record_end (1 byte)\n")); + break; + + default: + error (_("Format of core file is not right.")); + break; + } + + /* Add rec to record arch list. */ + record_arch_list_add (rec); + } + + discard_cleanups (old_cleanups); + + /* Add record_arch_list_head to the end of record list. */ + record_first.next = record_arch_list_head; + record_arch_list_head->prev = &record_first; + + /* Update record_insn_max_num. */ + 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); + } + + /* Succeeded. */ + fprintf_filtered (gdb_stdout, "Loaded records from core file.\n"); + + print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC); +} + +static struct target_ops *tmp_to_resume_ops; +static void (*tmp_to_resume) (struct target_ops *, ptid_t, int, + enum target_signal); +static struct target_ops *tmp_to_wait_ops; +static ptid_t (*tmp_to_wait) (struct target_ops *, ptid_t, + struct target_waitstatus *, + int); +static struct target_ops *tmp_to_store_registers_ops; +static void (*tmp_to_store_registers) (struct target_ops *, + struct regcache *, + int regno); +static struct target_ops *tmp_to_xfer_partial_ops; +static LONGEST (*tmp_to_xfer_partial) (struct target_ops *ops, + enum target_object object, + const char *annex, + gdb_byte *readbuf, + const gdb_byte *writebuf, + ULONGEST offset, + LONGEST len); +static int (*tmp_to_insert_breakpoint) (struct gdbarch *, + struct bp_target_info *); +static int (*tmp_to_remove_breakpoint) (struct gdbarch *, + struct bp_target_info *); + +static void +record_core_open_1 (char *name, int from_tty) +{ + struct regcache *regcache = get_current_regcache (); + int regnum = gdbarch_num_regs (get_regcache_arch (regcache)); + int i; + + /* Get record_core_regbuf. */ + 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)) + { + xfree (record_core_regbuf); + record_core_regbuf = NULL; + error (_("\"%s\": Can't find sections: %s"), + bfd_get_filename (core_bfd), bfd_errmsg (bfd_get_error ())); + } + + push_target (&record_core_ops); +} + +static void +record_open_1 (char *name, int from_tty) +{ + struct target_ops *t; /* check exec */ if (!target_has_execution) @@ -438,6 +782,28 @@ record_open (char *name, int from_tty) error (_("Process record: the current architecture doesn't support " "record function.")); + if (!tmp_to_resume) + error (_("Process record can't get to_resume.")); + if (!tmp_to_wait) + error (_("Process record can't get to_wait.")); + if (!tmp_to_store_registers) + error (_("Process record can't get to_store_registers.")); + if (!tmp_to_insert_breakpoint) + error (_("Process record can't get to_insert_breakpoint.")); + if (!tmp_to_remove_breakpoint) + error (_("Process record can't get to_remove_breakpoint.")); + + push_target (&record_ops); +} + +static void +record_open (char *name, int from_tty) +{ + struct target_ops *t; + + if (record_debug) + fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n"); + /* Check if record target is already running. */ if (current_target.to_stratum == record_stratum) { @@ -447,70 +813,102 @@ record_open (char *name, int from_tty) return; } - /*Reset the beneath function pointers. */ - record_beneath_to_resume = NULL; - record_beneath_to_wait = NULL; - record_beneath_to_store_registers = NULL; - record_beneath_to_xfer_partial = NULL; - record_beneath_to_insert_breakpoint = NULL; - record_beneath_to_remove_breakpoint = NULL; + /* Reset the tmp beneath pointers. */ + tmp_to_resume_ops = NULL; + tmp_to_resume = NULL; + tmp_to_wait_ops = NULL; + tmp_to_wait = NULL; + tmp_to_store_registers_ops = NULL; + tmp_to_store_registers = NULL; + tmp_to_xfer_partial_ops = NULL; + tmp_to_xfer_partial = NULL; + tmp_to_insert_breakpoint = NULL; + tmp_to_remove_breakpoint = NULL; /* Set the beneath function pointers. */ for (t = current_target.beneath; t != NULL; t = t->beneath) { - if (!record_beneath_to_resume) + if (!tmp_to_resume) { - record_beneath_to_resume = t->to_resume; - record_beneath_to_resume_ops = t; + tmp_to_resume = t->to_resume; + tmp_to_resume_ops = t; } - if (!record_beneath_to_wait) + if (!tmp_to_wait) { - record_beneath_to_wait = t->to_wait; - record_beneath_to_wait_ops = t; + tmp_to_wait = t->to_wait; + tmp_to_wait_ops = t; } - if (!record_beneath_to_store_registers) + if (!tmp_to_store_registers) { - record_beneath_to_store_registers = t->to_store_registers; - record_beneath_to_store_registers_ops = t; + tmp_to_store_registers = t->to_store_registers; + tmp_to_store_registers_ops = t; } - if (!record_beneath_to_xfer_partial) + if (!tmp_to_xfer_partial) { - record_beneath_to_xfer_partial = t->to_xfer_partial; - record_beneath_to_xfer_partial_ops = t; + tmp_to_xfer_partial = t->to_xfer_partial; + tmp_to_xfer_partial_ops = t; } - if (!record_beneath_to_insert_breakpoint) - 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 (!tmp_to_insert_breakpoint) + tmp_to_insert_breakpoint = t->to_insert_breakpoint; + if (!tmp_to_remove_breakpoint) + tmp_to_remove_breakpoint = t->to_remove_breakpoint; } - if (!record_beneath_to_resume) - error (_("Process record can't get to_resume.")); - if (!record_beneath_to_wait) - error (_("Process record can't get to_wait.")); - if (!record_beneath_to_store_registers) - error (_("Process record can't get to_store_registers.")); - if (!record_beneath_to_xfer_partial) + if (!tmp_to_xfer_partial) error (_("Process record can't get to_xfer_partial.")); - if (!record_beneath_to_insert_breakpoint) - error (_("Process record can't get to_insert_breakpoint.")); - if (!record_beneath_to_remove_breakpoint) - error (_("Process record can't get to_remove_breakpoint.")); - push_target (&record_ops); + if (current_target.to_stratum == core_stratum) + record_core_open_1 (name, from_tty); + else + record_open_1 (name, from_tty); /* Reset */ record_insn_num = 0; record_list = &record_first; record_list->next = NULL; + + /* Set the tmp beneath pointers to beneath pointers. */ + record_beneath_to_resume_ops = tmp_to_resume_ops; + record_beneath_to_resume = tmp_to_resume; + record_beneath_to_wait_ops = tmp_to_wait_ops; + record_beneath_to_wait = tmp_to_wait; + record_beneath_to_store_registers_ops = tmp_to_store_registers_ops; + record_beneath_to_store_registers = tmp_to_store_registers; + record_beneath_to_xfer_partial_ops = tmp_to_xfer_partial_ops; + record_beneath_to_xfer_partial = tmp_to_xfer_partial; + record_beneath_to_insert_breakpoint = tmp_to_insert_breakpoint; + record_beneath_to_remove_breakpoint = tmp_to_remove_breakpoint; + + if (strcmp (current_target.to_shortname, "record_core") == 0) + record_load (); } 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. */ + if (record_core_regbuf) + { + xfree (record_core_regbuf); + record_core_regbuf = NULL; + } + + /* 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; @@ -584,7 +982,7 @@ record_wait (struct target_ops *ops, "record_resume_step = %d\n", record_resume_step); - if (!RECORD_IS_REPLAY) + if (!RECORD_IS_REPLAY && ops != &record_core_ops) { if (record_resume_error) { @@ -712,76 +1110,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,6 +1232,7 @@ record_kill (struct target_ops *ops) fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n"); unpush_target (&record_ops); + target_kill (); } @@ -945,7 +1277,7 @@ 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++; } @@ -1058,7 +1390,7 @@ 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++; } @@ -1138,6 +1470,191 @@ init_record_ops (void) } static void +record_core_resume (struct target_ops *ops, ptid_t ptid, int step, + enum target_signal siggnal) +{ + record_resume_step = step; + record_resume_siggnal = siggnal; +} + +static void +record_core_kill (struct target_ops *ops) +{ + if (record_debug) + fprintf_unfiltered (gdb_stdlog, "Process record: record_core_kill\n"); + + unpush_target (&record_core_ops); +} + +static void +record_core_fetch_registers (struct target_ops *ops, + struct regcache *regcache, + int regno) +{ + 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); +} + +static void +record_core_prepare_to_store (struct regcache *regcache) +{ +} + +static void +record_core_store_registers (struct target_ops *ops, + struct regcache *regcache, + int regno) +{ + 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.")); +} + +static LONGEST +record_core_xfer_partial (struct target_ops *ops, enum target_object object, + const char *annex, gdb_byte *readbuf, + const gdb_byte *writebuf, ULONGEST offset, + LONGEST len) +{ + if (object == TARGET_OBJECT_MEMORY) + { + 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 -1; + } + else + error (_("You can't do that without a process to debug.")); + } + + return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops, + object, annex, readbuf, writebuf, + offset, len); +} + +static int +record_core_insert_breakpoint (struct gdbarch *gdbarch, + struct bp_target_info *bp_tgt) +{ + return 0; +} + +static int +record_core_remove_breakpoint (struct gdbarch *gdbarch, + struct bp_target_info *bp_tgt) +{ + return 0; +} + +int +record_core_has_execution (struct target_ops *ops) +{ + return 1; +} + +static void +init_record_core_ops (void) +{ + record_core_ops.to_shortname = "record_core"; + record_core_ops.to_longname = "Process record and replay target"; + record_core_ops.to_doc = + "Log program while executing and replay execution from log."; + record_core_ops.to_open = record_open; + record_core_ops.to_close = record_close; + record_core_ops.to_resume = record_core_resume; + record_core_ops.to_wait = record_wait; + record_core_ops.to_kill = record_core_kill; + record_core_ops.to_fetch_registers = record_core_fetch_registers; + record_core_ops.to_prepare_to_store = record_core_prepare_to_store; + record_core_ops.to_store_registers = record_core_store_registers; + record_core_ops.to_xfer_partial = record_core_xfer_partial; + record_core_ops.to_insert_breakpoint = record_core_insert_breakpoint; + record_core_ops.to_remove_breakpoint = record_core_remove_breakpoint; + record_core_ops.to_can_execute_reverse = record_can_execute_reverse; + record_core_ops.to_has_execution = record_core_has_execution; + record_core_ops.to_stratum = record_stratum; + record_core_ops.to_magic = OPS_MAGIC; +} + +static void show_record_debug (struct ui_file *file, int from_tty, struct cmd_list_element *c, const char *value) { @@ -1153,6 +1670,262 @@ cmd_record_start (char *args, int from_t execute_command ("target record", from_tty); } +/* Record log save-file format + Version 1 + + Header: + 4 bytes: magic number htonl(0x20090829). + 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 length (network byte order). + 8 bytes: memory address (network byte order). + n bytes: memory value (n == memory length). +*/ + +/* bfdcore_write -- write bytes into a core file section. */ + +static int +bfdcore_write (bfd *obfd, asection *osec, void *buf, int len, int *offset) +{ + int ret = bfd_set_section_contents (obfd, osec, buf, *offset, len); + + if (ret) + *offset += len; + return ret; +} + +/* 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; + bfd *obfd; + int dump_size = 0; + asection *osec = NULL; + int bfd_offset = 0; + + if (strcmp (current_target.to_shortname, "record") != 0) + error (_("Process record is not started.\n")); + + if (args && *args) + recfilename = args; + else + { + /* Default recfile name is "gdb_record.PID". */ + snprintf (recfilename_buffer, sizeof (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); + /* Open the output file. */ + obfd = create_gcore_bfd (recfilename); + /* Need a cleanup that will close the file (FIXME: delete it?). */ + old_cleanups = make_cleanup_bfd_close (obfd); + + /* 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 (); + + /* 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; + } + + /* Compute the size needed for the extra bfd section. */ + dump_size = 4; /* magic cookie */ + for (record_list = &record_first; record_list; + record_list = record_list->next) + switch (record_list->type) + { + case record_end: + dump_size += 1; + break; + case record_reg: + dump_size += 1 + 8 + MAX_REGISTER_SIZE; + break; + case record_mem: + dump_size += 1 + 8 + 8 + record_list->u.mem.len; + break; + } + + /* Make the new bfd section. */ + osec = bfd_make_section_anyway_with_flags (obfd, "precord", + SEC_HAS_CONTENTS + | SEC_READONLY); + if (osec == NULL) + error (_("Failed to create 'precord' section for corefile: %s"), + bfd_errmsg (bfd_get_error ())); + bfd_set_section_size (obfd, osec, dump_size); + bfd_set_section_vma (obfd, osec, 0); + bfd_set_section_alignment (obfd, osec, 0); + bfd_section_lma (obfd, osec) = 0; + + /* Save corefile state. */ + write_gcore_file (obfd); + + /* Write out the record log. */ + /* Write the magic code. */ + magic = RECORD_FILE_MAGIC; + if (record_debug) + fprintf_filtered (gdb_stdlog, _("\ +Writing 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n"), + magic); + if (!bfdcore_write (obfd, osec, &magic, sizeof (magic), &bfd_offset)) + error (_("Failed to write 'magic' to %s (%s)"), + recfilename, bfd_errmsg (bfd_get_error ())); + + /* Dump the entries to recfd and forward execute to the end of + record list. */ + record_list = &record_first; + while (1) + { + /* Dump entry. */ + if (record_list != &record_first) + { + uint8_t tmpu8; + uint64_t tmpu64; + + tmpu8 = record_list->type; + if (!bfdcore_write (obfd, osec, &tmpu8, sizeof (tmpu8), + &bfd_offset)) + error (_("Failed to write 'type' to %s (%s)"), + recfilename, bfd_errmsg (bfd_get_error ())); + + 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); + + /* Write regnum. */ + tmpu64 = record_list->u.reg.num; + if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); + if (!bfdcore_write (obfd, osec, &tmpu64, + sizeof (tmpu64), &bfd_offset)) + error (_("Failed to write regnum to %s (%s)"), + recfilename, bfd_errmsg (bfd_get_error ())); + + /* Write regval. */ + if (!bfdcore_write (obfd, osec, record_list->u.reg.val, + MAX_REGISTER_SIZE, &bfd_offset)) + error (_("Failed to write regval to %s (%s)"), + recfilename, bfd_errmsg (bfd_get_error ())); + 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); + + /* Write memlen. */ + tmpu64 = record_list->u.mem.len; + if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); + if (!bfdcore_write (obfd, osec, &tmpu64, + sizeof (tmpu64), &bfd_offset)) + error (_("Failed to write memlen to %s (%s)"), + recfilename, bfd_errmsg (bfd_get_error ())); + + /* Write memaddr. */ + tmpu64 = record_list->u.mem.addr; + if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); + if (!bfdcore_write (obfd, osec, &tmpu64, + sizeof (tmpu64), &bfd_offset)) + error (_("Failed to write memaddr to %s (%s)"), + recfilename, bfd_errmsg (bfd_get_error ())); + + /* Write memval. */ + if (!bfdcore_write (obfd, osec, record_list->u.mem.val, + record_list->u.mem.len, &bfd_offset)) + error (_("Failed to write memval to %s (%s)"), + recfilename, bfd_errmsg (bfd_get_error ())); + } + 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 recfile %s.\n"), recfilename); +} + /* Truncate the record log from the present point of replay until the end. */ @@ -1185,7 +1958,12 @@ cmd_record_stop (char *args, int from_tt { if (!record_list || !from_tty || query (_("Delete recorded log and " "stop recording?"))) - unpush_target (&record_ops); + { + if (strcmp (current_target.to_shortname, "record") == 0) + unpush_target (&record_ops); + else + unpush_target (&record_core_ops); + } } else printf_unfiltered (_("Process record is not started.\n")); @@ -1203,7 +1981,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 (); } } @@ -1243,6 +2021,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 +2030,8 @@ _initialize_record (void) init_record_ops (); add_target (&record_ops); + init_record_core_ops (); + add_target (&record_core_ops); add_setshow_zinteger_cmd ("record", no_class, &record_debug, _("Set debugging of record/replay feature."), @@ -1259,9 +2041,10 @@ _initialize_record (void) NULL, show_record_debug, &setdebuglist, &showdebuglist); - add_prefix_cmd ("record", class_obscure, cmd_record_start, - _("Abbreviated form of \"target record\" command."), - &record_cmdlist, "record ", 0, &cmdlist); + c = add_prefix_cmd ("record", class_obscure, cmd_record_start, + _("Abbreviated form of \"target record\" command."), + &record_cmdlist, "record ", 0, &cmdlist); + set_cmd_completer (c, filename_completer); add_com_alias ("rec", "record", class_obscure, 1); add_prefix_cmd ("record", class_support, set_record_command, _("Set record options"), &set_record_cmdlist, @@ -1276,6 +2059,11 @@ _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); add_cmd ("delete", class_obscure, cmd_record_delete, _("Delete the rest of execution log and start recording it anew."), --- doc/gdb.texinfo | 10 ++++++++++ 1 file changed, 10 insertions(+) --- a/doc/gdb.texinfo +++ b/doc/gdb.texinfo @@ -5214,6 +5214,16 @@ 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}] +Dump the execution records of the inferior process to a core file. +The optional argument @var{file} specifies the file name where to put +the record dump. If not specified, the file name defaults +to @file{gdb_record.@var{pid}}, where @var{pid} is is the PID of the +inferior process. @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] |