Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [rfc 5/5] record, disas: add record disassemble command
  2013-02-08 15:30 [rfc 0/5] record-btrace markus.t.metzger
@ 2013-02-08 15:30 ` markus.t.metzger
  2013-02-10 22:11   ` Jan Kratochvil
  2013-02-08 15:30 ` [rfc 1/5] target: add add_deprecated_target_alias markus.t.metzger
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 23+ messages in thread
From: markus.t.metzger @ 2013-02-08 15:30 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches, markus.t.metzger, Markus Metzger

From: Markus Metzger <markus.t.metzger@intel.com>

Add a command to provide a disassembly of the execution trace log.

2013-02-08 Markus Metzger <markus.t.metzger@intel.com>

	* target.h (target_ops): Add to_disas_record and
	to_disas_record_range fields.
	(target_disas_record): New.
	(target_disas_record_range): New.
	* target.c (target_disas_record): New.
	(target_disas_record_range): New.
	* record.c: Include cli/cli-utils.h, disasm.h, ctype.h.
	(record_disas_size): New.
	(get_insn_number): New.
	(get_disas_modifiers): New.
	(cmd_record_disas): New.
	(_initialize_record): Add "set/show record disas-size" command.
	Add "record disassemble" command.


---
 gdb/record.c |  150 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/target.c |   34 +++++++++++++
 gdb/target.h |   12 +++++
 3 files changed, 196 insertions(+), 0 deletions(-)

diff --git a/gdb/record.c b/gdb/record.c
index 7f26b41..e081110 100644
--- a/gdb/record.c
+++ b/gdb/record.c
@@ -23,10 +23,17 @@
 #include "record.h"
 #include "observer.h"
 #include "inferior.h"
+#include "cli/cli-utils.h"
+#include "disasm.h"
+
+#include <ctype.h>
 
 /* This is the debug switch for process record.  */
 unsigned int record_debug = 0;
 
+/* The number of instructions to disassemble in "record disas".  */
+static unsigned int record_disas_size = 10;
+
 struct cmd_list_element *record_cmdlist = NULL;
 struct cmd_list_element *set_record_cmdlist = NULL;
 struct cmd_list_element *show_record_cmdlist = NULL;
@@ -271,6 +278,127 @@ cmd_record_goto (char *arg, int from_tty)
     }
 }
 
+/* Read an instruction number from an argument string.  */
+
+static ULONGEST
+get_insn_number (char **arg)
+{
+  ULONGEST number;
+  const char *begin, *end, *pos;
+
+  begin = *arg;
+  pos = skip_spaces_const (begin);
+
+  if (!isdigit (*pos))
+    error (_("Expected positive number, got: %s."), pos);
+
+  number = strtoulst (pos, &end, 10);
+
+  *arg += (end - begin);
+
+  return number;
+}
+
+/* Read disassembly modifiers from an argument string.  */
+
+static int
+get_disas_modifiers (char **arg)
+{
+  int modifiers;
+  char *args;
+
+  modifiers = 0;
+  args = *arg;
+
+  if (args == NULL)
+    return 0;
+
+  while (*args == '/')
+    {
+      ++args;
+
+      if (*args == '\0')
+	error (_("Missing modifier."));
+
+      for (; *args; ++args)
+	{
+	  if (isspace (*args))
+	    break;
+
+	  if (*args == '/')
+	    continue;
+
+	  switch (*args)
+	    {
+	    case 'm':
+	      modifiers |= DISASSEMBLY_SOURCE;
+	      modifiers |= DISASSEMBLY_FILENAME;
+	      break;
+	    case 'r':
+	      modifiers |= DISASSEMBLY_RAW_INSN;
+	      break;
+	    default:
+	      error (_("Invalid modifier: %c."), *args);
+	    }
+	}
+
+      args = skip_spaces (args);
+    }
+
+  /* Update the argument string.  */
+  *arg = args;
+
+  return modifiers;
+}
+
+/* The "record disassemble" command.  */
+
+static void
+cmd_record_disas (char *arg, int from_tty)
+{
+  int flags;
+
+  require_record_target ();
+
+  flags = get_disas_modifiers (&arg);
+
+  if (arg == NULL || *arg == 0 || strcmp (arg, "+") == 0)
+    target_disas_record ((int) record_disas_size, flags);
+  else if (strcmp (arg, "-") == 0)
+    target_disas_record (- (int) record_disas_size, flags);
+  else
+    {
+      ULONGEST begin, end;
+
+      begin = get_insn_number (&arg);
+
+      if (*arg == ',')
+	{
+	  ++arg;
+	  end = get_insn_number (&arg);
+	}
+      else
+	{
+	  ULONGEST before;
+
+	  /* If the execution log does not start at zero, we might not
+	     disassemble the entire record_disas_size instructions.  */
+
+	  before = record_disas_size / 2;
+	  if (begin < before)
+	    before = begin;
+
+	  begin -= before;
+	  end = begin + record_disas_size;
+	}
+
+      if (*arg != 0)
+	error (_("Junk after argument: %s."), arg);
+
+      target_disas_record_range (begin, end, flags);
+    }
+}
+
 /* Provide a prototype to silence -Wmissing-prototypes.  */
 extern initialize_file_ftype _initialize_record;
 
@@ -287,6 +415,12 @@ _initialize_record (void)
 			     NULL, show_record_debug, &setdebuglist,
 			     &showdebuglist);
 
+  add_setshow_uinteger_cmd ("disas-size", no_class, &record_disas_size, _("\
+Set number of instructions to print in \"record disassemble\"."), _("\
+Show number of instructions to print in \"record disassemble\"."),
+			    NULL, NULL, NULL, &set_record_cmdlist,
+			    &show_record_cmdlist);
+
   c = add_prefix_cmd ("record", class_obscure, cmd_record_start,
 		      _("Start recording."),
 		      &record_cmdlist, "record ", 0, &cmdlist);
@@ -328,4 +462,20 @@ Default filename is 'gdb_record.<process_id>'."),
 Restore the program to its state at instruction number N.\n\
 Argument is instruction number, as shown by 'info record'."),
 	   &record_cmdlist);
+
+  add_cmd ("disassemble", class_obscure, cmd_record_disas, _("\
+Disassemble a section of the execution log.\n\
+With a /m modifier, source lines are included (if available).\n\
+With a /r modifier, raw instructions in hex are included.\n\
+With no argument, disassembles ten more instructions after or around the \
+previous disassembly.\n\
+\"disassemble -\" disassembles ten instructions before a previous ten-line \
+disassembly.\n\
+One argument specifies an instruction, and ten instructions are disassembled \
+around that instruction.\n\
+Two arguments with comma between specify starting and ending instructions to \
+disassemble.\n\
+The number of instructions to disassemble can be defined with \"set record \
+disas-size\"."),
+           &record_cmdlist);
 }
diff --git a/gdb/target.c b/gdb/target.c
index e71ab96..6b65ef5 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -4361,6 +4361,40 @@ target_goto_record (ULONGEST insn)
   tcomplain ();
 }
 
+/* See target.h.  */
+
+void
+target_disas_record (int size, int flags)
+{
+  struct target_ops *t;
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    if (t->to_disas_record != NULL)
+      {
+	t->to_disas_record (size, flags);
+	return;
+      }
+
+  tcomplain ();
+}
+
+/* See target.h.  */
+
+void
+target_disas_record_range (ULONGEST begin, ULONGEST end, int flags)
+{
+  struct target_ops *t;
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    if (t->to_disas_record_range != NULL)
+      {
+	t->to_disas_record_range (begin, end, flags);
+	return;
+      }
+
+  tcomplain ();
+}
+
 static void
 debug_to_prepare_to_store (struct regcache *regcache)
 {
diff --git a/gdb/target.h b/gdb/target.h
index e4fe5da..bf0d825 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -897,6 +897,12 @@ struct target_ops
     /* Go to a specific location in the recorded execution trace.  */
     void (*to_goto_record) (ULONGEST);
 
+    /* Disassemble the recorded execution trace.  */
+    void (*to_disas_record) (int size, int flags);
+
+    /* Disassemble a section of the recorded execution trace.  */
+    void (*to_disas_record_range) (ULONGEST begin, ULONGEST end, int flags);
+
     int to_magic;
     /* Need sub-structure for target machine related rather than comm related?
      */
@@ -1983,4 +1989,10 @@ extern void target_goto_record_end (void);
 /* Go to a specific location in the recorded execution trace.  */
 extern void target_goto_record (ULONGEST);
 
+/* Disassemble the recorded execution trace.  */
+extern void target_disas_record (int size, int flags);
+
+/* Disassemble a section of the recorded execution trace.  */
+extern void target_disas_record_range (ULONGEST begin, ULONGEST end, int flags);
+
 #endif /* !defined (TARGET_H) */
-- 
1.7.0.7


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [rfc 0/5] record-btrace
@ 2013-02-08 15:30 markus.t.metzger
  2013-02-08 15:30 ` [rfc 5/5] record, disas: add record disassemble command markus.t.metzger
                   ` (4 more replies)
  0 siblings, 5 replies; 23+ messages in thread
From: markus.t.metzger @ 2013-02-08 15:30 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches, markus.t.metzger, Markus Metzger

From: Markus Metzger <markus.t.metzger@intel.com>

Hi Jan,

Here are a few patches to refactor record and add the first new record command
for disassembling a section of the execution log.

I developed this on top of the btrace series, but the patches should apply on
master, as well.

I hope I incorporated all of your feedback.

regards,
markus.


Markus Metzger (5):
  target: add add_deprecated_target_alias
  record: split record
  record: make it build again
  record: default target methods.
  record, disas: add record disassemble command

 gdb/Makefile.in        |    5 +-
 gdb/amd64-linux-tdep.c |    2 +-
 gdb/arm-tdep.c         |    1 +
 gdb/i386-linux-tdep.c  |    2 +-
 gdb/i386-tdep.c        |    1 +
 gdb/infrun.c           |    1 +
 gdb/linux-record.c     |    1 +
 gdb/moxie-tdep.c       |    1 +
 gdb/record-full.c      | 2984 +++++++++++++++++++++++++++++++++++++++++++++++
 gdb/record-full.h      |   36 +
 gdb/record.c           | 3036 ++++--------------------------------------------
 gdb/record.h           |   35 +-
 gdb/target.c           |  177 +++
 gdb/target.h           |   61 +
 14 files changed, 3511 insertions(+), 2832 deletions(-)
 create mode 100644 gdb/record-full.c
 create mode 100644 gdb/record-full.h


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [rfc 1/5] target: add add_deprecated_target_alias
  2013-02-08 15:30 [rfc 0/5] record-btrace markus.t.metzger
  2013-02-08 15:30 ` [rfc 5/5] record, disas: add record disassemble command markus.t.metzger
@ 2013-02-08 15:30 ` markus.t.metzger
  2013-02-10 22:10   ` Jan Kratochvil
  2013-02-08 15:31 ` [rfc 4/5] record: default target methods markus.t.metzger
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 23+ messages in thread
From: markus.t.metzger @ 2013-02-08 15:30 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches, markus.t.metzger, Markus Metzger

From: Markus Metzger <markus.t.metzger@intel.com>

Add a new function to target.h to add an alias command for a target and mark it
deprecated.  This is useful when renaming targets.

2013-02-08 Markus Metzger <markus.t.metzger@intel.com>

	* target.h (add_deprecated_target_alias): New.
	* target.c (add_deprecated_target_alias): New.


---
 gdb/target.c |   14 ++++++++++++++
 gdb/target.h |    5 +++++
 2 files changed, 19 insertions(+), 0 deletions(-)

diff --git a/gdb/target.c b/gdb/target.c
index 10f69dc..25f4629 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -434,6 +434,20 @@ information on the arguments for a particular protocol, type\n\
   add_cmd (t->to_shortname, no_class, t->to_open, t->to_doc, &targetlist);
 }
 
+/* See target.h.  */
+
+void
+add_deprecated_target_alias (struct target_ops *t, char *alias)
+{
+  struct cmd_list_element *c;
+  char *alt;
+
+  /* If we use add_alias_cmd, here, we do not get the deprecated warning.  */
+  c = add_cmd (alias, no_class, t->to_open, t->to_doc, &targetlist);
+  alt = xstrprintf ("target %s", t->to_shortname);
+  deprecate_cmd (c, alt);
+}
+
 /* Stub functions */
 
 void
diff --git a/gdb/target.h b/gdb/target.h
index c543118..1d73336 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -1779,6 +1779,11 @@ int target_verify_memory (const gdb_byte *data,
 
 extern void add_target (struct target_ops *);
 
+/* Adds a command ALIAS for target T and marks it deprecated.  This is useful
+   for maintaining backwards compatibility when renaming targets.  */
+
+extern void add_deprecated_target_alias (struct target_ops *t, char *alias);
+
 extern void push_target (struct target_ops *);
 
 extern int unpush_target (struct target_ops *);
-- 
1.7.0.7


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [rfc 4/5] record: default target methods.
  2013-02-08 15:30 [rfc 0/5] record-btrace markus.t.metzger
  2013-02-08 15:30 ` [rfc 5/5] record, disas: add record disassemble command markus.t.metzger
  2013-02-08 15:30 ` [rfc 1/5] target: add add_deprecated_target_alias markus.t.metzger
@ 2013-02-08 15:31 ` markus.t.metzger
  2013-02-10 22:11   ` Jan Kratochvil
  2013-02-08 15:31 ` [rfc 3/5] record: make it build again markus.t.metzger
  2013-02-08 15:32 ` [rfc 2/5] record: split record markus.t.metzger
  4 siblings, 1 reply; 23+ messages in thread
From: markus.t.metzger @ 2013-02-08 15:31 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches, markus.t.metzger, Markus Metzger

From: Markus Metzger <markus.t.metzger@intel.com>

Provide default target methods for record targets that are likely to be shared
between different record targets.

2013-02-08 Markus Metzger <markus.t.metzger@intel.com>

	* record.h (record_disconnect): New.
	(record_detach): New.
	(record_mourn_inferior): New.
	(record_kill): New.
	* record-full.c (record_disconnect, record_detach,
	record_mourn_inferior, record_kill): Move to...
	* record.c: ...here.
	(DEBUG): New.
	(record_unpush): New.
	(cmd_record_stop): Use record_unpush.
	(record_disconnect, record_detach, record_mourn_inferior,
	record_kill): Use record_unpush and DEBUG.


---
 gdb/record-full.c |   49 ----------------------------------------
 gdb/record.c      |   64 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/record.h      |   15 +++++++++++-
 3 files changed, 78 insertions(+), 50 deletions(-)

diff --git a/gdb/record-full.c b/gdb/record-full.c
index 4e61c5a..6aa0033 100644
--- a/gdb/record-full.c
+++ b/gdb/record-full.c
@@ -1515,55 +1515,6 @@ record_stopped_by_watchpoint (void)
     return record_beneath_to_stopped_by_watchpoint ();
 }
 
-/* "to_disconnect" method for process record target.  */
-
-static void
-record_disconnect (struct target_ops *target, char *args, int from_tty)
-{
-  if (record_debug)
-    fprintf_unfiltered (gdb_stdlog, "Process record: record_disconnect\n");
-
-  unpush_target (&record_ops);
-  target_disconnect (args, from_tty);
-}
-
-/* "to_detach" method for process record target.  */
-
-static void
-record_detach (struct target_ops *ops, char *args, int from_tty)
-{
-  if (record_debug)
-    fprintf_unfiltered (gdb_stdlog, "Process record: record_detach\n");
-
-  unpush_target (&record_ops);
-  target_detach (args, from_tty);
-}
-
-/* "to_mourn_inferior" method for process record target.  */
-
-static void
-record_mourn_inferior (struct target_ops *ops)
-{
-  if (record_debug)
-    fprintf_unfiltered (gdb_stdlog, "Process record: "
-			            "record_mourn_inferior\n");
-
-  unpush_target (&record_ops);
-  target_mourn_inferior ();
-}
-
-/* Close process record target before killing the inferior process.  */
-
-static void
-record_kill (struct target_ops *ops)
-{
-  if (record_debug)
-    fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n");
-
-  unpush_target (&record_ops);
-  target_kill ();
-}
-
 static int
 record_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p)
 {
diff --git a/gdb/record.c b/gdb/record.c
index 5293417..7f26b41 100644
--- a/gdb/record.c
+++ b/gdb/record.c
@@ -32,6 +32,10 @@ struct cmd_list_element *set_record_cmdlist = NULL;
 struct cmd_list_element *show_record_cmdlist = NULL;
 struct cmd_list_element *info_record_cmdlist = NULL;
 
+#define DEBUG(msg, args...)			\
+  if (record_debug) \
+    fprintf_unfiltered (gdb_stdlog, "record: " msg "\n", ##args)
+
 /* Find the record target in the target stack.  */
 
 static struct target_ops *
@@ -60,6 +64,66 @@ require_record_target (void)
   return t;
 }
 
+/* Unpush the record target.  */
+
+static void
+record_unpush (void)
+{
+  struct target_ops *t;
+
+  t = find_record_target ();
+  if (t == NULL)
+    internal_error (__FILE__, __LINE__, _("Couldn't find record target."));
+
+  DEBUG ("unpush %s", t->to_shortname);
+
+  unpush_target (t);
+}
+
+/* See record.h.  */
+
+void
+record_disconnect (struct target_ops *target, char *args, int from_tty)
+{
+  DEBUG ("disconnect");
+
+  record_unpush ();
+  target_disconnect (args, from_tty);
+}
+
+/* See record.h.  */
+
+void
+record_detach (struct target_ops *ops, char *args, int from_tty)
+{
+  DEBUG ("detach");
+
+  record_unpush ();
+  target_detach (args, from_tty);
+}
+
+/* See record.h.  */
+
+void
+record_mourn_inferior (struct target_ops *ops)
+{
+  DEBUG ("mourn_inferior");
+
+  record_unpush ();
+  target_mourn_inferior ();
+}
+
+/* See record.h.  */
+
+void
+record_kill (struct target_ops *ops)
+{
+  DEBUG ("kill");
+
+  record_unpush ();
+  target_kill ();
+}
+
 /* Implement "show record debug" command.  */
 
 static void
diff --git a/gdb/record.h b/gdb/record.h
index dfd8361..fe93184 100644
--- a/gdb/record.h
+++ b/gdb/record.h
@@ -32,6 +32,19 @@ extern struct cmd_list_element *set_record_cmdlist;
 extern struct cmd_list_element *show_record_cmdlist;
 extern struct cmd_list_element *info_record_cmdlist;
 
-extern void cmd_record_goto (char *arg, int from_tty);
+/* The default "to_disconnect" target method for record targets.  */
+extern void record_disconnect (struct target_ops *, char *, int);
+
+/* The default "to_detach" target method for record targets.  */
+extern void record_detach (struct target_ops *, char *, int);
+
+/* The default "to_mourn_inferior" target method for record targets.  */
+extern void record_mourn_inferior (struct target_ops *);
+
+/* The default "to_kill" target method for record targets.  */
+extern void record_kill (struct target_ops *);
+
+/* The "record goto" command.  */
+extern void cmd_record_goto (char *, int);
 
 #endif /* _RECORD_H_ */
-- 
1.7.0.7


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [rfc 3/5] record: make it build again
  2013-02-08 15:30 [rfc 0/5] record-btrace markus.t.metzger
                   ` (2 preceding siblings ...)
  2013-02-08 15:31 ` [rfc 4/5] record: default target methods markus.t.metzger
@ 2013-02-08 15:31 ` markus.t.metzger
  2013-02-10 22:11   ` Jan Kratochvil
  2013-02-08 15:32 ` [rfc 2/5] record: split record markus.t.metzger
  4 siblings, 1 reply; 23+ messages in thread
From: markus.t.metzger @ 2013-02-08 15:31 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches, markus.t.metzger, Markus Metzger

From: Markus Metzger <markus.t.metzger@intel.com>

Complete the split of record into record.c and record-full.c

I ran the gdb.reverse suite on 64bit IA gnu/linux - no regressions.

2013-02-08 Markus Metzger <markus.t.metzger@intel.com>

	* target.h (target_ops): Add new fields to_info_record,
	to_save_record, to_delete_record, to_record_is_replaying,
	to_goto_record_begin, to_goto_record_end, to_goto_record.
	(target_info_record): New.
	(target_save_record): New.
	(target_supports_delete_record): New.
	(target_delete_record): New.
	(target_record_is_replaying): New.
	(target_goto_record_begin): New.
	(target_goto_record_end): New.
	(target_goto_record): New.
	* target.c (target_info_record): New.
	(target_save_record): New.
	(target_supports_delete_record): New.
	(target_delete_record): New.
	(target_record_is_replaying): New.
	(target_goto_record_begin): New.
	(target_goto_record_end): New.
	(target_goto_record): New.
	* record.h: Declare struct cmd_list_element.
	(record_cmdlist): New declaration.
	(set_record_cmdlist): New declaration.
	(show_record_cmdlist): New declaration.
	(info_record_cmdlist): New declaration.
	(cmd_record_goto): New declaration.
	* record.c: Remove unnecessary includes.
	Include inferior.h.
	(cmd_record_goto): Remove declaration.
	(record_cmdlist): Now extern. Initialize.
	(set_record_cmdlist): Now extern. Initialize.
	(show_record_cmdlist): Now extern. Initialize.
	(info_record_cmdlist): Now extern. Initialize.
	(find_record_target): New.
	(require_record_target): New.
	(cmd_record_start): Update.
	(cmd_record_delete): Remove target-specific code.
	Call target_delete_record.
	(cmd_record_stop): Unpush any record target.
	(set_record_insn_max_num): Move to record-full.c
	(set_record_command): Add comment.
	(show_record_command): Add comment.
	(info_record_command): Update comment.
	Remove target-specific code.
	Call the record target's to_info_record.
	(cmd_record_start): New.
	(cmd_record_goto): Now extern.
	Remove target-specific code.
	Call target_goto_begin,  target_goto_end, or target_goto.
	(_initialize_record): Move record target ops initialization to
	record-full.c.
	Change "record" command help text.
	Move "record restore", "record set", and "record show" commands to
	record-full.c.
	* Makefile.in (SFILES): Add record-full.c.
	(HFILES_NO_SRCDIR): Add record-full.h.
	(COMMON_OBS): Add record-full.o.
	* amd64-linux-tdep.c: Include record-full.h instead of record.h.
	* arm-tdep.c: Include record-full.h.
	* i386-linux-tdep.c: Include record-full.h instead of record.h.
	* i386-tdep.c: Include record-full.h.
	* infrun.c: Include record-full.h.
	* linux-record.c: Include record-full.h.
	* moxie-tdep.c: Include record-full.h.
	* record-full.c: Include record-full.h.
	Change module comment.
	(set_record_full_cmdlist): New.
	(show_record_full_cmdlist): New.
	(record_full_cmdlist): New.
	(record_goto_insn): New declaration.
	(record_save): New declaration.
	(record_check_insn_num): Change query string.
	(record_info): New.
	(record_delete): New.
	(record_is_replaying): New.
	(record_goto_entry): New.
	(record_goto_begin): New.
	(record_goto_end): New.
	(record_goto): New.
	(init_record_ops): Update.
	(init_record_core_ops): Update.
	(cmd_record_save): Rename to record_save. Remove target and arg checks.
	(cmd_record_start): New.
	(set_record_insn_max_num): Moved from record.c
	(set_record_full_command): New.
	(show_record_full_command): New.
	(_initialize_record_full): New.


---
 gdb/Makefile.in        |    5 +-
 gdb/amd64-linux-tdep.c |    2 +-
 gdb/arm-tdep.c         |    1 +
 gdb/i386-linux-tdep.c  |    2 +-
 gdb/i386-tdep.c        |    1 +
 gdb/infrun.c           |    1 +
 gdb/linux-record.c     |    1 +
 gdb/moxie-tdep.c       |    1 +
 gdb/record-full.c      |  319 +++++++++++++++++++++++++++++++++++++++++++++---
 gdb/record.c           |  286 ++++++++++++++-----------------------------
 gdb/record.h           |   10 ++
 gdb/target.c           |  129 +++++++++++++++++++
 gdb/target.h           |   44 +++++++
 13 files changed, 588 insertions(+), 214 deletions(-)

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 336e8d0..ba284ee 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -752,7 +752,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
 	valarith.c valops.c valprint.c value.c varobj.c common/vec.c \
 	xml-tdesc.c xml-support.c \
 	inferior.c gdb_usleep.c \
-	record.c gcore.c \
+	record.c record-full.c gcore.c \
 	jit.c \
 	xml-syscall.c \
 	annotate.c common/signals.c copying.c dfp.c gdb.c inf-child.c \
@@ -828,6 +828,7 @@ dicos-tdep.h filesystem.h gcore.h gdb_wchar.h hppabsd-tdep.h \
 i386-darwin-tdep.h i386-nat.h linux-record.h moxie-tdep.h \
 osdata.h procfs.h python/py-event.h python/py-events.h python/py-stopevent.h \
 python/python-internal.h python/python.h ravenscar-thread.h record.h \
+record-full.h \
 solib-darwin.h solib-ia64-hpux.h solib-spu.h windows-nat.h xcoffread.h \
 gnulib/import/extra/snippet/arg-nonnull.h gnulib/import/extra/snippet/c++defs.h \
 gnulib/import/extra/snippet/warn-on-use.h \
@@ -925,7 +926,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	prologue-value.o memory-map.o memrange.o \
 	xml-support.o xml-syscall.o xml-utils.o \
 	target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
-	inferior.o osdata.o gdb_usleep.o record.o gcore.o \
+	inferior.o osdata.o gdb_usleep.o record.o record-full.o gcore.o \
 	gdb_vecs.o jit.o progspace.o skip.o probe.o \
 	common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \
 	format.o registry.o btrace.o
diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index e262c19..4f383db 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -48,7 +48,7 @@
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_AMD64 "syscalls/amd64-linux.xml"
 
-#include "record.h"
+#include "record-full.h"
 #include "linux-record.h"
 
 /* Supported register note sections.  */
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c
index ead09d7..abe895c 100644
--- a/gdb/arm-tdep.c
+++ b/gdb/arm-tdep.c
@@ -56,6 +56,7 @@
 #include "vec.h"
 
 #include "record.h"
+#include "record-full.h"
 
 #include "features/arm-with-m.c"
 #include "features/arm-with-m-fpa-layout.c"
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index 15a1247..f96fc81 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -44,7 +44,7 @@
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_I386 "syscalls/i386-linux.xml"
 
-#include "record.h"
+#include "record-full.h"
 #include "linux-record.h"
 #include <stdint.h>
 
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index df077bc..11ac930 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -52,6 +52,7 @@
 #include "i386-xstate.h"
 
 #include "record.h"
+#include "record-full.h"
 #include <stdint.h>
 
 #include "features/i386/i386.c"
diff --git a/gdb/infrun.c b/gdb/infrun.c
index f4ff5bb..58ef8d0 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -49,6 +49,7 @@
 #include "mi/mi-common.h"
 #include "event-top.h"
 #include "record.h"
+#include "record-full.h"
 #include "inline-frame.h"
 #include "jit.h"
 #include "tracepoint.h"
diff --git a/gdb/linux-record.c b/gdb/linux-record.c
index 5a9ec99..b8c7a4e 100644
--- a/gdb/linux-record.c
+++ b/gdb/linux-record.c
@@ -22,6 +22,7 @@
 #include "gdbtypes.h"
 #include "regcache.h"
 #include "record.h"
+#include "record-full.h"
 #include "linux-record.h"
 
 /* These macros are the values of the first argument of system call
diff --git a/gdb/moxie-tdep.c b/gdb/moxie-tdep.c
index 4b250f8..fc0f85c 100644
--- a/gdb/moxie-tdep.c
+++ b/gdb/moxie-tdep.c
@@ -37,6 +37,7 @@
 #include "trad-frame.h"
 #include "dis-asm.h"
 #include "record.h"
+#include "record-full.h"
 
 #include "gdb_assert.h"
 
diff --git a/gdb/record-full.c b/gdb/record-full.c
index 88d0730..4e61c5a 100644
--- a/gdb/record-full.c
+++ b/gdb/record-full.c
@@ -28,6 +28,7 @@
 #include "gdbcore.h"
 #include "exec.h"
 #include "record.h"
+#include "record-full.h"
 #include "elf-bfd.h"
 #include "gcore.h"
 #include "event-loop.h"
@@ -37,7 +38,7 @@
 
 #include <signal.h>
 
-/* This module implements "target record", also known as "process
+/* This module implements "target record-full", also known as "process
    record and replay".  This target sits on top of a "normal" target
    (a target that "has execution"), and provides a record and replay
    functionality, including reverse debugging.
@@ -205,6 +206,13 @@ static ULONGEST record_insn_count;
 static struct target_ops record_ops;
 static struct target_ops record_core_ops;
 
+/* Command lists for "set/show record full".  */
+static struct cmd_list_element *set_record_full_cmdlist;
+static struct cmd_list_element *show_record_full_cmdlist;
+
+/* Command list for "record full".  */
+static struct cmd_list_element *record_full_cmdlist;
+
 /* The beneath function pointers.  */
 static struct target_ops *record_beneath_to_resume_ops;
 static void (*record_beneath_to_resume) (struct target_ops *, ptid_t, int,
@@ -234,6 +242,10 @@ static int (*record_beneath_to_stopped_data_address) (struct target_ops *,
 						      CORE_ADDR *);
 static void (*record_beneath_to_async) (void (*) (enum inferior_event_type, void *), void *);
 
+static void record_goto_insn (struct record_entry *entry,
+			      enum exec_direction_kind dir);
+static void record_save (char *recfilename);
+
 /* Alloc and free functions for record_reg, record_mem, and record_end 
    entries.  */
 
@@ -564,7 +576,7 @@ record_check_insn_num (int set_terminal)
 		target_terminal_ours ();
 	      q = yquery (_("Do you want to auto delete previous execution "
 			    "log entries when record/replay buffer becomes "
-			    "full (record stop-at-limit)?"));
+			    "full (record full stop-at-limit)?"));
 	      if (set_terminal)
 		target_terminal_inferior ();
 	      if (q)
@@ -1948,9 +1960,140 @@ record_execution_direction (void)
 }
 
 static void
+record_info (void)
+{
+  struct record_entry *p;
+
+  if (RECORD_IS_REPLAY)
+    printf_filtered (_("Replay mode:\n"));
+  else
+    printf_filtered (_("Record mode:\n"));
+
+  /* Find entry for first actual instruction in the log.  */
+  for (p = record_first.next;
+       p != NULL && p->type != record_end;
+       p = p->next)
+    ;
+
+  /* Do we have a log at all?  */
+  if (p != NULL && p->type == record_end)
+    {
+      /* Display instruction number for first instruction in the log.  */
+      printf_filtered (_("Lowest recorded instruction number is %s.\n"),
+		       pulongest (p->u.end.insn_num));
+
+      /* If in replay mode, display where we are in the log.  */
+      if (RECORD_IS_REPLAY)
+	printf_filtered (_("Current instruction number is %s.\n"),
+			 pulongest (record_list->u.end.insn_num));
+
+      /* Display instruction number for last instruction in the log.  */
+      printf_filtered (_("Highest recorded instruction number is %s.\n"),
+		       pulongest (record_insn_count));
+
+      /* Display log count.  */
+      printf_filtered (_("Log contains %d instructions.\n"),
+		       record_insn_num);
+    }
+  else
+    printf_filtered (_("No instructions have been logged.\n"));
+
+  /* Display max log size.  */
+  printf_filtered (_("Max logged instructions is %d.\n"),
+		   record_insn_max_num);
+}
+
+/* The "to_record_delete" target method.  */
+
+static void
+record_delete (void)
+{
+  record_list_release_following (record_list);
+}
+
+/* The "to_record_is_replaying" target method.  */
+
+static int
+record_is_replaying (void)
+{
+  return RECORD_IS_REPLAY;
+}
+
+/* Go to a specific entry.  */
+
+static void
+record_goto_entry (struct record_entry *p)
+{
+  if (p == NULL)
+    error (_("Target insn not found."));
+  else if (p == record_list)
+    error (_("Already at target insn."));
+  else if (p->u.end.insn_num > record_list->u.end.insn_num)
+    {
+      printf_filtered (_("Go forward to insn number %s\n"),
+		       pulongest (p->u.end.insn_num));
+      record_goto_insn (p, EXEC_FORWARD);
+    }
+  else
+    {
+      printf_filtered (_("Go backward to insn number %s\n"),
+		       pulongest (p->u.end.insn_num));
+      record_goto_insn (p, EXEC_REVERSE);
+    }
+
+  registers_changed ();
+  reinit_frame_cache ();
+  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
+}
+
+/* The "to_goto_record_begin" target method.  */
+
+static void
+record_goto_begin (void)
+{
+  struct record_entry *p = NULL;
+
+  for (p = &record_first; p != NULL; p = p->next)
+    if (p->type == record_end)
+      break;
+
+  record_goto_entry (p);
+}
+
+/* The "to_goto_record_end" target method.  */
+
+static void
+record_goto_end (void)
+{
+  struct record_entry *p = NULL;
+
+  for (p = record_list; p->next != NULL; p = p->next)
+    ;
+  for (; p!= NULL; p = p->prev)
+    if (p->type == record_end)
+      break;
+
+  record_goto_entry (p);
+}
+
+/* The "to_goto_record" target method.  */
+
+static void
+record_goto (ULONGEST target_insn)
+{
+  struct record_entry *p = NULL;
+
+  for (p = &record_first; p != NULL; p = p->next)
+    if (p->type == record_end && p->u.end.insn_num == target_insn)
+      break;
+
+  record_goto_entry (p);
+}
+
+static void
 init_record_ops (void)
 {
-  record_ops.to_shortname = "record";
+  record_ops.to_shortname = "record-full";
   record_ops.to_longname = "Process record and replay target";
   record_ops.to_doc =
     "Log program while executing and replay execution from log.";
@@ -1978,6 +2121,13 @@ init_record_ops (void)
   record_ops.to_can_async_p = record_can_async_p;
   record_ops.to_is_async_p = record_is_async_p;
   record_ops.to_execution_direction = record_execution_direction;
+  record_ops.to_info_record = record_info;
+  record_ops.to_save_record = record_save;
+  record_ops.to_delete_record = record_delete;
+  record_ops.to_record_is_replaying = record_is_replaying;
+  record_ops.to_goto_record_begin = record_goto_begin;
+  record_ops.to_goto_record_end = record_goto_end;
+  record_ops.to_goto_record = record_goto;
   record_ops.to_magic = OPS_MAGIC;
 }
 
@@ -2203,6 +2353,12 @@ init_record_core_ops (void)
   record_core_ops.to_can_async_p = record_can_async_p;
   record_core_ops.to_is_async_p = record_is_async_p;
   record_core_ops.to_execution_direction = record_execution_direction;
+  record_core_ops.to_info_record = record_info;
+  record_core_ops.to_delete_record = record_delete;
+  record_core_ops.to_record_is_replaying = record_is_replaying;
+  record_core_ops.to_goto_record_begin = record_goto_begin;
+  record_core_ops.to_goto_record_end = record_goto_end;
+  record_core_ops.to_goto_record = record_goto;
   record_core_ops.to_magic = OPS_MAGIC;
 }
 
@@ -2507,9 +2663,8 @@ record_save_cleanups (void *data)
    format, with an extra section for our data.  */
 
 static void
-cmd_record_save (char *args, int from_tty)
+record_save (char *recfilename)
 {
-  char *recfilename, recfilename_buffer[40];
   struct record_entry *cur_record_list;
   uint32_t magic;
   struct regcache *regcache;
@@ -2521,20 +2676,6 @@ cmd_record_save (char *args, int from_tty)
   asection *osec = NULL;
   int bfd_offset = 0;
 
-  if (strcmp (current_target.to_shortname, "record") != 0)
-    error (_("This command can only be used with target 'record'.\n"
-	     "Use 'target record' first.\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 save file.  */
   if (record_debug)
     fprintf_unfiltered (gdb_stdlog, "Saving execution log to core file '%s'\n",
@@ -2750,3 +2891,143 @@ record_goto_insn (struct record_entry *entry,
     } while (record_list != entry);
   do_cleanups (set_cleanups);
 }
+
+/* Alias for "target record-full".  */
+
+static void
+cmd_record_start (char *args, int from_tty)
+{
+  execute_command ("target record-full", from_tty);
+}
+
+static void
+set_record_insn_max_num (char *args, int from_tty, struct cmd_list_element *c)
+{
+  if (record_insn_num > record_insn_max_num && record_insn_max_num)
+    {
+      /* Count down record_insn_num while releasing records from list.  */
+      while (record_insn_num > record_insn_max_num)
+       {
+         record_list_release_first ();
+         record_insn_num--;
+       }
+    }
+}
+
+/* The "set record full" command.  */
+
+static void
+set_record_full_command (char *args, int from_tty)
+{
+  printf_unfiltered (_("\"set record full\" must be followed "
+		       "by an apporpriate subcommand.\n"));
+  help_list (set_record_full_cmdlist, "set record full ", all_commands,
+	     gdb_stdout);
+}
+
+/* The "show record full" command.  */
+
+static void
+show_record_full_command (char *args, int from_tty)
+{
+  cmd_show_list (show_record_full_cmdlist, from_tty, "");
+}
+
+/* Provide a prototype to silence -Wmissing-prototypes.  */
+extern initialize_file_ftype _initialize_record_full;
+
+void
+_initialize_record_full (void)
+{
+  struct cmd_list_element *c;
+
+  /* Init record_first.  */
+  record_first.prev = NULL;
+  record_first.next = NULL;
+  record_first.type = record_end;
+
+  init_record_ops ();
+  add_target (&record_ops);
+  add_deprecated_target_alias (&record_ops, "record");
+  init_record_core_ops ();
+  add_target (&record_core_ops);
+
+  add_prefix_cmd ("full", class_obscure, cmd_record_start,
+		  _("Start full execution recording."), &record_full_cmdlist,
+		  "record full ", 0, &record_cmdlist);
+
+  c = add_cmd ("restore", class_obscure, cmd_record_restore,
+	       _("Restore the execution log from a file.\n\
+Argument is filename.  File must be created with 'record save'."),
+	       &record_full_cmdlist);
+  set_cmd_completer (c, filename_completer);
+
+  /* Deprecate the old version without "full" prefix.  */
+  c = add_alias_cmd ("restore", "full restore", class_obscure, 1,
+		     &record_cmdlist);
+  set_cmd_completer (c, filename_completer);
+  deprecate_cmd (c, "record full restore");
+
+  add_prefix_cmd ("full", class_support, set_record_full_command,
+		  _("Set record options"), &set_record_full_cmdlist,
+		  "set record full ", 0, &set_record_cmdlist);
+
+  add_prefix_cmd ("full", class_support, show_record_full_command,
+		  _("Show record options"), &show_record_full_cmdlist,
+		  "show record full ", 0, &show_record_cmdlist);
+
+  /* Record instructions number limit command.  */
+  add_setshow_boolean_cmd ("stop-at-limit", no_class,
+			   &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_full_cmdlist, &show_record_full_cmdlist);
+
+  c = add_alias_cmd ("stop-at-limit", "full stop-at-limit", no_class, 1,
+		     &set_record_cmdlist);
+  deprecate_cmd (c, "set record full stop-at-limit");
+
+  c = add_alias_cmd ("stop-at-limit", "full stop-at-limit", no_class, 1,
+		     &show_record_cmdlist);
+  deprecate_cmd (c, "show record full stop-at-limit");
+
+  add_setshow_uinteger_cmd ("insn-number-max", no_class, &record_insn_max_num,
+			    _("Set record/replay buffer limit."),
+			    _("Show record/replay buffer limit."), _("\
+Set the maximum number of instructions to be stored in the\n\
+record/replay buffer.  Zero means unlimited.  Default is 200000."),
+			    set_record_insn_max_num,
+			    NULL, &set_record_full_cmdlist,
+			    &show_record_full_cmdlist);
+
+  c = add_alias_cmd ("insn-number-max", "full insn-number-max", no_class, 1,
+		     &set_record_cmdlist);
+  deprecate_cmd (c, "set record full insn-number-max");
+
+  c = add_alias_cmd ("insn-number-max", "full insn-number-max", no_class, 1,
+		     &show_record_cmdlist);
+  deprecate_cmd (c, "show record full insn-number-max");
+
+  add_setshow_boolean_cmd ("memory-query", no_class, &record_memory_query, _("\
+Set whether query if PREC cannot record memory change of next instruction."),
+                           _("\
+Show whether query if PREC cannot record memory change of next instruction."),
+                           _("\
+Default is OFF.\n\
+When ON, query if PREC cannot record memory change of next instruction."),
+			   NULL, NULL,
+			   &set_record_full_cmdlist, &show_record_full_cmdlist);
+
+  c = add_alias_cmd ("memory-query", "full memory-query", no_class, 1,
+		     &set_record_cmdlist);
+  deprecate_cmd (c, "set record full memory-query");
+
+  c = add_alias_cmd ("memory-query", "full memory-query", no_class, 1,
+		     &show_record_cmdlist);
+  deprecate_cmd (c, "show record full memory-query");
+}
diff --git a/gdb/record.c b/gdb/record.c
index 2e970ae..5293417 100644
--- a/gdb/record.c
+++ b/gdb/record.c
@@ -19,29 +19,46 @@
 
 #include "defs.h"
 #include "gdbcmd.h"
-#include "regcache.h"
-#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 "event-loop.h"
-#include "inf-loop.h"
-#include "gdb_bfd.h"
 #include "observer.h"
-
-#include <signal.h>
+#include "inferior.h"
 
 /* This is the debug switch for process record.  */
 unsigned int record_debug = 0;
 
-/* The implementation of the command "record goto".  */
-static void cmd_record_goto (char *, int);
+struct cmd_list_element *record_cmdlist = NULL;
+struct cmd_list_element *set_record_cmdlist = NULL;
+struct cmd_list_element *show_record_cmdlist = NULL;
+struct cmd_list_element *info_record_cmdlist = NULL;
+
+/* Find the record target in the target stack.  */
+
+static struct target_ops *
+find_record_target (void)
+{
+  struct target_ops *t;
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    if (t->to_stratum == record_stratum)
+      return t;
+
+  return NULL;
+}
+
+/* Check that recording is active.  Throw an error, if it isn't.  */
+
+static struct target_ops *
+require_record_target (void)
+{
+  struct target_ops *t;
+
+  t = find_record_target ();
+  if (t == NULL)
+    error (_("No record target is currently active."));
+
+  return t;
+}
 
 /* Implement "show record debug" command.  */
 
@@ -58,7 +75,7 @@ show_record_debug (struct ui_file *file, int from_tty,
 static void
 cmd_record_start (char *args, int from_tty)
 {
-  execute_command ("target record", from_tty);
+  execute_command ("target record-full", from_tty);
 }
 
 /* Truncate the record log from the present point
@@ -67,21 +84,25 @@ cmd_record_start (char *args, int from_tty)
 static void
 cmd_record_delete (char *args, int from_tty)
 {
-  if (current_target.to_stratum == record_stratum)
+  require_record_target ();
+
+  if (!target_record_is_replaying ())
     {
-      if (RECORD_IS_REPLAY)
-	{
-	  if (!from_tty || query (_("Delete the log from this point forward "
-		                    "and begin to record the running message "
-		                    "at current PC?")))
-	    record_list_release_following (record_list);
-	}
-      else
-	  printf_unfiltered (_("Already at end of record list.\n"));
+      printf_unfiltered (_("Already at end of record list.\n"));
+      return;
+    }
 
+  if (!target_supports_delete_record ())
+    {
+      printf_unfiltered (_("The current record target does not support "
+			   "this operation.\n"));
+      return;
     }
-  else
-    printf_unfiltered (_("Process record is not started.\n"));
+
+  if (!from_tty || query (_("Delete the log from this point forward "
+			    "and begin to record the running message "
+			    "at current PC?")))
+    target_delete_record ();
 }
 
 /* Implement the "stoprecord" or "record stop" command.  */
@@ -89,36 +110,18 @@ cmd_record_delete (char *args, int from_tty)
 static void
 cmd_record_stop (char *args, int from_tty)
 {
-  if (current_target.to_stratum == record_stratum)
-    {
-      unpush_target (&record_ops);
-      printf_unfiltered (_("Process record is stopped and all execution "
-                           "logs are deleted.\n"));
+  struct target_ops *t;
 
-      observer_notify_record_changed (current_inferior (), 0);
-    }
-  else
-    printf_unfiltered (_("Process record is not started.\n"));
-}
+  t = require_record_target ();
+  unpush_target (t);
 
-/* Set upper limit of record log size.  */
+  printf_unfiltered (_("Process record is stopped and all execution "
+		       "logs are deleted.\n"));
 
-static void
-set_record_insn_max_num (char *args, int from_tty, struct cmd_list_element *c)
-{
-  if (record_insn_num > record_insn_max_num && record_insn_max_num)
-    {
-      /* Count down record_insn_num while releasing records from list.  */
-      while (record_insn_num > record_insn_max_num)
-	{
-	  record_list_release_first ();
-	  record_insn_num--;
-	}
-    }
+  observer_notify_record_changed (current_inferior (), 0);
 }
 
-static struct cmd_list_element *record_cmdlist, *set_record_cmdlist,
-			       *show_record_cmdlist, *info_record_cmdlist;
+/* The "set record" command.  */
 
 static void
 set_record_command (char *args, int from_tty)
@@ -128,65 +131,53 @@ set_record_command (char *args, int from_tty)
   help_list (set_record_cmdlist, "set record ", all_commands, gdb_stdout);
 }
 
+/* The "show record" command.  */
+
 static void
 show_record_command (char *args, int from_tty)
 {
   cmd_show_list (show_record_cmdlist, from_tty, "");
 }
 
-/* Display some statistics about the execution log.  */
+/* The "info record" command.  */
 
 static void
 info_record_command (char *args, int from_tty)
 {
-  struct record_entry *p;
+  struct target_ops *t;
 
-  if (current_target.to_stratum == record_stratum)
+  t = find_record_target ();
+  if (t == NULL)
     {
-      if (RECORD_IS_REPLAY)
-	printf_filtered (_("Replay mode:\n"));
-      else
-	printf_filtered (_("Record mode:\n"));
-
-      /* Find entry for first actual instruction in the log.  */
-      for (p = record_first.next;
-	   p != NULL && p->type != record_end;
-	   p = p->next)
-	;
-
-      /* Do we have a log at all?  */
-      if (p != NULL && p->type == record_end)
-	{
-	  /* Display instruction number for first instruction in the log.  */
-	  printf_filtered (_("Lowest recorded instruction number is %s.\n"),
-			   pulongest (p->u.end.insn_num));
-
-	  /* If in replay mode, display where we are in the log.  */
-	  if (RECORD_IS_REPLAY)
-	    printf_filtered (_("Current instruction number is %s.\n"),
-			     pulongest (record_list->u.end.insn_num));
-
-	  /* Display instruction number for last instruction in the log.  */
-	  printf_filtered (_("Highest recorded instruction number is %s.\n"), 
-			   pulongest (record_insn_count));
-
-	  /* Display log count.  */
-	  printf_filtered (_("Log contains %d instructions.\n"), 
-			   record_insn_num);
-	}
-      else
-	{
-	  printf_filtered (_("No instructions have been logged.\n"));
-	}
+      printf_filtered (_("No record target is currently active.\n"));
+      return;
     }
+
+  printf_filtered (_("Active record target: %s\n"), t->to_shortname);
+  if (t->to_info_record)
+    t->to_info_record ();
+}
+
+/* The "record save" command.  */
+
+static void
+cmd_record_save (char *args, int from_tty)
+{
+  char *recfilename, recfilename_buffer[40];
+
+  require_record_target ();
+
+  if (args && *args)
+    recfilename = args;
   else
     {
-      printf_filtered (_("target record is not active.\n"));
+      /* Default recfile name is "gdb_record.PID".  */
+      snprintf (recfilename_buffer, sizeof (recfilename_buffer),
+                "gdb_record.%d", PIDGET (inferior_ptid));
+      recfilename = recfilename_buffer;
     }
 
-  /* Display max log size.  */
-  printf_filtered (_("Max logged instructions is %d.\n"),
-		   record_insn_max_num);
+  target_save_record (recfilename);
 }
 
 /* "record goto" command.  Argument is an instruction number,
@@ -194,65 +185,26 @@ info_record_command (char *args, int from_tty)
 
    Rewinds the recording (forward or backward) to the given instruction.  */
 
-static void
+void
 cmd_record_goto (char *arg, int from_tty)
 {
-  struct record_entry *p = NULL;
-  ULONGEST target_insn = 0;
+  require_record_target ();
 
   if (arg == NULL || *arg == '\0')
     error (_("Command requires an argument (insn number to go to)."));
 
   if (strncmp (arg, "start", strlen ("start")) == 0
       || strncmp (arg, "begin", strlen ("begin")) == 0)
-    {
-      /* Special case.  Find first insn.  */
-      for (p = &record_first; p != NULL; p = p->next)
-	if (p->type == record_end)
-	  break;
-      if (p)
-	target_insn = p->u.end.insn_num;
-    }
+    target_goto_record_begin ();
   else if (strncmp (arg, "end", strlen ("end")) == 0)
-    {
-      /* Special case.  Find last insn.  */
-      for (p = record_list; p->next != NULL; p = p->next)
-	;
-      for (; p!= NULL; p = p->prev)
-	if (p->type == record_end)
-	  break;
-      if (p)
-	target_insn = p->u.end.insn_num;
-    }
+    target_goto_record_end ();
   else
     {
-      /* General case.  Find designated insn.  */
-      target_insn = parse_and_eval_long (arg);
+      ULONGEST insn;
 
-      for (p = &record_first; p != NULL; p = p->next)
-	if (p->type == record_end && p->u.end.insn_num == target_insn)
-	  break;
+      insn = parse_and_eval_long (arg);
+      target_goto_record (insn);
     }
-
-  if (p == NULL)
-    error (_("Target insn '%s' not found."), arg);
-  else if (p == record_list)
-    error (_("Already at insn '%s'."), arg);
-  else if (p->u.end.insn_num > record_list->u.end.insn_num)
-    {
-      printf_filtered (_("Go forward to insn number %s\n"),
-		       pulongest (target_insn));
-      record_goto_insn (p, EXEC_FORWARD);
-    }
-  else
-    {
-      printf_filtered (_("Go backward to insn number %s\n"),
-		       pulongest (target_insn));
-      record_goto_insn (p, EXEC_REVERSE);
-    }
-  registers_changed ();
-  reinit_frame_cache ();
-  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
 }
 
 /* Provide a prototype to silence -Wmissing-prototypes.  */
@@ -263,16 +215,6 @@ _initialize_record (void)
 {
   struct cmd_list_element *c;
 
-  /* Init record_first.  */
-  record_first.prev = NULL;
-  record_first.next = NULL;
-  record_first.type = record_end;
-
-  init_record_ops ();
-  add_target (&record_ops);
-  init_record_core_ops ();
-  add_target (&record_core_ops);
-
   add_setshow_zuinteger_cmd ("record", no_class, &record_debug,
 			     _("Set debugging of record/replay feature."),
 			     _("Show debugging of record/replay feature."),
@@ -282,7 +224,7 @@ _initialize_record (void)
 			     &showdebuglist);
 
   c = add_prefix_cmd ("record", class_obscure, cmd_record_start,
-		      _("Abbreviated form of \"target record\" command."),
+		      _("Start recording."),
 		      &record_cmdlist, "record ", 0, &cmdlist);
   set_cmd_completer (c, filename_completer);
 
@@ -307,12 +249,6 @@ Default filename is 'gdb_record.<process_id>'."),
 	       &record_cmdlist);
   set_cmd_completer (c, filename_completer);
 
-  c = add_cmd ("restore", class_obscure, cmd_record_restore,
-	       _("Restore the execution log from a file.\n\
-Argument is filename.  File must be created with 'record save'."),
-	       &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."),
            &record_cmdlist);
@@ -324,40 +260,8 @@ Argument is filename.  File must be created with 'record save'."),
            &record_cmdlist);
   add_alias_cmd ("s", "stop", class_obscure, 1, &record_cmdlist);
 
-  /* Record instructions number limit command.  */
-  add_setshow_boolean_cmd ("stop-at-limit", no_class,
-			   &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);
-  add_setshow_uinteger_cmd ("insn-number-max", no_class,
-			    &record_insn_max_num,
-			    _("Set record/replay buffer limit."),
-			    _("Show record/replay buffer limit."), _("\
-Set the maximum number of instructions to be stored in the\n\
-record/replay buffer.  Zero means unlimited.  Default is 200000."),
-			    set_record_insn_max_num,
-			    NULL, &set_record_cmdlist, &show_record_cmdlist);
-
   add_cmd ("goto", class_obscure, cmd_record_goto, _("\
 Restore the program to its state at instruction number N.\n\
 Argument is instruction number, as shown by 'info record'."),
 	   &record_cmdlist);
-
-  add_setshow_boolean_cmd ("memory-query", no_class,
-			   &record_memory_query, _("\
-Set whether query if PREC cannot record memory change of next instruction."),
-                           _("\
-Show whether query if PREC cannot record memory change of next instruction."),
-                           _("\
-Default is OFF.\n\
-When ON, query if PREC cannot record memory change of next instruction."),
-			   NULL, NULL,
-			   &set_record_cmdlist, &show_record_cmdlist);
-
 }
diff --git a/gdb/record.h b/gdb/record.h
index 280f4ec..dfd8361 100644
--- a/gdb/record.h
+++ b/gdb/record.h
@@ -20,8 +20,18 @@
 #ifndef _RECORD_H_
 #define _RECORD_H_
 
+struct cmd_list_element;
+
 #define RECORD_IS_USED	(current_target.to_stratum == record_stratum)
 
 extern unsigned int record_debug;
 
+/* Allow record targets to add their own sub-commands.  */
+extern struct cmd_list_element *record_cmdlist;
+extern struct cmd_list_element *set_record_cmdlist;
+extern struct cmd_list_element *show_record_cmdlist;
+extern struct cmd_list_element *info_record_cmdlist;
+
+extern void cmd_record_goto (char *arg, int from_tty);
+
 #endif /* _RECORD_H_ */
diff --git a/gdb/target.c b/gdb/target.c
index 25f4629..e71ab96 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -4231,6 +4231,135 @@ target_read_btrace (struct btrace_target_info *btinfo)
   return NULL;
 }
 
+/* See target.h.  */
+
+void
+target_info_record (void)
+{
+  struct target_ops *t;
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    if (t->to_info_record != NULL)
+      {
+	t->to_info_record ();
+	return;
+      }
+
+  tcomplain ();
+}
+
+/* See target.h.  */
+
+void
+target_save_record (char *filename)
+{
+  struct target_ops *t;
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    if (t->to_save_record != NULL)
+      {
+	t->to_save_record (filename);
+	return;
+      }
+
+  tcomplain ();
+}
+
+/* See target.h.  */
+
+int
+target_supports_delete_record (void)
+{
+  struct target_ops *t;
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    if (t->to_delete_record != NULL)
+      return 1;
+
+  return 0;
+}
+
+/* See target.h.  */
+
+void
+target_delete_record (void)
+{
+  struct target_ops *t;
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    if (t->to_delete_record != NULL)
+      {
+	t->to_delete_record ();
+	return;
+      }
+
+  tcomplain ();
+}
+
+/* See target.h.  */
+
+int
+target_record_is_replaying (void)
+{
+  struct target_ops *t;
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    if (t->to_record_is_replaying != NULL)
+	return t->to_record_is_replaying ();
+
+  return 0;
+}
+
+/* See target.h.  */
+
+void
+target_goto_record_begin (void)
+{
+  struct target_ops *t;
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    if (t->to_goto_record_begin != NULL)
+      {
+	t->to_goto_record_begin ();
+	return;
+      }
+
+  tcomplain ();
+}
+
+/* See target.h.  */
+
+void
+target_goto_record_end (void)
+{
+  struct target_ops *t;
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    if (t->to_goto_record_end != NULL)
+      {
+	t->to_goto_record_end ();
+	return;
+      }
+
+  tcomplain ();
+}
+
+/* See target.h.  */
+
+void
+target_goto_record (ULONGEST insn)
+{
+  struct target_ops *t;
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    if (t->to_goto_record != NULL)
+      {
+	t->to_goto_record (insn);
+	return;
+      }
+
+  tcomplain ();
+}
 
 static void
 debug_to_prepare_to_store (struct regcache *regcache)
diff --git a/gdb/target.h b/gdb/target.h
index 1d73336..e4fe5da 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -876,6 +876,27 @@ struct target_ops
     /* Read branch trace data.  */
     VEC (btrace_block_s) *(*to_read_btrace) (struct btrace_target_info *);
 
+    /* Print information about the recording.  */
+    void (*to_info_record) (void);
+
+    /* Save the recorded execution trace into a file.  */
+    void (*to_save_record) (char *filename);
+
+    /* Delete the recorded execution trace from the current position onwards.  */
+    void (*to_delete_record) (void);
+
+    /* Query if the record target is currently replaying.  */
+    int (*to_record_is_replaying) (void);
+
+    /* Go to the begin of the execution trace.  */
+    void (*to_goto_record_begin) (void);
+
+    /* Go to the end of the execution trace.  */
+    void (*to_goto_record_end) (void);
+
+    /* Go to a specific location in the recorded execution trace.  */
+    void (*to_goto_record) (ULONGEST);
+
     int to_magic;
     /* Need sub-structure for target machine related rather than comm related?
      */
@@ -1938,5 +1959,28 @@ extern int target_btrace_has_changed (struct btrace_target_info *btinfo);
    Returns a vector of branch trace blocks with the latest entry at index 0.  */
 extern VEC (btrace_block_s) *target_read_btrace (struct btrace_target_info *);
 
+/* Print record information for this record target.  */
+extern void target_info_record (void);
+
+/* Save the recorded execution trace into a file.  */
+extern void target_save_record (char *filename);
+
+/* Query if the target supports deleting the execution log.  */
+extern int target_supports_delete_record (void);
+
+/* Delete the recorded execution trace from the current position onwards.  */
+extern void target_delete_record (void);
+
+/* Query if the record target is currently replaying.  */
+extern int target_record_is_replaying (void);
+
+/* Go to the begin of the execution trace.  */
+extern void target_goto_record_begin (void);
+
+/* Go to the end of the execution trace.  */
+extern void target_goto_record_end (void);
+
+/* Go to a specific location in the recorded execution trace.  */
+extern void target_goto_record (ULONGEST);
 
 #endif /* !defined (TARGET_H) */
-- 
1.7.0.7


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [rfc 2/5] record: split record
  2013-02-08 15:30 [rfc 0/5] record-btrace markus.t.metzger
                   ` (3 preceding siblings ...)
  2013-02-08 15:31 ` [rfc 3/5] record: make it build again markus.t.metzger
@ 2013-02-08 15:32 ` markus.t.metzger
  2013-02-10 22:11   ` Jan Kratochvil
  4 siblings, 1 reply; 23+ messages in thread
From: markus.t.metzger @ 2013-02-08 15:32 UTC (permalink / raw)
  To: jan.kratochvil; +Cc: gdb-patches, markus.t.metzger, Markus Metzger

From: Markus Metzger <markus.t.metzger@intel.com>

Split record.h into record.h and record-full.h.
Split record.c into record.c and record-full.c.

The split leaves the command part in record.c and moves the target part into
record-full.c.

The result does not build!

2013-02-08 Markus Metzger <markus.t.metzger@intel.com>

	* record.h: Split into this and ...
	* record-full.h: ... this.
	* record.c: Split into this and ...
	* record-full.c: ... this.


---
 gdb/record-full.c | 2752 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/record-full.h |   36 +
 gdb/record.c      | 2714 ----------------------------------------------------
 gdb/record.h      |   12 -
 4 files changed, 2788 insertions(+), 2726 deletions(-)
 create mode 100644 gdb/record-full.c
 create mode 100644 gdb/record-full.h

