From: Michael Snyder <msnyder@vmware.com>
To: "tromey@redhat.com" <tromey@redhat.com>
Cc: Hui Zhu <teawater@gmail.com>,
"gdb-patches@sourceware.org" <gdb-patches@sourceware.org>
Subject: Re: [RFA/RFC] Add dump and load command to process record (file format etc)
Date: Wed, 12 Aug 2009 22:38:00 -0000 [thread overview]
Message-ID: <4A8342AC.6070507@vmware.com> (raw)
In-Reply-To: <m3ab25rrtr.fsf@fleche.redhat.com>
[-- Attachment #1: Type: text/plain, Size: 913 bytes --]
Tom Tromey wrote:
>>>>>> "Michael" == Michael Snyder <msnyder@vmware.com> 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 <fcntl.h>
> 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).
[-- Attachment #2: dumpload3.txt --]
[-- Type: text/plain, Size: 39307 bytes --]
2009-08-11 Hui Zhu <teawater@gmail.com>
Michael Snyder <msnyder@vmware.com>
* 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 <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);
+
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 <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,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 <byteswap.h>
#include <signal.h>
+#include <netinet/in.h>
+#include "elf-bfd.h"
+#include "gdbcore.h"
+#include <ctype.h>
+#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.<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."),
@@ -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);
next prev parent reply other threads:[~2009-08-12 22:33 UTC|newest]
Thread overview: 54+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-08-01 7:31 [RFA/RFC] Add dump and load command to process record and replay Hui Zhu
2009-08-01 9:57 ` Eli Zaretskii
2009-08-01 19:20 ` Michael Snyder
2009-08-02 3:18 ` Michael Snyder
2009-08-02 5:58 ` Hui Zhu
2009-08-03 4:12 ` Hui Zhu
2009-08-03 18:29 ` Eli Zaretskii
2009-08-04 1:58 ` Hui Zhu
2009-08-04 2:07 ` Hui Zhu
2009-08-04 18:26 ` Eli Zaretskii
2009-08-04 20:01 ` Michael Snyder
2009-08-05 9:21 ` Hui Zhu
2009-08-05 20:19 ` [RFA/RFC] Add dump and load command to process record (file format etc) Michael Snyder
2009-08-06 2:17 ` Hui Zhu
2009-08-12 14:11 ` Michael Snyder
2009-08-12 15:16 ` Tom Tromey
2009-08-12 22:38 ` Michael Snyder [this message]
2009-08-16 0:04 ` Hui Zhu
2009-08-05 21:23 ` [RFA/RFC] Add dump and load command to process record and replay Michael Snyder
2009-08-06 3:14 ` Eli Zaretskii
2009-08-06 14:16 ` Hui Zhu
2009-08-07 3:27 ` Michael Snyder
2009-08-07 3:29 ` Hui Zhu
2009-08-07 3:34 ` Michael Snyder
2009-08-07 4:06 ` Hui Zhu
2009-08-07 8:41 ` Eli Zaretskii
2009-08-07 9:53 ` Hui Zhu
2009-08-07 12:51 ` Eli Zaretskii
2009-08-07 16:22 ` Hui Zhu
2009-08-07 17:42 ` Michael Snyder
2009-08-08 13:28 ` Hui Zhu
2009-08-10 3:09 ` Michael Snyder
2009-08-22 17:39 ` Hui Zhu
2009-08-23 1:14 ` Hui Zhu
2009-08-23 23:43 ` Michael Snyder
2009-08-24 8:20 ` Hui Zhu
2009-08-24 18:32 ` Michael Snyder
2009-08-25 8:47 ` Hui Zhu
2009-08-26 1:40 ` Michael Snyder
2009-08-26 2:59 ` Michael Snyder
2009-08-29 15:53 ` Hui Zhu
2009-08-29 18:06 ` Eli Zaretskii
2009-08-29 18:28 ` Hui Zhu
2009-08-29 20:26 ` Eli Zaretskii
2009-08-29 20:39 ` Michael Snyder
2009-08-30 3:03 ` Eli Zaretskii
2009-08-30 5:36 ` Hui Zhu
2009-08-30 23:40 ` Eli Zaretskii
2009-08-31 7:10 ` Hui Zhu
2009-09-05 3:30 ` Michael Snyder
2009-09-06 16:29 ` Hui Zhu
2009-08-29 20:05 ` Michael Snyder
2009-08-29 20:33 ` Eli Zaretskii
2009-08-29 21:20 ` Michael Snyder
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4A8342AC.6070507@vmware.com \
--to=msnyder@vmware.com \
--cc=gdb-patches@sourceware.org \
--cc=teawater@gmail.com \
--cc=tromey@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox