From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 31527 invoked by alias); 12 Aug 2009 22:33:46 -0000 Received: (qmail 31383 invoked by uid 22791); 12 Aug 2009 22:33:40 -0000 X-SWARE-Spam-Status: No, hits=-2.4 required=5.0 tests=AWL,BAYES_00 X-Spam-Check-By: sourceware.org Received: from smtp-outbound-1.vmware.com (HELO smtp-outbound-1.vmware.com) (65.115.85.69) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Wed, 12 Aug 2009 22:33:29 +0000 Received: from mailhost3.vmware.com (mailhost3.vmware.com [10.16.27.45]) by smtp-outbound-1.vmware.com (Postfix) with ESMTP id 06AFDB00F; Wed, 12 Aug 2009 15:33:27 -0700 (PDT) Received: from [10.20.94.141] (msnyder-server.eng.vmware.com [10.20.94.141]) by mailhost3.vmware.com (Postfix) with ESMTP id F028ECD91B; Wed, 12 Aug 2009 15:33:26 -0700 (PDT) Message-ID: <4A8342AC.6070507@vmware.com> Date: Wed, 12 Aug 2009 22:38:00 -0000 From: Michael Snyder User-Agent: Thunderbird 1.5.0.12 (X11/20080411) MIME-Version: 1.0 To: "tromey@redhat.com" CC: Hui Zhu , "gdb-patches@sourceware.org" Subject: Re: [RFA/RFC] Add dump and load command to process record (file format etc) References: <4A75054C.9030304@vmware.com> <83ws5koinl.fsf@gnu.org> <83my6fo2pa.fsf@gnu.org> <4A78935B.5030508@vmware.com> <4A79E90E.6090801@vmware.com> <4A82119A.3010405@vmware.com> In-Reply-To: Content-Type: multipart/mixed; boundary="------------010900080309050809000002" X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2009-08/txt/msg00172.txt.bz2 This is a multi-part message in MIME format. --------------010900080309050809000002 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Content-length: 913 Tom Tromey wrote: >>>>>> "Michael" == Michael Snyder writes: > > A few little nits in this patch. Most of these appear more than once. > > Michael> +extern bfd * > Michael> +create_gcore_bfd (char *filename) > Michael> { > > Don't use "extern" on definitions. > > Michael> +static bfd_vma > Michael> +call_target_sbrk (int sbrk_arg) > > New functions should have an explanatory comment. > > Michael> +#include > Michael> +#ifndef O_BINARY > Michael> +#define O_BINARY 0 > Michael> +#endif > Michael> + > Michael> +#include "regcache.h" > Michael> +#include "regset.h" > > I think adding #includes in the middle of a file is ugly. > > Also, I think the O_BINARY define isn't needed -- it looks to me that > defs.h does this. Tom, thanks for the review. Added gcore.h to export those externs, and moved one decl into inferior.h (always meant to do that for linux-fork). --------------010900080309050809000002 Content-Type: text/plain; name="dumpload3.txt" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="dumpload3.txt" Content-length: 39307 2009-08-11 Hui Zhu Michael Snyder * record.c (RECORD_FILE_MAGIC): New constant. (cmd_record_dump): New function. (cmd_record_load): New function. (bfdcore_read): New function. (bfdcore_write): New function. (record_exec_entry): New function, abstracted from record_wait. (record_wait): Call record_exec_entry. (_initialize_record): Add 'record dump' and 'record load' commands. (record_arch_list_add_reg): Use xcalloc instead of xmalloc. (record_list_release): Finish releasing record list. * 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. * inferior.h: Export nullify_last_target_wait_ptid from infrun.c for use by record.c and linux-fork.c. * linux-fork.c (fork_load_infrun_state): Delete extern decl. Index: gcore.h =================================================================== RCS file: gcore.h diff -N gcore.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ gcore.h 12 Aug 2009 22:28:43 -0000 @@ -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 . */ + +extern bfd *create_gcore_bfd (char *filename); +extern void write_gcore_file (bfd *obfd); +extern bfd *load_corefile (char *filename, int from_tty); + Index: inferior.h =================================================================== RCS file: /cvs/src/src/gdb/inferior.h,v retrieving revision 1.135 diff -u -p -r1.135 inferior.h --- inferior.h 28 Jun 2009 00:20:22 -0000 1.135 +++ inferior.h 12 Aug 2009 22:28:43 -0000 @@ -236,6 +236,8 @@ extern void get_last_target_status(ptid_ extern void follow_inferior_reset_breakpoints (void); +extern void nullify_last_target_wait_ptid (void); + /* Throw an error indicating the current thread is running. */ extern void error_is_running (void); Index: gcore.c =================================================================== RCS file: /cvs/src/src/gdb/gcore.c,v retrieving revision 1.34 diff -u -p -r1.34 gcore.c --- gcore.c 2 Jul 2009 17:21:06 -0000 1.34 +++ gcore.c 12 Aug 2009 22:28:43 -0000 @@ -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 +#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,9 +93,47 @@ 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; Index: record.c =================================================================== RCS file: /cvs/src/src/gdb/record.c,v retrieving revision 1.11 diff -u -p -r1.11 record.c --- record.c 8 Aug 2009 01:57:44 -0000 1.11 +++ record.c 12 Aug 2009 22:28:43 -0000 @@ -23,15 +23,24 @@ #include "gdbthread.h" #include "event-top.h" #include "exceptions.h" +#include "completer.h" #include "record.h" +#include #include +#include +#include "elf-bfd.h" +#include "gdbcore.h" +#include +#include "gcore.h" #define DEFAULT_RECORD_INSN_MAX_NUM 200000 #define RECORD_IS_REPLAY \ (record_list->next || execution_direction == EXEC_REVERSE) +#define RECORD_FILE_MAGIC htonl(0x20090726) /* Host to network order */ + /* These are the core struct of record function. An record_entry is a record of the value change of a register @@ -146,6 +155,11 @@ record_list_release (struct record_entry if (rec != &record_first) xfree (rec); + + record_list = &record_first; + record_arch_list_tail = NULL; + record_arch_list_tail = NULL; + record_insn_num = 0; } static void @@ -241,7 +255,7 @@ record_arch_list_add_reg (struct regcach num); rec = (struct record_entry *) xmalloc (sizeof (struct record_entry)); - rec->u.reg.val = (gdb_byte *) xmalloc (MAX_REGISTER_SIZE); + rec->u.reg.val = (gdb_byte *) xcalloc (1, MAX_REGISTER_SIZE); rec->prev = NULL; rec->next = NULL; rec->type = record_reg; @@ -416,6 +430,95 @@ 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); + + memset (reg, 0, sizeof (reg)); + 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 */ + { + /* Nothing to do if the entry is flagged not_accessible. */ + 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) + { + /* Read failed -- + flag entry as not_accessible. */ + 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) + { + /* Write failed -- + flag entry as not_accessible. */ + 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); + } + else + memcpy (entry->u.mem.val, mem, entry->u.mem.len); + } + } + } + break; + } +} + static void record_open (char *name, int from_tty) { @@ -712,76 +815,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); - - 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 + record_exec_entry (regcache, gdbarch, record_list); + + if (record_list->type == record_end) { if (record_debug > 1) fprintf_unfiltered (gdb_stdlog, @@ -999,6 +1035,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); } @@ -1153,6 +1190,448 @@ 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 RECORD_FILE_MAGIC. + 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) + 16 bytes: register value (target byte order) + record_mem: + 1 byte: record type (record_mem) + 8 bytes: memory address (network byte order) + 8 bytes: memory length (network byte order) + n bytes: memory value (n == memory length, target byte order) + + Version 2 (proposed) + + Header: + 4 bytes: magic number RECORD_FILE_MAGIC. + NOTE: be sure to change whenever this file format changes! + n bytes: architecture... + 4 bytes: size of register snapshot + n bytes: register snapshot + 4 bytes: number of section crcs + n bytes: section names with crcs + + Records: + See version 1. + */ + +/* 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; + struct record_entry *p; + int bfd_offset = 0; + + + if (current_target.to_stratum != record_stratum) + error (_("Process record is not started.\n")); + + if (args && *args) + recfilename = args; + else + { + /* Default recfile name is "rec.PID". */ + sprintf (recfilename_buffer, "rec.%d", PIDGET (inferior_ptid)); + recfilename = recfilename_buffer; + } + + /* Open the dump file. */ + if (record_debug) + fprintf_filtered (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. */ + for (; record_list && record_list != &record_first; + record_list = record_list->prev) + record_exec_entry (regcache, gdbarch, record_list); + + /* Compute the size needed for the extra bfd section. */ + dump_size = 4; /* magic cookie */ + for (p = &record_first; p; p = p->next) + switch (p->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 + p->u.mem.len; + break; + } + + /* Make the new bfd section. */ + osec = bfd_make_section_anyway (obfd, "precord"); + bfd_set_section_size (obfd, osec, dump_size); + bfd_set_section_vma (obfd, osec, 0); + bfd_section_lma (obfd, osec) = 0; + bfd_set_section_flags (obfd, osec, SEC_ALLOC | SEC_HAS_CONTENTS); + + /* Save corefile state. */ + write_gcore_file (obfd); + + /* Write out the record log (modified Hui method). */ + /* 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 into the new bfd section. */ + for (p = &record_first; p; p = p->next) + { + uint8_t tmpu8; + uint64_t tmpu64; + + tmpu8 = p->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 (p->type) + { + case record_reg: /* reg */ + tmpu64 = p->u.reg.num; + if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); + + if (record_debug) + fprintf_filtered (gdb_stdlog, _("\ + Writing register %d val 0x%016llx (1 plus 8 plus %d bytes)\n"), + p->u.reg.num, + *(ULONGEST *) p->u.reg.val, + MAX_REGISTER_SIZE); + /* FIXME: register num does not need 8 bytes. */ + if (!bfdcore_write (obfd, osec, &tmpu64, + sizeof (tmpu64), &bfd_offset)) + error (_("Failed to write regnum to %s (%s)"), + recfilename, bfd_errmsg (bfd_get_error ())); + + /* FIXME: add a len field, and write the smaller value. */ + if (!bfdcore_write (obfd, osec, p->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 */ + tmpu64 = p->u.mem.addr; + if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); + + if (record_debug) + fprintf_filtered (gdb_stdlog, _("\ + Writing memory 0x%08x (1 plus 8 plus 8 bytes plus %d bytes)\n"), + (unsigned int) p->u.mem.addr, + p->u.mem.len); + if (!bfdcore_write (obfd, osec, &tmpu64, sizeof (tmpu64), &bfd_offset)) + error (_("Failed to write memaddr to %s (%s)"), + recfilename, bfd_errmsg (bfd_get_error ())); + + tmpu64 = p->u.mem.len; + if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); + + /* FIXME: len does not need 8 bytes. */ + if (!bfdcore_write (obfd, osec, &tmpu64, sizeof (tmpu64), &bfd_offset)) + error (_("Failed to write memlen to %s (%s)"), + recfilename, bfd_errmsg (bfd_get_error ())); + + if (!bfdcore_write (obfd, osec, p->u.mem.val, + p->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_filtered (gdb_stdlog, _("\ + Writing record_end (1 byte)\n")); + break; + } + } + + /* Now forward-execute back to the saved entry. */ + for (record_list = &record_first; + record_list && record_list != cur_record_list; + record_list = record_list->next) + record_exec_entry (regcache, gdbarch, record_list); + + /* Clean-ups will close the output file and free malloc memory. */ + do_cleanups (old_cleanups); + + /* Succeeded. */ + fprintf_filtered (gdb_stdout, "Saved recfile %s.\n", recfilename); +} + +/* bfdcore_read -- read bytes from a core file section. */ + +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; +} + +/* 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; + bfd *core_bfd; + asection *osec; + + if (!args || (args && !*args)) + error (_("Argument for filename required.\n")); + + /* Open the load file. */ + if (record_debug) + fprintf_filtered (gdb_stdlog, + _("Restoring recording from file '%s'\n"), args); + + /* Restore corefile regs and mem sections. */ + core_bfd = load_corefile (args, from_tty); + old_cleanups = make_cleanup_bfd_close (core_bfd); + + /* 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) + { + int i, len; + int bfd_offset = 0; + + if (record_debug) + fprintf_filtered (gdb_stdlog, "osec name = '%s'\n", + bfd_section_name (core_bfd, osec)); + len = (int) bfd_section_size (core_bfd, osec); + printf_filtered ("osec size = %d\n", len); + + /* Check the magic code. */ + if (!bfdcore_read (core_bfd, osec, &magic, + sizeof (magic), &bfd_offset)) + error (_("Failed to read 'magic' from %s (%s)"), + args, bfd_errmsg (bfd_get_error ())); + + if (magic != RECORD_FILE_MAGIC) + error (_("'%s', version mis-match / file format error."), + args); + + if (record_debug) + fprintf_filtered (gdb_stdlog, _("\ + Reading 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n"), + magic); + if (current_target.to_stratum != record_stratum) + { + /* FIXME need cleanup! We might error out. */ + cmd_record_start (NULL, from_tty); + printf_unfiltered (_("Auto start process record.\n")); + } + + /* Free any existing record log, and load the entries in + core_bfd to the new record log. */ + record_list_release (record_arch_list_tail); + old_cleanups2 = make_cleanup (record_message_cleanups, 0); + + while (1) + { + 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 */ + /* FIXME: abstract out into an 'insert' function. */ + rec = (struct record_entry *) + xmalloc (sizeof (struct record_entry)); + rec->u.reg.val = (gdb_byte *) xcalloc (1, MAX_REGISTER_SIZE); + rec->prev = NULL; + rec->next = NULL; + rec->type = record_reg; + /* Get num. */ + /* FIXME: register num does not need 8 bytes. */ + if (!bfdcore_read (core_bfd, osec, &tmpu64, + sizeof (tmpu64), &bfd_offset)) + error (_("Failed to read regnum from %s (%s)"), + args, bfd_errmsg (bfd_get_error ())); + + if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); + rec->u.reg.num = tmpu64; + + /* Get val. */ + if (!bfdcore_read (core_bfd, osec, rec->u.reg.val, + MAX_REGISTER_SIZE, &bfd_offset)) + error (_("Failed to read regval from %s (%s)"), + args, bfd_errmsg (bfd_get_error ())); + + if (record_debug) + fprintf_filtered (gdb_stdlog, _("\ + Reading register %d val 0x%016llx (1 plus 8 plus %d bytes)\n"), + rec->u.reg.num, + *(ULONGEST *) 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. */ + if (!bfdcore_read (core_bfd, osec, &tmpu64, + sizeof (tmpu64), &bfd_offset)) + error (_("Failed to read memaddr from %s (%s)"), + args, bfd_errmsg (bfd_get_error ())); + if (BYTE_ORDER == LITTLE_ENDIAN) + tmpu64 = bswap_64 (tmpu64); + rec->u.mem.addr = tmpu64; + + /* Get len. */ + /* FIXME: len does not need 8 bytes. */ + if (!bfdcore_read (core_bfd, osec, &tmpu64, + sizeof (tmpu64), &bfd_offset)) + error (_("Failed to read memlen from %s (%s)"), + args, bfd_errmsg (bfd_get_error ())); + 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. */ + if (!bfdcore_read (core_bfd, osec, rec->u.mem.val, + rec->u.mem.len, &bfd_offset)) + error (_("Failed to read memval from %s (%s)"), + args, bfd_errmsg (bfd_get_error ())); + if (record_debug) + fprintf_filtered (gdb_stdlog, _("\ + Reading memory 0x%08x (1 plus 8 plus %d bytes)\n"), + (unsigned int) rec->u.mem.addr, + rec->u.mem.len); + record_arch_list_add (rec); + break; + + case record_end: /* end */ + /* FIXME: restore the contents of record_end rec. */ + rec = (struct record_entry *) + xmalloc (sizeof (struct record_entry)); + rec->prev = NULL; + rec->next = NULL; + rec->type = record_end; + if (record_debug) + fprintf_filtered (gdb_stdlog, _("\ + Reading record_end (one byte)\n")); + 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. (??? FIXME)*/ + 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); + registers_changed (); + reinit_frame_cache (); + nullify_last_target_wait_ptid (); + print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC); +} + /* Truncate the record log from the present point of replay until the end. */ @@ -1243,6 +1722,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; @@ -1276,6 +1757,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.'."), + &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."), @@ -1290,15 +1780,15 @@ _initialize_record (void) /* Record instructions number limit command. */ add_setshow_boolean_cmd ("stop-at-limit", no_class, - &record_stop_at_limit, _("\ + &record_stop_at_limit, _("\ Set whether record/replay stops when record/replay buffer becomes full."), _("\ Show whether record/replay stops when record/replay buffer becomes full."), _("\ Default is ON.\n\ When ON, if the record/replay buffer becomes full, ask user what to do.\n\ When OFF, if the record/replay buffer becomes full,\n\ delete the oldest recorded instruction to make room for each new one."), - NULL, NULL, - &set_record_cmdlist, &show_record_cmdlist); + NULL, NULL, + &set_record_cmdlist, &show_record_cmdlist); add_setshow_zinteger_cmd ("insn-number-max", no_class, &record_insn_max_num, _("Set record/replay buffer limit."), Index: linux-fork.c =================================================================== RCS file: /cvs/src/src/gdb/linux-fork.c,v retrieving revision 1.29 diff -u -p -r1.29 linux-fork.c --- linux-fork.c 2 Jul 2009 22:24:07 -0000 1.29 +++ linux-fork.c 12 Aug 2009 22:28:43 -0000 @@ -235,7 +235,6 @@ call_lseek (int fd, off_t offset, int wh static void fork_load_infrun_state (struct fork_info *fp) { - extern void nullify_last_target_wait_ptid (); int i; linux_nat_switch_fork (fp->ptid); --------------010900080309050809000002--