diff --git a/gdb/record-full.c b/gdb/record-full.c
new file mode 100644
index 0000000..88d0730
--- /dev/null
+++ b/gdb/record-full.c
@@ -0,0 +1,2752 @@
+/* Process record and replay target for GDB, the GNU debugger.
+
+   Copyright (C) 2013 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/>.  */
+
+#include "defs.h"
+#include "gdbcmd.h"
+#include "regcache.h"
+#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 "event-loop.h"
+#include "inf-loop.h"
+#include "gdb_bfd.h"
+#include "observer.h"
+
+#include <signal.h>
+
+/* This module implements "target record", also known as "process
+   record and replay".  This target sits on top of a "normal" target
+   (a target that "has execution"), and provides a record and replay
+   functionality, including reverse debugging.
+
+   Target record has two modes: recording, and replaying.
+
+   In record mode, we intercept the to_resume and to_wait methods.
+   Whenever gdb resumes the target, we run the target in single step
+   mode, and we build up an execution log in which, for each executed
+   instruction, we record all changes in memory and register state.
+   This is invisible to the user, to whom it just looks like an
+   ordinary debugging session (except for performance degredation).
+
+   In replay mode, instead of actually letting the inferior run as a
+   process, we simulate its execution by playing back the recorded
+   execution log.  For each instruction in the log, we simulate the
+   instruction's side effects by duplicating the changes that it would
+   have made on memory and registers.  */
+
+#define DEFAULT_RECORD_INSN_MAX_NUM	200000
+
+#define RECORD_IS_REPLAY \
+     (record_list->next || execution_direction == EXEC_REVERSE)
+
+#define RECORD_FILE_MAGIC	netorder32(0x20091016)
+
+/* These are the core structs of the process record functionality.
+
+   A record_entry is a record of the value change of a register
+   ("record_reg") or a part of memory ("record_mem").  And each
+   instruction must have a struct record_entry ("record_end") that
+   indicates that this is the last struct record_entry of this
+   instruction.
+
+   Each struct record_entry is linked to "record_list" by "prev" and
+   "next" pointers.  */
+
+struct record_mem_entry
+{
+  CORE_ADDR addr;
+  int len;
+  /* Set this flag if target memory for this entry
+     can no longer be accessed.  */
+  int mem_entry_not_accessible;
+  union
+  {
+    gdb_byte *ptr;
+    gdb_byte buf[sizeof (gdb_byte *)];
+  } u;
+};
+
+struct record_reg_entry
+{
+  unsigned short num;
+  unsigned short len;
+  union 
+  {
+    gdb_byte *ptr;
+    gdb_byte buf[2 * sizeof (gdb_byte *)];
+  } u;
+};
+
+struct record_end_entry
+{
+  enum gdb_signal sigval;
+  ULONGEST insn_num;
+};
+
+enum record_type
+{
+  record_end = 0,
+  record_reg,
+  record_mem
+};
+
+/* This is the data structure that makes up the execution log.
+
+   The execution log consists of a single linked list of entries
+   of type "struct record_entry".  It is doubly linked so that it
+   can be traversed in either direction.
+
+   The start of the list is anchored by a struct called
+   "record_first".  The pointer "record_list" either points to the
+   last entry that was added to the list (in record mode), or to the
+   next entry in the list that will be executed (in replay mode).
+
+   Each list element (struct record_entry), in addition to next and
+   prev pointers, consists of a union of three entry types: mem, reg,
+   and end.  A field called "type" determines which entry type is
+   represented by a given list element.
+
+   Each instruction that is added to the execution log is represented
+   by a variable number of list elements ('entries').  The instruction
+   will have one "reg" entry for each register that is changed by 
+   executing the instruction (including the PC in every case).  It 
+   will also have one "mem" entry for each memory change.  Finally,
+   each instruction will have an "end" entry that separates it from
+   the changes associated with the next instruction.  */
+
+struct record_entry
+{
+  struct record_entry *prev;
+  struct record_entry *next;
+  enum record_type type;
+  union
+  {
+    /* reg */
+    struct record_reg_entry reg;
+    /* mem */
+    struct record_mem_entry mem;
+    /* end */
+    struct record_end_entry end;
+  } u;
+};
+
+/* If true, query if PREC cannot record memory
+   change of next instruction.  */
+int record_memory_query = 0;
+
+struct record_core_buf_entry
+{
+  struct record_core_buf_entry *prev;
+  struct target_section *p;
+  bfd_byte *buf;
+};
+
+/* 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;
+
+/* The following variables are used for managing the linked list that
+   represents the execution log.
+
+   record_first is the anchor that holds down the beginning of the list.
+
+   record_list serves two functions:
+     1) In record mode, it anchors the end of the list.
+     2) In replay mode, it traverses the list and points to
+        the next instruction that must be emulated.
+
+   record_arch_list_head and record_arch_list_tail are used to manage
+   a separate list, which is used to build up the change elements of
+   the currently executing instruction during record mode.  When this
+   instruction has been completely annotated in the "arch list", it 
+   will be appended to the main execution log.  */
+
+static struct record_entry record_first;
+static struct record_entry *record_list = &record_first;
+static struct record_entry *record_arch_list_head = NULL;
+static struct record_entry *record_arch_list_tail = NULL;
+
+/* 1 ask user. 0 auto delete the last struct record_entry.  */
+static int record_stop_at_limit = 1;
+/* Maximum allowed number of insns in execution log.  */
+static unsigned int record_insn_max_num = DEFAULT_RECORD_INSN_MAX_NUM;
+/* Actual count of insns presently in execution log.  */
+static int record_insn_num = 0;
+/* Count of insns logged so far (may be larger
+   than count of insns presently in execution log).  */
+static ULONGEST record_insn_count;
+
+/* 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;
+static void (*record_beneath_to_resume) (struct target_ops *, ptid_t, int,
+                                         enum gdb_signal);
+static struct target_ops *record_beneath_to_wait_ops;
+static ptid_t (*record_beneath_to_wait) (struct target_ops *, ptid_t,
+					 struct target_waitstatus *,
+					 int);
+static struct target_ops *record_beneath_to_store_registers_ops;
+static void (*record_beneath_to_store_registers) (struct target_ops *,
+                                                  struct regcache *,
+						  int regno);
+static struct target_ops *record_beneath_to_xfer_partial_ops;
+static LONGEST (*record_beneath_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 (*record_beneath_to_insert_breakpoint) (struct gdbarch *,
+						   struct bp_target_info *);
+static int (*record_beneath_to_remove_breakpoint) (struct gdbarch *,
+						   struct bp_target_info *);
+static int (*record_beneath_to_stopped_by_watchpoint) (void);
+static int (*record_beneath_to_stopped_data_address) (struct target_ops *,
+						      CORE_ADDR *);
+static void (*record_beneath_to_async) (void (*) (enum inferior_event_type, void *), void *);
+
+/* Alloc and free functions for record_reg, record_mem, and record_end 
+   entries.  */
+
+/* Alloc a record_reg record entry.  */
+
+static inline struct record_entry *
+record_reg_alloc (struct regcache *regcache, int regnum)
+{
+  struct record_entry *rec;
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+
+  rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry));
+  rec->type = record_reg;
+  rec->u.reg.num = regnum;
+  rec->u.reg.len = register_size (gdbarch, regnum);
+  if (rec->u.reg.len > sizeof (rec->u.reg.u.buf))
+    rec->u.reg.u.ptr = (gdb_byte *) xmalloc (rec->u.reg.len);
+
+  return rec;
+}
+
+/* Free a record_reg record entry.  */
+
+static inline void
+record_reg_release (struct record_entry *rec)
+{
+  gdb_assert (rec->type == record_reg);
+  if (rec->u.reg.len > sizeof (rec->u.reg.u.buf))
+    xfree (rec->u.reg.u.ptr);
+  xfree (rec);
+}
+
+/* Alloc a record_mem record entry.  */
+
+static inline struct record_entry *
+record_mem_alloc (CORE_ADDR addr, int len)
+{
+  struct record_entry *rec;
+
+  rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry));
+  rec->type = record_mem;
+  rec->u.mem.addr = addr;
+  rec->u.mem.len = len;
+  if (rec->u.mem.len > sizeof (rec->u.mem.u.buf))
+    rec->u.mem.u.ptr = (gdb_byte *) xmalloc (len);
+
+  return rec;
+}
+
+/* Free a record_mem record entry.  */
+
+static inline void
+record_mem_release (struct record_entry *rec)
+{
+  gdb_assert (rec->type == record_mem);
+  if (rec->u.mem.len > sizeof (rec->u.mem.u.buf))
+    xfree (rec->u.mem.u.ptr);
+  xfree (rec);
+}
+
+/* Alloc a record_end record entry.  */
+
+static inline struct record_entry *
+record_end_alloc (void)
+{
+  struct record_entry *rec;
+
+  rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry));
+  rec->type = record_end;
+
+  return rec;
+}
+
+/* Free a record_end record entry.  */
+
+static inline void
+record_end_release (struct record_entry *rec)
+{
+  xfree (rec);
+}
+
+/* Free one record entry, any type.
+   Return entry->type, in case caller wants to know.  */
+
+static inline enum record_type
+record_entry_release (struct record_entry *rec)
+{
+  enum record_type type = rec->type;
+
+  switch (type) {
+  case record_reg:
+    record_reg_release (rec);
+    break;
+  case record_mem:
+    record_mem_release (rec);
+    break;
+  case record_end:
+    record_end_release (rec);
+    break;
+  }
+  return type;
+}
+
+/* Free all record entries in list pointed to by REC.  */
+
+static void
+record_list_release (struct record_entry *rec)
+{
+  if (!rec)
+    return;
+
+  while (rec->next)
+    rec = rec->next;
+
+  while (rec->prev)
+    {
+      rec = rec->prev;
+      record_entry_release (rec->next);
+    }
+
+  if (rec == &record_first)
+    {
+      record_insn_num = 0;
+      record_first.next = NULL;
+    }
+  else
+    record_entry_release (rec);
+}
+
+/* Free all record entries forward of the given list position.  */
+
+static void
+record_list_release_following (struct record_entry *rec)
+{
+  struct record_entry *tmp = rec->next;
+
+  rec->next = NULL;
+  while (tmp)
+    {
+      rec = tmp->next;
+      if (record_entry_release (tmp) == record_end)
+	{
+	  record_insn_num--;
+	  record_insn_count--;
+	}
+      tmp = rec;
+    }
+}
+
+/* Delete the first instruction from the beginning of the log, to make
+   room for adding a new instruction at the end of the log.
+
+   Note -- this function does not modify record_insn_num.  */
+
+static void
+record_list_release_first (void)
+{
+  struct record_entry *tmp;
+
+  if (!record_first.next)
+    return;
+
+  /* Loop until a record_end.  */
+  while (1)
+    {
+      /* Cut record_first.next out of the linked list.  */
+      tmp = record_first.next;
+      record_first.next = tmp->next;
+      tmp->next->prev = &record_first;
+
+      /* tmp is now isolated, and can be deleted.  */
+      if (record_entry_release (tmp) == record_end)
+	break;	/* End loop at first record_end.  */
+
+      if (!record_first.next)
+	{
+	  gdb_assert (record_insn_num == 1);
+	  break;	/* End loop when list is empty.  */
+	}
+    }
+}
+
+/* Add a struct record_entry to record_arch_list.  */
+
+static void
+record_arch_list_add (struct record_entry *rec)
+{
+  if (record_debug > 1)
+    fprintf_unfiltered (gdb_stdlog,
+			"Process record: record_arch_list_add %s.\n",
+			host_address_to_string (rec));
+
+  if (record_arch_list_tail)
+    {
+      record_arch_list_tail->next = rec;
+      rec->prev = record_arch_list_tail;
+      record_arch_list_tail = rec;
+    }
+  else
+    {
+      record_arch_list_head = rec;
+      record_arch_list_tail = rec;
+    }
+}
+
+/* Return the value storage location of a record entry.  */
+static inline gdb_byte *
+record_get_loc (struct record_entry *rec)
+{
+  switch (rec->type) {
+  case record_mem:
+    if (rec->u.mem.len > sizeof (rec->u.mem.u.buf))
+      return rec->u.mem.u.ptr;
+    else
+      return rec->u.mem.u.buf;
+  case record_reg:
+    if (rec->u.reg.len > sizeof (rec->u.reg.u.buf))
+      return rec->u.reg.u.ptr;
+    else
+      return rec->u.reg.u.buf;
+  case record_end:
+  default:
+    gdb_assert_not_reached ("unexpected record_entry type");
+    return NULL;
+  }
+}
+
+/* Record the value of a register NUM to record_arch_list.  */
+
+int
+record_arch_list_add_reg (struct regcache *regcache, int regnum)
+{
+  struct record_entry *rec;
+
+  if (record_debug > 1)
+    fprintf_unfiltered (gdb_stdlog,
+			"Process record: add register num = %d to "
+			"record list.\n",
+			regnum);
+
+  rec = record_reg_alloc (regcache, regnum);
+
+  regcache_raw_read (regcache, regnum, record_get_loc (rec));
+
+  record_arch_list_add (rec);
+
+  return 0;
+}
+
+int
+record_read_memory (struct gdbarch *gdbarch,
+		    CORE_ADDR memaddr, gdb_byte *myaddr,
+		    ssize_t len)
+{
+  int ret = target_read_memory (memaddr, myaddr, len);
+
+  if (ret && record_debug)
+    printf_unfiltered (_("Process record: error reading memory "
+			 "at addr %s len = %ld.\n"),
+		       paddress (gdbarch, memaddr), (long) len);
+  return ret;
+}
+
+/* Record the value of a region of memory whose address is ADDR and
+   length is LEN to record_arch_list.  */
+
+int
+record_arch_list_add_mem (CORE_ADDR addr, int len)
+{
+  struct record_entry *rec;
+
+  if (record_debug > 1)
+    fprintf_unfiltered (gdb_stdlog,
+			"Process record: add mem addr = %s len = %d to "
+			"record list.\n",
+			paddress (target_gdbarch (), addr), len);
+
+  if (!addr)	/* FIXME: Why?  Some arch must permit it...  */
+    return 0;
+
+  rec = record_mem_alloc (addr, len);
+
+  if (record_read_memory (target_gdbarch (), addr, record_get_loc (rec), len))
+    {
+      record_mem_release (rec);
+      return -1;
+    }
+
+  record_arch_list_add (rec);
+
+  return 0;
+}
+
+/* Add a record_end type struct record_entry to record_arch_list.  */
+
+int
+record_arch_list_add_end (void)
+{
+  struct record_entry *rec;
+
+  if (record_debug > 1)
+    fprintf_unfiltered (gdb_stdlog,
+			"Process record: add end to arch list.\n");
+
+  rec = record_end_alloc ();
+  rec->u.end.sigval = GDB_SIGNAL_0;
+  rec->u.end.insn_num = ++record_insn_count;
+
+  record_arch_list_add (rec);
+
+  return 0;
+}
+
+static void
+record_check_insn_num (int set_terminal)
+{
+  if (record_insn_max_num)
+    {
+      gdb_assert (record_insn_num <= record_insn_max_num);
+      if (record_insn_num == record_insn_max_num)
+	{
+	  /* Ask user what to do.  */
+	  if (record_stop_at_limit)
+	    {
+	      int q;
+
+	      if (set_terminal)
+		target_terminal_ours ();
+	      q = yquery (_("Do you want to auto delete previous execution "
+			    "log entries when record/replay buffer becomes "
+			    "full (record stop-at-limit)?"));
+	      if (set_terminal)
+		target_terminal_inferior ();
+	      if (q)
+		record_stop_at_limit = 0;
+	      else
+		error (_("Process record: stopped 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 int
+record_message (struct regcache *regcache, enum gdb_signal signal)
+{
+  int ret;
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  struct cleanup *old_cleanups = make_cleanup (record_arch_list_cleanups, 0);
+
+  record_arch_list_head = NULL;
+  record_arch_list_tail = NULL;
+
+  /* Check record_insn_num.  */
+  record_check_insn_num (1);
+
+  /* If gdb sends a signal value to target_resume,
+     save it in the 'end' field of the previous instruction.
+
+     Maybe process record should record what really happened,
+     rather than what gdb pretends has happened.
+
+     So if Linux delivered the signal to the child process during
+     the record mode, we will record it and deliver it again in
+     the replay mode.
+
+     If user says "ignore this signal" during the record mode, then
+     it will be ignored again during the replay mode (no matter if
+     the user says something different, like "deliver this signal"
+     during the replay mode).
+
+     User should understand that nothing he does during the replay
+     mode will change the behavior of the child.  If he tries,
+     then that is a user error.
+
+     But we should still deliver the signal to gdb during the replay,
+     if we delivered it during the recording.  Therefore we should
+     record the signal during record_wait, not record_resume.  */
+  if (record_list != &record_first)    /* FIXME better way to check */
+    {
+      gdb_assert (record_list->type == record_end);
+      record_list->u.end.sigval = signal;
+    }
+
+  if (signal == GDB_SIGNAL_0
+      || !gdbarch_process_record_signal_p (gdbarch))
+    ret = gdbarch_process_record (gdbarch,
+				  regcache,
+				  regcache_read_pc (regcache));
+  else
+    ret = gdbarch_process_record_signal (gdbarch,
+					 regcache,
+					 signal);
+
+  if (ret > 0)
+    error (_("Process record: inferior program stopped."));
+  if (ret < 0)
+    error (_("Process record: failed to record execution log."));
+
+  discard_cleanups (old_cleanups);
+
+  record_list->next = record_arch_list_head;
+  record_arch_list_head->prev = record_list;
+  record_list = record_arch_list_tail;
+
+  if (record_insn_num == record_insn_max_num && record_insn_max_num)
+    record_list_release_first ();
+  else
+    record_insn_num++;
+
+  return 1;
+}
+
+struct record_message_args {
+  struct regcache *regcache;
+  enum gdb_signal signal;
+};
+
+static int
+record_message_wrapper (void *args)
+{
+  struct record_message_args *record_args = args;
+
+  return record_message (record_args->regcache, record_args->signal);
+}
+
+static int
+record_message_wrapper_safe (struct regcache *regcache,
+                             enum gdb_signal signal)
+{
+  struct record_message_args args;
+
+  args.regcache = regcache;
+  args.signal = signal;
+
+  return catch_errors (record_message_wrapper, &args, NULL, RETURN_MASK_ALL);
+}
+
+/* Set to 1 if record_store_registers and record_xfer_partial
+   doesn't need record.  */
+
+static int record_gdb_operation_disable = 0;
+
+struct cleanup *
+record_gdb_operation_disable_set (void)
+{
+  struct cleanup *old_cleanups = NULL;
+
+  old_cleanups =
+    make_cleanup_restore_integer (&record_gdb_operation_disable);
+  record_gdb_operation_disable = 1;
+
+  return old_cleanups;
+}
+
+/* Flag set to TRUE for target_stopped_by_watchpoint.  */
+static int record_hw_watchpoint = 0;
+
+/* Execute one instruction from the record log.  Each instruction in
+   the log will be represented by an arbitrary sequence of register
+   entries and memory entries, followed by an 'end' entry.  */
+
+static inline void
+record_exec_insn (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, 
+			       record_get_loc (entry));
+        memcpy (record_get_loc (entry), reg, entry->u.reg.len);
+      }
+      break;
+
+    case record_mem: /* mem */
+      {
+	/* Nothing to do if the entry is flagged not_accessible.  */
+        if (!entry->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),
+                                  entry->u.mem.len);
+
+            if (record_read_memory (gdbarch,
+				    entry->u.mem.addr, mem, entry->u.mem.len))
+	      entry->u.mem.mem_entry_not_accessible = 1;
+            else
+              {
+                if (target_write_memory (entry->u.mem.addr, 
+					 record_get_loc (entry),
+					 entry->u.mem.len))
+                  {
+                    entry->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 (record_get_loc (entry), mem, entry->u.mem.len);
+
+		    /* We've changed memory --- check if a hardware
+		       watchpoint should trap.  Note that this
+		       presently assumes the target beneath supports
+		       continuable watchpoints.  On non-continuable
+		       watchpoints target, we'll want to check this
+		       _before_ actually doing the memory change, and
+		       not doing the change at all if the watchpoint
+		       traps.  */
+		    if (hardware_watchpoint_inserted_in_range
+			(get_regcache_aspace (regcache),
+			 entry->u.mem.addr, entry->u.mem.len))
+		      record_hw_watchpoint = 1;
+		  }
+              }
+          }
+      }
+      break;
+    }
+}
+
+static struct target_ops *tmp_to_resume_ops;
+static void (*tmp_to_resume) (struct target_ops *, ptid_t, int,
+			      enum gdb_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 int (*tmp_to_stopped_by_watchpoint) (void);
+static int (*tmp_to_stopped_data_address) (struct target_ops *, CORE_ADDR *);
+static int (*tmp_to_stopped_data_address) (struct target_ops *, CORE_ADDR *);
+static void (*tmp_to_async) (void (*) (enum inferior_event_type, void *), void *);
+
+static void record_restore (void);
+
+/* Asynchronous signal handle registered as event loop source for when
+   we have pending events ready to be passed to the core.  */
+
+static struct async_event_handler *record_async_inferior_event_token;
+
+static void
+record_async_inferior_event_handler (gdb_client_data data)
+{
+  inferior_event_handler (INF_REG_EVENT, NULL);
+}
+
+/* Open the process record target.  */
+
+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);
+  record_restore ();
+}
+
+/* "to_open" target method for 'live' processes.  */
+
+static void
+record_open_1 (char *name, int from_tty)
+{
+  if (record_debug)
+    fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n");
+
+  /* check exec */
+  if (!target_has_execution)
+    error (_("Process record: the program is not being run."));
+  if (non_stop)
+    error (_("Process record target can't debug inferior in non-stop mode "
+	     "(non-stop)."));
+
+  if (!gdbarch_process_record_p (target_gdbarch ()))
+    error (_("Process record: the current architecture doesn't support "
+	     "record function."));
+
+  if (!tmp_to_resume)
+    error (_("Could not find 'to_resume' method on the target stack."));
+  if (!tmp_to_wait)
+    error (_("Could not find 'to_wait' method on the target stack."));
+  if (!tmp_to_store_registers)
+    error (_("Could not find 'to_store_registers' "
+	     "method on the target stack."));
+  if (!tmp_to_insert_breakpoint)
+    error (_("Could not find 'to_insert_breakpoint' "
+	     "method on the target stack."));
+  if (!tmp_to_remove_breakpoint)
+    error (_("Could not find 'to_remove_breakpoint' "
+	     "method on the target stack."));
+  if (!tmp_to_stopped_by_watchpoint)
+    error (_("Could not find 'to_stopped_by_watchpoint' "
+	     "method on the target stack."));
+  if (!tmp_to_stopped_data_address)
+    error (_("Could not find 'to_stopped_data_address' "
+	     "method on the target stack."));
+
+  push_target (&record_ops);
+}
+
+static void record_init_record_breakpoints (void);
+
+/* "to_open" target method.  Open the process record target.  */
+
+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)
+    error (_("Process record target already running.  Use \"record stop\" to "
+             "stop record target first."));
+
+  /* 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;
+  tmp_to_stopped_by_watchpoint = NULL;
+  tmp_to_stopped_data_address = NULL;
+  tmp_to_async = NULL;
+
+  /* Set the beneath function pointers.  */
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    {
+      if (!tmp_to_resume)
+        {
+	  tmp_to_resume = t->to_resume;
+	  tmp_to_resume_ops = t;
+        }
+      if (!tmp_to_wait)
+        {
+	  tmp_to_wait = t->to_wait;
+	  tmp_to_wait_ops = t;
+        }
+      if (!tmp_to_store_registers)
+        {
+	  tmp_to_store_registers = t->to_store_registers;
+	  tmp_to_store_registers_ops = t;
+        }
+      if (!tmp_to_xfer_partial)
+        {
+	  tmp_to_xfer_partial = t->to_xfer_partial;
+	  tmp_to_xfer_partial_ops = t;
+        }
+      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 (!tmp_to_stopped_by_watchpoint)
+	tmp_to_stopped_by_watchpoint = t->to_stopped_by_watchpoint;
+      if (!tmp_to_stopped_data_address)
+	tmp_to_stopped_data_address = t->to_stopped_data_address;
+      if (!tmp_to_async)
+	tmp_to_async = t->to_async;
+    }
+  if (!tmp_to_xfer_partial)
+    error (_("Could not find 'to_xfer_partial' method on the target stack."));
+
+  /* Reset */
+  record_insn_num = 0;
+  record_insn_count = 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;
+  record_beneath_to_stopped_by_watchpoint = tmp_to_stopped_by_watchpoint;
+  record_beneath_to_stopped_data_address = tmp_to_stopped_data_address;
+  record_beneath_to_async = tmp_to_async;
+
+  if (core_bfd)
+    record_core_open_1 (name, from_tty);
+  else
+    record_open_1 (name, from_tty);
+
+  /* Register extra event sources in the event loop.  */
+  record_async_inferior_event_token
+    = create_async_event_handler (record_async_inferior_event_handler,
+				  NULL);
+
+  record_init_record_breakpoints ();
+
+  observer_notify_record_changed (current_inferior (),  1);
+}
+
+/* "to_close" target method.  Close the process record target.  */
+
+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;
+    }
+
+  if (record_async_inferior_event_token)
+    delete_async_event_handler (&record_async_inferior_event_token);
+}
+
+static int record_resume_step = 0;
+
+/* True if we've been resumed, and so each record_wait call should
+   advance execution.  If this is false, record_wait will return a
+   TARGET_WAITKIND_IGNORE.  */
+static int record_resumed = 0;
+
+/* The execution direction of the last resume we got.  This is
+   necessary for async mode.  Vis (order is not strictly accurate):
+
+   1. user has the global execution direction set to forward
+   2. user does a reverse-step command
+   3. record_resume is called with global execution direction
+      temporarily switched to reverse
+   4. GDB's execution direction is reverted back to forward
+   5. target record notifies event loop there's an event to handle
+   6. infrun asks the target which direction was it going, and switches
+      the global execution direction accordingly (to reverse)
+   7. infrun polls an event out of the record target, and handles it
+   8. GDB goes back to the event loop, and goto #4.
+*/
+static enum exec_direction_kind record_execution_dir = EXEC_FORWARD;
+
+/* "to_resume" target method.  Resume the process record target.  */
+
+static void
+record_resume (struct target_ops *ops, ptid_t ptid, int step,
+               enum gdb_signal signal)
+{
+  record_resume_step = step;
+  record_resumed = 1;
+  record_execution_dir = execution_direction;
+
+  if (!RECORD_IS_REPLAY)
+    {
+      struct gdbarch *gdbarch = target_thread_architecture (ptid);
+
+      record_message (get_current_regcache (), signal);
+
+      if (!step)
+        {
+          /* This is not hard single step.  */
+          if (!gdbarch_software_single_step_p (gdbarch))
+            {
+              /* This is a normal continue.  */
+              step = 1;
+            }
+          else
+            {
+              /* This arch support soft sigle step.  */
+              if (single_step_breakpoints_inserted ())
+                {
+                  /* This is a soft single step.  */
+                  record_resume_step = 1;
+                }
+              else
+                {
+                  /* This is a continue.
+                     Try to insert a soft single step breakpoint.  */
+                  if (!gdbarch_software_single_step (gdbarch,
+                                                     get_current_frame ()))
+                    {
+                      /* This system don't want use soft single step.
+                         Use hard sigle step.  */
+                      step = 1;
+                    }
+                }
+            }
+        }
+
+      /* Make sure the target beneath reports all signals.  */
+      target_pass_signals (0, NULL);
+
+      record_beneath_to_resume (record_beneath_to_resume_ops,
+                                ptid, step, signal);
+    }
+
+  /* We are about to start executing the inferior (or simulate it),
+     let's register it with the event loop.  */
+  if (target_can_async_p ())
+    {
+      target_async (inferior_event_handler, 0);
+      /* Notify the event loop there's an event to wait for.  We do
+	 most of the work in record_wait.  */
+      mark_async_event_handler (record_async_inferior_event_token);
+    }
+}
+
+static int record_get_sig = 0;
+
+/* SIGINT signal handler, registered by "to_wait" method.  */
+
+static void
+record_sig_handler (int signo)
+{
+  if (record_debug)
+    fprintf_unfiltered (gdb_stdlog, "Process record: get a signal\n");
+
+  /* It will break the running inferior in replay mode.  */
+  record_resume_step = 1;
+
+  /* It will let record_wait set inferior status to get the signal
+     SIGINT.  */
+  record_get_sig = 1;
+}
+
+static void
+record_wait_cleanups (void *ignore)
+{
+  if (execution_direction == EXEC_REVERSE)
+    {
+      if (record_list->next)
+	record_list = record_list->next;
+    }
+  else
+    record_list = record_list->prev;
+}
+
+/* "to_wait" target method for process record target.
+
+   In record mode, the target is always run in singlestep mode
+   (even when gdb says to continue).  The to_wait method intercepts
+   the stop events and determines which ones are to be passed on to
+   gdb.  Most stop events are just singlestep events that gdb is not
+   to know about, so the to_wait method just records them and keeps
+   singlestepping.
+
+   In replay mode, this function emulates the recorded execution log, 
+   one instruction at a time (forward or backward), and determines 
+   where to stop.  */
+
+static ptid_t
+record_wait_1 (struct target_ops *ops,
+	       ptid_t ptid, struct target_waitstatus *status,
+	       int options)
+{
+  struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
+
+  if (record_debug)
+    fprintf_unfiltered (gdb_stdlog,
+			"Process record: record_wait "
+			"record_resume_step = %d, record_resumed = %d, direction=%s\n",
+			record_resume_step, record_resumed,
+			record_execution_dir == EXEC_FORWARD ? "forward" : "reverse");
+
+  if (!record_resumed)
+    {
+      gdb_assert ((options & TARGET_WNOHANG) != 0);
+
+      /* No interesting event.  */
+      status->kind = TARGET_WAITKIND_IGNORE;
+      return minus_one_ptid;
+    }
+
+  record_get_sig = 0;
+  signal (SIGINT, record_sig_handler);
+
+  if (!RECORD_IS_REPLAY && ops != &record_core_ops)
+    {
+      if (record_resume_step)
+	{
+	  /* This is a single step.  */
+	  return record_beneath_to_wait (record_beneath_to_wait_ops,
+					 ptid, status, options);
+	}
+      else
+	{
+	  /* This is not a single step.  */
+	  ptid_t ret;
+	  CORE_ADDR tmp_pc;
+	  struct gdbarch *gdbarch = target_thread_architecture (inferior_ptid);
+
+	  while (1)
+	    {
+	      ret = record_beneath_to_wait (record_beneath_to_wait_ops,
+					    ptid, status, options);
+	      if (status->kind == TARGET_WAITKIND_IGNORE)
+		{
+		  if (record_debug)
+		    fprintf_unfiltered (gdb_stdlog,
+					"Process record: record_wait "
+					"target beneath not done yet\n");
+		  return ret;
+		}
+
+              if (single_step_breakpoints_inserted ())
+                remove_single_step_breakpoints ();
+
+	      if (record_resume_step)
+		return ret;
+
+	      /* Is this a SIGTRAP?  */
+	      if (status->kind == TARGET_WAITKIND_STOPPED
+		  && status->value.sig == GDB_SIGNAL_TRAP)
+		{
+		  struct regcache *regcache;
+		  struct address_space *aspace;
+
+		  /* Yes -- this is likely our single-step finishing,
+		     but check if there's any reason the core would be
+		     interested in the event.  */
+
+		  registers_changed ();
+		  regcache = get_current_regcache ();
+		  tmp_pc = regcache_read_pc (regcache);
+		  aspace = get_regcache_aspace (regcache);
+
+		  if (target_stopped_by_watchpoint ())
+		    {
+		      /* Always interested in watchpoints.  */
+		    }
+		  else if (breakpoint_inserted_here_p (aspace, tmp_pc))
+		    {
+		      /* There is a breakpoint here.  Let the core
+			 handle it.  */
+		      if (software_breakpoint_inserted_here_p (aspace, tmp_pc))
+			{
+			  struct gdbarch *gdbarch
+			    = get_regcache_arch (regcache);
+			  CORE_ADDR decr_pc_after_break
+			    = gdbarch_decr_pc_after_break (gdbarch);
+			  if (decr_pc_after_break)
+			    regcache_write_pc (regcache,
+					       tmp_pc + decr_pc_after_break);
+			}
+		    }
+		  else
+		    {
+		      /* This is a single-step trap.  Record the
+		         insn and issue another step.
+                         FIXME: this part can be a random SIGTRAP too.
+                         But GDB cannot handle it.  */
+                      int step = 1;
+
+		      if (!record_message_wrapper_safe (regcache,
+                                                        GDB_SIGNAL_0))
+  			{
+                           status->kind = TARGET_WAITKIND_STOPPED;
+                           status->value.sig = GDB_SIGNAL_0;
+                           break;
+  			}
+
+                      if (gdbarch_software_single_step_p (gdbarch))
+			{
+			  /* Try to insert the software single step breakpoint.
+			     If insert success, set step to 0.  */
+			  set_executing (inferior_ptid, 0);
+			  reinit_frame_cache ();
+			  if (gdbarch_software_single_step (gdbarch,
+                                                            get_current_frame ()))
+			    step = 0;
+			  set_executing (inferior_ptid, 1);
+			}
+
+		      if (record_debug)
+			fprintf_unfiltered (gdb_stdlog,
+					    "Process record: record_wait "
+					    "issuing one more step in the target beneath\n");
+		      record_beneath_to_resume (record_beneath_to_resume_ops,
+						ptid, step,
+						GDB_SIGNAL_0);
+		      continue;
+		    }
+		}
+
+	      /* The inferior is broken by a breakpoint or a signal.  */
+	      break;
+	    }
+
+	  return ret;
+	}
+    }
+  else
+    {
+      struct regcache *regcache = get_current_regcache ();
+      struct gdbarch *gdbarch = get_regcache_arch (regcache);
+      struct address_space *aspace = get_regcache_aspace (regcache);
+      int continue_flag = 1;
+      int first_record_end = 1;
+      struct cleanup *old_cleanups = make_cleanup (record_wait_cleanups, 0);
+      CORE_ADDR tmp_pc;
+
+      record_hw_watchpoint = 0;
+      status->kind = TARGET_WAITKIND_STOPPED;
+
+      /* Check breakpoint when forward execute.  */
+      if (execution_direction == EXEC_FORWARD)
+	{
+	  tmp_pc = regcache_read_pc (regcache);
+	  if (breakpoint_inserted_here_p (aspace, tmp_pc))
+	    {
+	      int decr_pc_after_break = gdbarch_decr_pc_after_break (gdbarch);
+
+	      if (record_debug)
+		fprintf_unfiltered (gdb_stdlog,
+				    "Process record: break at %s.\n",
+				    paddress (gdbarch, tmp_pc));
+
+	      if (decr_pc_after_break
+		  && !record_resume_step
+		  && software_breakpoint_inserted_here_p (aspace, tmp_pc))
+		regcache_write_pc (regcache,
+				   tmp_pc + decr_pc_after_break);
+	      goto replay_out;
+	    }
+	}
+
+      /* If GDB is in terminal_inferior mode, it will not get the signal.
+         And in GDB replay mode, GDB doesn't need to be in terminal_inferior
+         mode, because inferior will not executed.
+         Then set it to terminal_ours to make GDB get the signal.  */
+      target_terminal_ours ();
+
+      /* In EXEC_FORWARD mode, record_list points to the tail of prev
+         instruction.  */
+      if (execution_direction == EXEC_FORWARD && record_list->next)
+	record_list = record_list->next;
+
+      /* Loop over the record_list, looking for the next place to
+	 stop.  */
+      do
+	{
+	  /* Check for beginning and end of log.  */
+	  if (execution_direction == EXEC_REVERSE
+	      && record_list == &record_first)
+	    {
+	      /* Hit beginning of record log in reverse.  */
+	      status->kind = TARGET_WAITKIND_NO_HISTORY;
+	      break;
+	    }
+	  if (execution_direction != EXEC_REVERSE && !record_list->next)
+	    {
+	      /* Hit end of record log going forward.  */
+	      status->kind = TARGET_WAITKIND_NO_HISTORY;
+	      break;
+	    }
+
+          record_exec_insn (regcache, gdbarch, record_list);
+
+	  if (record_list->type == record_end)
+	    {
+	      if (record_debug > 1)
+		fprintf_unfiltered (gdb_stdlog,
+				    "Process record: record_end %s to "
+				    "inferior.\n",
+				    host_address_to_string (record_list));
+
+	      if (first_record_end && execution_direction == EXEC_REVERSE)
+		{
+		  /* When reverse excute, the first record_end is the part of
+		     current instruction.  */
+		  first_record_end = 0;
+		}
+	      else
+		{
+		  /* In EXEC_REVERSE mode, this is the record_end of prev
+		     instruction.
+		     In EXEC_FORWARD mode, this is the record_end of current
+		     instruction.  */
+		  /* step */
+		  if (record_resume_step)
+		    {
+		      if (record_debug > 1)
+			fprintf_unfiltered (gdb_stdlog,
+					    "Process record: step.\n");
+		      continue_flag = 0;
+		    }
+
+		  /* check breakpoint */
+		  tmp_pc = regcache_read_pc (regcache);
+		  if (breakpoint_inserted_here_p (aspace, tmp_pc))
+		    {
+		      int decr_pc_after_break
+			= gdbarch_decr_pc_after_break (gdbarch);
+
+		      if (record_debug)
+			fprintf_unfiltered (gdb_stdlog,
+					    "Process record: break "
+					    "at %s.\n",
+					    paddress (gdbarch, tmp_pc));
+		      if (decr_pc_after_break
+			  && execution_direction == EXEC_FORWARD
+			  && !record_resume_step
+			  && software_breakpoint_inserted_here_p (aspace,
+								  tmp_pc))
+			regcache_write_pc (regcache,
+					   tmp_pc + decr_pc_after_break);
+		      continue_flag = 0;
+		    }
+
+		  if (record_hw_watchpoint)
+		    {
+		      if (record_debug)
+			fprintf_unfiltered (gdb_stdlog,
+					    "Process record: hit hw "
+					    "watchpoint.\n");
+		      continue_flag = 0;
+		    }
+		  /* Check target signal */
+		  if (record_list->u.end.sigval != GDB_SIGNAL_0)
+		    /* FIXME: better way to check */
+		    continue_flag = 0;
+		}
+	    }
+
+	  if (continue_flag)
+	    {
+	      if (execution_direction == EXEC_REVERSE)
+		{
+		  if (record_list->prev)
+		    record_list = record_list->prev;
+		}
+	      else
+		{
+		  if (record_list->next)
+		    record_list = record_list->next;
+		}
+	    }
+	}
+      while (continue_flag);
+
+replay_out:
+      if (record_get_sig)
+	status->value.sig = GDB_SIGNAL_INT;
+      else if (record_list->u.end.sigval != GDB_SIGNAL_0)
+	/* FIXME: better way to check */
+	status->value.sig = record_list->u.end.sigval;
+      else
+	status->value.sig = GDB_SIGNAL_TRAP;
+
+      discard_cleanups (old_cleanups);
+    }
+
+  signal (SIGINT, handle_sigint);
+
+  do_cleanups (set_cleanups);
+  return inferior_ptid;
+}
+
+static ptid_t
+record_wait (struct target_ops *ops,
+	     ptid_t ptid, struct target_waitstatus *status,
+	     int options)
+{
+  ptid_t return_ptid;
+
+  return_ptid = record_wait_1 (ops, ptid, status, options);
+  if (status->kind != TARGET_WAITKIND_IGNORE)
+    {
+      /* We're reporting a stop.  Make sure any spurious
+	 target_wait(WNOHANG) doesn't advance the target until the
+	 core wants us resumed again.  */
+      record_resumed = 0;
+    }
+  return return_ptid;
+}
+
+static int
+record_stopped_by_watchpoint (void)
+{
+  if (RECORD_IS_REPLAY)
+    return record_hw_watchpoint;
+  else
+    return record_beneath_to_stopped_by_watchpoint ();
+}
+
+/* "to_disconnect" method for process record target.  */
+
+static void
+record_disconnect (struct target_ops *target, char *args, int from_tty)
+{
+  if (record_debug)
+    fprintf_unfiltered (gdb_stdlog, "Process record: record_disconnect\n");
+
+  unpush_target (&record_ops);
+  target_disconnect (args, from_tty);
+}
+
+/* "to_detach" method for process record target.  */
+
+static void
+record_detach (struct target_ops *ops, char *args, int from_tty)
+{
+  if (record_debug)
+    fprintf_unfiltered (gdb_stdlog, "Process record: record_detach\n");
+
+  unpush_target (&record_ops);
+  target_detach (args, from_tty);
+}
+
+/* "to_mourn_inferior" method for process record target.  */
+
+static void
+record_mourn_inferior (struct target_ops *ops)
+{
+  if (record_debug)
+    fprintf_unfiltered (gdb_stdlog, "Process record: "
+			            "record_mourn_inferior\n");
+
+  unpush_target (&record_ops);
+  target_mourn_inferior ();
+}
+
+/* Close process record target before killing the inferior process.  */
+
+static void
+record_kill (struct target_ops *ops)
+{
+  if (record_debug)
+    fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n");
+
+  unpush_target (&record_ops);
+  target_kill ();
+}
+
+static int
+record_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p)
+{
+  if (RECORD_IS_REPLAY)
+    return 0;
+  else
+    return record_beneath_to_stopped_data_address (ops, addr_p);
+}
+
+/* Record registers change (by user or by GDB) to list as an instruction.  */
+
+static void
+record_registers_change (struct regcache *regcache, int regnum)
+{
+  /* Check record_insn_num.  */
+  record_check_insn_num (0);
+
+  record_arch_list_head = NULL;
+  record_arch_list_tail = NULL;
+
+  if (regnum < 0)
+    {
+      int i;
+
+      for (i = 0; i < gdbarch_num_regs (get_regcache_arch (regcache)); i++)
+	{
+	  if (record_arch_list_add_reg (regcache, i))
+	    {
+	      record_list_release (record_arch_list_tail);
+	      error (_("Process record: failed to record execution log."));
+	    }
+	}
+    }
+  else
+    {
+      if (record_arch_list_add_reg (regcache, regnum))
+	{
+	  record_list_release (record_arch_list_tail);
+	  error (_("Process record: failed to record execution log."));
+	}
+    }
+  if (record_arch_list_add_end ())
+    {
+      record_list_release (record_arch_list_tail);
+      error (_("Process record: failed to record execution log."));
+    }
+  record_list->next = record_arch_list_head;
+  record_arch_list_head->prev = record_list;
+  record_list = record_arch_list_tail;
+
+  if (record_insn_num == record_insn_max_num && record_insn_max_num)
+    record_list_release_first ();
+  else
+    record_insn_num++;
+}
+
+/* "to_store_registers" method for process record target.  */
+
+static void
+record_store_registers (struct target_ops *ops, struct regcache *regcache,
+                        int regno)
+{
+  if (!record_gdb_operation_disable)
+    {
+      if (RECORD_IS_REPLAY)
+	{
+	  int n;
+
+	  /* Let user choose if he wants to write register or not.  */
+	  if (regno < 0)
+	    n =
+	      query (_("Because GDB is in replay mode, changing the "
+		       "value of a register will make the execution "
+		       "log unusable from this point onward.  "
+		       "Change all registers?"));
+	  else
+	    n =
+	      query (_("Because GDB is in replay mode, changing the value "
+		       "of a register will make the execution log unusable "
+		       "from this point onward.  Change register %s?"),
+		      gdbarch_register_name (get_regcache_arch (regcache),
+					       regno));
+
+	  if (!n)
+	    {
+	      /* Invalidate the value of regcache that was set in function
+	         "regcache_raw_write".  */
+	      if (regno < 0)
+		{
+		  int i;
+
+		  for (i = 0;
+		       i < gdbarch_num_regs (get_regcache_arch (regcache));
+		       i++)
+		    regcache_invalidate (regcache, i);
+		}
+	      else
+		regcache_invalidate (regcache, regno);
+
+	      error (_("Process record canceled the operation."));
+	    }
+
+	  /* Destroy the record from here forward.  */
+	  record_list_release_following (record_list);
+	}
+
+      record_registers_change (regcache, regno);
+    }
+  record_beneath_to_store_registers (record_beneath_to_store_registers_ops,
+                                     regcache, regno);
+}
+
+/* "to_xfer_partial" method.  Behavior is conditional on RECORD_IS_REPLAY.
+   In replay mode, we cannot write memory unles we are willing to
+   invalidate the record/replay log from this point forward.  */
+
+static LONGEST
+record_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 (!record_gdb_operation_disable
+      && (object == TARGET_OBJECT_MEMORY
+	  || object == TARGET_OBJECT_RAW_MEMORY) && writebuf)
+    {
+      if (RECORD_IS_REPLAY)
+	{
+	  /* Let user choose if he wants to write memory or not.  */
+	  if (!query (_("Because GDB is in replay mode, writing to memory "
+		        "will make the execution log unusable from this "
+		        "point onward.  Write memory at address %s?"),
+		       paddress (target_gdbarch (), offset)))
+	    error (_("Process record canceled the operation."));
+
+	  /* Destroy the record from here forward.  */
+	  record_list_release_following (record_list);
+	}
+
+      /* Check record_insn_num */
+      record_check_insn_num (0);
+
+      /* Record registers change to list as an instruction.  */
+      record_arch_list_head = NULL;
+      record_arch_list_tail = NULL;
+      if (record_arch_list_add_mem (offset, len))
+	{
+	  record_list_release (record_arch_list_tail);
+	  if (record_debug)
+	    fprintf_unfiltered (gdb_stdlog,
+				"Process record: failed to record "
+				"execution log.");
+	  return -1;
+	}
+      if (record_arch_list_add_end ())
+	{
+	  record_list_release (record_arch_list_tail);
+	  if (record_debug)
+	    fprintf_unfiltered (gdb_stdlog,
+				"Process record: failed to record "
+				"execution log.");
+	  return -1;
+	}
+      record_list->next = record_arch_list_head;
+      record_arch_list_head->prev = record_list;
+      record_list = record_arch_list_tail;
+
+      if (record_insn_num == record_insn_max_num && record_insn_max_num)
+	record_list_release_first ();
+      else
+	record_insn_num++;
+    }
+
+  return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops,
+                                         object, annex, readbuf, writebuf,
+                                         offset, len);
+}
+
+/* This structure represents a breakpoint inserted while the record
+   target is active.  We use this to know when to install/remove
+   breakpoints in/from the target beneath.  For example, a breakpoint
+   may be inserted while recording, but removed when not replaying nor
+   recording.  In that case, the breakpoint had not been inserted on
+   the target beneath, so we should not try to remove it there.  */
+
+struct record_breakpoint
+{
+  /* The address and address space the breakpoint was set at.  */
+  struct address_space *address_space;
+  CORE_ADDR addr;
+
+  /* True when the breakpoint has been also installed in the target
+     beneath.  This will be false for breakpoints set during replay or
+     when recording.  */
+  int in_target_beneath;
+};
+
+typedef struct record_breakpoint *record_breakpoint_p;
+DEF_VEC_P(record_breakpoint_p);
+
+/* The list of breakpoints inserted while the record target is
+   active.  */
+VEC(record_breakpoint_p) *record_breakpoints = NULL;
+
+static void
+record_sync_record_breakpoints (struct bp_location *loc, void *data)
+{
+  if (loc->loc_type != bp_loc_software_breakpoint)
+      return;
+
+  if (loc->inserted)
+    {
+      struct record_breakpoint *bp = XNEW (struct record_breakpoint);
+
+      bp->addr = loc->target_info.placed_address;
+      bp->address_space = loc->target_info.placed_address_space;
+
+      bp->in_target_beneath = 1;
+
+      VEC_safe_push (record_breakpoint_p, record_breakpoints, bp);
+    }
+}
+
+/* Sync existing breakpoints to record_breakpoints.  */
+
+static void
+record_init_record_breakpoints (void)
+{
+  VEC_free (record_breakpoint_p, record_breakpoints);
+
+  iterate_over_bp_locations (record_sync_record_breakpoints);
+}
+
+/* Behavior is conditional on RECORD_IS_REPLAY.  We will not actually
+   insert or remove breakpoints in the real target when replaying, nor
+   when recording.  */
+
+static int
+record_insert_breakpoint (struct gdbarch *gdbarch,
+			  struct bp_target_info *bp_tgt)
+{
+  struct record_breakpoint *bp;
+  int in_target_beneath = 0;
+
+  if (!RECORD_IS_REPLAY)
+    {
+      /* When recording, we currently always single-step, so we don't
+	 really need to install regular breakpoints in the inferior.
+	 However, we do have to insert software single-step
+	 breakpoints, in case the target can't hardware step.  To keep
+	 things single, we always insert.  */
+      struct cleanup *old_cleanups;
+      int ret;
+
+      old_cleanups = record_gdb_operation_disable_set ();
+      ret = record_beneath_to_insert_breakpoint (gdbarch, bp_tgt);
+      do_cleanups (old_cleanups);
+
+      if (ret != 0)
+	return ret;
+
+      in_target_beneath = 1;
+    }
+
+  bp = XNEW (struct record_breakpoint);
+  bp->addr = bp_tgt->placed_address;
+  bp->address_space = bp_tgt->placed_address_space;
+  bp->in_target_beneath = in_target_beneath;
+  VEC_safe_push (record_breakpoint_p, record_breakpoints, bp);
+  return 0;
+}
+
+/* "to_remove_breakpoint" method for process record target.  */
+
+static int
+record_remove_breakpoint (struct gdbarch *gdbarch,
+			  struct bp_target_info *bp_tgt)
+{
+  struct record_breakpoint *bp;
+  int ix;
+
+  for (ix = 0;
+       VEC_iterate (record_breakpoint_p, record_breakpoints, ix, bp);
+       ++ix)
+    {
+      if (bp->addr == bp_tgt->placed_address
+	  && bp->address_space == bp_tgt->placed_address_space)
+	{
+	  if (bp->in_target_beneath)
+	    {
+	      struct cleanup *old_cleanups;
+	      int ret;
+
+	      old_cleanups = record_gdb_operation_disable_set ();
+	      ret = record_beneath_to_remove_breakpoint (gdbarch, bp_tgt);
+	      do_cleanups (old_cleanups);
+
+	      if (ret != 0)
+		return ret;
+	    }
+
+	  VEC_unordered_remove (record_breakpoint_p, record_breakpoints, ix);
+	  return 0;
+	}
+    }
+
+  gdb_assert_not_reached ("removing unknown breakpoint");
+}
+
+/* "to_can_execute_reverse" method for process record target.  */
+
+static int
+record_can_execute_reverse (void)
+{
+  return 1;
+}
+
+/* "to_get_bookmark" method for process record and prec over core.  */
+
+static gdb_byte *
+record_get_bookmark (char *args, int from_tty)
+{
+  gdb_byte *ret = NULL;
+
+  /* Return stringified form of instruction count.  */
+  if (record_list && record_list->type == record_end)
+    ret = xstrdup (pulongest (record_list->u.end.insn_num));
+
+  if (record_debug)
+    {
+      if (ret)
+	fprintf_unfiltered (gdb_stdlog,
+			    "record_get_bookmark returns %s\n", ret);
+      else
+	fprintf_unfiltered (gdb_stdlog,
+			    "record_get_bookmark returns NULL\n");
+    }
+  return ret;
+}
+
+/* "to_goto_bookmark" method for process record and prec over core.  */
+
+static void
+record_goto_bookmark (gdb_byte *bookmark, int from_tty)
+{
+  if (record_debug)
+    fprintf_unfiltered (gdb_stdlog,
+			"record_goto_bookmark receives %s\n", bookmark);
+
+  if (bookmark[0] == '\'' || bookmark[0] == '\"')
+    {
+      if (bookmark[strlen (bookmark) - 1] != bookmark[0])
+	error (_("Unbalanced quotes: %s"), bookmark);
+
+      /* Strip trailing quote.  */
+      bookmark[strlen (bookmark) - 1] = '\0';
+      /* Strip leading quote.  */
+      bookmark++;
+      /* Pass along to cmd_record_goto.  */
+    }
+
+  cmd_record_goto ((char *) bookmark, from_tty);
+  return;
+}
+
+static void
+record_async (void (*callback) (enum inferior_event_type event_type,
+				void *context), void *context)
+{
+  /* If we're on top of a line target (e.g., linux-nat, remote), then
+     set it to async mode as well.  Will be NULL if we're sitting on
+     top of the core target, for "record restore".  */
+  if (record_beneath_to_async != NULL)
+    record_beneath_to_async (callback, context);
+}
+
+static int
+record_can_async_p (void)
+{
+  /* We only enable async when the user specifically asks for it.  */
+  return target_async_permitted;
+}
+
+static int
+record_is_async_p (void)
+{
+  /* We only enable async when the user specifically asks for it.  */
+  return target_async_permitted;
+}
+
+static enum exec_direction_kind
+record_execution_direction (void)
+{
+  return record_execution_dir;
+}
+
+static void
+init_record_ops (void)
+{
+  record_ops.to_shortname = "record";
+  record_ops.to_longname = "Process record and replay target";
+  record_ops.to_doc =
+    "Log program while executing and replay execution from log.";
+  record_ops.to_open = record_open;
+  record_ops.to_close = record_close;
+  record_ops.to_resume = record_resume;
+  record_ops.to_wait = record_wait;
+  record_ops.to_disconnect = record_disconnect;
+  record_ops.to_detach = record_detach;
+  record_ops.to_mourn_inferior = record_mourn_inferior;
+  record_ops.to_kill = record_kill;
+  record_ops.to_create_inferior = find_default_create_inferior;
+  record_ops.to_store_registers = record_store_registers;
+  record_ops.to_xfer_partial = record_xfer_partial;
+  record_ops.to_insert_breakpoint = record_insert_breakpoint;
+  record_ops.to_remove_breakpoint = record_remove_breakpoint;
+  record_ops.to_stopped_by_watchpoint = record_stopped_by_watchpoint;
+  record_ops.to_stopped_data_address = record_stopped_data_address;
+  record_ops.to_can_execute_reverse = record_can_execute_reverse;
+  record_ops.to_stratum = record_stratum;
+  /* Add bookmark target methods.  */
+  record_ops.to_get_bookmark = record_get_bookmark;
+  record_ops.to_goto_bookmark = record_goto_bookmark;
+  record_ops.to_async = record_async;
+  record_ops.to_can_async_p = record_can_async_p;
+  record_ops.to_is_async_p = record_is_async_p;
+  record_ops.to_execution_direction = record_execution_direction;
+  record_ops.to_magic = OPS_MAGIC;
+}
+
+/* "to_resume" method for prec over corefile.  */
+
+static void
+record_core_resume (struct target_ops *ops, ptid_t ptid, int step,
+                    enum gdb_signal signal)
+{
+  record_resume_step = step;
+  record_resumed = 1;
+  record_execution_dir = execution_direction;
+
+  /* We are about to start executing the inferior (or simulate it),
+     let's register it with the event loop.  */
+  if (target_can_async_p ())
+    {
+      target_async (inferior_event_handler, 0);
+
+      /* Notify the event loop there's an event to wait for.  */
+      mark_async_event_handler (record_async_inferior_event_token);
+    }
+}
+
+/* "to_kill" method for prec over corefile.  */
+
+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);
+}
+
+/* "to_fetch_registers" method for prec over corefile.  */
+
+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);
+}
+
+/* "to_prepare_to_store" method for prec over corefile.  */
+
+static void
+record_core_prepare_to_store (struct regcache *regcache)
+{
+}
+
+/* "to_store_registers" method for prec over corefile.  */
+
+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."));
+}
+
+/* "to_xfer_partial" method for prec over corefile.  */
+
+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;
+		  ULONGEST sec_offset;
+
+		  if (offset >= p->endaddr)
+		    continue;
+
+		  if (offset + len > p->endaddr)
+		    len = p->endaddr - offset;
+
+		  sec_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 + sec_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 + sec_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);
+}
+
+/* "to_insert_breakpoint" method for prec over corefile.  */
+
+static int
+record_core_insert_breakpoint (struct gdbarch *gdbarch,
+			       struct bp_target_info *bp_tgt)
+{
+  return 0;
+}
+
+/* "to_remove_breakpoint" method for prec over corefile.  */
+
+static int
+record_core_remove_breakpoint (struct gdbarch *gdbarch,
+			       struct bp_target_info *bp_tgt)
+{
+  return 0;
+}
+
+/* "to_has_execution" method for prec over corefile.  */
+
+static int
+record_core_has_execution (struct target_ops *ops, ptid_t the_ptid)
+{
+  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_stopped_by_watchpoint = record_stopped_by_watchpoint;
+  record_core_ops.to_stopped_data_address = record_stopped_data_address;
+  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;
+  /* Add bookmark target methods.  */
+  record_core_ops.to_get_bookmark = record_get_bookmark;
+  record_core_ops.to_goto_bookmark = record_goto_bookmark;
+  record_core_ops.to_async = record_async;
+  record_core_ops.to_can_async_p = record_can_async_p;
+  record_core_ops.to_is_async_p = record_is_async_p;
+  record_core_ops.to_execution_direction = record_execution_direction;
+  record_core_ops.to_magic = OPS_MAGIC;
+}
+
+/* Record log save-file format
+   Version 1 (never released)
+
+   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, see enum record_type).
+     record_reg:
+       1 byte:  record type (record_reg, see enum record_type).
+       8 bytes: register id (network byte order).
+       MAX_REGISTER_SIZE bytes: register value.
+     record_mem:
+       1 byte:  record type (record_mem, see enum record_type).
+       8 bytes: memory length (network byte order).
+       8 bytes: memory address (network byte order).
+       n bytes: memory value (n == memory length).
+
+   Version 2
+     4 bytes: magic number netorder32(0x20091016).
+       NOTE: be sure to change whenever this file format changes!
+
+   Records:
+     record_end:
+       1 byte:  record type (record_end, see enum record_type).
+       4 bytes: signal
+       4 bytes: instruction count
+     record_reg:
+       1 byte:  record type (record_reg, see enum record_type).
+       4 bytes: register id (network byte order).
+       n bytes: register value (n == actual register size).
+                (eg. 4 bytes for x86 general registers).
+     record_mem:
+       1 byte:  record type (record_mem, see enum record_type).
+       4 bytes: memory length (network byte order).
+       8 bytes: memory address (network byte order).
+       n bytes: memory value (n == memory length).
+
+*/
+
+/* bfdcore_read -- read bytes from a core file section.  */
+
+static inline void
+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;
+  else
+    error (_("Failed to read %d bytes from core file %s ('%s')."),
+	   len, bfd_get_filename (obfd),
+	   bfd_errmsg (bfd_get_error ()));
+}
+
+static inline uint64_t
+netorder64 (uint64_t input)
+{
+  uint64_t ret;
+
+  store_unsigned_integer ((gdb_byte *) &ret, sizeof (ret), 
+			  BFD_ENDIAN_BIG, input);
+  return ret;
+}
+
+static inline uint32_t
+netorder32 (uint32_t input)
+{
+  uint32_t ret;
+
+  store_unsigned_integer ((gdb_byte *) &ret, sizeof (ret), 
+			  BFD_ENDIAN_BIG, input);
+  return ret;
+}
+
+static inline uint16_t
+netorder16 (uint16_t input)
+{
+  uint16_t ret;
+
+  store_unsigned_integer ((gdb_byte *) &ret, sizeof (ret), 
+			  BFD_ENDIAN_BIG, input);
+  return ret;
+}
+
+/* Restore the execution log from a core_bfd file.  */
+static void
+record_restore (void)
+{
+  uint32_t magic;
+  struct cleanup *old_cleanups;
+  struct record_entry *rec;
+  asection *osec;
+  uint32_t osec_size;
+  int bfd_offset = 0;
+  struct regcache *regcache;
+
+  /* We restore the execution log from the open core bfd,
+     if there is one.  */
+  if (core_bfd == NULL)
+    return;
+
+  /* "record_restore" can only be called when record list is empty.  */
+  gdb_assert (record_first.next == NULL);
+ 
+  if (record_debug)
+    fprintf_unfiltered (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");
+  if (record_debug)
+    fprintf_unfiltered (gdb_stdlog, "Find precord section %s.\n",
+			osec ? "succeeded" : "failed");
+  if (osec == NULL)
+    return;
+  osec_size = bfd_section_size (core_bfd, osec);
+  if (record_debug)
+    fprintf_unfiltered (gdb_stdlog, "%s", bfd_section_name (core_bfd, osec));
+
+  /* Check the magic code.  */
+  bfdcore_read (core_bfd, osec, &magic, sizeof (magic), &bfd_offset);
+  if (magic != RECORD_FILE_MAGIC)
+    error (_("Version mis-match or file format error in core file %s."),
+	   bfd_get_filename (core_bfd));
+  if (record_debug)
+    fprintf_unfiltered (gdb_stdlog,
+			"  Reading 4-byte magic cookie "
+			"RECORD_FILE_MAGIC (0x%s)\n",
+			phex_nz (netorder32 (magic), 4));
+
+  /* Restore the entries in recfd into 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);
+  regcache = get_current_regcache ();
+
+  while (1)
+    {
+      uint8_t rectype;
+      uint32_t regnum, len, signal, count;
+      uint64_t addr;
+
+      /* We are finished when offset reaches osec_size.  */
+      if (bfd_offset >= osec_size)
+	break;
+      bfdcore_read (core_bfd, osec, &rectype, sizeof (rectype), &bfd_offset);
+
+      switch (rectype)
+        {
+        case record_reg: /* reg */
+          /* Get register number to regnum.  */
+          bfdcore_read (core_bfd, osec, &regnum,
+			sizeof (regnum), &bfd_offset);
+	  regnum = netorder32 (regnum);
+
+          rec = record_reg_alloc (regcache, regnum);
+
+          /* Get val.  */
+          bfdcore_read (core_bfd, osec, record_get_loc (rec),
+			rec->u.reg.len, &bfd_offset);
+
+	  if (record_debug)
+	    fprintf_unfiltered (gdb_stdlog,
+				"  Reading register %d (1 "
+				"plus %lu plus %d bytes)\n",
+				rec->u.reg.num,
+				(unsigned long) sizeof (regnum),
+				rec->u.reg.len);
+          break;
+
+        case record_mem: /* mem */
+          /* Get len.  */
+          bfdcore_read (core_bfd, osec, &len, 
+			sizeof (len), &bfd_offset);
+	  len = netorder32 (len);
+
+          /* Get addr.  */
+          bfdcore_read (core_bfd, osec, &addr,
+			sizeof (addr), &bfd_offset);
+	  addr = netorder64 (addr);
+
+          rec = record_mem_alloc (addr, len);
+
+          /* Get val.  */
+          bfdcore_read (core_bfd, osec, record_get_loc (rec),
+			rec->u.mem.len, &bfd_offset);
+
+	  if (record_debug)
+	    fprintf_unfiltered (gdb_stdlog,
+				"  Reading memory %s (1 plus "
+				"%lu plus %lu plus %d bytes)\n",
+				paddress (get_current_arch (),
+					  rec->u.mem.addr),
+				(unsigned long) sizeof (addr),
+				(unsigned long) sizeof (len),
+				rec->u.mem.len);
+          break;
+
+        case record_end: /* end */
+          rec = record_end_alloc ();
+          record_insn_num ++;
+
+	  /* Get signal value.  */
+	  bfdcore_read (core_bfd, osec, &signal, 
+			sizeof (signal), &bfd_offset);
+	  signal = netorder32 (signal);
+	  rec->u.end.sigval = signal;
+
+	  /* Get insn count.  */
+	  bfdcore_read (core_bfd, osec, &count, 
+			sizeof (count), &bfd_offset);
+	  count = netorder32 (count);
+	  rec->u.end.insn_num = count;
+	  record_insn_count = count + 1;
+	  if (record_debug)
+	    fprintf_unfiltered (gdb_stdlog,
+				"  Reading record_end (1 + "
+				"%lu + %lu bytes), offset == %s\n",
+				(unsigned long) sizeof (signal),
+				(unsigned long) sizeof (count),
+				paddress (get_current_arch (),
+					  bfd_offset));
+          break;
+
+        default:
+          error (_("Bad entry type in core file %s."),
+		 bfd_get_filename (core_bfd));
+          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;
+  record_arch_list_tail->next = NULL;
+  record_list = &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.  */
+  printf_filtered (_("Restored records from core file %s.\n"),
+		   bfd_get_filename (core_bfd));
+
+  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
+}
+
+/* bfdcore_write -- write bytes into a core file section.  */
+
+static inline void
+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;
+  else
+    error (_("Failed to write %d bytes to core file %s ('%s')."),
+	   len, bfd_get_filename (obfd),
+	   bfd_errmsg (bfd_get_error ()));
+}
+
+/* Restore the execution log from a file.  We use a modified elf
+   corefile format, with an extra section for our data.  */
+
+static void
+cmd_record_restore (char *args, int from_tty)
+{
+  core_file_command (args, from_tty);
+  record_open (args, from_tty);
+}
+
+static void
+record_save_cleanups (void *data)
+{
+  bfd *obfd = data;
+  char *pathname = xstrdup (bfd_get_filename (obfd));
+
+  gdb_bfd_unref (obfd);
+  unlink (pathname);
+  xfree (pathname);
+}
+
+/* Save the execution log to a file.  We use a modified elf corefile
+   format, with an extra section for our data.  */
+
+static void
+cmd_record_save (char *args, int from_tty)
+{
+  char *recfilename, recfilename_buffer[40];
+  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 save_size = 0;
+  asection *osec = NULL;
+  int bfd_offset = 0;
+
+  if (strcmp (current_target.to_shortname, "record") != 0)
+    error (_("This command can only be used with target 'record'.\n"
+	     "Use 'target record' first.\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 save file.  */
+  if (record_debug)
+    fprintf_unfiltered (gdb_stdlog, "Saving execution log to core file '%s'\n",
+			recfilename);
+
+  /* Open the output file.  */
+  obfd = create_gcore_bfd (recfilename);
+  old_cleanups = make_cleanup (record_save_cleanups, 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_insn (regcache, gdbarch, record_list);
+
+      if (record_list->prev)
+        record_list = record_list->prev;
+    }
+
+  /* Compute the size needed for the extra bfd section.  */
+  save_size = 4;	/* magic cookie */
+  for (record_list = record_first.next; record_list;
+       record_list = record_list->next)
+    switch (record_list->type)
+      {
+      case record_end:
+	save_size += 1 + 4 + 4;
+	break;
+      case record_reg:
+	save_size += 1 + 4 + record_list->u.reg.len;
+	break;
+      case record_mem:
+	save_size += 1 + 4 + 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: %s"),
+	   recfilename,
+           bfd_errmsg (bfd_get_error ()));
+  bfd_set_section_size (obfd, osec, save_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_unfiltered (gdb_stdlog,
+			"  Writing 4-byte magic cookie "
+			"RECORD_FILE_MAGIC (0x%s)\n",
+		      phex_nz (magic, 4));
+  bfdcore_write (obfd, osec, &magic, sizeof (magic), &bfd_offset);
+
+  /* Save the entries to recfd and forward execute to the end of
+     record list.  */
+  record_list = &record_first;
+  while (1)
+    {
+      /* Save entry.  */
+      if (record_list != &record_first)
+        {
+	  uint8_t type;
+	  uint32_t regnum, len, signal, count;
+          uint64_t addr;
+
+	  type = record_list->type;
+          bfdcore_write (obfd, osec, &type, sizeof (type), &bfd_offset);
+
+          switch (record_list->type)
+            {
+            case record_reg: /* reg */
+	      if (record_debug)
+		fprintf_unfiltered (gdb_stdlog,
+				    "  Writing register %d (1 "
+				    "plus %lu plus %d bytes)\n",
+				    record_list->u.reg.num,
+				    (unsigned long) sizeof (regnum),
+				    record_list->u.reg.len);
+
+              /* Write regnum.  */
+              regnum = netorder32 (record_list->u.reg.num);
+              bfdcore_write (obfd, osec, &regnum,
+			     sizeof (regnum), &bfd_offset);
+
+              /* Write regval.  */
+              bfdcore_write (obfd, osec, record_get_loc (record_list),
+			     record_list->u.reg.len, &bfd_offset);
+              break;
+
+            case record_mem: /* mem */
+	      if (record_debug)
+		fprintf_unfiltered (gdb_stdlog,
+				    "  Writing memory %s (1 plus "
+				    "%lu plus %lu plus %d bytes)\n",
+				    paddress (gdbarch,
+					      record_list->u.mem.addr),
+				    (unsigned long) sizeof (addr),
+				    (unsigned long) sizeof (len),
+				    record_list->u.mem.len);
+
+	      /* Write memlen.  */
+	      len = netorder32 (record_list->u.mem.len);
+	      bfdcore_write (obfd, osec, &len, sizeof (len), &bfd_offset);
+
+	      /* Write memaddr.  */
+	      addr = netorder64 (record_list->u.mem.addr);
+	      bfdcore_write (obfd, osec, &addr, 
+			     sizeof (addr), &bfd_offset);
+
+	      /* Write memval.  */
+	      bfdcore_write (obfd, osec, record_get_loc (record_list),
+			     record_list->u.mem.len, &bfd_offset);
+              break;
+
+              case record_end:
+		if (record_debug)
+		  fprintf_unfiltered (gdb_stdlog,
+				      "  Writing record_end (1 + "
+				      "%lu + %lu bytes)\n", 
+				      (unsigned long) sizeof (signal),
+				      (unsigned long) sizeof (count));
+		/* Write signal value.  */
+		signal = netorder32 (record_list->u.end.sigval);
+		bfdcore_write (obfd, osec, &signal,
+			       sizeof (signal), &bfd_offset);
+
+		/* Write insn count.  */
+		count = netorder32 (record_list->u.end.insn_num);
+		bfdcore_write (obfd, osec, &count,
+			       sizeof (count), &bfd_offset);
+                break;
+            }
+        }
+
+      /* Execute entry.  */
+      record_exec_insn (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_insn (regcache, gdbarch, record_list);
+
+      if (record_list->prev)
+        record_list = record_list->prev;
+    }
+
+  do_cleanups (set_cleanups);
+  gdb_bfd_unref (obfd);
+  discard_cleanups (old_cleanups);
+
+  /* Succeeded.  */
+  printf_filtered (_("Saved core file %s with execution log.\n"),
+		   recfilename);
+}
+
+/* record_goto_insn -- rewind the record log (forward or backward,
+   depending on DIR) to the given entry, changing the program state
+   correspondingly.  */
+
+static void
+record_goto_insn (struct record_entry *entry,
+		  enum exec_direction_kind dir)
+{
+  struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
+  struct regcache *regcache = get_current_regcache ();
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+
+  /* Assume everything is valid: we will hit the entry,
+     and we will not hit the end of the recording.  */
+
+  if (dir == EXEC_FORWARD)
+    record_list = record_list->next;
+
+  do
+    {
+      record_exec_insn (regcache, gdbarch, record_list);
+      if (dir == EXEC_REVERSE)
+	record_list = record_list->prev;
+      else
+	record_list = record_list->next;
+    } while (record_list != entry);
+  do_cleanups (set_cleanups);
+}
diff --git a/gdb/record-full.h b/gdb/record-full.h
new file mode 100644
index 0000000..e712fcc
--- /dev/null
+++ b/gdb/record-full.h
@@ -0,0 +1,36 @@
+/* Process record and replay target for GDB, the GNU debugger.
+
+   Copyright (C) 2013 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/>.  */
+
+#ifndef RECORD_FULL_H
+#define RECORD_FULL_H
+
+extern int record_memory_query;
+
+extern int record_arch_list_add_reg (struct regcache *regcache, int num);
+extern int record_arch_list_add_mem (CORE_ADDR addr, int len);
+extern int record_arch_list_add_end (void);
+extern struct cleanup *record_gdb_operation_disable_set (void);
+
+/* Wrapper for target_read_memory that prints a debug message if
+   reading memory fails.  */
+extern int record_read_memory (struct gdbarch *gdbarch,
+			       CORE_ADDR memaddr, gdb_byte *myaddr,
+			       ssize_t len);
+
+#endif /* RECORD_FULL_H */
diff --git a/gdb/record.c b/gdb/record.c
index 1a68738..2e970ae 100644
--- a/gdb/record.c
+++ b/gdb/record.c
@@ -37,2181 +37,12 @@
 
 #include <signal.h>
 
-/* This module implements "target record", also known as "process
-   record and replay".  This target sits on top of a "normal" target
-   (a target that "has execution"), and provides a record and replay
-   functionality, including reverse debugging.
-
-   Target record has two modes: recording, and replaying.
-
-   In record mode, we intercept the to_resume and to_wait methods.
-   Whenever gdb resumes the target, we run the target in single step
-   mode, and we build up an execution log in which, for each executed
-   instruction, we record all changes in memory and register state.
-   This is invisible to the user, to whom it just looks like an
-   ordinary debugging session (except for performance degredation).
-
-   In replay mode, instead of actually letting the inferior run as a
-   process, we simulate its execution by playing back the recorded
-   execution log.  For each instruction in the log, we simulate the
-   instruction's side effects by duplicating the changes that it would
-   have made on memory and registers.  */
-
-#define DEFAULT_RECORD_INSN_MAX_NUM	200000
-
-#define RECORD_IS_REPLAY \
-     (record_list->next || execution_direction == EXEC_REVERSE)
-
-#define RECORD_FILE_MAGIC	netorder32(0x20091016)
-
-/* These are the core structs of the process record functionality.
-
-   A record_entry is a record of the value change of a register
-   ("record_reg") or a part of memory ("record_mem").  And each
-   instruction must have a struct record_entry ("record_end") that
-   indicates that this is the last struct record_entry of this
-   instruction.
-
-   Each struct record_entry is linked to "record_list" by "prev" and
-   "next" pointers.  */
-
-struct record_mem_entry
-{
-  CORE_ADDR addr;
-  int len;
-  /* Set this flag if target memory for this entry
-     can no longer be accessed.  */
-  int mem_entry_not_accessible;
-  union
-  {
-    gdb_byte *ptr;
-    gdb_byte buf[sizeof (gdb_byte *)];
-  } u;
-};
-
-struct record_reg_entry
-{
-  unsigned short num;
-  unsigned short len;
-  union 
-  {
-    gdb_byte *ptr;
-    gdb_byte buf[2 * sizeof (gdb_byte *)];
-  } u;
-};
-
-struct record_end_entry
-{
-  enum gdb_signal sigval;
-  ULONGEST insn_num;
-};
-
-enum record_type
-{
-  record_end = 0,
-  record_reg,
-  record_mem
-};
-
-/* This is the data structure that makes up the execution log.
-
-   The execution log consists of a single linked list of entries
-   of type "struct record_entry".  It is doubly linked so that it
-   can be traversed in either direction.
-
-   The start of the list is anchored by a struct called
-   "record_first".  The pointer "record_list" either points to the
-   last entry that was added to the list (in record mode), or to the
-   next entry in the list that will be executed (in replay mode).
-
-   Each list element (struct record_entry), in addition to next and
-   prev pointers, consists of a union of three entry types: mem, reg,
-   and end.  A field called "type" determines which entry type is
-   represented by a given list element.
-
-   Each instruction that is added to the execution log is represented
-   by a variable number of list elements ('entries').  The instruction
-   will have one "reg" entry for each register that is changed by 
-   executing the instruction (including the PC in every case).  It 
-   will also have one "mem" entry for each memory change.  Finally,
-   each instruction will have an "end" entry that separates it from
-   the changes associated with the next instruction.  */
-
-struct record_entry
-{
-  struct record_entry *prev;
-  struct record_entry *next;
-  enum record_type type;
-  union
-  {
-    /* reg */
-    struct record_reg_entry reg;
-    /* mem */
-    struct record_mem_entry mem;
-    /* end */
-    struct record_end_entry end;
-  } u;
-};
-
 /* This is the debug switch for process record.  */
 unsigned int record_debug = 0;
 
-/* If true, query if PREC cannot record memory
-   change of next instruction.  */
-int record_memory_query = 0;
-
-struct record_core_buf_entry
-{
-  struct record_core_buf_entry *prev;
-  struct target_section *p;
-  bfd_byte *buf;
-};
-
-/* 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;
-
-/* The following variables are used for managing the linked list that
-   represents the execution log.
-
-   record_first is the anchor that holds down the beginning of the list.
-
-   record_list serves two functions:
-     1) In record mode, it anchors the end of the list.
-     2) In replay mode, it traverses the list and points to
-        the next instruction that must be emulated.
-
-   record_arch_list_head and record_arch_list_tail are used to manage
-   a separate list, which is used to build up the change elements of
-   the currently executing instruction during record mode.  When this
-   instruction has been completely annotated in the "arch list", it 
-   will be appended to the main execution log.  */
-
-static struct record_entry record_first;
-static struct record_entry *record_list = &record_first;
-static struct record_entry *record_arch_list_head = NULL;
-static struct record_entry *record_arch_list_tail = NULL;
-
-/* 1 ask user. 0 auto delete the last struct record_entry.  */
-static int record_stop_at_limit = 1;
-/* Maximum allowed number of insns in execution log.  */
-static unsigned int record_insn_max_num = DEFAULT_RECORD_INSN_MAX_NUM;
-/* Actual count of insns presently in execution log.  */
-static int record_insn_num = 0;
-/* Count of insns logged so far (may be larger
-   than count of insns presently in execution log).  */
-static ULONGEST record_insn_count;
-
-/* 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;
-static void (*record_beneath_to_resume) (struct target_ops *, ptid_t, int,
-                                         enum gdb_signal);
-static struct target_ops *record_beneath_to_wait_ops;
-static ptid_t (*record_beneath_to_wait) (struct target_ops *, ptid_t,
-					 struct target_waitstatus *,
-					 int);
-static struct target_ops *record_beneath_to_store_registers_ops;
-static void (*record_beneath_to_store_registers) (struct target_ops *,
-                                                  struct regcache *,
-						  int regno);
-static struct target_ops *record_beneath_to_xfer_partial_ops;
-static LONGEST (*record_beneath_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 (*record_beneath_to_insert_breakpoint) (struct gdbarch *,
-						   struct bp_target_info *);
-static int (*record_beneath_to_remove_breakpoint) (struct gdbarch *,
-						   struct bp_target_info *);
-static int (*record_beneath_to_stopped_by_watchpoint) (void);
-static int (*record_beneath_to_stopped_data_address) (struct target_ops *,
-						      CORE_ADDR *);
-static void (*record_beneath_to_async) (void (*) (enum inferior_event_type, void *), void *);
-
-/* Alloc and free functions for record_reg, record_mem, and record_end 
-   entries.  */
-
-/* Alloc a record_reg record entry.  */
-
-static inline struct record_entry *
-record_reg_alloc (struct regcache *regcache, int regnum)
-{
-  struct record_entry *rec;
-  struct gdbarch *gdbarch = get_regcache_arch (regcache);
-
-  rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry));
-  rec->type = record_reg;
-  rec->u.reg.num = regnum;
-  rec->u.reg.len = register_size (gdbarch, regnum);
-  if (rec->u.reg.len > sizeof (rec->u.reg.u.buf))
-    rec->u.reg.u.ptr = (gdb_byte *) xmalloc (rec->u.reg.len);
-
-  return rec;
-}
-
-/* Free a record_reg record entry.  */
-
-static inline void
-record_reg_release (struct record_entry *rec)
-{
-  gdb_assert (rec->type == record_reg);
-  if (rec->u.reg.len > sizeof (rec->u.reg.u.buf))
-    xfree (rec->u.reg.u.ptr);
-  xfree (rec);
-}
-
-/* Alloc a record_mem record entry.  */
-
-static inline struct record_entry *
-record_mem_alloc (CORE_ADDR addr, int len)
-{
-  struct record_entry *rec;
-
-  rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry));
-  rec->type = record_mem;
-  rec->u.mem.addr = addr;
-  rec->u.mem.len = len;
-  if (rec->u.mem.len > sizeof (rec->u.mem.u.buf))
-    rec->u.mem.u.ptr = (gdb_byte *) xmalloc (len);
-
-  return rec;
-}
-
-/* Free a record_mem record entry.  */
-
-static inline void
-record_mem_release (struct record_entry *rec)
-{
-  gdb_assert (rec->type == record_mem);
-  if (rec->u.mem.len > sizeof (rec->u.mem.u.buf))
-    xfree (rec->u.mem.u.ptr);
-  xfree (rec);
-}
-
-/* Alloc a record_end record entry.  */
-
-static inline struct record_entry *
-record_end_alloc (void)
-{
-  struct record_entry *rec;
-
-  rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry));
-  rec->type = record_end;
-
-  return rec;
-}
-
-/* Free a record_end record entry.  */
-
-static inline void
-record_end_release (struct record_entry *rec)
-{
-  xfree (rec);
-}
-
-/* Free one record entry, any type.
-   Return entry->type, in case caller wants to know.  */
-
-static inline enum record_type
-record_entry_release (struct record_entry *rec)
-{
-  enum record_type type = rec->type;
-
-  switch (type) {
-  case record_reg:
-    record_reg_release (rec);
-    break;
-  case record_mem:
-    record_mem_release (rec);
-    break;
-  case record_end:
-    record_end_release (rec);
-    break;
-  }
-  return type;
-}
-
-/* Free all record entries in list pointed to by REC.  */
-
-static void
-record_list_release (struct record_entry *rec)
-{
-  if (!rec)
-    return;
-
-  while (rec->next)
-    rec = rec->next;
-
-  while (rec->prev)
-    {
-      rec = rec->prev;
-      record_entry_release (rec->next);
-    }
-
-  if (rec == &record_first)
-    {
-      record_insn_num = 0;
-      record_first.next = NULL;
-    }
-  else
-    record_entry_release (rec);
-}
-
-/* Free all record entries forward of the given list position.  */
-
-static void
-record_list_release_following (struct record_entry *rec)
-{
-  struct record_entry *tmp = rec->next;
-
-  rec->next = NULL;
-  while (tmp)
-    {
-      rec = tmp->next;
-      if (record_entry_release (tmp) == record_end)
-	{
-	  record_insn_num--;
-	  record_insn_count--;
-	}
-      tmp = rec;
-    }
-}
-
-/* Delete the first instruction from the beginning of the log, to make
-   room for adding a new instruction at the end of the log.
-
-   Note -- this function does not modify record_insn_num.  */
-
-static void
-record_list_release_first (void)
-{
-  struct record_entry *tmp;
-
-  if (!record_first.next)
-    return;
-
-  /* Loop until a record_end.  */
-  while (1)
-    {
-      /* Cut record_first.next out of the linked list.  */
-      tmp = record_first.next;
-      record_first.next = tmp->next;
-      tmp->next->prev = &record_first;
-
-      /* tmp is now isolated, and can be deleted.  */
-      if (record_entry_release (tmp) == record_end)
-	break;	/* End loop at first record_end.  */
-
-      if (!record_first.next)
-	{
-	  gdb_assert (record_insn_num == 1);
-	  break;	/* End loop when list is empty.  */
-	}
-    }
-}
-
-/* Add a struct record_entry to record_arch_list.  */
-
-static void
-record_arch_list_add (struct record_entry *rec)
-{
-  if (record_debug > 1)
-    fprintf_unfiltered (gdb_stdlog,
-			"Process record: record_arch_list_add %s.\n",
-			host_address_to_string (rec));
-
-  if (record_arch_list_tail)
-    {
-      record_arch_list_tail->next = rec;
-      rec->prev = record_arch_list_tail;
-      record_arch_list_tail = rec;
-    }
-  else
-    {
-      record_arch_list_head = rec;
-      record_arch_list_tail = rec;
-    }
-}
-
-/* Return the value storage location of a record entry.  */
-static inline gdb_byte *
-record_get_loc (struct record_entry *rec)
-{
-  switch (rec->type) {
-  case record_mem:
-    if (rec->u.mem.len > sizeof (rec->u.mem.u.buf))
-      return rec->u.mem.u.ptr;
-    else
-      return rec->u.mem.u.buf;
-  case record_reg:
-    if (rec->u.reg.len > sizeof (rec->u.reg.u.buf))
-      return rec->u.reg.u.ptr;
-    else
-      return rec->u.reg.u.buf;
-  case record_end:
-  default:
-    gdb_assert_not_reached ("unexpected record_entry type");
-    return NULL;
-  }
-}
-
-/* Record the value of a register NUM to record_arch_list.  */
-
-int
-record_arch_list_add_reg (struct regcache *regcache, int regnum)
-{
-  struct record_entry *rec;
-
-  if (record_debug > 1)
-    fprintf_unfiltered (gdb_stdlog,
-			"Process record: add register num = %d to "
-			"record list.\n",
-			regnum);
-
-  rec = record_reg_alloc (regcache, regnum);
-
-  regcache_raw_read (regcache, regnum, record_get_loc (rec));
-
-  record_arch_list_add (rec);
-
-  return 0;
-}
-
-int
-record_read_memory (struct gdbarch *gdbarch,
-		    CORE_ADDR memaddr, gdb_byte *myaddr,
-		    ssize_t len)
-{
-  int ret = target_read_memory (memaddr, myaddr, len);
-
-  if (ret && record_debug)
-    printf_unfiltered (_("Process record: error reading memory "
-			 "at addr %s len = %ld.\n"),
-		       paddress (gdbarch, memaddr), (long) len);
-  return ret;
-}
-
-/* Record the value of a region of memory whose address is ADDR and
-   length is LEN to record_arch_list.  */
-
-int
-record_arch_list_add_mem (CORE_ADDR addr, int len)
-{
-  struct record_entry *rec;
-
-  if (record_debug > 1)
-    fprintf_unfiltered (gdb_stdlog,
-			"Process record: add mem addr = %s len = %d to "
-			"record list.\n",
-			paddress (target_gdbarch (), addr), len);
-
-  if (!addr)	/* FIXME: Why?  Some arch must permit it...  */
-    return 0;
-
-  rec = record_mem_alloc (addr, len);
-
-  if (record_read_memory (target_gdbarch (), addr, record_get_loc (rec), len))
-    {
-      record_mem_release (rec);
-      return -1;
-    }
-
-  record_arch_list_add (rec);
-
-  return 0;
-}
-
-/* Add a record_end type struct record_entry to record_arch_list.  */
-
-int
-record_arch_list_add_end (void)
-{
-  struct record_entry *rec;
-
-  if (record_debug > 1)
-    fprintf_unfiltered (gdb_stdlog,
-			"Process record: add end to arch list.\n");
-
-  rec = record_end_alloc ();
-  rec->u.end.sigval = GDB_SIGNAL_0;
-  rec->u.end.insn_num = ++record_insn_count;
-
-  record_arch_list_add (rec);
-
-  return 0;
-}
-
-static void
-record_check_insn_num (int set_terminal)
-{
-  if (record_insn_max_num)
-    {
-      gdb_assert (record_insn_num <= record_insn_max_num);
-      if (record_insn_num == record_insn_max_num)
-	{
-	  /* Ask user what to do.  */
-	  if (record_stop_at_limit)
-	    {
-	      int q;
-
-	      if (set_terminal)
-		target_terminal_ours ();
-	      q = yquery (_("Do you want to auto delete previous execution "
-			    "log entries when record/replay buffer becomes "
-			    "full (record stop-at-limit)?"));
-	      if (set_terminal)
-		target_terminal_inferior ();
-	      if (q)
-		record_stop_at_limit = 0;
-	      else
-		error (_("Process record: stopped 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 int
-record_message (struct regcache *regcache, enum gdb_signal signal)
-{
-  int ret;
-  struct gdbarch *gdbarch = get_regcache_arch (regcache);
-  struct cleanup *old_cleanups = make_cleanup (record_arch_list_cleanups, 0);
-
-  record_arch_list_head = NULL;
-  record_arch_list_tail = NULL;
-
-  /* Check record_insn_num.  */
-  record_check_insn_num (1);
-
-  /* If gdb sends a signal value to target_resume,
-     save it in the 'end' field of the previous instruction.
-
-     Maybe process record should record what really happened,
-     rather than what gdb pretends has happened.
-
-     So if Linux delivered the signal to the child process during
-     the record mode, we will record it and deliver it again in
-     the replay mode.
-
-     If user says "ignore this signal" during the record mode, then
-     it will be ignored again during the replay mode (no matter if
-     the user says something different, like "deliver this signal"
-     during the replay mode).
-
-     User should understand that nothing he does during the replay
-     mode will change the behavior of the child.  If he tries,
-     then that is a user error.
-
-     But we should still deliver the signal to gdb during the replay,
-     if we delivered it during the recording.  Therefore we should
-     record the signal during record_wait, not record_resume.  */
-  if (record_list != &record_first)    /* FIXME better way to check */
-    {
-      gdb_assert (record_list->type == record_end);
-      record_list->u.end.sigval = signal;
-    }
-
-  if (signal == GDB_SIGNAL_0
-      || !gdbarch_process_record_signal_p (gdbarch))
-    ret = gdbarch_process_record (gdbarch,
-				  regcache,
-				  regcache_read_pc (regcache));
-  else
-    ret = gdbarch_process_record_signal (gdbarch,
-					 regcache,
-					 signal);
-
-  if (ret > 0)
-    error (_("Process record: inferior program stopped."));
-  if (ret < 0)
-    error (_("Process record: failed to record execution log."));
-
-  discard_cleanups (old_cleanups);
-
-  record_list->next = record_arch_list_head;
-  record_arch_list_head->prev = record_list;
-  record_list = record_arch_list_tail;
-
-  if (record_insn_num == record_insn_max_num && record_insn_max_num)
-    record_list_release_first ();
-  else
-    record_insn_num++;
-
-  return 1;
-}
-
-struct record_message_args {
-  struct regcache *regcache;
-  enum gdb_signal signal;
-};
-
-static int
-record_message_wrapper (void *args)
-{
-  struct record_message_args *record_args = args;
-
-  return record_message (record_args->regcache, record_args->signal);
-}
-
-static int
-record_message_wrapper_safe (struct regcache *regcache,
-                             enum gdb_signal signal)
-{
-  struct record_message_args args;
-
-  args.regcache = regcache;
-  args.signal = signal;
-
-  return catch_errors (record_message_wrapper, &args, NULL, RETURN_MASK_ALL);
-}
-
-/* Set to 1 if record_store_registers and record_xfer_partial
-   doesn't need record.  */
-
-static int record_gdb_operation_disable = 0;
-
-struct cleanup *
-record_gdb_operation_disable_set (void)
-{
-  struct cleanup *old_cleanups = NULL;
-
-  old_cleanups =
-    make_cleanup_restore_integer (&record_gdb_operation_disable);
-  record_gdb_operation_disable = 1;
-
-  return old_cleanups;
-}
-
-/* Flag set to TRUE for target_stopped_by_watchpoint.  */
-static int record_hw_watchpoint = 0;
-
-/* Execute one instruction from the record log.  Each instruction in
-   the log will be represented by an arbitrary sequence of register
-   entries and memory entries, followed by an 'end' entry.  */
-
-static inline void
-record_exec_insn (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, 
-			       record_get_loc (entry));
-        memcpy (record_get_loc (entry), reg, entry->u.reg.len);
-      }
-      break;
-
-    case record_mem: /* mem */
-      {
-	/* Nothing to do if the entry is flagged not_accessible.  */
-        if (!entry->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),
-                                  entry->u.mem.len);
-
-            if (record_read_memory (gdbarch,
-				    entry->u.mem.addr, mem, entry->u.mem.len))
-	      entry->u.mem.mem_entry_not_accessible = 1;
-            else
-              {
-                if (target_write_memory (entry->u.mem.addr, 
-					 record_get_loc (entry),
-					 entry->u.mem.len))
-                  {
-                    entry->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 (record_get_loc (entry), mem, entry->u.mem.len);
-
-		    /* We've changed memory --- check if a hardware
-		       watchpoint should trap.  Note that this
-		       presently assumes the target beneath supports
-		       continuable watchpoints.  On non-continuable
-		       watchpoints target, we'll want to check this
-		       _before_ actually doing the memory change, and
-		       not doing the change at all if the watchpoint
-		       traps.  */
-		    if (hardware_watchpoint_inserted_in_range
-			(get_regcache_aspace (regcache),
-			 entry->u.mem.addr, entry->u.mem.len))
-		      record_hw_watchpoint = 1;
-		  }
-              }
-          }
-      }
-      break;
-    }
-}
-
-static struct target_ops *tmp_to_resume_ops;
-static void (*tmp_to_resume) (struct target_ops *, ptid_t, int,
-			      enum gdb_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 int (*tmp_to_stopped_by_watchpoint) (void);
-static int (*tmp_to_stopped_data_address) (struct target_ops *, CORE_ADDR *);
-static int (*tmp_to_stopped_data_address) (struct target_ops *, CORE_ADDR *);
-static void (*tmp_to_async) (void (*) (enum inferior_event_type, void *), void *);
-
-static void record_restore (void);
-
-/* Asynchronous signal handle registered as event loop source for when
-   we have pending events ready to be passed to the core.  */
-
-static struct async_event_handler *record_async_inferior_event_token;
-
-static void
-record_async_inferior_event_handler (gdb_client_data data)
-{
-  inferior_event_handler (INF_REG_EVENT, NULL);
-}
-
-/* Open the process record target.  */
-
-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);
-  record_restore ();
-}
-
-/* "to_open" target method for 'live' processes.  */
-
-static void
-record_open_1 (char *name, int from_tty)
-{
-  if (record_debug)
-    fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n");
-
-  /* check exec */
-  if (!target_has_execution)
-    error (_("Process record: the program is not being run."));
-  if (non_stop)
-    error (_("Process record target can't debug inferior in non-stop mode "
-	     "(non-stop)."));
-
-  if (!gdbarch_process_record_p (target_gdbarch ()))
-    error (_("Process record: the current architecture doesn't support "
-	     "record function."));
-
-  if (!tmp_to_resume)
-    error (_("Could not find 'to_resume' method on the target stack."));
-  if (!tmp_to_wait)
-    error (_("Could not find 'to_wait' method on the target stack."));
-  if (!tmp_to_store_registers)
-    error (_("Could not find 'to_store_registers' "
-	     "method on the target stack."));
-  if (!tmp_to_insert_breakpoint)
-    error (_("Could not find 'to_insert_breakpoint' "
-	     "method on the target stack."));
-  if (!tmp_to_remove_breakpoint)
-    error (_("Could not find 'to_remove_breakpoint' "
-	     "method on the target stack."));
-  if (!tmp_to_stopped_by_watchpoint)
-    error (_("Could not find 'to_stopped_by_watchpoint' "
-	     "method on the target stack."));
-  if (!tmp_to_stopped_data_address)
-    error (_("Could not find 'to_stopped_data_address' "
-	     "method on the target stack."));
-
-  push_target (&record_ops);
-}
-
-static void record_init_record_breakpoints (void);
-
-/* "to_open" target method.  Open the process record target.  */
-
-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)
-    error (_("Process record target already running.  Use \"record stop\" to "
-             "stop record target first."));
-
-  /* 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;
-  tmp_to_stopped_by_watchpoint = NULL;
-  tmp_to_stopped_data_address = NULL;
-  tmp_to_async = NULL;
-
-  /* Set the beneath function pointers.  */
-  for (t = current_target.beneath; t != NULL; t = t->beneath)
-    {
-      if (!tmp_to_resume)
-        {
-	  tmp_to_resume = t->to_resume;
-	  tmp_to_resume_ops = t;
-        }
-      if (!tmp_to_wait)
-        {
-	  tmp_to_wait = t->to_wait;
-	  tmp_to_wait_ops = t;
-        }
-      if (!tmp_to_store_registers)
-        {
-	  tmp_to_store_registers = t->to_store_registers;
-	  tmp_to_store_registers_ops = t;
-        }
-      if (!tmp_to_xfer_partial)
-        {
-	  tmp_to_xfer_partial = t->to_xfer_partial;
-	  tmp_to_xfer_partial_ops = t;
-        }
-      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 (!tmp_to_stopped_by_watchpoint)
-	tmp_to_stopped_by_watchpoint = t->to_stopped_by_watchpoint;
-      if (!tmp_to_stopped_data_address)
-	tmp_to_stopped_data_address = t->to_stopped_data_address;
-      if (!tmp_to_async)
-	tmp_to_async = t->to_async;
-    }
-  if (!tmp_to_xfer_partial)
-    error (_("Could not find 'to_xfer_partial' method on the target stack."));
-
-  /* Reset */
-  record_insn_num = 0;
-  record_insn_count = 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;
-  record_beneath_to_stopped_by_watchpoint = tmp_to_stopped_by_watchpoint;
-  record_beneath_to_stopped_data_address = tmp_to_stopped_data_address;
-  record_beneath_to_async = tmp_to_async;
-
-  if (core_bfd)
-    record_core_open_1 (name, from_tty);
-  else
-    record_open_1 (name, from_tty);
-
-  /* Register extra event sources in the event loop.  */
-  record_async_inferior_event_token
-    = create_async_event_handler (record_async_inferior_event_handler,
-				  NULL);
-
-  record_init_record_breakpoints ();
-
-  observer_notify_record_changed (current_inferior (),  1);
-}
-
-/* "to_close" target method.  Close the process record target.  */
-
-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;
-    }
-
-  if (record_async_inferior_event_token)
-    delete_async_event_handler (&record_async_inferior_event_token);
-}
-
-static int record_resume_step = 0;
-
-/* True if we've been resumed, and so each record_wait call should
-   advance execution.  If this is false, record_wait will return a
-   TARGET_WAITKIND_IGNORE.  */
-static int record_resumed = 0;
-
-/* The execution direction of the last resume we got.  This is
-   necessary for async mode.  Vis (order is not strictly accurate):
-
-   1. user has the global execution direction set to forward
-   2. user does a reverse-step command
-   3. record_resume is called with global execution direction
-      temporarily switched to reverse
-   4. GDB's execution direction is reverted back to forward
-   5. target record notifies event loop there's an event to handle
-   6. infrun asks the target which direction was it going, and switches
-      the global execution direction accordingly (to reverse)
-   7. infrun polls an event out of the record target, and handles it
-   8. GDB goes back to the event loop, and goto #4.
-*/
-static enum exec_direction_kind record_execution_dir = EXEC_FORWARD;
-
-/* "to_resume" target method.  Resume the process record target.  */
-
-static void
-record_resume (struct target_ops *ops, ptid_t ptid, int step,
-               enum gdb_signal signal)
-{
-  record_resume_step = step;
-  record_resumed = 1;
-  record_execution_dir = execution_direction;
-
-  if (!RECORD_IS_REPLAY)
-    {
-      struct gdbarch *gdbarch = target_thread_architecture (ptid);
-
-      record_message (get_current_regcache (), signal);
-
-      if (!step)
-        {
-          /* This is not hard single step.  */
-          if (!gdbarch_software_single_step_p (gdbarch))
-            {
-              /* This is a normal continue.  */
-              step = 1;
-            }
-          else
-            {
-              /* This arch support soft sigle step.  */
-              if (single_step_breakpoints_inserted ())
-                {
-                  /* This is a soft single step.  */
-                  record_resume_step = 1;
-                }
-              else
-                {
-                  /* This is a continue.
-                     Try to insert a soft single step breakpoint.  */
-                  if (!gdbarch_software_single_step (gdbarch,
-                                                     get_current_frame ()))
-                    {
-                      /* This system don't want use soft single step.
-                         Use hard sigle step.  */
-                      step = 1;
-                    }
-                }
-            }
-        }
-
-      /* Make sure the target beneath reports all signals.  */
-      target_pass_signals (0, NULL);
-
-      record_beneath_to_resume (record_beneath_to_resume_ops,
-                                ptid, step, signal);
-    }
-
-  /* We are about to start executing the inferior (or simulate it),
-     let's register it with the event loop.  */
-  if (target_can_async_p ())
-    {
-      target_async (inferior_event_handler, 0);
-      /* Notify the event loop there's an event to wait for.  We do
-	 most of the work in record_wait.  */
-      mark_async_event_handler (record_async_inferior_event_token);
-    }
-}
-
-static int record_get_sig = 0;
-
-/* SIGINT signal handler, registered by "to_wait" method.  */
-
-static void
-record_sig_handler (int signo)
-{
-  if (record_debug)
-    fprintf_unfiltered (gdb_stdlog, "Process record: get a signal\n");
-
-  /* It will break the running inferior in replay mode.  */
-  record_resume_step = 1;
-
-  /* It will let record_wait set inferior status to get the signal
-     SIGINT.  */
-  record_get_sig = 1;
-}
-
-static void
-record_wait_cleanups (void *ignore)
-{
-  if (execution_direction == EXEC_REVERSE)
-    {
-      if (record_list->next)
-	record_list = record_list->next;
-    }
-  else
-    record_list = record_list->prev;
-}
-
-/* "to_wait" target method for process record target.
-
-   In record mode, the target is always run in singlestep mode
-   (even when gdb says to continue).  The to_wait method intercepts
-   the stop events and determines which ones are to be passed on to
-   gdb.  Most stop events are just singlestep events that gdb is not
-   to know about, so the to_wait method just records them and keeps
-   singlestepping.
-
-   In replay mode, this function emulates the recorded execution log, 
-   one instruction at a time (forward or backward), and determines 
-   where to stop.  */
-
-static ptid_t
-record_wait_1 (struct target_ops *ops,
-	       ptid_t ptid, struct target_waitstatus *status,
-	       int options)
-{
-  struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
-
-  if (record_debug)
-    fprintf_unfiltered (gdb_stdlog,
-			"Process record: record_wait "
-			"record_resume_step = %d, record_resumed = %d, direction=%s\n",
-			record_resume_step, record_resumed,
-			record_execution_dir == EXEC_FORWARD ? "forward" : "reverse");
-
-  if (!record_resumed)
-    {
-      gdb_assert ((options & TARGET_WNOHANG) != 0);
-
-      /* No interesting event.  */
-      status->kind = TARGET_WAITKIND_IGNORE;
-      return minus_one_ptid;
-    }
-
-  record_get_sig = 0;
-  signal (SIGINT, record_sig_handler);
-
-  if (!RECORD_IS_REPLAY && ops != &record_core_ops)
-    {
-      if (record_resume_step)
-	{
-	  /* This is a single step.  */
-	  return record_beneath_to_wait (record_beneath_to_wait_ops,
-					 ptid, status, options);
-	}
-      else
-	{
-	  /* This is not a single step.  */
-	  ptid_t ret;
-	  CORE_ADDR tmp_pc;
-	  struct gdbarch *gdbarch = target_thread_architecture (inferior_ptid);
-
-	  while (1)
-	    {
-	      ret = record_beneath_to_wait (record_beneath_to_wait_ops,
-					    ptid, status, options);
-	      if (status->kind == TARGET_WAITKIND_IGNORE)
-		{
-		  if (record_debug)
-		    fprintf_unfiltered (gdb_stdlog,
-					"Process record: record_wait "
-					"target beneath not done yet\n");
-		  return ret;
-		}
-
-              if (single_step_breakpoints_inserted ())
-                remove_single_step_breakpoints ();
-
-	      if (record_resume_step)
-		return ret;
-
-	      /* Is this a SIGTRAP?  */
-	      if (status->kind == TARGET_WAITKIND_STOPPED
-		  && status->value.sig == GDB_SIGNAL_TRAP)
-		{
-		  struct regcache *regcache;
-		  struct address_space *aspace;
-
-		  /* Yes -- this is likely our single-step finishing,
-		     but check if there's any reason the core would be
-		     interested in the event.  */
-
-		  registers_changed ();
-		  regcache = get_current_regcache ();
-		  tmp_pc = regcache_read_pc (regcache);
-		  aspace = get_regcache_aspace (regcache);
-
-		  if (target_stopped_by_watchpoint ())
-		    {
-		      /* Always interested in watchpoints.  */
-		    }
-		  else if (breakpoint_inserted_here_p (aspace, tmp_pc))
-		    {
-		      /* There is a breakpoint here.  Let the core
-			 handle it.  */
-		      if (software_breakpoint_inserted_here_p (aspace, tmp_pc))
-			{
-			  struct gdbarch *gdbarch
-			    = get_regcache_arch (regcache);
-			  CORE_ADDR decr_pc_after_break
-			    = gdbarch_decr_pc_after_break (gdbarch);
-			  if (decr_pc_after_break)
-			    regcache_write_pc (regcache,
-					       tmp_pc + decr_pc_after_break);
-			}
-		    }
-		  else
-		    {
-		      /* This is a single-step trap.  Record the
-		         insn and issue another step.
-                         FIXME: this part can be a random SIGTRAP too.
-                         But GDB cannot handle it.  */
-                      int step = 1;
-
-		      if (!record_message_wrapper_safe (regcache,
-                                                        GDB_SIGNAL_0))
-  			{
-                           status->kind = TARGET_WAITKIND_STOPPED;
-                           status->value.sig = GDB_SIGNAL_0;
-                           break;
-  			}
-
-                      if (gdbarch_software_single_step_p (gdbarch))
-			{
-			  /* Try to insert the software single step breakpoint.
-			     If insert success, set step to 0.  */
-			  set_executing (inferior_ptid, 0);
-			  reinit_frame_cache ();
-			  if (gdbarch_software_single_step (gdbarch,
-                                                            get_current_frame ()))
-			    step = 0;
-			  set_executing (inferior_ptid, 1);
-			}
-
-		      if (record_debug)
-			fprintf_unfiltered (gdb_stdlog,
-					    "Process record: record_wait "
-					    "issuing one more step in the target beneath\n");
-		      record_beneath_to_resume (record_beneath_to_resume_ops,
-						ptid, step,
-						GDB_SIGNAL_0);
-		      continue;
-		    }
-		}
-
-	      /* The inferior is broken by a breakpoint or a signal.  */
-	      break;
-	    }
-
-	  return ret;
-	}
-    }
-  else
-    {
-      struct regcache *regcache = get_current_regcache ();
-      struct gdbarch *gdbarch = get_regcache_arch (regcache);
-      struct address_space *aspace = get_regcache_aspace (regcache);
-      int continue_flag = 1;
-      int first_record_end = 1;
-      struct cleanup *old_cleanups = make_cleanup (record_wait_cleanups, 0);
-      CORE_ADDR tmp_pc;
-
-      record_hw_watchpoint = 0;
-      status->kind = TARGET_WAITKIND_STOPPED;
-
-      /* Check breakpoint when forward execute.  */
-      if (execution_direction == EXEC_FORWARD)
-	{
-	  tmp_pc = regcache_read_pc (regcache);
-	  if (breakpoint_inserted_here_p (aspace, tmp_pc))
-	    {
-	      int decr_pc_after_break = gdbarch_decr_pc_after_break (gdbarch);
-
-	      if (record_debug)
-		fprintf_unfiltered (gdb_stdlog,
-				    "Process record: break at %s.\n",
-				    paddress (gdbarch, tmp_pc));
-
-	      if (decr_pc_after_break
-		  && !record_resume_step
-		  && software_breakpoint_inserted_here_p (aspace, tmp_pc))
-		regcache_write_pc (regcache,
-				   tmp_pc + decr_pc_after_break);
-	      goto replay_out;
-	    }
-	}
-
-      /* If GDB is in terminal_inferior mode, it will not get the signal.
-         And in GDB replay mode, GDB doesn't need to be in terminal_inferior
-         mode, because inferior will not executed.
-         Then set it to terminal_ours to make GDB get the signal.  */
-      target_terminal_ours ();
-
-      /* In EXEC_FORWARD mode, record_list points to the tail of prev
-         instruction.  */
-      if (execution_direction == EXEC_FORWARD && record_list->next)
-	record_list = record_list->next;
-
-      /* Loop over the record_list, looking for the next place to
-	 stop.  */
-      do
-	{
-	  /* Check for beginning and end of log.  */
-	  if (execution_direction == EXEC_REVERSE
-	      && record_list == &record_first)
-	    {
-	      /* Hit beginning of record log in reverse.  */
-	      status->kind = TARGET_WAITKIND_NO_HISTORY;
-	      break;
-	    }
-	  if (execution_direction != EXEC_REVERSE && !record_list->next)
-	    {
-	      /* Hit end of record log going forward.  */
-	      status->kind = TARGET_WAITKIND_NO_HISTORY;
-	      break;
-	    }
-
-          record_exec_insn (regcache, gdbarch, record_list);
-
-	  if (record_list->type == record_end)
-	    {
-	      if (record_debug > 1)
-		fprintf_unfiltered (gdb_stdlog,
-				    "Process record: record_end %s to "
-				    "inferior.\n",
-				    host_address_to_string (record_list));
-
-	      if (first_record_end && execution_direction == EXEC_REVERSE)
-		{
-		  /* When reverse excute, the first record_end is the part of
-		     current instruction.  */
-		  first_record_end = 0;
-		}
-	      else
-		{
-		  /* In EXEC_REVERSE mode, this is the record_end of prev
-		     instruction.
-		     In EXEC_FORWARD mode, this is the record_end of current
-		     instruction.  */
-		  /* step */
-		  if (record_resume_step)
-		    {
-		      if (record_debug > 1)
-			fprintf_unfiltered (gdb_stdlog,
-					    "Process record: step.\n");
-		      continue_flag = 0;
-		    }
-
-		  /* check breakpoint */
-		  tmp_pc = regcache_read_pc (regcache);
-		  if (breakpoint_inserted_here_p (aspace, tmp_pc))
-		    {
-		      int decr_pc_after_break
-			= gdbarch_decr_pc_after_break (gdbarch);
-
-		      if (record_debug)
-			fprintf_unfiltered (gdb_stdlog,
-					    "Process record: break "
-					    "at %s.\n",
-					    paddress (gdbarch, tmp_pc));
-		      if (decr_pc_after_break
-			  && execution_direction == EXEC_FORWARD
-			  && !record_resume_step
-			  && software_breakpoint_inserted_here_p (aspace,
-								  tmp_pc))
-			regcache_write_pc (regcache,
-					   tmp_pc + decr_pc_after_break);
-		      continue_flag = 0;
-		    }
-
-		  if (record_hw_watchpoint)
-		    {
-		      if (record_debug)
-			fprintf_unfiltered (gdb_stdlog,
-					    "Process record: hit hw "
-					    "watchpoint.\n");
-		      continue_flag = 0;
-		    }
-		  /* Check target signal */
-		  if (record_list->u.end.sigval != GDB_SIGNAL_0)
-		    /* FIXME: better way to check */
-		    continue_flag = 0;
-		}
-	    }
-
-	  if (continue_flag)
-	    {
-	      if (execution_direction == EXEC_REVERSE)
-		{
-		  if (record_list->prev)
-		    record_list = record_list->prev;
-		}
-	      else
-		{
-		  if (record_list->next)
-		    record_list = record_list->next;
-		}
-	    }
-	}
-      while (continue_flag);
-
-replay_out:
-      if (record_get_sig)
-	status->value.sig = GDB_SIGNAL_INT;
-      else if (record_list->u.end.sigval != GDB_SIGNAL_0)
-	/* FIXME: better way to check */
-	status->value.sig = record_list->u.end.sigval;
-      else
-	status->value.sig = GDB_SIGNAL_TRAP;
-
-      discard_cleanups (old_cleanups);
-    }
-
-  signal (SIGINT, handle_sigint);
-
-  do_cleanups (set_cleanups);
-  return inferior_ptid;
-}
-
-static ptid_t
-record_wait (struct target_ops *ops,
-	     ptid_t ptid, struct target_waitstatus *status,
-	     int options)
-{
-  ptid_t return_ptid;
-
-  return_ptid = record_wait_1 (ops, ptid, status, options);
-  if (status->kind != TARGET_WAITKIND_IGNORE)
-    {
-      /* We're reporting a stop.  Make sure any spurious
-	 target_wait(WNOHANG) doesn't advance the target until the
-	 core wants us resumed again.  */
-      record_resumed = 0;
-    }
-  return return_ptid;
-}
-
-static int
-record_stopped_by_watchpoint (void)
-{
-  if (RECORD_IS_REPLAY)
-    return record_hw_watchpoint;
-  else
-    return record_beneath_to_stopped_by_watchpoint ();
-}
-
-static int
-record_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p)
-{
-  if (RECORD_IS_REPLAY)
-    return 0;
-  else
-    return record_beneath_to_stopped_data_address (ops, addr_p);
-}
-
-/* "to_disconnect" method for process record target.  */
-
-static void
-record_disconnect (struct target_ops *target, char *args, int from_tty)
-{
-  if (record_debug)
-    fprintf_unfiltered (gdb_stdlog, "Process record: record_disconnect\n");
-
-  unpush_target (&record_ops);
-  target_disconnect (args, from_tty);
-}
-
-/* "to_detach" method for process record target.  */
-
-static void
-record_detach (struct target_ops *ops, char *args, int from_tty)
-{
-  if (record_debug)
-    fprintf_unfiltered (gdb_stdlog, "Process record: record_detach\n");
-
-  unpush_target (&record_ops);
-  target_detach (args, from_tty);
-}
-
-/* "to_mourn_inferior" method for process record target.  */
-
-static void
-record_mourn_inferior (struct target_ops *ops)
-{
-  if (record_debug)
-    fprintf_unfiltered (gdb_stdlog, "Process record: "
-			            "record_mourn_inferior\n");
-
-  unpush_target (&record_ops);
-  target_mourn_inferior ();
-}
-
-/* Close process record target before killing the inferior process.  */
-
-static void
-record_kill (struct target_ops *ops)
-{
-  if (record_debug)
-    fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n");
-
-  unpush_target (&record_ops);
-  target_kill ();
-}
-
-/* Record registers change (by user or by GDB) to list as an instruction.  */
-
-static void
-record_registers_change (struct regcache *regcache, int regnum)
-{
-  /* Check record_insn_num.  */
-  record_check_insn_num (0);
-
-  record_arch_list_head = NULL;
-  record_arch_list_tail = NULL;
-
-  if (regnum < 0)
-    {
-      int i;
-
-      for (i = 0; i < gdbarch_num_regs (get_regcache_arch (regcache)); i++)
-	{
-	  if (record_arch_list_add_reg (regcache, i))
-	    {
-	      record_list_release (record_arch_list_tail);
-	      error (_("Process record: failed to record execution log."));
-	    }
-	}
-    }
-  else
-    {
-      if (record_arch_list_add_reg (regcache, regnum))
-	{
-	  record_list_release (record_arch_list_tail);
-	  error (_("Process record: failed to record execution log."));
-	}
-    }
-  if (record_arch_list_add_end ())
-    {
-      record_list_release (record_arch_list_tail);
-      error (_("Process record: failed to record execution log."));
-    }
-  record_list->next = record_arch_list_head;
-  record_arch_list_head->prev = record_list;
-  record_list = record_arch_list_tail;
-
-  if (record_insn_num == record_insn_max_num && record_insn_max_num)
-    record_list_release_first ();
-  else
-    record_insn_num++;
-}
-
-/* "to_store_registers" method for process record target.  */
-
-static void
-record_store_registers (struct target_ops *ops, struct regcache *regcache,
-                        int regno)
-{
-  if (!record_gdb_operation_disable)
-    {
-      if (RECORD_IS_REPLAY)
-	{
-	  int n;
-
-	  /* Let user choose if he wants to write register or not.  */
-	  if (regno < 0)
-	    n =
-	      query (_("Because GDB is in replay mode, changing the "
-		       "value of a register will make the execution "
-		       "log unusable from this point onward.  "
-		       "Change all registers?"));
-	  else
-	    n =
-	      query (_("Because GDB is in replay mode, changing the value "
-		       "of a register will make the execution log unusable "
-		       "from this point onward.  Change register %s?"),
-		      gdbarch_register_name (get_regcache_arch (regcache),
-					       regno));
-
-	  if (!n)
-	    {
-	      /* Invalidate the value of regcache that was set in function
-	         "regcache_raw_write".  */
-	      if (regno < 0)
-		{
-		  int i;
-
-		  for (i = 0;
-		       i < gdbarch_num_regs (get_regcache_arch (regcache));
-		       i++)
-		    regcache_invalidate (regcache, i);
-		}
-	      else
-		regcache_invalidate (regcache, regno);
-
-	      error (_("Process record canceled the operation."));
-	    }
-
-	  /* Destroy the record from here forward.  */
-	  record_list_release_following (record_list);
-	}
-
-      record_registers_change (regcache, regno);
-    }
-  record_beneath_to_store_registers (record_beneath_to_store_registers_ops,
-                                     regcache, regno);
-}
-
-/* "to_xfer_partial" method.  Behavior is conditional on RECORD_IS_REPLAY.
-   In replay mode, we cannot write memory unles we are willing to
-   invalidate the record/replay log from this point forward.  */
-
-static LONGEST
-record_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 (!record_gdb_operation_disable
-      && (object == TARGET_OBJECT_MEMORY
-	  || object == TARGET_OBJECT_RAW_MEMORY) && writebuf)
-    {
-      if (RECORD_IS_REPLAY)
-	{
-	  /* Let user choose if he wants to write memory or not.  */
-	  if (!query (_("Because GDB is in replay mode, writing to memory "
-		        "will make the execution log unusable from this "
-		        "point onward.  Write memory at address %s?"),
-		       paddress (target_gdbarch (), offset)))
-	    error (_("Process record canceled the operation."));
-
-	  /* Destroy the record from here forward.  */
-	  record_list_release_following (record_list);
-	}
-
-      /* Check record_insn_num */
-      record_check_insn_num (0);
-
-      /* Record registers change to list as an instruction.  */
-      record_arch_list_head = NULL;
-      record_arch_list_tail = NULL;
-      if (record_arch_list_add_mem (offset, len))
-	{
-	  record_list_release (record_arch_list_tail);
-	  if (record_debug)
-	    fprintf_unfiltered (gdb_stdlog,
-				"Process record: failed to record "
-				"execution log.");
-	  return -1;
-	}
-      if (record_arch_list_add_end ())
-	{
-	  record_list_release (record_arch_list_tail);
-	  if (record_debug)
-	    fprintf_unfiltered (gdb_stdlog,
-				"Process record: failed to record "
-				"execution log.");
-	  return -1;
-	}
-      record_list->next = record_arch_list_head;
-      record_arch_list_head->prev = record_list;
-      record_list = record_arch_list_tail;
-
-      if (record_insn_num == record_insn_max_num && record_insn_max_num)
-	record_list_release_first ();
-      else
-	record_insn_num++;
-    }
-
-  return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops,
-                                         object, annex, readbuf, writebuf,
-                                         offset, len);
-}
-
-/* This structure represents a breakpoint inserted while the record
-   target is active.  We use this to know when to install/remove
-   breakpoints in/from the target beneath.  For example, a breakpoint
-   may be inserted while recording, but removed when not replaying nor
-   recording.  In that case, the breakpoint had not been inserted on
-   the target beneath, so we should not try to remove it there.  */
-
-struct record_breakpoint
-{
-  /* The address and address space the breakpoint was set at.  */
-  struct address_space *address_space;
-  CORE_ADDR addr;
-
-  /* True when the breakpoint has been also installed in the target
-     beneath.  This will be false for breakpoints set during replay or
-     when recording.  */
-  int in_target_beneath;
-};
-
-typedef struct record_breakpoint *record_breakpoint_p;
-DEF_VEC_P(record_breakpoint_p);
-
-/* The list of breakpoints inserted while the record target is
-   active.  */
-VEC(record_breakpoint_p) *record_breakpoints = NULL;
-
-static void
-record_sync_record_breakpoints (struct bp_location *loc, void *data)
-{
-  if (loc->loc_type != bp_loc_software_breakpoint)
-      return;
-
-  if (loc->inserted)
-    {
-      struct record_breakpoint *bp = XNEW (struct record_breakpoint);
-
-      bp->addr = loc->target_info.placed_address;
-      bp->address_space = loc->target_info.placed_address_space;
-
-      bp->in_target_beneath = 1;
-
-      VEC_safe_push (record_breakpoint_p, record_breakpoints, bp);
-    }
-}
-
-/* Sync existing breakpoints to record_breakpoints.  */
-
-static void
-record_init_record_breakpoints (void)
-{
-  VEC_free (record_breakpoint_p, record_breakpoints);
-
-  iterate_over_bp_locations (record_sync_record_breakpoints);
-}
-
-/* Behavior is conditional on RECORD_IS_REPLAY.  We will not actually
-   insert or remove breakpoints in the real target when replaying, nor
-   when recording.  */
-
-static int
-record_insert_breakpoint (struct gdbarch *gdbarch,
-			  struct bp_target_info *bp_tgt)
-{
-  struct record_breakpoint *bp;
-  int in_target_beneath = 0;
-
-  if (!RECORD_IS_REPLAY)
-    {
-      /* When recording, we currently always single-step, so we don't
-	 really need to install regular breakpoints in the inferior.
-	 However, we do have to insert software single-step
-	 breakpoints, in case the target can't hardware step.  To keep
-	 things single, we always insert.  */
-      struct cleanup *old_cleanups;
-      int ret;
-
-      old_cleanups = record_gdb_operation_disable_set ();
-      ret = record_beneath_to_insert_breakpoint (gdbarch, bp_tgt);
-      do_cleanups (old_cleanups);
-
-      if (ret != 0)
-	return ret;
-
-      in_target_beneath = 1;
-    }
-
-  bp = XNEW (struct record_breakpoint);
-  bp->addr = bp_tgt->placed_address;
-  bp->address_space = bp_tgt->placed_address_space;
-  bp->in_target_beneath = in_target_beneath;
-  VEC_safe_push (record_breakpoint_p, record_breakpoints, bp);
-  return 0;
-}
-
-/* "to_remove_breakpoint" method for process record target.  */
-
-static int
-record_remove_breakpoint (struct gdbarch *gdbarch,
-			  struct bp_target_info *bp_tgt)
-{
-  struct record_breakpoint *bp;
-  int ix;
-
-  for (ix = 0;
-       VEC_iterate (record_breakpoint_p, record_breakpoints, ix, bp);
-       ++ix)
-    {
-      if (bp->addr == bp_tgt->placed_address
-	  && bp->address_space == bp_tgt->placed_address_space)
-	{
-	  if (bp->in_target_beneath)
-	    {
-	      struct cleanup *old_cleanups;
-	      int ret;
-
-	      old_cleanups = record_gdb_operation_disable_set ();
-	      ret = record_beneath_to_remove_breakpoint (gdbarch, bp_tgt);
-	      do_cleanups (old_cleanups);
-
-	      if (ret != 0)
-		return ret;
-	    }
-
-	  VEC_unordered_remove (record_breakpoint_p, record_breakpoints, ix);
-	  return 0;
-	}
-    }
-
-  gdb_assert_not_reached ("removing unknown breakpoint");
-}
-
-/* "to_can_execute_reverse" method for process record target.  */
-
-static int
-record_can_execute_reverse (void)
-{
-  return 1;
-}
-
-/* "to_get_bookmark" method for process record and prec over core.  */
-
-static gdb_byte *
-record_get_bookmark (char *args, int from_tty)
-{
-  gdb_byte *ret = NULL;
-
-  /* Return stringified form of instruction count.  */
-  if (record_list && record_list->type == record_end)
-    ret = xstrdup (pulongest (record_list->u.end.insn_num));
-
-  if (record_debug)
-    {
-      if (ret)
-	fprintf_unfiltered (gdb_stdlog,
-			    "record_get_bookmark returns %s\n", ret);
-      else
-	fprintf_unfiltered (gdb_stdlog,
-			    "record_get_bookmark returns NULL\n");
-    }
-  return ret;
-}
-
 /* The implementation of the command "record goto".  */
 static void cmd_record_goto (char *, int);
 
-/* "to_goto_bookmark" method for process record and prec over core.  */
-
-static void
-record_goto_bookmark (gdb_byte *bookmark, int from_tty)
-{
-  if (record_debug)
-    fprintf_unfiltered (gdb_stdlog,
-			"record_goto_bookmark receives %s\n", bookmark);
-
-  if (bookmark[0] == '\'' || bookmark[0] == '\"')
-    {
-      if (bookmark[strlen (bookmark) - 1] != bookmark[0])
-	error (_("Unbalanced quotes: %s"), bookmark);
-
-      /* Strip trailing quote.  */
-      bookmark[strlen (bookmark) - 1] = '\0';
-      /* Strip leading quote.  */
-      bookmark++;
-      /* Pass along to cmd_record_goto.  */
-    }
-
-  cmd_record_goto ((char *) bookmark, from_tty);
-  return;
-}
-
-static void
-record_async (void (*callback) (enum inferior_event_type event_type,
-				void *context), void *context)
-{
-  /* If we're on top of a line target (e.g., linux-nat, remote), then
-     set it to async mode as well.  Will be NULL if we're sitting on
-     top of the core target, for "record restore".  */
-  if (record_beneath_to_async != NULL)
-    record_beneath_to_async (callback, context);
-}
-
-static int
-record_can_async_p (void)
-{
-  /* We only enable async when the user specifically asks for it.  */
-  return target_async_permitted;
-}
-
-static int
-record_is_async_p (void)
-{
-  /* We only enable async when the user specifically asks for it.  */
-  return target_async_permitted;
-}
-
-static enum exec_direction_kind
-record_execution_direction (void)
-{
-  return record_execution_dir;
-}
-
-static void
-init_record_ops (void)
-{
-  record_ops.to_shortname = "record";
-  record_ops.to_longname = "Process record and replay target";
-  record_ops.to_doc =
-    "Log program while executing and replay execution from log.";
-  record_ops.to_open = record_open;
-  record_ops.to_close = record_close;
-  record_ops.to_resume = record_resume;
-  record_ops.to_wait = record_wait;
-  record_ops.to_disconnect = record_disconnect;
-  record_ops.to_detach = record_detach;
-  record_ops.to_mourn_inferior = record_mourn_inferior;
-  record_ops.to_kill = record_kill;
-  record_ops.to_create_inferior = find_default_create_inferior;
-  record_ops.to_store_registers = record_store_registers;
-  record_ops.to_xfer_partial = record_xfer_partial;
-  record_ops.to_insert_breakpoint = record_insert_breakpoint;
-  record_ops.to_remove_breakpoint = record_remove_breakpoint;
-  record_ops.to_stopped_by_watchpoint = record_stopped_by_watchpoint;
-  record_ops.to_stopped_data_address = record_stopped_data_address;
-  record_ops.to_can_execute_reverse = record_can_execute_reverse;
-  record_ops.to_stratum = record_stratum;
-  /* Add bookmark target methods.  */
-  record_ops.to_get_bookmark = record_get_bookmark;
-  record_ops.to_goto_bookmark = record_goto_bookmark;
-  record_ops.to_async = record_async;
-  record_ops.to_can_async_p = record_can_async_p;
-  record_ops.to_is_async_p = record_is_async_p;
-  record_ops.to_execution_direction = record_execution_direction;
-  record_ops.to_magic = OPS_MAGIC;
-}
-
-/* "to_resume" method for prec over corefile.  */
-
-static void
-record_core_resume (struct target_ops *ops, ptid_t ptid, int step,
-                    enum gdb_signal signal)
-{
-  record_resume_step = step;
-  record_resumed = 1;
-  record_execution_dir = execution_direction;
-
-  /* We are about to start executing the inferior (or simulate it),
-     let's register it with the event loop.  */
-  if (target_can_async_p ())
-    {
-      target_async (inferior_event_handler, 0);
-
-      /* Notify the event loop there's an event to wait for.  */
-      mark_async_event_handler (record_async_inferior_event_token);
-    }
-}
-
-/* "to_kill" method for prec over corefile.  */
-
-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);
-}
-
-/* "to_fetch_registers" method for prec over corefile.  */
-
-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);
-}
-
-/* "to_prepare_to_store" method for prec over corefile.  */
-
-static void
-record_core_prepare_to_store (struct regcache *regcache)
-{
-}
-
-/* "to_store_registers" method for prec over corefile.  */
-
-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."));
-}
-
-/* "to_xfer_partial" method for prec over corefile.  */
-
-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;
-		  ULONGEST sec_offset;
-
-		  if (offset >= p->endaddr)
-		    continue;
-
-		  if (offset + len > p->endaddr)
-		    len = p->endaddr - offset;
-
-		  sec_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 + sec_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 + sec_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);
-}
-
-/* "to_insert_breakpoint" method for prec over corefile.  */
-
-static int
-record_core_insert_breakpoint (struct gdbarch *gdbarch,
-			       struct bp_target_info *bp_tgt)
-{
-  return 0;
-}
-
-/* "to_remove_breakpoint" method for prec over corefile.  */
-
-static int
-record_core_remove_breakpoint (struct gdbarch *gdbarch,
-			       struct bp_target_info *bp_tgt)
-{
-  return 0;
-}
-
-/* "to_has_execution" method for prec over corefile.  */
-
-static int
-record_core_has_execution (struct target_ops *ops, ptid_t the_ptid)
-{
-  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_stopped_by_watchpoint = record_stopped_by_watchpoint;
-  record_core_ops.to_stopped_data_address = record_stopped_data_address;
-  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;
-  /* Add bookmark target methods.  */
-  record_core_ops.to_get_bookmark = record_get_bookmark;
-  record_core_ops.to_goto_bookmark = record_goto_bookmark;
-  record_core_ops.to_async = record_async;
-  record_core_ops.to_can_async_p = record_can_async_p;
-  record_core_ops.to_is_async_p = record_is_async_p;
-  record_core_ops.to_execution_direction = record_execution_direction;
-  record_core_ops.to_magic = OPS_MAGIC;
-}
-
 /* Implement "show record debug" command.  */
 
 static void
@@ -2358,551 +189,6 @@ info_record_command (char *args, int from_tty)
 		   record_insn_max_num);
 }
 
-/* Record log save-file format
-   Version 1 (never released)
-
-   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, see enum record_type).
-     record_reg:
-       1 byte:  record type (record_reg, see enum record_type).
-       8 bytes: register id (network byte order).
-       MAX_REGISTER_SIZE bytes: register value.
-     record_mem:
-       1 byte:  record type (record_mem, see enum record_type).
-       8 bytes: memory length (network byte order).
-       8 bytes: memory address (network byte order).
-       n bytes: memory value (n == memory length).
-
-   Version 2
-     4 bytes: magic number netorder32(0x20091016).
-       NOTE: be sure to change whenever this file format changes!
-
-   Records:
-     record_end:
-       1 byte:  record type (record_end, see enum record_type).
-       4 bytes: signal
-       4 bytes: instruction count
-     record_reg:
-       1 byte:  record type (record_reg, see enum record_type).
-       4 bytes: register id (network byte order).
-       n bytes: register value (n == actual register size).
-                (eg. 4 bytes for x86 general registers).
-     record_mem:
-       1 byte:  record type (record_mem, see enum record_type).
-       4 bytes: memory length (network byte order).
-       8 bytes: memory address (network byte order).
-       n bytes: memory value (n == memory length).
-
-*/
-
-/* bfdcore_read -- read bytes from a core file section.  */
-
-static inline void
-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;
-  else
-    error (_("Failed to read %d bytes from core file %s ('%s')."),
-	   len, bfd_get_filename (obfd),
-	   bfd_errmsg (bfd_get_error ()));
-}
-
-static inline uint64_t
-netorder64 (uint64_t input)
-{
-  uint64_t ret;
-
-  store_unsigned_integer ((gdb_byte *) &ret, sizeof (ret), 
-			  BFD_ENDIAN_BIG, input);
-  return ret;
-}
-
-static inline uint32_t
-netorder32 (uint32_t input)
-{
-  uint32_t ret;
-
-  store_unsigned_integer ((gdb_byte *) &ret, sizeof (ret), 
-			  BFD_ENDIAN_BIG, input);
-  return ret;
-}
-
-static inline uint16_t
-netorder16 (uint16_t input)
-{
-  uint16_t ret;
-
-  store_unsigned_integer ((gdb_byte *) &ret, sizeof (ret), 
-			  BFD_ENDIAN_BIG, input);
-  return ret;
-}
-
-/* Restore the execution log from a core_bfd file.  */
-static void
-record_restore (void)
-{
-  uint32_t magic;
-  struct cleanup *old_cleanups;
-  struct record_entry *rec;
-  asection *osec;
-  uint32_t osec_size;
-  int bfd_offset = 0;
-  struct regcache *regcache;
-
-  /* We restore the execution log from the open core bfd,
-     if there is one.  */
-  if (core_bfd == NULL)
-    return;
-
-  /* "record_restore" can only be called when record list is empty.  */
-  gdb_assert (record_first.next == NULL);
- 
-  if (record_debug)
-    fprintf_unfiltered (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");
-  if (record_debug)
-    fprintf_unfiltered (gdb_stdlog, "Find precord section %s.\n",
-			osec ? "succeeded" : "failed");
-  if (osec == NULL)
-    return;
-  osec_size = bfd_section_size (core_bfd, osec);
-  if (record_debug)
-    fprintf_unfiltered (gdb_stdlog, "%s", bfd_section_name (core_bfd, osec));
-
-  /* Check the magic code.  */
-  bfdcore_read (core_bfd, osec, &magic, sizeof (magic), &bfd_offset);
-  if (magic != RECORD_FILE_MAGIC)
-    error (_("Version mis-match or file format error in core file %s."),
-	   bfd_get_filename (core_bfd));
-  if (record_debug)
-    fprintf_unfiltered (gdb_stdlog,
-			"  Reading 4-byte magic cookie "
-			"RECORD_FILE_MAGIC (0x%s)\n",
-			phex_nz (netorder32 (magic), 4));
-
-  /* Restore the entries in recfd into 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);
-  regcache = get_current_regcache ();
-
-  while (1)
-    {
-      uint8_t rectype;
-      uint32_t regnum, len, signal, count;
-      uint64_t addr;
-
-      /* We are finished when offset reaches osec_size.  */
-      if (bfd_offset >= osec_size)
-	break;
-      bfdcore_read (core_bfd, osec, &rectype, sizeof (rectype), &bfd_offset);
-
-      switch (rectype)
-        {
-        case record_reg: /* reg */
-          /* Get register number to regnum.  */
-          bfdcore_read (core_bfd, osec, &regnum,
-			sizeof (regnum), &bfd_offset);
-	  regnum = netorder32 (regnum);
-
-          rec = record_reg_alloc (regcache, regnum);
-
-          /* Get val.  */
-          bfdcore_read (core_bfd, osec, record_get_loc (rec),
-			rec->u.reg.len, &bfd_offset);
-
-	  if (record_debug)
-	    fprintf_unfiltered (gdb_stdlog,
-				"  Reading register %d (1 "
-				"plus %lu plus %d bytes)\n",
-				rec->u.reg.num,
-				(unsigned long) sizeof (regnum),
-				rec->u.reg.len);
-          break;
-
-        case record_mem: /* mem */
-          /* Get len.  */
-          bfdcore_read (core_bfd, osec, &len, 
-			sizeof (len), &bfd_offset);
-	  len = netorder32 (len);
-
-          /* Get addr.  */
-          bfdcore_read (core_bfd, osec, &addr,
-			sizeof (addr), &bfd_offset);
-	  addr = netorder64 (addr);
-
-          rec = record_mem_alloc (addr, len);
-
-          /* Get val.  */
-          bfdcore_read (core_bfd, osec, record_get_loc (rec),
-			rec->u.mem.len, &bfd_offset);
-
-	  if (record_debug)
-	    fprintf_unfiltered (gdb_stdlog,
-				"  Reading memory %s (1 plus "
-				"%lu plus %lu plus %d bytes)\n",
-				paddress (get_current_arch (),
-					  rec->u.mem.addr),
-				(unsigned long) sizeof (addr),
-				(unsigned long) sizeof (len),
-				rec->u.mem.len);
-          break;
-
-        case record_end: /* end */
-          rec = record_end_alloc ();
-          record_insn_num ++;
-
-	  /* Get signal value.  */
-	  bfdcore_read (core_bfd, osec, &signal, 
-			sizeof (signal), &bfd_offset);
-	  signal = netorder32 (signal);
-	  rec->u.end.sigval = signal;
-
-	  /* Get insn count.  */
-	  bfdcore_read (core_bfd, osec, &count, 
-			sizeof (count), &bfd_offset);
-	  count = netorder32 (count);
-	  rec->u.end.insn_num = count;
-	  record_insn_count = count + 1;
-	  if (record_debug)
-	    fprintf_unfiltered (gdb_stdlog,
-				"  Reading record_end (1 + "
-				"%lu + %lu bytes), offset == %s\n",
-				(unsigned long) sizeof (signal),
-				(unsigned long) sizeof (count),
-				paddress (get_current_arch (),
-					  bfd_offset));
-          break;
-
-        default:
-          error (_("Bad entry type in core file %s."),
-		 bfd_get_filename (core_bfd));
-          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;
-  record_arch_list_tail->next = NULL;
-  record_list = &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.  */
-  printf_filtered (_("Restored records from core file %s.\n"),
-		   bfd_get_filename (core_bfd));
-
-  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
-}
-
-/* bfdcore_write -- write bytes into a core file section.  */
-
-static inline void
-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;
-  else
-    error (_("Failed to write %d bytes to core file %s ('%s')."),
-	   len, bfd_get_filename (obfd),
-	   bfd_errmsg (bfd_get_error ()));
-}
-
-/* Restore the execution log from a file.  We use a modified elf
-   corefile format, with an extra section for our data.  */
-
-static void
-cmd_record_restore (char *args, int from_tty)
-{
-  core_file_command (args, from_tty);
-  record_open (args, from_tty);
-}
-
-static void
-record_save_cleanups (void *data)
-{
-  bfd *obfd = data;
-  char *pathname = xstrdup (bfd_get_filename (obfd));
-
-  gdb_bfd_unref (obfd);
-  unlink (pathname);
-  xfree (pathname);
-}
-
-/* Save the execution log to a file.  We use a modified elf corefile
-   format, with an extra section for our data.  */
-
-static void
-cmd_record_save (char *args, int from_tty)
-{
-  char *recfilename, recfilename_buffer[40];
-  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 save_size = 0;
-  asection *osec = NULL;
-  int bfd_offset = 0;
-
-  if (strcmp (current_target.to_shortname, "record") != 0)
-    error (_("This command can only be used with target 'record'.\n"
-	     "Use 'target record' first.\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 save file.  */
-  if (record_debug)
-    fprintf_unfiltered (gdb_stdlog, "Saving execution log to core file '%s'\n",
-			recfilename);
-
-  /* Open the output file.  */
-  obfd = create_gcore_bfd (recfilename);
-  old_cleanups = make_cleanup (record_save_cleanups, 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_insn (regcache, gdbarch, record_list);
-
-      if (record_list->prev)
-        record_list = record_list->prev;
-    }
-
-  /* Compute the size needed for the extra bfd section.  */
-  save_size = 4;	/* magic cookie */
-  for (record_list = record_first.next; record_list;
-       record_list = record_list->next)
-    switch (record_list->type)
-      {
-      case record_end:
-	save_size += 1 + 4 + 4;
-	break;
-      case record_reg:
-	save_size += 1 + 4 + record_list->u.reg.len;
-	break;
-      case record_mem:
-	save_size += 1 + 4 + 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: %s"),
-	   recfilename,
-           bfd_errmsg (bfd_get_error ()));
-  bfd_set_section_size (obfd, osec, save_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_unfiltered (gdb_stdlog,
-			"  Writing 4-byte magic cookie "
-			"RECORD_FILE_MAGIC (0x%s)\n",
-		      phex_nz (magic, 4));
-  bfdcore_write (obfd, osec, &magic, sizeof (magic), &bfd_offset);
-
-  /* Save the entries to recfd and forward execute to the end of
-     record list.  */
-  record_list = &record_first;
-  while (1)
-    {
-      /* Save entry.  */
-      if (record_list != &record_first)
-        {
-	  uint8_t type;
-	  uint32_t regnum, len, signal, count;
-          uint64_t addr;
-
-	  type = record_list->type;
-          bfdcore_write (obfd, osec, &type, sizeof (type), &bfd_offset);
-
-          switch (record_list->type)
-            {
-            case record_reg: /* reg */
-	      if (record_debug)
-		fprintf_unfiltered (gdb_stdlog,
-				    "  Writing register %d (1 "
-				    "plus %lu plus %d bytes)\n",
-				    record_list->u.reg.num,
-				    (unsigned long) sizeof (regnum),
-				    record_list->u.reg.len);
-
-              /* Write regnum.  */
-              regnum = netorder32 (record_list->u.reg.num);
-              bfdcore_write (obfd, osec, &regnum,
-			     sizeof (regnum), &bfd_offset);
-
-              /* Write regval.  */
-              bfdcore_write (obfd, osec, record_get_loc (record_list),
-			     record_list->u.reg.len, &bfd_offset);
-              break;
-
-            case record_mem: /* mem */
-	      if (record_debug)
-		fprintf_unfiltered (gdb_stdlog,
-				    "  Writing memory %s (1 plus "
-				    "%lu plus %lu plus %d bytes)\n",
-				    paddress (gdbarch,
-					      record_list->u.mem.addr),
-				    (unsigned long) sizeof (addr),
-				    (unsigned long) sizeof (len),
-				    record_list->u.mem.len);
-
-	      /* Write memlen.  */
-	      len = netorder32 (record_list->u.mem.len);
-	      bfdcore_write (obfd, osec, &len, sizeof (len), &bfd_offset);
-
-	      /* Write memaddr.  */
-	      addr = netorder64 (record_list->u.mem.addr);
-	      bfdcore_write (obfd, osec, &addr, 
-			     sizeof (addr), &bfd_offset);
-
-	      /* Write memval.  */
-	      bfdcore_write (obfd, osec, record_get_loc (record_list),
-			     record_list->u.mem.len, &bfd_offset);
-              break;
-
-              case record_end:
-		if (record_debug)
-		  fprintf_unfiltered (gdb_stdlog,
-				      "  Writing record_end (1 + "
-				      "%lu + %lu bytes)\n", 
-				      (unsigned long) sizeof (signal),
-				      (unsigned long) sizeof (count));
-		/* Write signal value.  */
-		signal = netorder32 (record_list->u.end.sigval);
-		bfdcore_write (obfd, osec, &signal,
-			       sizeof (signal), &bfd_offset);
-
-		/* Write insn count.  */
-		count = netorder32 (record_list->u.end.insn_num);
-		bfdcore_write (obfd, osec, &count,
-			       sizeof (count), &bfd_offset);
-                break;
-            }
-        }
-
-      /* Execute entry.  */
-      record_exec_insn (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_insn (regcache, gdbarch, record_list);
-
-      if (record_list->prev)
-        record_list = record_list->prev;
-    }
-
-  do_cleanups (set_cleanups);
-  gdb_bfd_unref (obfd);
-  discard_cleanups (old_cleanups);
-
-  /* Succeeded.  */
-  printf_filtered (_("Saved core file %s with execution log.\n"),
-		   recfilename);
-}
-
-/* record_goto_insn -- rewind the record log (forward or backward,
-   depending on DIR) to the given entry, changing the program state
-   correspondingly.  */
-
-static void
-record_goto_insn (struct record_entry *entry,
-		  enum exec_direction_kind dir)
-{
-  struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
-  struct regcache *regcache = get_current_regcache ();
-  struct gdbarch *gdbarch = get_regcache_arch (regcache);
-
-  /* Assume everything is valid: we will hit the entry,
-     and we will not hit the end of the recording.  */
-
-  if (dir == EXEC_FORWARD)
-    record_list = record_list->next;
-
-  do
-    {
-      record_exec_insn (regcache, gdbarch, record_list);
-      if (dir == EXEC_REVERSE)
-	record_list = record_list->prev;
-      else
-	record_list = record_list->next;
-    } while (record_list != entry);
-  do_cleanups (set_cleanups);
-}
-
 /* "record goto" command.  Argument is an instruction number,
    as given by "info record".
 
diff --git a/gdb/record.h b/gdb/record.h
index 9cf9223..280f4ec 100644
--- a/gdb/record.h
+++ b/gdb/record.h
@@ -23,17 +23,5 @@
 #define RECORD_IS_USED	(current_target.to_stratum == record_stratum)
 
 extern unsigned int record_debug;
-extern int record_memory_query;
-
-extern int record_arch_list_add_reg (struct regcache *regcache, int num);
-extern int record_arch_list_add_mem (CORE_ADDR addr, int len);
-extern int record_arch_list_add_end (void);
-extern struct cleanup *record_gdb_operation_disable_set (void);
-
-/* Wrapper for target_read_memory that prints a debug message if
-   reading memory fails.  */
-extern int record_read_memory (struct gdbarch *gdbarch,
-			       CORE_ADDR memaddr, gdb_byte *myaddr,
-			       ssize_t len);
 
 #endif /* _RECORD_H_ */
-- 
1.7.0.7


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [rfc 1/5] target: add add_deprecated_target_alias
  2013-02-08 15:30 ` [rfc 1/5] target: add add_deprecated_target_alias markus.t.metzger
@ 2013-02-10 22:10   ` Jan Kratochvil
  0 siblings, 0 replies; 23+ messages in thread
From: Jan Kratochvil @ 2013-02-10 22:10 UTC (permalink / raw)
  To: markus.t.metzger; +Cc: gdb-patches, markus.t.metzger

On Fri, 08 Feb 2013 16:30:19 +0100, markus.t.metzger@intel.com wrote:
> 2013-02-08 Markus Metzger <markus.t.metzger@intel.com>
> 
> 	* target.h (add_deprecated_target_alias): New.
> 	* target.c (add_deprecated_target_alias): New.

OK for check-in with the nit below.


> --- a/gdb/target.c
> +++ b/gdb/target.c
> @@ -434,6 +434,20 @@ information on the arguments for a particular protocol, type\n\
>    add_cmd (t->to_shortname, no_class, t->to_open, t->to_doc, &targetlist);
>  }
>  
> +/* See target.h.  */
> +
> +void
> +add_deprecated_target_alias (struct target_ops *t, char *alias)
> +{
> +  struct cmd_list_element *c;
> +  char *alt;
> +
> +  /* If we use add_alias_cmd, here, we do not get the deprecated warning.  */

  /* If we use add_alias_cmd, here, we do not get the deprecated warning,
     see PR cli/15104.  */


> +  c = add_cmd (alias, no_class, t->to_open, t->to_doc, &targetlist);
> +  alt = xstrprintf ("target %s", t->to_shortname);
> +  deprecate_cmd (c, alt);
> +}
> +
>  /* Stub functions */
>  
>  void


Thanks,
Jan


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [rfc 4/5] record: default target methods.
  2013-02-08 15:31 ` [rfc 4/5] record: default target methods markus.t.metzger
@ 2013-02-10 22:11   ` Jan Kratochvil
  0 siblings, 0 replies; 23+ messages in thread
From: Jan Kratochvil @ 2013-02-10 22:11 UTC (permalink / raw)
  To: markus.t.metzger; +Cc: gdb-patches, markus.t.metzger

On Fri, 08 Feb 2013 16:30:22 +0100, markus.t.metzger@intel.com wrote:
> diff --git a/gdb/record.c b/gdb/record.c
> index 5293417..7f26b41 100644
> --- a/gdb/record.c
> +++ b/gdb/record.c
> @@ -32,6 +32,10 @@ struct cmd_list_element *set_record_cmdlist = NULL;
>  struct cmd_list_element *show_record_cmdlist = NULL;
>  struct cmd_list_element *info_record_cmdlist = NULL;
>  
> +#define DEBUG(msg, args...)			\
> +  if (record_debug) \

Backslashes are in such case generally indented in one column:

#define DEBUG(msg, args...)			\
  if (record_debug)				\


> +    fprintf_unfiltered (gdb_stdlog, "record: " msg "\n", ##args)
> +
>  /* Find the record target in the target stack.  */
>  
>  static struct target_ops *


Thanks,
Jan


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [rfc 2/5] record: split record
  2013-02-08 15:32 ` [rfc 2/5] record: split record markus.t.metzger
@ 2013-02-10 22:11   ` Jan Kratochvil
  0 siblings, 0 replies; 23+ messages in thread
From: Jan Kratochvil @ 2013-02-10 22:11 UTC (permalink / raw)
  To: markus.t.metzger; +Cc: gdb-patches, markus.t.metzger

On Fri, 08 Feb 2013 16:30:20 +0100, markus.t.metzger@intel.com wrote:
> 2013-02-08 Markus Metzger <markus.t.metzger@intel.com>
> 
> 	* record.h: Split into this and ...
> 	* record-full.h: ... this.
> 	* record.c: Split into this and ...
> 	* record-full.c: ... this.

OK; BTW 'git diff -B -M' shows record_stopped_data_address is needlessly moved
around but this was no longer a difficulty for review.


Thanks,
Jan


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [rfc 5/5] record, disas: add record disassemble command
  2013-02-08 15:30 ` [rfc 5/5] record, disas: add record disassemble command markus.t.metzger
@ 2013-02-10 22:11   ` Jan Kratochvil
  0 siblings, 0 replies; 23+ messages in thread
From: Jan Kratochvil @ 2013-02-10 22:11 UTC (permalink / raw)
  To: markus.t.metzger; +Cc: gdb-patches, markus.t.metzger

On Fri, 08 Feb 2013 16:30:23 +0100, markus.t.metzger@intel.com wrote:
> 	* target.h (target_ops): Add to_disas_record and
> 	to_disas_record_range fields.
> 	(target_disas_record): New.
> 	(target_disas_record_range): New.
> 	* target.c (target_disas_record): New.
> 	(target_disas_record_range): New.
> 	* record.c: Include cli/cli-utils.h, disasm.h, ctype.h.
> 	(record_disas_size): New.
> 	(get_insn_number): New.
> 	(get_disas_modifiers): New.
> 	(cmd_record_disas): New.
> 	(_initialize_record): Add "set/show record disas-size" command.
> 	Add "record disassemble" command.

OK.



> diff --git a/gdb/target.h b/gdb/target.h
> index e4fe5da..bf0d825 100644
> --- a/gdb/target.h
> +++ b/gdb/target.h
> @@ -1983,4 +1989,10 @@ extern void target_goto_record_end (void);
>  /* Go to a specific location in the recorded execution trace.  */
>  extern void target_goto_record (ULONGEST);
>  
> +/* Disassemble the recorded execution trace.  */

Likewise about comments, "See to_disas_record.".


> +extern void target_disas_record (int size, int flags);
> +
> +/* Disassemble a section of the recorded execution trace.  */
> +extern void target_disas_record_range (ULONGEST begin, ULONGEST end, int flags);
> +
>  #endif /* !defined (TARGET_H) */
> -- 
> 1.7.0.7


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [rfc 3/5] record: make it build again
  2013-02-08 15:31 ` [rfc 3/5] record: make it build again markus.t.metzger
@ 2013-02-10 22:11   ` Jan Kratochvil
  2013-02-11 13:41     ` Metzger, Markus T
  0 siblings, 1 reply; 23+ messages in thread
From: Jan Kratochvil @ 2013-02-10 22:11 UTC (permalink / raw)
  To: markus.t.metzger; +Cc: gdb-patches, markus.t.metzger

On Fri, 08 Feb 2013 16:30:21 +0100, markus.t.metzger@intel.com wrote:
[...]
> --- a/gdb/record-full.c
> +++ b/gdb/record-full.c
> @@ -28,6 +28,7 @@
>  #include "gdbcore.h"
>  #include "exec.h"
>  #include "record.h"
> +#include "record-full.h"
>  #include "elf-bfd.h"
>  #include "gcore.h"
>  #include "event-loop.h"
> @@ -37,7 +38,7 @@
>  
>  #include <signal.h>
>  
> -/* This module implements "target record", also known as "process
> +/* This module implements "target record-full", also known as "process
>     record and replay".  This target sits on top of a "normal" target
>     (a target that "has execution"), and provides a record and replay
>     functionality, including reverse debugging.
> @@ -205,6 +206,13 @@ static ULONGEST record_insn_count;
>  static struct target_ops record_ops;
>  static struct target_ops record_core_ops;
>  
> +/* Command lists for "set/show record full".  */
> +static struct cmd_list_element *set_record_full_cmdlist;
> +static struct cmd_list_element *show_record_full_cmdlist;
> +
> +/* Command list for "record full".  */
> +static struct cmd_list_element *record_full_cmdlist;
> +
>  /* The beneath function pointers.  */
>  static struct target_ops *record_beneath_to_resume_ops;
>  static void (*record_beneath_to_resume) (struct target_ops *, ptid_t, int,
> @@ -234,6 +242,10 @@ static int (*record_beneath_to_stopped_data_address) (struct target_ops *,
>  						      CORE_ADDR *);
>  static void (*record_beneath_to_async) (void (*) (enum inferior_event_type, void *), void *);
>  
> +static void record_goto_insn (struct record_entry *entry,
> +			      enum exec_direction_kind dir);
> +static void record_save (char *recfilename);
> +
>  /* Alloc and free functions for record_reg, record_mem, and record_end 
>     entries.  */
>  
> @@ -564,7 +576,7 @@ record_check_insn_num (int set_terminal)
>  		target_terminal_ours ();
>  	      q = yquery (_("Do you want to auto delete previous execution "
>  			    "log entries when record/replay buffer becomes "
> -			    "full (record stop-at-limit)?"));
> +			    "full (record full stop-at-limit)?"));
>  	      if (set_terminal)
>  		target_terminal_inferior ();
>  	      if (q)
> @@ -1948,9 +1960,140 @@ record_execution_direction (void)
>  }
>  
>  static void
> +record_info (void)

Such functions should be called/renamed to record_full_info, they are specific
for record-full.c and moreover other backends will have the same function.

You can rename everything, also record_ops, record_core_ops -> record_full_*.

GDB prevents using static names duplicated across files.  (Maybe it comes from
the time before "ambiguous linespec" start to put breakpoints on all of them.)


> +{
> +  struct record_entry *p;
> +
> +  if (RECORD_IS_REPLAY)
> +    printf_filtered (_("Replay mode:\n"));
> +  else
> +    printf_filtered (_("Record mode:\n"));
> +
> +  /* Find entry for first actual instruction in the log.  */
> +  for (p = record_first.next;
> +       p != NULL && p->type != record_end;
> +       p = p->next)
> +    ;
> +
> +  /* Do we have a log at all?  */
> +  if (p != NULL && p->type == record_end)
> +    {
> +      /* Display instruction number for first instruction in the log.  */
> +      printf_filtered (_("Lowest recorded instruction number is %s.\n"),
> +		       pulongest (p->u.end.insn_num));
> +
> +      /* If in replay mode, display where we are in the log.  */
> +      if (RECORD_IS_REPLAY)
> +	printf_filtered (_("Current instruction number is %s.\n"),
> +			 pulongest (record_list->u.end.insn_num));
> +
> +      /* Display instruction number for last instruction in the log.  */
> +      printf_filtered (_("Highest recorded instruction number is %s.\n"),
> +		       pulongest (record_insn_count));
> +
> +      /* Display log count.  */
> +      printf_filtered (_("Log contains %d instructions.\n"),
> +		       record_insn_num);
> +    }
> +  else
> +    printf_filtered (_("No instructions have been logged.\n"));
> +
> +  /* Display max log size.  */
> +  printf_filtered (_("Max logged instructions is %d.\n"),
> +		   record_insn_max_num);
> +}
> +
> +/* The "to_record_delete" target method.  */
> +
> +static void
> +record_delete (void)
> +{
> +  record_list_release_following (record_list);
> +}
> +
> +/* The "to_record_is_replaying" target method.  */
> +
> +static int
> +record_is_replaying (void)
> +{
> +  return RECORD_IS_REPLAY;
> +}
> +
> +/* Go to a specific entry.  */
> +
> +static void
> +record_goto_entry (struct record_entry *p)
> +{
> +  if (p == NULL)
> +    error (_("Target insn not found."));
> +  else if (p == record_list)
> +    error (_("Already at target insn."));
> +  else if (p->u.end.insn_num > record_list->u.end.insn_num)
> +    {
> +      printf_filtered (_("Go forward to insn number %s\n"),
> +		       pulongest (p->u.end.insn_num));
> +      record_goto_insn (p, EXEC_FORWARD);
> +    }
> +  else
> +    {
> +      printf_filtered (_("Go backward to insn number %s\n"),
> +		       pulongest (p->u.end.insn_num));
> +      record_goto_insn (p, EXEC_REVERSE);
> +    }
> +
> +  registers_changed ();
> +  reinit_frame_cache ();
> +  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
> +}
> +
> +/* The "to_goto_record_begin" target method.  */
> +
> +static void
> +record_goto_begin (void)
> +{
> +  struct record_entry *p = NULL;
> +
> +  for (p = &record_first; p != NULL; p = p->next)
> +    if (p->type == record_end)
> +      break;
> +
> +  record_goto_entry (p);
> +}
> +
> +/* The "to_goto_record_end" target method.  */
> +
> +static void
> +record_goto_end (void)
> +{
> +  struct record_entry *p = NULL;
> +
> +  for (p = record_list; p->next != NULL; p = p->next)
> +    ;
> +  for (; p!= NULL; p = p->prev)
> +    if (p->type == record_end)
> +      break;
> +
> +  record_goto_entry (p);
> +}
> +
> +/* The "to_goto_record" target method.  */
> +
> +static void
> +record_goto (ULONGEST target_insn)
> +{
> +  struct record_entry *p = NULL;
> +
> +  for (p = &record_first; p != NULL; p = p->next)
> +    if (p->type == record_end && p->u.end.insn_num == target_insn)
> +      break;
> +
> +  record_goto_entry (p);
> +}
> +
> +static void
>  init_record_ops (void)
>  {
> -  record_ops.to_shortname = "record";
> +  record_ops.to_shortname = "record-full";
>    record_ops.to_longname = "Process record and replay target";
>    record_ops.to_doc =
>      "Log program while executing and replay execution from log.";
> @@ -1978,6 +2121,13 @@ init_record_ops (void)
>    record_ops.to_can_async_p = record_can_async_p;
>    record_ops.to_is_async_p = record_is_async_p;
>    record_ops.to_execution_direction = record_execution_direction;
> +  record_ops.to_info_record = record_info;
> +  record_ops.to_save_record = record_save;
> +  record_ops.to_delete_record = record_delete;
> +  record_ops.to_record_is_replaying = record_is_replaying;
> +  record_ops.to_goto_record_begin = record_goto_begin;
> +  record_ops.to_goto_record_end = record_goto_end;
> +  record_ops.to_goto_record = record_goto;
>    record_ops.to_magic = OPS_MAGIC;
>  }
>  
> @@ -2203,6 +2353,12 @@ init_record_core_ops (void)
>    record_core_ops.to_can_async_p = record_can_async_p;
>    record_core_ops.to_is_async_p = record_is_async_p;
>    record_core_ops.to_execution_direction = record_execution_direction;
> +  record_core_ops.to_info_record = record_info;
> +  record_core_ops.to_delete_record = record_delete;
> +  record_core_ops.to_record_is_replaying = record_is_replaying;
> +  record_core_ops.to_goto_record_begin = record_goto_begin;
> +  record_core_ops.to_goto_record_end = record_goto_end;
> +  record_core_ops.to_goto_record = record_goto;
>    record_core_ops.to_magic = OPS_MAGIC;
>  }
>  
> @@ -2507,9 +2663,8 @@ record_save_cleanups (void *data)
>     format, with an extra section for our data.  */
>  
>  static void
> -cmd_record_save (char *args, int from_tty)
> +record_save (char *recfilename)

record_full_save etc.

>  {
> -  char *recfilename, recfilename_buffer[40];
>    struct record_entry *cur_record_list;
>    uint32_t magic;
>    struct regcache *regcache;
> @@ -2521,20 +2676,6 @@ cmd_record_save (char *args, int from_tty)
>    asection *osec = NULL;
>    int bfd_offset = 0;
>  
> -  if (strcmp (current_target.to_shortname, "record") != 0)
> -    error (_("This command can only be used with target 'record'.\n"
> -	     "Use 'target record' first.\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 save file.  */
>    if (record_debug)
>      fprintf_unfiltered (gdb_stdlog, "Saving execution log to core file '%s'\n",
> @@ -2750,3 +2891,143 @@ record_goto_insn (struct record_entry *entry,
>      } while (record_list != entry);
>    do_cleanups (set_cleanups);
>  }
> +
> +/* Alias for "target record-full".  */
> +
> +static void
> +cmd_record_start (char *args, int from_tty)

cmd_record_full_start


> +{
> +  execute_command ("target record-full", from_tty);
> +}
> +
> +static void
> +set_record_insn_max_num (char *args, int from_tty, struct cmd_list_element *c)

set_record_full_insn_max_num


> +{
> +  if (record_insn_num > record_insn_max_num && record_insn_max_num)

record_full_insn_num

> +    {
> +      /* Count down record_insn_num while releasing records from list.  */
> +      while (record_insn_num > record_insn_max_num)
> +       {
> +         record_list_release_first ();

record_full_list_release_first etc.

> +         record_insn_num--;
> +       }
> +    }
> +}
> +
> +/* The "set record full" command.  */
> +
> +static void
> +set_record_full_command (char *args, int from_tty)
> +{
> +  printf_unfiltered (_("\"set record full\" must be followed "
> +		       "by an apporpriate subcommand.\n"));
> +  help_list (set_record_full_cmdlist, "set record full ", all_commands,
> +	     gdb_stdout);
> +}
> +
> +/* The "show record full" command.  */
> +
> +static void
> +show_record_full_command (char *args, int from_tty)
> +{
> +  cmd_show_list (show_record_full_cmdlist, from_tty, "");
> +}
> +
> +/* Provide a prototype to silence -Wmissing-prototypes.  */
> +extern initialize_file_ftype _initialize_record_full;
> +
> +void
> +_initialize_record_full (void)
> +{
> +  struct cmd_list_element *c;
> +
> +  /* Init record_first.  */
> +  record_first.prev = NULL;
> +  record_first.next = NULL;
> +  record_first.type = record_end;
> +
> +  init_record_ops ();
> +  add_target (&record_ops);
> +  add_deprecated_target_alias (&record_ops, "record");
> +  init_record_core_ops ();
> +  add_target (&record_core_ops);
> +
> +  add_prefix_cmd ("full", class_obscure, cmd_record_start,
> +		  _("Start full execution recording."), &record_full_cmdlist,
> +		  "record full ", 0, &record_cmdlist);
> +
> +  c = add_cmd ("restore", class_obscure, cmd_record_restore,
> +	       _("Restore the execution log from a file.\n\
> +Argument is filename.  File must be created with 'record save'."),
> +	       &record_full_cmdlist);
> +  set_cmd_completer (c, filename_completer);
> +
> +  /* Deprecate the old version without "full" prefix.  */
> +  c = add_alias_cmd ("restore", "full restore", class_obscure, 1,
> +		     &record_cmdlist);
> +  set_cmd_completer (c, filename_completer);
> +  deprecate_cmd (c, "record full restore");

This (and all add_alias_cmd below) don't display the warning as discussed
before.

I guess we can keep it as is as the missing warning is tracked
	deprecated_cmd_warning does not work for prefixed commands
	http://sourceware.org/bugzilla/show_bug.cgi?id=15104
and the only command where it is most visible is "target record" which you
have successfully workarounded in the patchset.


a bit offtopic: 'git am' somehow broken on applicating the patchset (but later
it went OK by hand), it would be easier to have it in a public GIT branch,
possibly in
	http://sourceware.org/gdb/wiki/ArcherBranchManagement
needing an account http://sourceware.org/cgi-bin/pdw/ps_form.cgi which you are
going to get for the later check-in anyway; or you could use github or some
such site.


> +
> +  add_prefix_cmd ("full", class_support, set_record_full_command,
> +		  _("Set record options"), &set_record_full_cmdlist,
> +		  "set record full ", 0, &set_record_cmdlist);
> +
> +  add_prefix_cmd ("full", class_support, show_record_full_command,
> +		  _("Show record options"), &show_record_full_cmdlist,
> +		  "show record full ", 0, &show_record_cmdlist);
> +
> +  /* Record instructions number limit command.  */
> +  add_setshow_boolean_cmd ("stop-at-limit", no_class,
> +			   &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_full_cmdlist, &show_record_full_cmdlist);
> +
> +  c = add_alias_cmd ("stop-at-limit", "full stop-at-limit", no_class, 1,
> +		     &set_record_cmdlist);
> +  deprecate_cmd (c, "set record full stop-at-limit");
> +
> +  c = add_alias_cmd ("stop-at-limit", "full stop-at-limit", no_class, 1,
> +		     &show_record_cmdlist);
> +  deprecate_cmd (c, "show record full stop-at-limit");
> +
> +  add_setshow_uinteger_cmd ("insn-number-max", no_class, &record_insn_max_num,
> +			    _("Set record/replay buffer limit."),
> +			    _("Show record/replay buffer limit."), _("\
> +Set the maximum number of instructions to be stored in the\n\
> +record/replay buffer.  Zero means unlimited.  Default is 200000."),
> +			    set_record_insn_max_num,
> +			    NULL, &set_record_full_cmdlist,
> +			    &show_record_full_cmdlist);
> +
> +  c = add_alias_cmd ("insn-number-max", "full insn-number-max", no_class, 1,
> +		     &set_record_cmdlist);
> +  deprecate_cmd (c, "set record full insn-number-max");
> +
> +  c = add_alias_cmd ("insn-number-max", "full insn-number-max", no_class, 1,
> +		     &show_record_cmdlist);
> +  deprecate_cmd (c, "show record full insn-number-max");
> +
> +  add_setshow_boolean_cmd ("memory-query", no_class, &record_memory_query, _("\
> +Set whether query if PREC cannot record memory change of next instruction."),
> +                           _("\
> +Show whether query if PREC cannot record memory change of next instruction."),
> +                           _("\
> +Default is OFF.\n\
> +When ON, query if PREC cannot record memory change of next instruction."),
> +			   NULL, NULL,
> +			   &set_record_full_cmdlist, &show_record_full_cmdlist);
> +
> +  c = add_alias_cmd ("memory-query", "full memory-query", no_class, 1,
> +		     &set_record_cmdlist);
> +  deprecate_cmd (c, "set record full memory-query");
> +
> +  c = add_alias_cmd ("memory-query", "full memory-query", no_class, 1,
> +		     &show_record_cmdlist);
> +  deprecate_cmd (c, "show record full memory-query");
> +}
> diff --git a/gdb/record.c b/gdb/record.c
> index 2e970ae..5293417 100644
> --- a/gdb/record.c
> +++ b/gdb/record.c
> @@ -19,29 +19,46 @@
>  
>  #include "defs.h"
>  #include "gdbcmd.h"
> -#include "regcache.h"
> -#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 "event-loop.h"
> -#include "inf-loop.h"
> -#include "gdb_bfd.h"
>  #include "observer.h"
> -
> -#include <signal.h>
> +#include "inferior.h"
>  
>  /* This is the debug switch for process record.  */
>  unsigned int record_debug = 0;
>  
> -/* The implementation of the command "record goto".  */
> -static void cmd_record_goto (char *, int);
> +struct cmd_list_element *record_cmdlist = NULL;
> +struct cmd_list_element *set_record_cmdlist = NULL;
> +struct cmd_list_element *show_record_cmdlist = NULL;
> +struct cmd_list_element *info_record_cmdlist = NULL;
> +
> +/* Find the record target in the target stack.  */
> +
> +static struct target_ops *
> +find_record_target (void)
> +{
> +  struct target_ops *t;
> +
> +  for (t = current_target.beneath; t != NULL; t = t->beneath)
> +    if (t->to_stratum == record_stratum)
> +      return t;
> +
> +  return NULL;
> +}
> +
> +/* Check that recording is active.  Throw an error, if it isn't.  */
> +
> +static struct target_ops *
> +require_record_target (void)
> +{
> +  struct target_ops *t;
> +
> +  t = find_record_target ();
> +  if (t == NULL)
> +    error (_("No record target is currently active."));

cmd_record_save formerly had more suggestive:
    error (_("This command can only be used with target 'record'.\n"
             "Use 'target record' first.\n"));

So require_record_target could have now:
       error (_("No record target is currently active.\n"
                "Use one of the 'target record-<tab><tab>' commands first.\n"));


> +
> +  return t;
> +}
>  
>  /* Implement "show record debug" command.  */
>  
> @@ -58,7 +75,7 @@ show_record_debug (struct ui_file *file, int from_tty,
>  static void
>  cmd_record_start (char *args, int from_tty)
>  {
> -  execute_command ("target record", from_tty);
> +  execute_command ("target record-full", from_tty);
>  }
>  
>  /* Truncate the record log from the present point
> @@ -67,21 +84,25 @@ cmd_record_start (char *args, int from_tty)
>  static void
>  cmd_record_delete (char *args, int from_tty)
>  {
> -  if (current_target.to_stratum == record_stratum)
> +  require_record_target ();
> +
> +  if (!target_record_is_replaying ())
>      {
> -      if (RECORD_IS_REPLAY)
> -	{
> -	  if (!from_tty || query (_("Delete the log from this point forward "
> -		                    "and begin to record the running message "
> -		                    "at current PC?")))
> -	    record_list_release_following (record_list);
> -	}
> -      else
> -	  printf_unfiltered (_("Already at end of record list.\n"));
> +      printf_unfiltered (_("Already at end of record list.\n"));
> +      return;
> +    }
>  
> +  if (!target_supports_delete_record ())
> +    {
> +      printf_unfiltered (_("The current record target does not support "
> +			   "this operation.\n"));
> +      return;
>      }
> -  else
> -    printf_unfiltered (_("Process record is not started.\n"));
> +
> +  if (!from_tty || query (_("Delete the log from this point forward "
> +			    "and begin to record the running message "
> +			    "at current PC?")))
> +    target_delete_record ();
>  }
>  
>  /* Implement the "stoprecord" or "record stop" command.  */
> @@ -89,36 +110,18 @@ cmd_record_delete (char *args, int from_tty)
>  static void
>  cmd_record_stop (char *args, int from_tty)
>  {
> -  if (current_target.to_stratum == record_stratum)
> -    {
> -      unpush_target (&record_ops);
> -      printf_unfiltered (_("Process record is stopped and all execution "
> -                           "logs are deleted.\n"));
> +  struct target_ops *t;
>  
> -      observer_notify_record_changed (current_inferior (), 0);
> -    }
> -  else
> -    printf_unfiltered (_("Process record is not started.\n"));
> -}
> +  t = require_record_target ();
> +  unpush_target (t);
>  
> -/* Set upper limit of record log size.  */
> +  printf_unfiltered (_("Process record is stopped and all execution "
> +		       "logs are deleted.\n"));
>  
> -static void
> -set_record_insn_max_num (char *args, int from_tty, struct cmd_list_element *c)
> -{
> -  if (record_insn_num > record_insn_max_num && record_insn_max_num)
> -    {
> -      /* Count down record_insn_num while releasing records from list.  */
> -      while (record_insn_num > record_insn_max_num)
> -	{
> -	  record_list_release_first ();
> -	  record_insn_num--;
> -	}
> -    }
> +  observer_notify_record_changed (current_inferior (), 0);
>  }
>  
> -static struct cmd_list_element *record_cmdlist, *set_record_cmdlist,
> -			       *show_record_cmdlist, *info_record_cmdlist;
> +/* The "set record" command.  */
>  
>  static void
>  set_record_command (char *args, int from_tty)
> @@ -128,65 +131,53 @@ set_record_command (char *args, int from_tty)
>    help_list (set_record_cmdlist, "set record ", all_commands, gdb_stdout);
>  }
>  
> +/* The "show record" command.  */
> +
>  static void
>  show_record_command (char *args, int from_tty)
>  {
>    cmd_show_list (show_record_cmdlist, from_tty, "");
>  }
>  
> -/* Display some statistics about the execution log.  */
> +/* The "info record" command.  */
>  
>  static void
>  info_record_command (char *args, int from_tty)
>  {
> -  struct record_entry *p;
> +  struct target_ops *t;
>  
> -  if (current_target.to_stratum == record_stratum)
> +  t = find_record_target ();
> +  if (t == NULL)
>      {
> -      if (RECORD_IS_REPLAY)
> -	printf_filtered (_("Replay mode:\n"));
> -      else
> -	printf_filtered (_("Record mode:\n"));
> -
> -      /* Find entry for first actual instruction in the log.  */
> -      for (p = record_first.next;
> -	   p != NULL && p->type != record_end;
> -	   p = p->next)
> -	;
> -
> -      /* Do we have a log at all?  */
> -      if (p != NULL && p->type == record_end)
> -	{
> -	  /* Display instruction number for first instruction in the log.  */
> -	  printf_filtered (_("Lowest recorded instruction number is %s.\n"),
> -			   pulongest (p->u.end.insn_num));
> -
> -	  /* If in replay mode, display where we are in the log.  */
> -	  if (RECORD_IS_REPLAY)
> -	    printf_filtered (_("Current instruction number is %s.\n"),
> -			     pulongest (record_list->u.end.insn_num));
> -
> -	  /* Display instruction number for last instruction in the log.  */
> -	  printf_filtered (_("Highest recorded instruction number is %s.\n"), 
> -			   pulongest (record_insn_count));
> -
> -	  /* Display log count.  */
> -	  printf_filtered (_("Log contains %d instructions.\n"), 
> -			   record_insn_num);
> -	}
> -      else
> -	{
> -	  printf_filtered (_("No instructions have been logged.\n"));
> -	}
> +      printf_filtered (_("No record target is currently active.\n"));
> +      return;
>      }
> +
> +  printf_filtered (_("Active record target: %s\n"), t->to_shortname);
> +  if (t->to_info_record)

nit: According to the new GDB coding style rules it should be:
	if (t->to_info_record != NULL)


> +    t->to_info_record ();
> +}
> +
> +/* The "record save" command.  */
> +
> +static void
> +cmd_record_save (char *args, int from_tty)
> +{
> +  char *recfilename, recfilename_buffer[40];
> +
> +  require_record_target ();
> +
> +  if (args && *args)
> +    recfilename = args;
>    else
>      {
> -      printf_filtered (_("target record is not active.\n"));
> +      /* Default recfile name is "gdb_record.PID".  */
> +      snprintf (recfilename_buffer, sizeof (recfilename_buffer),
> +                "gdb_record.%d", PIDGET (inferior_ptid));

xsnprintf, intended for better OS compatibility.


> +      recfilename = recfilename_buffer;
>      }
>  
> -  /* Display max log size.  */
> -  printf_filtered (_("Max logged instructions is %d.\n"),
> -		   record_insn_max_num);
> +  target_save_record (recfilename);
>  }
>  
>  /* "record goto" command.  Argument is an instruction number,
> @@ -194,65 +185,26 @@ info_record_command (char *args, int from_tty)
>  
>     Rewinds the recording (forward or backward) to the given instruction.  */
>  
> -static void
> +void
>  cmd_record_goto (char *arg, int from_tty)
>  {
> -  struct record_entry *p = NULL;
> -  ULONGEST target_insn = 0;
> +  require_record_target ();
>  
>    if (arg == NULL || *arg == '\0')
>      error (_("Command requires an argument (insn number to go to)."));
>  
>    if (strncmp (arg, "start", strlen ("start")) == 0
>        || strncmp (arg, "begin", strlen ("begin")) == 0)
> -    {
> -      /* Special case.  Find first insn.  */
> -      for (p = &record_first; p != NULL; p = p->next)
> -	if (p->type == record_end)
> -	  break;
> -      if (p)
> -	target_insn = p->u.end.insn_num;
> -    }
> +    target_goto_record_begin ();
>    else if (strncmp (arg, "end", strlen ("end")) == 0)
> -    {
> -      /* Special case.  Find last insn.  */
> -      for (p = record_list; p->next != NULL; p = p->next)
> -	;
> -      for (; p!= NULL; p = p->prev)
> -	if (p->type == record_end)
> -	  break;
> -      if (p)
> -	target_insn = p->u.end.insn_num;
> -    }
> +    target_goto_record_end ();
>    else
>      {
> -      /* General case.  Find designated insn.  */
> -      target_insn = parse_and_eval_long (arg);
> +      ULONGEST insn;
>  
> -      for (p = &record_first; p != NULL; p = p->next)
> -	if (p->type == record_end && p->u.end.insn_num == target_insn)
> -	  break;
> +      insn = parse_and_eval_long (arg);
> +      target_goto_record (insn);
>      }
> -
> -  if (p == NULL)
> -    error (_("Target insn '%s' not found."), arg);
> -  else if (p == record_list)
> -    error (_("Already at insn '%s'."), arg);
> -  else if (p->u.end.insn_num > record_list->u.end.insn_num)
> -    {
> -      printf_filtered (_("Go forward to insn number %s\n"),
> -		       pulongest (target_insn));
> -      record_goto_insn (p, EXEC_FORWARD);
> -    }
> -  else
> -    {
> -      printf_filtered (_("Go backward to insn number %s\n"),
> -		       pulongest (target_insn));
> -      record_goto_insn (p, EXEC_REVERSE);
> -    }
> -  registers_changed ();
> -  reinit_frame_cache ();
> -  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
>  }
>  
>  /* Provide a prototype to silence -Wmissing-prototypes.  */
> @@ -263,16 +215,6 @@ _initialize_record (void)
>  {
>    struct cmd_list_element *c;
>  
> -  /* Init record_first.  */
> -  record_first.prev = NULL;
> -  record_first.next = NULL;
> -  record_first.type = record_end;
> -
> -  init_record_ops ();
> -  add_target (&record_ops);
> -  init_record_core_ops ();
> -  add_target (&record_core_ops);
> -
>    add_setshow_zuinteger_cmd ("record", no_class, &record_debug,
>  			     _("Set debugging of record/replay feature."),
>  			     _("Show debugging of record/replay feature."),
> @@ -282,7 +224,7 @@ _initialize_record (void)
>  			     &showdebuglist);
>  
>    c = add_prefix_cmd ("record", class_obscure, cmd_record_start,
> -		      _("Abbreviated form of \"target record\" command."),
> +		      _("Start recording."),
>  		      &record_cmdlist, "record ", 0, &cmdlist);
>    set_cmd_completer (c, filename_completer);
>  
> @@ -307,12 +249,6 @@ Default filename is 'gdb_record.<process_id>'."),
>  	       &record_cmdlist);
>    set_cmd_completer (c, filename_completer);
>  
> -  c = add_cmd ("restore", class_obscure, cmd_record_restore,
> -	       _("Restore the execution log from a file.\n\
> -Argument is filename.  File must be created with 'record save'."),
> -	       &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."),
>             &record_cmdlist);
> @@ -324,40 +260,8 @@ Argument is filename.  File must be created with 'record save'."),
>             &record_cmdlist);
>    add_alias_cmd ("s", "stop", class_obscure, 1, &record_cmdlist);
>  
> -  /* Record instructions number limit command.  */
> -  add_setshow_boolean_cmd ("stop-at-limit", no_class,
> -			   &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);
> -  add_setshow_uinteger_cmd ("insn-number-max", no_class,
> -			    &record_insn_max_num,
> -			    _("Set record/replay buffer limit."),
> -			    _("Show record/replay buffer limit."), _("\
> -Set the maximum number of instructions to be stored in the\n\
> -record/replay buffer.  Zero means unlimited.  Default is 200000."),
> -			    set_record_insn_max_num,
> -			    NULL, &set_record_cmdlist, &show_record_cmdlist);
> -
>    add_cmd ("goto", class_obscure, cmd_record_goto, _("\
>  Restore the program to its state at instruction number N.\n\
>  Argument is instruction number, as shown by 'info record'."),
>  	   &record_cmdlist);
> -
> -  add_setshow_boolean_cmd ("memory-query", no_class,
> -			   &record_memory_query, _("\
> -Set whether query if PREC cannot record memory change of next instruction."),
> -                           _("\
> -Show whether query if PREC cannot record memory change of next instruction."),
> -                           _("\
> -Default is OFF.\n\
> -When ON, query if PREC cannot record memory change of next instruction."),
> -			   NULL, NULL,
> -			   &set_record_cmdlist, &show_record_cmdlist);
> -
>  }
> diff --git a/gdb/record.h b/gdb/record.h
> index 280f4ec..dfd8361 100644
> --- a/gdb/record.h
> +++ b/gdb/record.h
> @@ -20,8 +20,18 @@
>  #ifndef _RECORD_H_
>  #define _RECORD_H_
>  
> +struct cmd_list_element;
> +
>  #define RECORD_IS_USED	(current_target.to_stratum == record_stratum)
>  
>  extern unsigned int record_debug;
>  
> +/* Allow record targets to add their own sub-commands.  */
> +extern struct cmd_list_element *record_cmdlist;
> +extern struct cmd_list_element *set_record_cmdlist;
> +extern struct cmd_list_element *show_record_cmdlist;
> +extern struct cmd_list_element *info_record_cmdlist;
> +
> +extern void cmd_record_goto (char *arg, int from_tty);
> +
>  #endif /* _RECORD_H_ */
> diff --git a/gdb/target.c b/gdb/target.c
> index 25f4629..e71ab96 100644
> --- a/gdb/target.c
> +++ b/gdb/target.c
> @@ -4231,6 +4231,135 @@ target_read_btrace (struct btrace_target_info *btinfo)
>    return NULL;
>  }
>  
> +/* See target.h.  */
> +
> +void
> +target_info_record (void)
> +{
> +  struct target_ops *t;
> +
> +  for (t = current_target.beneath; t != NULL; t = t->beneath)
> +    if (t->to_info_record != NULL)
> +      {
> +	t->to_info_record ();
> +	return;
> +      }
> +
> +  tcomplain ();
> +}
> +
> +/* See target.h.  */
> +
> +void
> +target_save_record (char *filename)
> +{
> +  struct target_ops *t;
> +
> +  for (t = current_target.beneath; t != NULL; t = t->beneath)
> +    if (t->to_save_record != NULL)
> +      {
> +	t->to_save_record (filename);
> +	return;
> +      }
> +
> +  tcomplain ();
> +}
> +
> +/* See target.h.  */
> +
> +int
> +target_supports_delete_record (void)
> +{
> +  struct target_ops *t;
> +
> +  for (t = current_target.beneath; t != NULL; t = t->beneath)
> +    if (t->to_delete_record != NULL)
> +      return 1;
> +
> +  return 0;
> +}
> +
> +/* See target.h.  */
> +
> +void
> +target_delete_record (void)
> +{
> +  struct target_ops *t;
> +
> +  for (t = current_target.beneath; t != NULL; t = t->beneath)
> +    if (t->to_delete_record != NULL)
> +      {
> +	t->to_delete_record ();
> +	return;
> +      }
> +
> +  tcomplain ();
> +}
> +
> +/* See target.h.  */
> +
> +int
> +target_record_is_replaying (void)
> +{
> +  struct target_ops *t;
> +
> +  for (t = current_target.beneath; t != NULL; t = t->beneath)
> +    if (t->to_record_is_replaying != NULL)
> +	return t->to_record_is_replaying ();
> +
> +  return 0;
> +}
> +
> +/* See target.h.  */
> +
> +void
> +target_goto_record_begin (void)
> +{
> +  struct target_ops *t;
> +
> +  for (t = current_target.beneath; t != NULL; t = t->beneath)
> +    if (t->to_goto_record_begin != NULL)
> +      {
> +	t->to_goto_record_begin ();
> +	return;
> +      }
> +
> +  tcomplain ();
> +}
> +
> +/* See target.h.  */
> +
> +void
> +target_goto_record_end (void)
> +{
> +  struct target_ops *t;
> +
> +  for (t = current_target.beneath; t != NULL; t = t->beneath)
> +    if (t->to_goto_record_end != NULL)
> +      {
> +	t->to_goto_record_end ();
> +	return;
> +      }
> +
> +  tcomplain ();
> +}
> +
> +/* See target.h.  */
> +
> +void
> +target_goto_record (ULONGEST insn)
> +{
> +  struct target_ops *t;
> +
> +  for (t = current_target.beneath; t != NULL; t = t->beneath)
> +    if (t->to_goto_record != NULL)
> +      {
> +	t->to_goto_record (insn);
> +	return;
> +      }
> +
> +  tcomplain ();
> +}
>  
>  static void
>  debug_to_prepare_to_store (struct regcache *regcache)
> diff --git a/gdb/target.h b/gdb/target.h
> index 1d73336..e4fe5da 100644
> --- a/gdb/target.h
> +++ b/gdb/target.h
> @@ -876,6 +876,27 @@ struct target_ops
>      /* Read branch trace data.  */
>      VEC (btrace_block_s) *(*to_read_btrace) (struct btrace_target_info *);
>  
> +    /* Print information about the recording.  */
> +    void (*to_info_record) (void);
> +
> +    /* Save the recorded execution trace into a file.  */
> +    void (*to_save_record) (char *filename);
> +
> +    /* Delete the recorded execution trace from the current position onwards.  */
> +    void (*to_delete_record) (void);
> +
> +    /* Query if the record target is currently replaying.  */
> +    int (*to_record_is_replaying) (void);
> +
> +    /* Go to the begin of the execution trace.  */
> +    void (*to_goto_record_begin) (void);
> +
> +    /* Go to the end of the execution trace.  */
> +    void (*to_goto_record_end) (void);
> +
> +    /* Go to a specific location in the recorded execution trace.  */
> +    void (*to_goto_record) (ULONGEST);

Just a nit, here could be 'ULONGEST insn'.


> +
>      int to_magic;
>      /* Need sub-structure for target machine related rather than comm related?
>       */
> @@ -1938,5 +1959,28 @@ extern int target_btrace_has_changed (struct btrace_target_info *btinfo);
>     Returns a vector of branch trace blocks with the latest entry at index 0.  */
>  extern VEC (btrace_block_s) *target_read_btrace (struct btrace_target_info *);
>  
> +/* Print record information for this record target.  */
> +extern void target_info_record (void);

Please do not use comments for these target_* declarations, they duplicate
those of their to_* fields and they could become out of sync.  As you
commented the to_* fields just write:
	/* See to_info_record in struct target_ops.  */


> +
> +/* Save the recorded execution trace into a file.  */
> +extern void target_save_record (char *filename);
> +
> +/* Query if the target supports deleting the execution log.  */
> +extern int target_supports_delete_record (void);
> +
> +/* Delete the recorded execution trace from the current position onwards.  */
> +extern void target_delete_record (void);
> +
> +/* Query if the record target is currently replaying.  */
> +extern int target_record_is_replaying (void);
> +
> +/* Go to the begin of the execution trace.  */
> +extern void target_goto_record_begin (void);
> +
> +/* Go to the end of the execution trace.  */
> +extern void target_goto_record_end (void);
> +
> +/* Go to a specific location in the recorded execution trace.  */
> +extern void target_goto_record (ULONGEST);

Just a nit, here could be 'ULONGEST insn'.

>  
>  #endif /* !defined (TARGET_H) */
> -- 
> 1.7.0.7


^ permalink raw reply	[flat|nested] 23+ messages in thread

* RE: [rfc 3/5] record: make it build again
  2013-02-10 22:11   ` Jan Kratochvil
@ 2013-02-11 13:41     ` Metzger, Markus T
  2013-02-11 14:15       ` Jan Kratochvil
  0 siblings, 1 reply; 23+ messages in thread
From: Metzger, Markus T @ 2013-02-11 13:41 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches, markus.t.metzger

> -----Original Message-----
> From: gdb-patches-owner@sourceware.org [mailto:gdb-patches-owner@sourceware.org] On Behalf Of Jan Kratochvil
> Sent: Sunday, February 10, 2013 11:11 PM

Thanks for your review!

[...]

> >  static void
> > +record_info (void)
> 
> Such functions should be called/renamed to record_full_info, they are specific
> for record-full.c and moreover other backends will have the same function.
> 
> You can rename everything, also record_ops, record_core_ops -> record_full_*.
> 
> GDB prevents using static names duplicated across files.  (Maybe it comes from
> the time before "ambiguous linespec" start to put breakpoints on all of them.)

I also renamed struct, macro, and variable names to be consistent. To make this
easier to review, I'm doing this renaming in a separate patch. It's quite a lot.

[...]

> > +  /* Deprecate the old version without "full" prefix.  */
> > +  c = add_alias_cmd ("restore", "full restore", class_obscure, 1,
> > +		     &record_cmdlist);
> > +  set_cmd_completer (c, filename_completer);
> > +  deprecate_cmd (c, "record full restore");
> 
> This (and all add_alias_cmd below) don't display the warning as discussed
> before.
> 
> I guess we can keep it as is as the missing warning is tracked
> 	deprecated_cmd_warning does not work for prefixed commands
> 	http://sourceware.org/bugzilla/show_bug.cgi?id=15104
> and the only command where it is most visible is "target record" which you
> have successfully workarounded in the patchset.

Sounds good. Thanks.

> a bit offtopic: 'git am' somehow broken on applicating the patchset (but later
> it went OK by hand), it would be easier to have it in a public GIT branch,
> possibly in
> 	http://sourceware.org/gdb/wiki/ArcherBranchManagement
> needing an account http://sourceware.org/cgi-bin/pdw/ps_form.cgi which you are
> going to get for the later check-in anyway; or you could use github or some
> such site.

Can I use my sourceware account also for Archer or do I need to request a new
account for it?

Is it OK to rebase an archer branch? I do this to incorporate review comments to
maintain a reviewable patch series.

I would still send patches, right? And they are still expected to apply on gdb's
Master, right?

The next patch I'm adding will rely on the btrace series plus the patches in this
series. It will not apply without btrace below, anymore. That's quite a big series
for adding a small patch. Would Archer help me, there?

How would I send patches so that people know what to review and at the
same time allow them to apply the patch and try the changes?

[...]

I interpret your replies to the other patches in the series such that you
approved http://sourceware.org/ml/gdb-patches/2013-02/msg00217.html,
http://sourceware.org/ml/gdb-patches/2013-02/msg00216.html, and
http://sourceware.org/ml/gdb-patches/2013-02/msg00212.html. I added
comments to the commit messages.

Regards,
Markus.
Intel GmbH
Dornacher Strasse 1
85622 Feldkirchen/Muenchen, Deutschland
Sitz der Gesellschaft: Feldkirchen bei Muenchen
Geschaeftsfuehrer: Christian Lamprechter, Hannes Schwaderer, Douglas Lusk
Registergericht: Muenchen HRB 47456
Ust.-IdNr./VAT Registration No.: DE129385895
Citibank Frankfurt a.M. (BLZ 502 109 00) 600119052


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [rfc 3/5] record: make it build again
  2013-02-11 13:41     ` Metzger, Markus T
@ 2013-02-11 14:15       ` Jan Kratochvil
  2013-02-11 17:13         ` [draft patch] <unavailable> unwinder for btrace [Re: [rfc 3/5] record: make it build again] Jan Kratochvil
  0 siblings, 1 reply; 23+ messages in thread
From: Jan Kratochvil @ 2013-02-11 14:15 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: gdb-patches, markus.t.metzger

Hi Markus,

On Mon, 11 Feb 2013 14:41:38 +0100, Metzger, Markus T wrote:
> Can I use my sourceware account also for Archer or do I need to request a new
> account for it?

Just try it to push some new branch there, I do not know which groups you
exist in.  I guess archer.git has r/w access permitted for the binutils+gdb
group.


> Is it OK to rebase an archer branch? I do this to incorporate review comments to
> maintain a reviewable patch series.

You can do anything you want in archer-markus-* (or choose your preferred name
instead of "markus") branches on archer.git.  It is good to write a note about
your existing new branch(es) to:
	http://sourceware.org/gdb/wiki/ArcherBranchManagement


> I would still send patches, right? And they are still expected to apply on gdb's
> Master, right?

Yes, yes.  archer.git is just some additional space for convenience of the
communication, archer.git does not replace in any way existing GDB
contribution processes - those rely exclusively on the mailing list.


> The next patch I'm adding will rely on the btrace series plus the patches in this
> series. It will not apply without btrace below, anymore. That's quite a big series
> for adding a small patch. Would Archer help me, there?

With many pending patches it is no longer easy for mail readers to try the
patches, except for the dry (mail-only) review of the patches.

So it would be nice to also provide a branch with all the patches applied.


> How would I send patches so that people know what to review and at the
> same time allow them to apply the patch and try the changes?

I usually provide archer.git branch with everything applied together (one
could do better but I find it good enough that way).

The patches are sent normally, as you already do, each separately.

I do not find archer.git to be too magic, just do what you find convenient.


> I interpret your replies to the other patches in the series such that you
> approved http://sourceware.org/ml/gdb-patches/2013-02/msg00217.html,
> http://sourceware.org/ml/gdb-patches/2013-02/msg00216.html, and
> http://sourceware.org/ml/gdb-patches/2013-02/msg00212.html. I added
> comments to the commit messages.

Yes, those are approved.

Just as one should not check-in patches which do not make sense on their own
it all should go in only together with the btrace patchset.

That is we should not change "target record" -> "target record-full" without
checking in also btrace.


FYI I have some draft <unavailable>-limitation unwinder for btrace here today,
to post it later.


Thanks,
Jan


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [draft patch] <unavailable> unwinder for btrace  [Re: [rfc 3/5] record: make it build again]
  2013-02-11 14:15       ` Jan Kratochvil
@ 2013-02-11 17:13         ` Jan Kratochvil
  2013-02-11 21:24           ` Tom Tromey
                             ` (2 more replies)
  0 siblings, 3 replies; 23+ messages in thread
From: Jan Kratochvil @ 2013-02-11 17:13 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: gdb-patches, markus.t.metzger

On Mon, 11 Feb 2013 15:14:51 +0100, Jan Kratochvil wrote:
> FYI I have some draft <unavailable>-limitation unwinder for btrace here today,
> to post it later.

./gdb -q ./gdb -ex start -ex record -ex stepi -ex reverse-stepi -ex bt -ex q

No more reverse-execution history.
main (argc=<unavailable>, argv=<unavailable>) at gdb.c:29
29    memset (&args, 0, sizeof args);
#0  main (argc=<unavailable>, argv=<unavailable>) at gdb.c:29
Backtrace stopped: not enough registers or memory available to unwind further


That get_frame_unwind_stop_reason "optimization" to avoid calling an unwinder
was present already in the initial post:
	[rfc, frame] Add backtrace stop reasons
	http://sourceware.org/ml/gdb-patches/2006-08/msg00129.html
	Message-ID: <20060819154646.GA25238@nevyn.them.org>
I don't know its real reason but it is needed for this patch otherwise:
main (argc=<error reading variable: can't compute CFA for this frame>, argv=<error reading variable: can't compute CFA for this frame>) at gdb.c:29


For btrace you will need ptid_t of the unwound thread as the RECORD_IS_REPLAY
flag is apparently irrelevant there.  I find inferior_ptid to use there
acceptable despite it is discouraged now, passing there ptid_t from the
unwinder framework needs some larger changes.

No real regressions on {x86_64,x86_64-m32,i686}-fedora19pre-linux-gnu and in
gdbserver mode, with the patch as is the various *reverse* tests apparently
FAIL due to the <unavailable> values (both displayed and implicitly missing).

There should be a testcase but that will be dependent upon the btrace
integration.


Regards,
Jan


gdb/
2013-02-11  Jan Kratochvil  <jan.kratochvil@redhat.com>

	New record unwinder reporting <unavailable>.
	* dwarf2-frame.c (dwarf2_frame_cfa): Move UNWIND_UNAVAILABLE check
	earlier.
	* frame-unwind.c: Include target.h.
	(frame_unwind_try_unwinder): New function with code from ...
	(frame_unwind_find_by_frame): ... here.  New variable
	unwinder_from_target, call also target_get_unwinder and
	frame_unwind_try_unwinder for it.
	* frame.c (get_frame_unwind_stop_reason): Unconditionally call
	get_prev_frame_1.
	* record.c: Include frame-unwind.h.
	(record_frame_unwind_stop_reason, record_frame_this_id)
	(record_frame_prev_register, record_frame_sniffer, record_frame_unwind):
	New.
	(init_record_ops, init_record_core_ops): Install it.
	* target.c (target_get_unwinder): New.
	* target.h (struct target_ops): New field to_get_unwinder.
	(target_get_unwinder): New declaration.

diff --git a/gdb/dwarf2-frame.c b/gdb/dwarf2-frame.c
index ec4edfa..45be107 100644
--- a/gdb/dwarf2-frame.c
+++ b/gdb/dwarf2-frame.c
@@ -1497,16 +1497,16 @@ dwarf2_frame_cfa (struct frame_info *this_frame)
 {
   while (get_frame_type (this_frame) == INLINE_FRAME)
     this_frame = get_prev_frame (this_frame);
+  if (get_frame_unwind_stop_reason (this_frame) == UNWIND_UNAVAILABLE)
+    throw_error (NOT_AVAILABLE_ERROR,
+		 _("can't compute CFA for this frame: "
+		   "required registers or memory are unavailable"));
   /* This restriction could be lifted if other unwinders are known to
      compute the frame base in a way compatible with the DWARF
      unwinder.  */
   if (!frame_unwinder_is (this_frame, &dwarf2_frame_unwind)
       && !frame_unwinder_is (this_frame, &dwarf2_tailcall_frame_unwind))
     error (_("can't compute CFA for this frame"));
-  if (get_frame_unwind_stop_reason (this_frame) == UNWIND_UNAVAILABLE)
-    throw_error (NOT_AVAILABLE_ERROR,
-		 _("can't compute CFA for this frame: "
-		   "required registers or memory are unavailable"));
   return get_frame_base (this_frame);
 }
 \f
diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
index b66febf..cf33147 100644
--- a/gdb/frame-unwind.c
+++ b/gdb/frame-unwind.c
@@ -27,6 +27,7 @@
 #include "exceptions.h"
 #include "gdb_assert.h"
 #include "gdb_obstack.h"
+#include "target.h"
 
 static struct gdbarch_data *frame_unwind_data;
 
@@ -88,6 +89,48 @@ frame_unwind_append_unwinder (struct gdbarch *gdbarch,
   (*ip)->unwinder = unwinder;
 }
 
+/* Call SNIFFER from UNWINDER.  If it succeeded set UNWINDER for
+   THIS_FRAME and return 1.  Otherwise the function keeps THIS_FRAME
+   unchanged and returns 0.  */
+
+static int
+frame_unwind_try_unwinder (struct frame_info *this_frame, void **this_cache,
+			   const struct frame_unwind *unwinder)
+{
+  struct cleanup *old_cleanup;
+  volatile struct gdb_exception ex;
+  int res = 0;
+
+  old_cleanup = frame_prepare_for_sniffer (this_frame, unwinder);
+
+  TRY_CATCH (ex, RETURN_MASK_ERROR)
+    {
+      res = unwinder->sniffer (unwinder, this_frame, this_cache);
+    }
+  if (ex.reason < 0 && ex.error == NOT_AVAILABLE_ERROR)
+    {
+      /* This usually means that not even the PC is available,
+	 thus most unwinders aren't able to determine if they're
+	 the best fit.  Keep trying.  Fallback prologue unwinders
+	 should always accept the frame.  */
+      do_cleanups (old_cleanup);
+      return 0;
+    }
+  else if (ex.reason < 0)
+    throw_exception (ex);
+  else if (res)
+    {
+      discard_cleanups (old_cleanup);
+      return 1;
+    }
+  else
+    {
+      do_cleanups (old_cleanup);
+      return 0;
+    }
+  gdb_assert_not_reached ("frame_unwind_try_unwinder");
+}
+
 /* Iterate through sniffers for THIS_FRAME frame until one returns with an
    unwinder implementation.  THIS_FRAME->UNWIND must be NULL, it will get set
    by this function.  Possibly initialize THIS_CACHE.  */
@@ -98,37 +141,18 @@ frame_unwind_find_by_frame (struct frame_info *this_frame, void **this_cache)
   struct gdbarch *gdbarch = get_frame_arch (this_frame);
   struct frame_unwind_table *table = gdbarch_data (gdbarch, frame_unwind_data);
   struct frame_unwind_table_entry *entry;
+  const struct frame_unwind *unwinder_from_target;
+
+  unwinder_from_target = target_get_unwinder ();
+  if (unwinder_from_target != NULL
+      && frame_unwind_try_unwinder (this_frame, this_cache,
+				    unwinder_from_target))
+    return;
 
   for (entry = table->list; entry != NULL; entry = entry->next)
-    {
-      struct cleanup *old_cleanup;
-      volatile struct gdb_exception ex;
-      int res = 0;
-
-      old_cleanup = frame_prepare_for_sniffer (this_frame, entry->unwinder);
-
-      TRY_CATCH (ex, RETURN_MASK_ERROR)
-	{
-	  res = entry->unwinder->sniffer (entry->unwinder, this_frame,
-					  this_cache);
-	}
-      if (ex.reason < 0 && ex.error == NOT_AVAILABLE_ERROR)
-	{
-	  /* This usually means that not even the PC is available,
-	     thus most unwinders aren't able to determine if they're
-	     the best fit.  Keep trying.  Fallback prologue unwinders
-	     should always accept the frame.  */
-	}
-      else if (ex.reason < 0)
-	throw_exception (ex);
-      else if (res)
-        {
-          discard_cleanups (old_cleanup);
-          return;
-        }
+    if (frame_unwind_try_unwinder (this_frame, this_cache, entry->unwinder))
+      return;
 
-      do_cleanups (old_cleanup);
-    }
   internal_error (__FILE__, __LINE__, _("frame_unwind_find_by_frame failed"));
 }
 
diff --git a/gdb/frame.c b/gdb/frame.c
index 0821b6e..8fc1ca9 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -2357,13 +2357,10 @@ get_frame_sp (struct frame_info *this_frame)
 enum unwind_stop_reason
 get_frame_unwind_stop_reason (struct frame_info *frame)
 {
-  /* If we haven't tried to unwind past this point yet, then assume
-     that unwinding would succeed.  */
-  if (frame->prev_p == 0)
-    return UNWIND_NO_REASON;
+  /* Fill-in STOP_REASON.  */
+  get_prev_frame_1 (frame);
+  gdb_assert (frame->prev_p);
 
-  /* Otherwise, we set a reason when we succeeded (or failed) to
-     unwind.  */
   return frame->stop_reason;
 }
 
diff --git a/gdb/record.c b/gdb/record.c
index 1a68738..dffce98 100644
--- a/gdb/record.c
+++ b/gdb/record.c
@@ -34,6 +34,7 @@
 #include "inf-loop.h"
 #include "gdb_bfd.h"
 #include "observer.h"
+#include "frame-unwind.h"
 
 #include <signal.h>
 
@@ -1953,6 +1954,59 @@ record_execution_direction (void)
   return record_execution_dir;
 }
 
+/* Implement stop_reason method for record_frame_unwind.  */
+
+static enum unwind_stop_reason
+record_frame_unwind_stop_reason (struct frame_info *this_frame,
+				 void **this_cache)
+{
+  return UNWIND_UNAVAILABLE;
+}
+
+/* Implement this_id method for record_frame_unwind.  */
+
+static void
+record_frame_this_id (struct frame_info *this_frame, void **this_cache,
+		      struct frame_id *this_id)
+{
+  /* Leave there the outer_frame_id value.  */
+}
+
+/* Implement prev_register method for record_frame_unwind.  */
+
+static struct value *
+record_frame_prev_register (struct frame_info *this_frame, void **this_cache,
+			    int regnum)
+{
+  throw_error (NOT_AVAILABLE_ERROR,
+	       _("Registers are not available in btrace record history"));
+}
+
+/* Implement sniffer method for record_frame_unwind.  */
+
+static int
+record_frame_sniffer (const struct frame_unwind *self,
+		      struct frame_info *this_frame, void **this_cache)
+{
+  return RECORD_IS_REPLAY;
+}
+
+/* btrace recording does not store previous memory content, neither the stack
+   frames content.  Any unwinding would return errorneous results as the stack
+   contents no longer matches the changed PC value restored from history.
+   Therefore this unwinder reports any possibly unwound registers as
+   <unavailable>.  */
+
+static const struct frame_unwind record_frame_unwind =
+{
+  NORMAL_FRAME,
+  record_frame_unwind_stop_reason,
+  record_frame_this_id,
+  record_frame_prev_register,
+  NULL,
+  record_frame_sniffer
+};
+
 static void
 init_record_ops (void)
 {
@@ -1984,6 +2038,7 @@ init_record_ops (void)
   record_ops.to_can_async_p = record_can_async_p;
   record_ops.to_is_async_p = record_is_async_p;
   record_ops.to_execution_direction = record_execution_direction;
+  record_ops.to_get_unwinder = &record_frame_unwind;
   record_ops.to_magic = OPS_MAGIC;
 }
 
@@ -2209,6 +2264,7 @@ init_record_core_ops (void)
   record_core_ops.to_can_async_p = record_can_async_p;
   record_core_ops.to_is_async_p = record_is_async_p;
   record_core_ops.to_execution_direction = record_execution_direction;
+  record_core_ops.to_get_unwinder = &record_frame_unwind;
   record_core_ops.to_magic = OPS_MAGIC;
 }
 
diff --git a/gdb/target.c b/gdb/target.c
index 9d8bf6e..28f00af 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -4147,6 +4147,20 @@ target_ranged_break_num_registers (void)
   return -1;
 }
 
+/* See target.h.  */
+
+const struct frame_unwind *
+target_get_unwinder (void)
+{
+  struct target_ops *t;
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    if (t->to_get_unwinder != NULL)
+      return t->to_get_unwinder;
+
+  return NULL;
+}
+
 static void
 debug_to_prepare_to_store (struct regcache *regcache)
 {
diff --git a/gdb/target.h b/gdb/target.h
index 1971265..95bfb47 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -857,6 +857,10 @@ struct target_ops
     /* Is the target able to use agent in current state?  */
     int (*to_can_use_agent) (void);
 
+    /* This unwinder is tried before any other arch unwinders.  Use NULL if it
+       is not used.  */
+    const struct frame_unwind *to_get_unwinder;
+
     int to_magic;
     /* Need sub-structure for target machine related rather than comm related?
      */
@@ -1735,6 +1739,9 @@ extern char *target_fileio_read_stralloc (const char *filename);
 
 extern int target_core_of_thread (ptid_t ptid);
 
+/* See to_get_unwinder in struct target_ops.  */
+extern const struct frame_unwind *target_get_unwinder (void);
+
 /* Verify that the memory in the [MEMADDR, MEMADDR+SIZE) range matches
    the contents of [DATA,DATA+SIZE).  Returns 1 if there's a match, 0
    if there's a mismatch, and -1 if an error is encountered while


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [draft patch] <unavailable> unwinder for btrace  [Re: [rfc 3/5] record: make it build again]
  2013-02-11 17:13         ` [draft patch] <unavailable> unwinder for btrace [Re: [rfc 3/5] record: make it build again] Jan Kratochvil
@ 2013-02-11 21:24           ` Tom Tromey
  2013-02-13  7:35           ` Metzger, Markus T
  2013-03-27 18:09           ` Metzger, Markus T
  2 siblings, 0 replies; 23+ messages in thread
From: Tom Tromey @ 2013-02-11 21:24 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: Metzger, Markus T, gdb-patches, markus.t.metzger

>>>>> "Jan" == Jan Kratochvil <jan.kratochvil@redhat.com> writes:

Jan> 2013-02-11  Jan Kratochvil  <jan.kratochvil@redhat.com>
Jan> 	New record unwinder reporting <unavailable>.

This all looked pretty reasonable to me.

Tom


^ permalink raw reply	[flat|nested] 23+ messages in thread

* RE: [draft patch] <unavailable> unwinder for btrace  [Re: [rfc 3/5] record: make it build again]
  2013-02-11 17:13         ` [draft patch] <unavailable> unwinder for btrace [Re: [rfc 3/5] record: make it build again] Jan Kratochvil
  2013-02-11 21:24           ` Tom Tromey
@ 2013-02-13  7:35           ` Metzger, Markus T
  2013-02-13  7:58             ` Jan Kratochvil
  2013-03-27 18:09           ` Metzger, Markus T
  2 siblings, 1 reply; 23+ messages in thread
From: Metzger, Markus T @ 2013-02-13  7:35 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches, markus.t.metzger

> -----Original Message-----
> From: Jan Kratochvil [mailto:jan.kratochvil@redhat.com]
> Sent: Monday, February 11, 2013 6:13 PM

Thanks, Jan,

I'll look at it once the "record disassembly" and "record list" commands are working for record-btrace.

Did you commit this to some archer branch, as well?

Thanks,
Markus.


> To: Metzger, Markus T
> Cc: gdb-patches@sourceware.org; markus.t.metzger@gmail.com
> Subject: [draft patch] <unavailable> unwinder for btrace [Re: [rfc 3/5] record: make it build again]
> 
> On Mon, 11 Feb 2013 15:14:51 +0100, Jan Kratochvil wrote:
> > FYI I have some draft <unavailable>-limitation unwinder for btrace here today,
> > to post it later.
> 
> ./gdb -q ./gdb -ex start -ex record -ex stepi -ex reverse-stepi -ex bt -ex q
> 
> No more reverse-execution history.
> main (argc=<unavailable>, argv=<unavailable>) at gdb.c:29
> 29    memset (&args, 0, sizeof args);
> #0  main (argc=<unavailable>, argv=<unavailable>) at gdb.c:29
> Backtrace stopped: not enough registers or memory available to unwind further
> 
> 
> That get_frame_unwind_stop_reason "optimization" to avoid calling an unwinder
> was present already in the initial post:
> 	[rfc, frame] Add backtrace stop reasons
> 	http://sourceware.org/ml/gdb-patches/2006-08/msg00129.html
> 	Message-ID: <20060819154646.GA25238@nevyn.them.org>
> I don't know its real reason but it is needed for this patch otherwise:
> main (argc=<error reading variable: can't compute CFA for this frame>, argv=<error reading variable: can't compute CFA for this
> frame>) at gdb.c:29
> 
> 
> For btrace you will need ptid_t of the unwound thread as the RECORD_IS_REPLAY
> flag is apparently irrelevant there.  I find inferior_ptid to use there
> acceptable despite it is discouraged now, passing there ptid_t from the
> unwinder framework needs some larger changes.
> 
> No real regressions on {x86_64,x86_64-m32,i686}-fedora19pre-linux-gnu and in
> gdbserver mode, with the patch as is the various *reverse* tests apparently
> FAIL due to the <unavailable> values (both displayed and implicitly missing).
> 
> There should be a testcase but that will be dependent upon the btrace
> integration.
> 
> 
> Regards,
> Jan
> 
> 
> gdb/
> 2013-02-11  Jan Kratochvil  <jan.kratochvil@redhat.com>
> 
> 	New record unwinder reporting <unavailable>.
> 	* dwarf2-frame.c (dwarf2_frame_cfa): Move UNWIND_UNAVAILABLE check
> 	earlier.
> 	* frame-unwind.c: Include target.h.
> 	(frame_unwind_try_unwinder): New function with code from ...
> 	(frame_unwind_find_by_frame): ... here.  New variable
> 	unwinder_from_target, call also target_get_unwinder and
> 	frame_unwind_try_unwinder for it.
> 	* frame.c (get_frame_unwind_stop_reason): Unconditionally call
> 	get_prev_frame_1.
> 	* record.c: Include frame-unwind.h.
> 	(record_frame_unwind_stop_reason, record_frame_this_id)
> 	(record_frame_prev_register, record_frame_sniffer, record_frame_unwind):
> 	New.
> 	(init_record_ops, init_record_core_ops): Install it.
> 	* target.c (target_get_unwinder): New.
> 	* target.h (struct target_ops): New field to_get_unwinder.
> 	(target_get_unwinder): New declaration.
> 
> diff --git a/gdb/dwarf2-frame.c b/gdb/dwarf2-frame.c
> index ec4edfa..45be107 100644
> --- a/gdb/dwarf2-frame.c
> +++ b/gdb/dwarf2-frame.c
> @@ -1497,16 +1497,16 @@ dwarf2_frame_cfa (struct frame_info *this_frame)
>  {
>    while (get_frame_type (this_frame) == INLINE_FRAME)
>      this_frame = get_prev_frame (this_frame);
> +  if (get_frame_unwind_stop_reason (this_frame) == UNWIND_UNAVAILABLE)
> +    throw_error (NOT_AVAILABLE_ERROR,
> +		 _("can't compute CFA for this frame: "
> +		   "required registers or memory are unavailable"));
>    /* This restriction could be lifted if other unwinders are known to
>       compute the frame base in a way compatible with the DWARF
>       unwinder.  */
>    if (!frame_unwinder_is (this_frame, &dwarf2_frame_unwind)
>        && !frame_unwinder_is (this_frame, &dwarf2_tailcall_frame_unwind))
>      error (_("can't compute CFA for this frame"));
> -  if (get_frame_unwind_stop_reason (this_frame) == UNWIND_UNAVAILABLE)
> -    throw_error (NOT_AVAILABLE_ERROR,
> -		 _("can't compute CFA for this frame: "
> -		   "required registers or memory are unavailable"));
>    return get_frame_base (this_frame);
>  }
>  

> diff --git a/gdb/frame-unwind.c b/gdb/frame-unwind.c
> index b66febf..cf33147 100644
> --- a/gdb/frame-unwind.c
> +++ b/gdb/frame-unwind.c
> @@ -27,6 +27,7 @@
>  #include "exceptions.h"
>  #include "gdb_assert.h"
>  #include "gdb_obstack.h"
> +#include "target.h"
> 
>  static struct gdbarch_data *frame_unwind_data;
> 
> @@ -88,6 +89,48 @@ frame_unwind_append_unwinder (struct gdbarch *gdbarch,
>    (*ip)->unwinder = unwinder;
>  }
> 
> +/* Call SNIFFER from UNWINDER.  If it succeeded set UNWINDER for
> +   THIS_FRAME and return 1.  Otherwise the function keeps THIS_FRAME
> +   unchanged and returns 0.  */
> +
> +static int
> +frame_unwind_try_unwinder (struct frame_info *this_frame, void **this_cache,
> +			   const struct frame_unwind *unwinder)
> +{
> +  struct cleanup *old_cleanup;
> +  volatile struct gdb_exception ex;
> +  int res = 0;
> +
> +  old_cleanup = frame_prepare_for_sniffer (this_frame, unwinder);
> +
> +  TRY_CATCH (ex, RETURN_MASK_ERROR)
> +    {
> +      res = unwinder->sniffer (unwinder, this_frame, this_cache);
> +    }
> +  if (ex.reason < 0 && ex.error == NOT_AVAILABLE_ERROR)
> +    {
> +      /* This usually means that not even the PC is available,
> +	 thus most unwinders aren't able to determine if they're
> +	 the best fit.  Keep trying.  Fallback prologue unwinders
> +	 should always accept the frame.  */
> +      do_cleanups (old_cleanup);
> +      return 0;
> +    }
> +  else if (ex.reason < 0)
> +    throw_exception (ex);
> +  else if (res)
> +    {
> +      discard_cleanups (old_cleanup);
> +      return 1;
> +    }
> +  else
> +    {
> +      do_cleanups (old_cleanup);
> +      return 0;
> +    }
> +  gdb_assert_not_reached ("frame_unwind_try_unwinder");
> +}
> +
>  /* Iterate through sniffers for THIS_FRAME frame until one returns with an
>     unwinder implementation.  THIS_FRAME->UNWIND must be NULL, it will get set
>     by this function.  Possibly initialize THIS_CACHE.  */
> @@ -98,37 +141,18 @@ frame_unwind_find_by_frame (struct frame_info *this_frame, void **this_cache)
>    struct gdbarch *gdbarch = get_frame_arch (this_frame);
>    struct frame_unwind_table *table = gdbarch_data (gdbarch, frame_unwind_data);
>    struct frame_unwind_table_entry *entry;
> +  const struct frame_unwind *unwinder_from_target;
> +
> +  unwinder_from_target = target_get_unwinder ();
> +  if (unwinder_from_target != NULL
> +      && frame_unwind_try_unwinder (this_frame, this_cache,
> +				    unwinder_from_target))
> +    return;
> 
>    for (entry = table->list; entry != NULL; entry = entry->next)
> -    {
> -      struct cleanup *old_cleanup;
> -      volatile struct gdb_exception ex;
> -      int res = 0;
> -
> -      old_cleanup = frame_prepare_for_sniffer (this_frame, entry->unwinder);
> -
> -      TRY_CATCH (ex, RETURN_MASK_ERROR)
> -	{
> -	  res = entry->unwinder->sniffer (entry->unwinder, this_frame,
> -					  this_cache);
> -	}
> -      if (ex.reason < 0 && ex.error == NOT_AVAILABLE_ERROR)
> -	{
> -	  /* This usually means that not even the PC is available,
> -	     thus most unwinders aren't able to determine if they're
> -	     the best fit.  Keep trying.  Fallback prologue unwinders
> -	     should always accept the frame.  */
> -	}
> -      else if (ex.reason < 0)
> -	throw_exception (ex);
> -      else if (res)
> -        {
> -          discard_cleanups (old_cleanup);
> -          return;
> -        }
> +    if (frame_unwind_try_unwinder (this_frame, this_cache, entry->unwinder))
> +      return;
> 
> -      do_cleanups (old_cleanup);
> -    }
>    internal_error (__FILE__, __LINE__, _("frame_unwind_find_by_frame failed"));
>  }
> 
> diff --git a/gdb/frame.c b/gdb/frame.c
> index 0821b6e..8fc1ca9 100644
> --- a/gdb/frame.c
> +++ b/gdb/frame.c
> @@ -2357,13 +2357,10 @@ get_frame_sp (struct frame_info *this_frame)
>  enum unwind_stop_reason
>  get_frame_unwind_stop_reason (struct frame_info *frame)
>  {
> -  /* If we haven't tried to unwind past this point yet, then assume
> -     that unwinding would succeed.  */
> -  if (frame->prev_p == 0)
> -    return UNWIND_NO_REASON;
> +  /* Fill-in STOP_REASON.  */
> +  get_prev_frame_1 (frame);
> +  gdb_assert (frame->prev_p);
> 
> -  /* Otherwise, we set a reason when we succeeded (or failed) to
> -     unwind.  */
>    return frame->stop_reason;
>  }
> 
> diff --git a/gdb/record.c b/gdb/record.c
> index 1a68738..dffce98 100644
> --- a/gdb/record.c
> +++ b/gdb/record.c
> @@ -34,6 +34,7 @@
>  #include "inf-loop.h"
>  #include "gdb_bfd.h"
>  #include "observer.h"
> +#include "frame-unwind.h"
> 
>  #include <signal.h>
> 
> @@ -1953,6 +1954,59 @@ record_execution_direction (void)
>    return record_execution_dir;
>  }
> 
> +/* Implement stop_reason method for record_frame_unwind.  */
> +
> +static enum unwind_stop_reason
> +record_frame_unwind_stop_reason (struct frame_info *this_frame,
> +				 void **this_cache)
> +{
> +  return UNWIND_UNAVAILABLE;
> +}
> +
> +/* Implement this_id method for record_frame_unwind.  */
> +
> +static void
> +record_frame_this_id (struct frame_info *this_frame, void **this_cache,
> +		      struct frame_id *this_id)
> +{
> +  /* Leave there the outer_frame_id value.  */
> +}
> +
> +/* Implement prev_register method for record_frame_unwind.  */
> +
> +static struct value *
> +record_frame_prev_register (struct frame_info *this_frame, void **this_cache,
> +			    int regnum)
> +{
> +  throw_error (NOT_AVAILABLE_ERROR,
> +	       _("Registers are not available in btrace record history"));
> +}
> +
> +/* Implement sniffer method for record_frame_unwind.  */
> +
> +static int
> +record_frame_sniffer (const struct frame_unwind *self,
> +		      struct frame_info *this_frame, void **this_cache)
> +{
> +  return RECORD_IS_REPLAY;
> +}
> +
> +/* btrace recording does not store previous memory content, neither the stack
> +   frames content.  Any unwinding would return errorneous results as the stack
> +   contents no longer matches the changed PC value restored from history.
> +   Therefore this unwinder reports any possibly unwound registers as
> +   <unavailable>.  */
> +
> +static const struct frame_unwind record_frame_unwind =
> +{
> +  NORMAL_FRAME,
> +  record_frame_unwind_stop_reason,
> +  record_frame_this_id,
> +  record_frame_prev_register,
> +  NULL,
> +  record_frame_sniffer
> +};
> +
>  static void
>  init_record_ops (void)
>  {
> @@ -1984,6 +2038,7 @@ init_record_ops (void)
>    record_ops.to_can_async_p = record_can_async_p;
>    record_ops.to_is_async_p = record_is_async_p;
>    record_ops.to_execution_direction = record_execution_direction;
> +  record_ops.to_get_unwinder = &record_frame_unwind;
>    record_ops.to_magic = OPS_MAGIC;
>  }
> 
> @@ -2209,6 +2264,7 @@ init_record_core_ops (void)
>    record_core_ops.to_can_async_p = record_can_async_p;
>    record_core_ops.to_is_async_p = record_is_async_p;
>    record_core_ops.to_execution_direction = record_execution_direction;
> +  record_core_ops.to_get_unwinder = &record_frame_unwind;
>    record_core_ops.to_magic = OPS_MAGIC;
>  }
> 
> diff --git a/gdb/target.c b/gdb/target.c
> index 9d8bf6e..28f00af 100644
> --- a/gdb/target.c
> +++ b/gdb/target.c
> @@ -4147,6 +4147,20 @@ target_ranged_break_num_registers (void)
>    return -1;
>  }
> 
> +/* See target.h.  */
> +
> +const struct frame_unwind *
> +target_get_unwinder (void)
> +{
> +  struct target_ops *t;
> +
> +  for (t = current_target.beneath; t != NULL; t = t->beneath)
> +    if (t->to_get_unwinder != NULL)
> +      return t->to_get_unwinder;
> +
> +  return NULL;
> +}
> +
>  static void
>  debug_to_prepare_to_store (struct regcache *regcache)
>  {
> diff --git a/gdb/target.h b/gdb/target.h
> index 1971265..95bfb47 100644
> --- a/gdb/target.h
> +++ b/gdb/target.h
> @@ -857,6 +857,10 @@ struct target_ops
>      /* Is the target able to use agent in current state?  */
>      int (*to_can_use_agent) (void);
> 
> +    /* This unwinder is tried before any other arch unwinders.  Use NULL if it
> +       is not used.  */
> +    const struct frame_unwind *to_get_unwinder;
> +
>      int to_magic;
>      /* Need sub-structure for target machine related rather than comm related?
>       */
> @@ -1735,6 +1739,9 @@ extern char *target_fileio_read_stralloc (const char *filename);
> 
>  extern int target_core_of_thread (ptid_t ptid);
> 
> +/* See to_get_unwinder in struct target_ops.  */
> +extern const struct frame_unwind *target_get_unwinder (void);
> +
>  /* Verify that the memory in the [MEMADDR, MEMADDR+SIZE) range matches
>     the contents of [DATA,DATA+SIZE).  Returns 1 if there's a match, 0
>     if there's a mismatch, and -1 if an error is encountered while
Intel GmbH
Dornacher Strasse 1
85622 Feldkirchen/Muenchen, Deutschland
Sitz der Gesellschaft: Feldkirchen bei Muenchen
Geschaeftsfuehrer: Christian Lamprechter, Hannes Schwaderer, Douglas Lusk
Registergericht: Muenchen HRB 47456
Ust.-IdNr./VAT Registration No.: DE129385895
Citibank Frankfurt a.M. (BLZ 502 109 00) 600119052


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [draft patch] <unavailable> unwinder for btrace  [Re: [rfc 3/5] record: make it build again]
  2013-02-13  7:35           ` Metzger, Markus T
@ 2013-02-13  7:58             ` Jan Kratochvil
  2013-02-13  8:08               ` Metzger, Markus T
  0 siblings, 1 reply; 23+ messages in thread
From: Jan Kratochvil @ 2013-02-13  7:58 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: gdb-patches, markus.t.metzger

On Wed, 13 Feb 2013 08:32:59 +0100, Metzger, Markus T wrote:
> Did you commit this to some archer branch, as well?

No... Is it more convenient that way?  This is a single patch which applies to
FSF GDB HEAD, I find a burder to make a branch for very every patch posted.
Branches are useful for patch series and/or depenencies between multiple
patches.


Thanks,
Jan


^ permalink raw reply	[flat|nested] 23+ messages in thread

* RE: [draft patch] <unavailable> unwinder for btrace  [Re: [rfc 3/5] record: make it build again]
  2013-02-13  7:58             ` Jan Kratochvil
@ 2013-02-13  8:08               ` Metzger, Markus T
  0 siblings, 0 replies; 23+ messages in thread
From: Metzger, Markus T @ 2013-02-13  8:08 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches, markus.t.metzger

> -----Original Message-----
> From: Jan Kratochvil [mailto:jan.kratochvil@redhat.com]
> Sent: Wednesday, February 13, 2013 8:58 AM
> To: Metzger, Markus T
> Cc: gdb-patches@sourceware.org; markus.t.metzger@gmail.com
> Subject: Re: [draft patch] <unavailable> unwinder for btrace [Re: [rfc 3/5] record: make it build again]
> 
> On Wed, 13 Feb 2013 08:32:59 +0100, Metzger, Markus T wrote:
> > Did you commit this to some archer branch, as well?
> 
> No... Is it more convenient that way?  This is a single patch which applies to
> FSF GDB HEAD, I find a burder to make a branch for very every patch posted.
> Branches are useful for patch series and/or depenencies between multiple
> patches.

That's OK. I was just asking.

Markus.
Intel GmbH
Dornacher Strasse 1
85622 Feldkirchen/Muenchen, Deutschland
Sitz der Gesellschaft: Feldkirchen bei Muenchen
Geschaeftsfuehrer: Christian Lamprechter, Hannes Schwaderer, Douglas Lusk
Registergericht: Muenchen HRB 47456
Ust.-IdNr./VAT Registration No.: DE129385895
Citibank Frankfurt a.M. (BLZ 502 109 00) 600119052


^ permalink raw reply	[flat|nested] 23+ messages in thread

* RE: [draft patch] <unavailable> unwinder for btrace  [Re: [rfc 3/5] record: make it build again]
  2013-02-11 17:13         ` [draft patch] <unavailable> unwinder for btrace [Re: [rfc 3/5] record: make it build again] Jan Kratochvil
  2013-02-11 21:24           ` Tom Tromey
  2013-02-13  7:35           ` Metzger, Markus T
@ 2013-03-27 18:09           ` Metzger, Markus T
  2013-03-28 12:38             ` Markus Metzger
  2 siblings, 1 reply; 23+ messages in thread
From: Metzger, Markus T @ 2013-03-27 18:09 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches, markus.t.metzger

> -----Original Message-----
> From: Jan Kratochvil [mailto:jan.kratochvil@redhat.com]
> Sent: Monday, February 11, 2013 6:13 PM

 [...]

> gdb/
> 2013-02-11  Jan Kratochvil  <jan.kratochvil@redhat.com>
> 
> 	New record unwinder reporting <unavailable>.
> 	* dwarf2-frame.c (dwarf2_frame_cfa): Move UNWIND_UNAVAILABLE check
> 	earlier.
> 	* frame-unwind.c: Include target.h.
> 	(frame_unwind_try_unwinder): New function with code from ...
> 	(frame_unwind_find_by_frame): ... here.  New variable
> 	unwinder_from_target, call also target_get_unwinder and
> 	frame_unwind_try_unwinder for it.
> 	* frame.c (get_frame_unwind_stop_reason): Unconditionally call
> 	get_prev_frame_1.
> 	* record.c: Include frame-unwind.h.
> 	(record_frame_unwind_stop_reason, record_frame_this_id)
> 	(record_frame_prev_register, record_frame_sniffer, record_frame_unwind):
> 	New.
> 	(init_record_ops, init_record_core_ops): Install it.
> 	* target.c (target_get_unwinder): New.
> 	* target.h (struct target_ops): New field to_get_unwinder.
> 	(target_get_unwinder): New declaration.

I've been experimenting with this a bit.  It looks like there will always be
a sentinel frame at the very bottom that is reading the registers directly
from the inferior.  I can only hook in at the second frame.

In order to fake the back trace for btrace replay, I would also need to
replace the sentinel frame, since otherwise, the first frame will always
point to the current location.

Am I doing something wrong?

If not, would you please point me to some code that I would need to
touch to replace the sentinel frame?  Would I maybe provide another
target method to allow targets to overwrite the sentinel frame?


On a related but different topic, I added a btrace frame type and
prologue cache.  The cache holds a pointer to some btrace data
structure that is used to compute the fake back trace.  In order to
unwind a btrace frame, I would need to access the next frame's
location in this btrace data structure.

The easiest would be to check for the next frame's type and then
access it's cache - which doesn't work since struct frame_info is
opaque.  I ended up encoding the pointer into the special_addr
of a btrace frame's frame_id - which is somewhat ugly.  Any better
idea?

Also what's the lifetime of a frame_info and frame_id object?
When the branch trace is cleared, any pointers to it will become
stale.

Thanks,
Markus.
Intel GmbH
Dornacher Strasse 1
85622 Feldkirchen/Muenchen, Deutschland
Sitz der Gesellschaft: Feldkirchen bei Muenchen
Geschaeftsfuehrer: Christian Lamprechter, Hannes Schwaderer, Douglas Lusk
Registergericht: Muenchen HRB 47456
Ust.-IdNr./VAT Registration No.: DE129385895
Citibank Frankfurt a.M. (BLZ 502 109 00) 600119052


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [draft patch] <unavailable> unwinder for btrace  [Re: [rfc 3/5] record: make it build again]
  2013-03-27 18:09           ` Metzger, Markus T
@ 2013-03-28 12:38             ` Markus Metzger
       [not found]               ` <20130328062747.GA27157@host2.jankratochvil.net>
  0 siblings, 1 reply; 23+ messages in thread
From: Markus Metzger @ 2013-03-28 12:38 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches, Metzger, Markus T


On Mar 27, 2013, at 16:22 , "Metzger, Markus T" <markus.t.metzger@intel.com> wrote:

>> -----Original Message-----
>> From: Jan Kratochvil [mailto:jan.kratochvil@redhat.com]
>> Sent: Monday, February 11, 2013 6:13 PM
> 
> [...]
> 
>> gdb/
>> 2013-02-11  Jan Kratochvil  <jan.kratochvil@redhat.com>
>> 
>> 	New record unwinder reporting <unavailable>.
>> 	* dwarf2-frame.c (dwarf2_frame_cfa): Move UNWIND_UNAVAILABLE check
>> 	earlier.
>> 	* frame-unwind.c: Include target.h.
>> 	(frame_unwind_try_unwinder): New function with code from ...
>> 	(frame_unwind_find_by_frame): ... here.  New variable
>> 	unwinder_from_target, call also target_get_unwinder and
>> 	frame_unwind_try_unwinder for it.
>> 	* frame.c (get_frame_unwind_stop_reason): Unconditionally call
>> 	get_prev_frame_1.
>> 	* record.c: Include frame-unwind.h.
>> 	(record_frame_unwind_stop_reason, record_frame_this_id)
>> 	(record_frame_prev_register, record_frame_sniffer, record_frame_unwind):
>> 	New.
>> 	(init_record_ops, init_record_core_ops): Install it.
>> 	* target.c (target_get_unwinder): New.
>> 	* target.h (struct target_ops): New field to_get_unwinder.
>> 	(target_get_unwinder): New declaration.
> 
> I've been experimenting with this a bit.  It looks like there will always be
> a sentinel frame at the very bottom that is reading the registers directly
> from the inferior.  I can only hook in at the second frame.
> 
> In order to fake the back trace for btrace replay, I would also need to
> replace the sentinel frame, since otherwise, the first frame will always
> point to the current location.
> 
> Am I doing something wrong?

Yes, I am.  I need to also provide the target registers.  Then the
sentinel frame should do exactly what I want.


> On a related but different topic, I added a btrace frame type and
> prologue cache.  The cache holds a pointer to some btrace data
> structure that is used to compute the fake back trace.  In order to
> unwind a btrace frame, I would need to access the next frame's
> location in this btrace data structure.
> 
> The easiest would be to check for the next frame's type and then
> access it's cache - which doesn't work since struct frame_info is
> opaque.  I ended up encoding the pointer into the special_addr
> of a btrace frame's frame_id - which is somewhat ugly.  Any better
> idea?
> 
> Also what's the lifetime of a frame_info and frame_id object?
> When the branch trace is cleared, any pointers to it will become
> stale.

Those remain open.


regards,
markus.
From gdb-patches-return-99987-listarch-gdb-patches=sources.redhat.com@sourceware.org Thu Mar 28 06:28:08 2013
Return-Path: <gdb-patches-return-99987-listarch-gdb-patches=sources.redhat.com@sourceware.org>
Delivered-To: listarch-gdb-patches@sources.redhat.com
Received: (qmail 15197 invoked by alias); 28 Mar 2013 06:28:07 -0000
Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm
Precedence: bulk
List-Id: <gdb-patches.sourceware.org>
List-Subscribe: <mailto:gdb-patches-subscribe@sourceware.org>
List-Archive: <http://sourceware.org/ml/gdb-patches/>
List-Post: <mailto:gdb-patches@sourceware.org>
List-Help: <mailto:gdb-patches-help@sourceware.org>, <http://sourceware.org/ml/#faqs>
Sender: gdb-patches-owner@sourceware.org
Delivered-To: mailing list gdb-patches@sourceware.org
Received: (qmail 15177 invoked by uid 89); 28 Mar 2013 06:28:00 -0000
X-Spam-SWARE-Status: No, score=-7.0 required=5.0 tests=AWL,BAYES_00,KHOP_RCVD_UNTRUST,RCVD_IN_DNSWL_HI,RCVD_IN_HOSTKARMA_W,RP_MATCHES_RCVD,SPF_HELO_PASS autolearn=ham version=3.3.1
Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28)    by sourceware.org (qpsmtpd/0.84/v0.84-167-ge50287c) with ESMTP; Thu, 28 Mar 2013 06:27:58 +0000
Received: from int-mx02.intmail.prod.int.phx2.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12])	by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id r2S6RtEm025729	(version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits%6 verify=OK);	Thu, 28 Mar 2013 02:27:55 -0400
Received: from host2.jankratochvil.net (ovpn-116-39.ams2.redhat.com [10.36.116.39])	by int-mx02.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id r2S6Rm5m018800	(version=TLSv1/SSLv3 cipher=DHE-RSA-AES128-SHA bits\x128 verify=NO);	Thu, 28 Mar 2013 02:27:52 -0400
Date: Thu, 28 Mar 2013 14:21:00 -0000
From: Jan Kratochvil <jan.kratochvil@redhat.com>
To: Markus Metzger <markus.t.metzger@googlemail.com>
Cc: gdb-patches@sourceware.org,        "Metzger, Markus T" <markus.t.metzger@intel.com>
Subject: Re: [draft patch] <unavailable> unwinder for btrace  [Re: [rfc 3/5] record: make it build again]
Message-ID: <20130328062747.GA27157@host2.jankratochvil.net>
References: <1360337423-27095-1-git-send-email-markus.t.metzger@intel.com> <1360337423-27095-4-git-send-email-markus.t.metzger@intel.com> <20130210221059.GC4819@host2.jankratochvil.net> <A78C989F6D9628469189715575E55B2307B78A04@IRSMSX102.ger.corp.intel.com> <20130211141451.GA8962@host2.jankratochvil.net> <20130211171319.GA17524@host2.jankratochvil.net> <A78C989F6D9628469189715575E55B2307BA9030@IRSMSX102.ger.corp.intel.com> <9B969C1D-95E8-4AD5-BEF0-E269FF8771DF@gmail.com>
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
In-Reply-To: <9B969C1D-95E8-4AD5-BEF0-E269FF8771DF@gmail.com>
User-Agent: Mutt/1.5.21 (2010-09-15)
X-IsSubscribed: yes
X-SW-Source: 2013-03/txt/msg01052.txt.bz2
Content-length: 2091

On Thu, 28 Mar 2013 07:09:23 +0100, Markus Metzger wrote:
> On Mar 27, 2013, at 16:22 , "Metzger, Markus T" <markus.t.metzger@intel.com> wrote:
> > I've been experimenting with this a bit.  It looks like there will always be
> > a sentinel frame at the very bottom that is reading the registers directly
> > from the inferior.  I can only hook in at the second frame.
> >
> > In order to fake the back trace for btrace replay, I would also need to
> > replace the sentinel frame, since otherwise, the first frame will always
> > point to the current location.
> >
> > Am I doing something wrong?
>
> Yes, I am.  I need to also provide the target registers.  Then the
> sentinel frame should do exactly what I want.

It looks right to me.


> > On a related but different topic, I added a btrace frame type and
> > prologue cache.  The cache holds a pointer to some btrace data
> > structure that is used to compute the fake back trace.  In order to
> > unwind a btrace frame, I would need to access the next frame's
> > location in this btrace data structure.
> >
> > The easiest would be to check for the next frame's type and then
> > access it's cache - which doesn't work since struct frame_info is
> > opaque.  I ended up encoding the pointer into the special_addr
> > of a btrace frame's frame_id - which is somewhat ugly.  Any better
> > idea?

special_addr is really not right.

dwarf2-frame-tailcall.c uses for a similar problem 'htab_t cache_htab' which
is indexed by 'struct frame_info *' which you can iterate in any direction so
you even do not need a new cache entry for every 'struct frame_info *'.


> > Also what's the lifetime of a frame_info and frame_id object?
> > When the branch trace is cleared, any pointers to it will become
> > stale.

There is frame_unwind->dealloc_cache, any reinit_frame_cache() call inside GDB
will clear the prologue cache which is very common.

I see now btrace_thread_info->btrace may change more often - such as during
each "info record" command.  So call reinit_frame_cache() in the cases btrace
cache may get rebuilt.


Thanks,
Jan


^ permalink raw reply	[flat|nested] 23+ messages in thread

* RE: [draft patch] <unavailable> unwinder for btrace  [Re: [rfc 3/5] record: make it build again]
       [not found]               ` <20130328062747.GA27157@host2.jankratochvil.net>
@ 2013-03-28 14:28                 ` Metzger, Markus T
  2013-03-28 14:38                   ` Jan Kratochvil
  0 siblings, 1 reply; 23+ messages in thread
From: Metzger, Markus T @ 2013-03-28 14:28 UTC (permalink / raw)
  To: Jan Kratochvil, Markus Metzger; +Cc: gdb-patches

> -----Original Message-----
> From: Jan Kratochvil [mailto:jan.kratochvil@redhat.com]
> Sent: Thursday, March 28, 2013 7:28 AM


> > > On a related but different topic, I added a btrace frame type and
> > > prologue cache.  The cache holds a pointer to some btrace data
> > > structure that is used to compute the fake back trace.  In order to
> > > unwind a btrace frame, I would need to access the next frame's
> > > location in this btrace data structure.
> > >
> > > The easiest would be to check for the next frame's type and then
> > > access it's cache - which doesn't work since struct frame_info is
> > > opaque.  I ended up encoding the pointer into the special_addr
> > > of a btrace frame's frame_id - which is somewhat ugly.  Any better
> > > idea?
> 
> special_addr is really not right.
> 
> dwarf2-frame-tailcall.c uses for a similar problem 'htab_t cache_htab' which
> is indexed by 'struct frame_info *' which you can iterate in any direction so
> you even do not need a new cache entry for every 'struct frame_info *'.

Thanks, I'll try that.  Although, in the end, it's really the same as if we made
other frames prologue cache visible.


> > > Also what's the lifetime of a frame_info and frame_id object?
> > > When the branch trace is cleared, any pointers to it will become
> > > stale.
> 
> There is frame_unwind->dealloc_cache, any reinit_frame_cache() call inside GDB
> will clear the prologue cache which is very common.
> 
> I see now btrace_thread_info->btrace may change more often - such as during
> each "info record" command.  So call reinit_frame_cache() in the cases btrace
> cache may get rebuilt.

The data structure should only change when there is new trace, which requires
the target to continue.  "info record" should, like any other record-btrace command,
fetch the new trace once and then operate on the cached trace data.

Is there a guarantee that frame_info and frame_id objects are destroyed
when the target resumes?  Or could I trigger their destruction from within
btrace_clear?

Thanks,
Markus.

Intel GmbH
Dornacher Strasse 1
85622 Feldkirchen/Muenchen, Deutschland
Sitz der Gesellschaft: Feldkirchen bei Muenchen
Geschaeftsfuehrer: Christian Lamprechter, Hannes Schwaderer, Douglas Lusk
Registergericht: Muenchen HRB 47456
Ust.-IdNr./VAT Registration No.: DE129385895
Citibank Frankfurt a.M. (BLZ 502 109 00) 600119052


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [draft patch] <unavailable> unwinder for btrace  [Re: [rfc 3/5] record: make it build again]
  2013-03-28 14:28                 ` Metzger, Markus T
@ 2013-03-28 14:38                   ` Jan Kratochvil
  2013-03-28 17:37                     ` Metzger, Markus T
  0 siblings, 1 reply; 23+ messages in thread
From: Jan Kratochvil @ 2013-03-28 14:38 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: Markus Metzger, gdb-patches

On Thu, 28 Mar 2013 08:44:12 +0100, Metzger, Markus T wrote:
> Thanks, I'll try that.  Although, in the end, it's really the same as if we made
> other frames prologue cache visible.

Yes.  You can make a separate patch to make it visible but one htab_t may be
easier.  With public cache pointer someone could misues it inappropriately
etc.


> > > > Also what's the lifetime of a frame_info and frame_id object?
> > > > When the branch trace is cleared, any pointers to it will become
> > > > stale.
> > 
> > There is frame_unwind->dealloc_cache, any reinit_frame_cache() call inside GDB
> > will clear the prologue cache which is very common.
> > 
> > I see now btrace_thread_info->btrace may change more often - such as during
> > each "info record" command.  So call reinit_frame_cache() in the cases btrace
> > cache may get rebuilt.
> 
> The data structure should only change when there is new trace, which requires
> the target to continue.  "info record" should, like any other record-btrace command,
> fetch the new trace once and then operate on the cached trace data.

In non-stop mode I belive there will be new btrace info on each "info record"
command, won't be?  I have not tried it but it seems so to me.


> Is there a guarantee that frame_info and frame_id objects are destroyed
> when the target resumes?  Or could I trigger their destruction from within
> btrace_clear?

"trigger frame_info and frame_id destruction" == reinit_frame_cache().

Accessing frame_info after reinit_frame_cache() is always a crash.

Accessing frame_id after reinit_frame_cache() is safe but one needs to be
prepared frame_find_by_id may return NULL if it is no longer available.

When you introduce new reinit_frame_cache() call one just needs to be careful
no caller holds that time a frame_info * pointer in a local variable. 
It would be a bug in such caller to call some non-trivial caller while holding
frame_info * but there were many such bugs in GDB.

I would not rely on any reinit_frame_cache() calls, calling
reinit_frame_cache() more times is zero-cost, I think you should call
reinit_frame_cache() from btrace_clear as you ask above.


Thanks,
Jan


^ permalink raw reply	[flat|nested] 23+ messages in thread

* RE: [draft patch] <unavailable> unwinder for btrace  [Re: [rfc 3/5] record: make it build again]
  2013-03-28 14:38                   ` Jan Kratochvil
@ 2013-03-28 17:37                     ` Metzger, Markus T
  0 siblings, 0 replies; 23+ messages in thread
From: Metzger, Markus T @ 2013-03-28 17:37 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: Markus Metzger, gdb-patches

> -----Original Message-----
> From: gdb-patches-owner@sourceware.org [mailto:gdb-patches-owner@sourceware.org] On Behalf Of Jan Kratochvil
> Sent: Thursday, March 28, 2013 10:12 AM


> > The data structure should only change when there is new trace, which requires
> > the target to continue.  "info record" should, like any other record-btrace command,
> > fetch the new trace once and then operate on the cached trace data.
> 
> In non-stop mode I belive there will be new btrace info on each "info record"
> command, won't be?  I have not tried it but it seems so to me.

Record_btrace_info, like any other record btrace command, calls btrace_fetch.
This queries the target for new branch trace.  If there is no new trace, it returns.
Only if there is new trace, it will call btrace_clear and then reconstruct the branch
trace.
All this is per-thread, so the branch trace for each thread should only be reconstructed
when it has changed - in non-stop and stop-all mode.


> > Is there a guarantee that frame_info and frame_id objects are destroyed
> > when the target resumes?  Or could I trigger their destruction from within
> > btrace_clear?
> 
> "trigger frame_info and frame_id destruction" == reinit_frame_cache().
> 
> Accessing frame_info after reinit_frame_cache() is always a crash.
> 
> Accessing frame_id after reinit_frame_cache() is safe but one needs to be
> prepared frame_find_by_id may return NULL if it is no longer available.
> 
> When you introduce new reinit_frame_cache() call one just needs to be careful
> no caller holds that time a frame_info * pointer in a local variable.
> It would be a bug in such caller to call some non-trivial caller while holding
> frame_info * but there were many such bugs in GDB.
> 
> I would not rely on any reinit_frame_cache() calls, calling
> reinit_frame_cache() more times is zero-cost, I think you should call
> reinit_frame_cache() from btrace_clear as you ask above.

Thanks.  I'm doing just that.  So far, I have not seen any issues.

Regards,
Markus.

Intel GmbH
Dornacher Strasse 1
85622 Feldkirchen/Muenchen, Deutschland
Sitz der Gesellschaft: Feldkirchen bei Muenchen
Geschaeftsfuehrer: Christian Lamprechter, Hannes Schwaderer, Douglas Lusk
Registergericht: Muenchen HRB 47456
Ust.-IdNr./VAT Registration No.: DE129385895
Citibank Frankfurt a.M. (BLZ 502 109 00) 600119052


^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2013-03-28 12:38 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-02-08 15:30 [rfc 0/5] record-btrace markus.t.metzger
2013-02-08 15:30 ` [rfc 5/5] record, disas: add record disassemble command markus.t.metzger
2013-02-10 22:11   ` Jan Kratochvil
2013-02-08 15:30 ` [rfc 1/5] target: add add_deprecated_target_alias markus.t.metzger
2013-02-10 22:10   ` Jan Kratochvil
2013-02-08 15:31 ` [rfc 4/5] record: default target methods markus.t.metzger
2013-02-10 22:11   ` Jan Kratochvil
2013-02-08 15:31 ` [rfc 3/5] record: make it build again markus.t.metzger
2013-02-10 22:11   ` Jan Kratochvil
2013-02-11 13:41     ` Metzger, Markus T
2013-02-11 14:15       ` Jan Kratochvil
2013-02-11 17:13         ` [draft patch] <unavailable> unwinder for btrace [Re: [rfc 3/5] record: make it build again] Jan Kratochvil
2013-02-11 21:24           ` Tom Tromey
2013-02-13  7:35           ` Metzger, Markus T
2013-02-13  7:58             ` Jan Kratochvil
2013-02-13  8:08               ` Metzger, Markus T
2013-03-27 18:09           ` Metzger, Markus T
2013-03-28 12:38             ` Markus Metzger
     [not found]               ` <20130328062747.GA27157@host2.jankratochvil.net>
2013-03-28 14:28                 ` Metzger, Markus T
2013-03-28 14:38                   ` Jan Kratochvil
2013-03-28 17:37                     ` Metzger, Markus T
2013-02-08 15:32 ` [rfc 2/5] record: split record markus.t.metzger
2013-02-10 22:11   ` Jan Kratochvil

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox