]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gdb/record.c
GDB copyright headers update after running GDB's copyright.py script.
[thirdparty/binutils-gdb.git] / gdb / record.c
index 24438132c5f117feac891dd1c0ffe20fd88dd02d..6190794492fd73f2b2ba67ab22a08e006b8cf154 100644 (file)
@@ -1,6 +1,6 @@
 /* Process record and replay target for GDB, the GNU debugger.
 
-   Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
+   Copyright (C) 2008-2016 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
 
 #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 "observer.h"
+#include "inferior.h"
+#include "common/common-utils.h"
+#include "cli/cli-utils.h"
+#include "disasm.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 target_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;
-};
+#include <ctype.h>
 
 /* This is the debug switch for process record.  */
-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 target_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 *);
-
-/* 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;
+unsigned int record_debug = 0;
 
-  while (rec->prev)
-    {
-      rec = rec->prev;
-      record_entry_release (rec->next);
-    }
+/* The number of instructions to print in "record instruction-history".  */
+static unsigned int record_insn_history_size = 10;
 
-  if (rec == &record_first)
-    {
-      record_insn_num = 0;
-      record_first.next = NULL;
-    }
-  else
-    record_entry_release (rec);
-}
+/* The variable registered as control variable in the "record
+   instruction-history" command.  Necessary for extra input
+   validation.  */
+static unsigned int record_insn_history_size_setshow_var;
 
-/* Free all record entries forward of the given list position.  */
+/* The number of functions to print in "record function-call-history".  */
+static unsigned int record_call_history_size = 10;
 
-static void
-record_list_release_following (struct record_entry *rec)
-{
-  struct record_entry *tmp = rec->next;
+/* The variable registered as control variable in the "record
+   call-history" command.  Necessary for extra input validation.  */
+static unsigned int record_call_history_size_setshow_var;
 
-  rec->next = NULL;
-  while (tmp)
-    {
-      rec = tmp->next;
-      if (record_entry_release (tmp) == record_end)
-       {
-         record_insn_num--;
-         record_insn_count--;
-       }
-      tmp = rec;
-    }
-}
+struct cmd_list_element *record_cmdlist = NULL;
+struct cmd_list_element *record_goto_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;
 
-/* Delete the first instruction from the beginning of the log, to make
-   room for adding a new instruction at the end of the log.
+#define DEBUG(msg, args...)                                            \
+  if (record_debug)                                                    \
+    fprintf_unfiltered (gdb_stdlog, "record: " msg "\n", ##args)
 
-   Note -- this function does not modify record_insn_num.  */
+/* See record.h.  */
 
-static void
-record_list_release_first (void)
+struct target_ops *
+find_record_target (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.  */
-       }
-    }
+  return find_target_at (record_stratum);
 }
 
-/* Add a struct record_entry to record_arch_list.  */
+/* Check that recording is active.  Throw an error, if it isn't.  */
 
-static void
-record_arch_list_add (struct record_entry *rec)
+static struct target_ops *
+require_record_target (void)
 {
-  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 (0);
-    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));
+  struct target_ops *t;
 
-  record_arch_list_add (rec);
+  t = find_record_target ();
+  if (t == NULL)
+    error (_("No record target is currently active.\n"
+            "Use one of the \"target record-<tab><tab>\" commands first."));
 
-  return 0;
+  return t;
 }
 
-/* Record the value of a region of memory whose address is ADDR and
-   length is LEN to record_arch_list.  */
+/* See record.h.  */
 
-int
-record_arch_list_add_mem (CORE_ADDR addr, int len)
+void
+record_preopen (void)
 {
-  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 (target_read_memory (addr, record_get_loc (rec), len))
-    {
-      if (record_debug)
-       fprintf_unfiltered (gdb_stdlog,
-                           "Process record: error reading memory at "
-                           "addr = %s len = %d.\n",
-                           paddress (target_gdbarch, addr), len);
-      record_mem_release (rec);
-      return -1;
-    }
-
-  record_arch_list_add (rec);
-
-  return 0;
+  /* Check if a record target is already running.  */
+  if (find_record_target () != NULL)
+    error (_("The process is already being recorded.  Use \"record stop\" to "
+            "stop recording first."));
 }
 
-/* Add a record_end type struct record_entry to record_arch_list.  */
+/* See record.h.  */
 
 int
-record_arch_list_add_end (void)
+record_read_memory (struct gdbarch *gdbarch,
+                   CORE_ADDR memaddr, gdb_byte *myaddr,
+                   ssize_t len)
 {
-  struct record_entry *rec;
-
-  if (record_debug > 1)
-    fprintf_unfiltered (gdb_stdlog,
-                       "Process record: add end to arch list.\n");
+  int ret = target_read_memory (memaddr, myaddr, len);
 
-  rec = record_end_alloc ();
-  rec->u.end.sigval = TARGET_SIGNAL_0;
-  rec->u.end.insn_num = ++record_insn_count;
+  if (ret != 0)
+    DEBUG ("error reading memory at addr %s len = %ld.\n",
+          paddress (gdbarch, memaddr), (long) len);
 
-  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 target_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 == TARGET_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 target_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 target_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 (target_read_memory (entry->u.mem.addr, mem, entry->u.mem.len))
-              {
-                entry->u.mem.mem_entry_not_accessible = 1;
-                if (record_debug)
-                  warning ("Process record: error reading memory at "
-                          "addr = %s len = %d.",
-                           paddress (gdbarch, entry->u.mem.addr),
-                           entry->u.mem.len);
-              }
-            else
-              {
-                if (target_write_memory (entry->u.mem.addr, 
-                                        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 target_signal);
-static struct target_ops *tmp_to_wait_ops;
-static ptid_t (*tmp_to_wait) (struct target_ops *, ptid_t,
-                             struct target_waitstatus *,
-                             int);
-static struct target_ops *tmp_to_store_registers_ops;
-static void (*tmp_to_store_registers) (struct target_ops *,
-                                      struct regcache *,
-                                      int regno);
-static struct target_ops *tmp_to_xfer_partial_ops;
-static LONGEST (*tmp_to_xfer_partial) (struct target_ops *ops,
-                                      enum target_object object,
-                                      const char *annex,
-                                      gdb_byte *readbuf,
-                                      const gdb_byte *writebuf,
-                                      ULONGEST offset,
-                                      LONGEST len);
-static int (*tmp_to_insert_breakpoint) (struct gdbarch *,
-                                       struct bp_target_info *);
-static int (*tmp_to_remove_breakpoint) (struct gdbarch *,
-                                       struct bp_target_info *);
-static int (*tmp_to_stopped_by_watchpoint) (void);
-static int (*tmp_to_stopped_data_address) (struct target_ops *, CORE_ADDR *);
-
-static void record_restore (void);
-
-/* 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 (target_async_permitted)
-    error (_("Process record target can't debug inferior in asynchronous "
-            "mode (target-async)."));
-
-  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);
-}
-
-/* "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;
-
-  /* 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_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;
-
-  if (current_target.to_stratum == core_stratum)
-    record_core_open_1 (name, from_tty);
-  else
-    record_open_1 (name, from_tty);
-}
-
-/* "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;
-    }
+  return ret;
 }
 
-static int record_resume_step = 0;
-
-/* "to_resume" target method.  Resume the process record target.  */
+/* Stop recording.  */
 
 static void
-record_resume (struct target_ops *ops, ptid_t ptid, int step,
-               enum target_signal signal)
+record_stop (struct target_ops *t)
 {
-  record_resume_step = step;
+  DEBUG ("stop %s", t->to_shortname);
 
-  if (!RECORD_IS_REPLAY)
-    {
-      record_message (get_current_regcache (), signal);
-      record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1,
-                                signal);
-    }
+  t->to_stop_recording (t);
 }
 
-static int record_get_sig = 0;
-
-/* SIGINT signal handler, registered by "to_wait" method.  */
+/* Unpush the record target.  */
 
 static void
-record_sig_handler (int signo)
+record_unpush (struct target_ops *t)
 {
-  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;
+  DEBUG ("unpush %s", t->to_shortname);
 
-  /* 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;
+  unpush_target (t);
 }
 
-/* "to_wait" target method for process record target.
+/* See record.h.  */
 
-   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 (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\n",
-                       record_resume_step);
-
-  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;
-
-         while (1)
-           {
-             ret = record_beneath_to_wait (record_beneath_to_wait_ops,
-                                           ptid, status, options);
-
-             if (record_resume_step)
-               return ret;
-
-             /* Is this a SIGTRAP?  */
-             if (status->kind == TARGET_WAITKIND_STOPPED
-                 && status->value.sig == TARGET_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 must be a single-step trap.  Record the
-                        insn and issue another step.  */
-                     if (!record_message_wrapper_safe (regcache,
-                                                        TARGET_SIGNAL_0))
-                       {
-                           status->kind = TARGET_WAITKIND_STOPPED;
-                           status->value.sig = TARGET_SIGNAL_0;
-                           break;
-                       }
-
-                     record_beneath_to_resume (record_beneath_to_resume_ops,
-                                               ptid, 1,
-                                               TARGET_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 != TARGET_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 = TARGET_SIGNAL_INT;
-      else if (record_list->u.end.sigval != TARGET_SIGNAL_0)
-       /* FIXME: better way to check */
-       status->value.sig = record_list->u.end.sigval;
-      else
-       status->value.sig = TARGET_SIGNAL_TRAP;
-
-      discard_cleanups (old_cleanups);
-    }
-
-  signal (SIGINT, handle_sigint);
-
-  do_cleanups (set_cleanups);
-  return inferior_ptid;
-}
-
-static int
-record_stopped_by_watchpoint (void)
+void
+record_disconnect (struct target_ops *t, const char *args, int from_tty)
 {
-  if (RECORD_IS_REPLAY)
-    return record_hw_watchpoint;
-  else
-    return record_beneath_to_stopped_by_watchpoint ();
-}
+  gdb_assert (t->to_stratum == record_stratum);
 
-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);
-}
+  DEBUG ("disconnect %s", t->to_shortname);
 
-/* "to_disconnect" method for process record target.  */
+  record_stop (t);
+  record_unpush (t);
 
-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);
-}
-
-/* Behavior is conditional on RECORD_IS_REPLAY.
-   We will not actually insert or remove breakpoints when replaying,
-   nor when recording.  */
-
-static int
-record_insert_breakpoint (struct gdbarch *gdbarch,
-                         struct bp_target_info *bp_tgt)
-{
-  if (!RECORD_IS_REPLAY)
-    {
-      struct cleanup *old_cleanups = record_gdb_operation_disable_set ();
-      int ret = record_beneath_to_insert_breakpoint (gdbarch, bp_tgt);
-
-      do_cleanups (old_cleanups);
-
-      return ret;
-    }
+/* See record.h.  */
 
-  return 0;
-}
-
-/* "to_remove_breakpoint" method for process record target.  */
-
-static int
-record_remove_breakpoint (struct gdbarch *gdbarch,
-                         struct bp_target_info *bp_tgt)
-{
-  if (!RECORD_IS_REPLAY)
-    {
-      struct cleanup *old_cleanups = record_gdb_operation_disable_set ();
-      int ret = record_beneath_to_remove_breakpoint (gdbarch, bp_tgt);
-
-      do_cleanups (old_cleanups);
-
-      return ret;
-    }
-
-  return 0;
-}
-
-/* "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
-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_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 target_signal signal)
-{
-  record_resume_step = step;
-}
-
-/* "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)
+void
+record_detach (struct target_ops *t, const char *args, int from_tty)
 {
-  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.  */
+  gdb_assert (t->to_stratum == record_stratum);
 
-static void
-record_core_prepare_to_store (struct regcache *regcache)
-{
-}
+  DEBUG ("detach %s", t->to_shortname);
 
-/* "to_store_registers" method for prec over corefile.  */
+  record_stop (t);
+  record_unpush (t);
 
-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."));
+  target_detach (args, from_tty);
 }
 
-/* "to_xfer_partial" method for prec over corefile.  */
+/* See record.h.  */
 
-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)
+void
+record_mourn_inferior (struct target_ops *t)
 {
-  if (object == TARGET_OBJECT_MEMORY)
-    {
-      if (record_gdb_operation_disable || !writebuf)
-       {
-         struct target_section *p;
+  gdb_assert (t->to_stratum == record_stratum);
 
-         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;
-               }
-           }
+  DEBUG ("mourn inferior %s", t->to_shortname);
 
-         return -1;
-       }
-      else
-       error (_("You can't do that without a process to debug."));
-    }
+  /* It is safer to not stop recording.  Resources will be freed when
+     threads are discarded.  */
+  record_unpush (t);
 
-  return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops,
-                                         object, annex, readbuf, writebuf,
-                                         offset, len);
+  target_mourn_inferior ();
 }
 
-/* "to_insert_breakpoint" method for prec over corefile.  */
+/* See record.h.  */
 
-static int
-record_core_insert_breakpoint (struct gdbarch *gdbarch,
-                              struct bp_target_info *bp_tgt)
-{
-  return 0;
-}
+void
+record_kill (struct target_ops *t)
+{
+  gdb_assert (t->to_stratum == record_stratum);
 
-/* "to_remove_breakpoint" method for prec over corefile.  */
+  DEBUG ("kill %s", t->to_shortname);
 
-static int
-record_core_remove_breakpoint (struct gdbarch *gdbarch,
-                              struct bp_target_info *bp_tgt)
-{
-  return 0;
+  /* It is safer to not stop recording.  Resources will be freed when
+     threads are discarded.  */
+  record_unpush (t);
+
+  target_kill ();
 }
 
-/* "to_has_execution" method for prec over corefile.  */
+/* See record.h.  */
 
 int
-record_core_has_execution (struct target_ops *ops)
+record_check_stopped_by_breakpoint (struct address_space *aspace, CORE_ADDR pc,
+                                   enum target_stop_reason *reason)
 {
-  return 1;
-}
+  if (breakpoint_inserted_here_p (aspace, pc))
+    {
+      if (hardware_breakpoint_inserted_here_p (aspace, pc))
+       *reason = TARGET_STOPPED_BY_HW_BREAKPOINT;
+      else
+       *reason = TARGET_STOPPED_BY_SW_BREAKPOINT;
+      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_magic = OPS_MAGIC;
+  *reason = TARGET_STOPPED_BY_NO_REASON;
+  return 0;
 }
 
 /* Implement "show record debug" command.  */
@@ -1912,7 +223,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
@@ -1921,21 +232,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 (inferior_ptid))
     {
-      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.  */
@@ -1943,1281 +258,515 @@ 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"));
-    }
-  else
-    printf_unfiltered (_("Process record is not started.\n"));
-}
+  struct target_ops *t;
 
-/* Set upper limit of record log size.  */
+  t = require_record_target ();
 
-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--;
-       }
-    }
+  record_stop (t);
+  record_unpush (t);
+
+  printf_unfiltered (_("Process record is stopped and all execution "
+                      "logs are deleted.\n"));
+
+  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)
 {
-  printf_unfiltered (_("\
-\"set record\" must be followed by an apporpriate subcommand.\n"));
+  printf_unfiltered (_("\"set record\" must be followed "
+                      "by an apporpriate subcommand.\n"));
   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"));
+      printf_filtered (_("No record target is currently active.\n"));
+      return;
+    }
 
-      /* Find entry for first actual instruction in the log.  */
-      for (p = record_first.next;
-          p != NULL && p->type != record_end;
-          p = p->next)
-       ;
+  printf_filtered (_("Active record target: %s\n"), t->to_shortname);
+  t->to_info_record (t);
+}
 
-      /* 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"));
-       }
-    }
+/* The "record save" command.  */
+
+static void
+cmd_record_save (char *args, int from_tty)
+{
+  char *recfilename, recfilename_buffer[40];
+
+  require_record_target ();
+
+  if (args != NULL && *args != 0)
+    recfilename = args;
   else
     {
-      printf_filtered (_("target record is not active.\n"));
+      /* Default recfile name is "gdb_record.PID".  */
+      xsnprintf (recfilename_buffer, sizeof (recfilename_buffer),
+                "gdb_record.%d", ptid_get_pid (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 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)
+/* See record.h.  */
+
+void
+record_goto (const char *arg)
 {
-  int ret = bfd_get_section_contents (obfd, osec, buf, *offset, len);
+  ULONGEST insn;
 
-  if (ret)
-    *offset += len;
-  else
-    error (_("Failed to read %d bytes from core file %s ('%s').\n"),
-          len, bfd_get_filename (obfd),
-          bfd_errmsg (bfd_get_error ()));
-}
+  if (arg == NULL || *arg == '\0')
+    error (_("Command requires an argument (insn number to go to)."));
 
-static inline uint64_t
-netorder64 (uint64_t input)
-{
-  uint64_t ret;
+  insn = parse_and_eval_long (arg);
 
-  store_unsigned_integer ((gdb_byte *) &ret, sizeof (ret), 
-                         BFD_ENDIAN_BIG, input);
-  return ret;
+  require_record_target ();
+  target_goto_record (insn);
 }
 
-static inline uint32_t
-netorder32 (uint32_t input)
-{
-  uint32_t ret;
+/* "record goto" command.  Argument is an instruction number,
+   as given by "info record".
 
-  store_unsigned_integer ((gdb_byte *) &ret, sizeof (ret), 
-                         BFD_ENDIAN_BIG, input);
-  return ret;
+   Rewinds the recording (forward or backward) to the given instruction.  */
+
+static void
+cmd_record_goto (char *arg, int from_tty)
+{
+  record_goto (arg);
 }
 
-static inline uint16_t
-netorder16 (uint16_t input)
+/* The "record goto begin" command.  */
+
+static void
+cmd_record_goto_begin (char *arg, int from_tty)
 {
-  uint16_t ret;
+  if (arg != NULL && *arg != '\0')
+    error (_("Junk after argument: %s."), arg);
 
-  store_unsigned_integer ((gdb_byte *) &ret, sizeof (ret), 
-                         BFD_ENDIAN_BIG, input);
-  return ret;
+  require_record_target ();
+  target_goto_record_begin ();
 }
 
-/* Restore the execution log from a core_bfd file.  */
+/* The "record goto end" command.  */
+
 static void
-record_restore (void)
+cmd_record_goto_end (char *arg, int from_tty)
 {
-  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");
-  osec_size = bfd_section_size (core_bfd, osec);
-  if (record_debug)
-    fprintf_unfiltered (gdb_stdlog, "Find precord section %s.\n",
-                       osec ? "succeeded" : "failed");
-  if (osec == NULL)
-    return;
-  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);
-    }
+  if (arg != NULL && *arg != '\0')
+    error (_("Junk after argument: %s."), arg);
 
-  discard_cleanups (old_cleanups);
+  require_record_target ();
+  target_goto_record_end ();
+}
 
-  /* 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;
+/* Read an instruction number from an argument string.  */
 
-  /* 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);
-    }
+static ULONGEST
+get_insn_number (char **arg)
+{
+  ULONGEST number;
+  const char *begin, *end, *pos;
 
-  /* Succeeded.  */
-  printf_filtered (_("Restored records from core file %s.\n"),
-                  bfd_get_filename (core_bfd));
+  begin = *arg;
+  pos = skip_spaces_const (begin);
 
-  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
-}
+  if (!isdigit (*pos))
+    error (_("Expected positive number, got: %s."), pos);
 
-/* bfdcore_write -- write bytes into a core file section.  */
+  number = strtoulst (pos, &end, 10);
 
-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);
+  *arg += (end - begin);
 
-  if (ret)
-    *offset += len;
-  else
-    error (_("Failed to write %d bytes to core file %s ('%s').\n"),
-          len, bfd_get_filename (obfd),
-          bfd_errmsg (bfd_get_error ()));
+  return number;
 }
 
-/* Restore the execution log from a file.  We use a modified elf
-   corefile format, with an extra section for our data.  */
+/* Read a context size from an argument string.  */
 
-static void
-cmd_record_restore (char *args, int from_tty)
+static int
+get_context_size (char **arg)
 {
-  core_file_command (args, from_tty);
-  record_open (args, from_tty);
-}
+  char *pos;
+  int number;
 
-static void
-record_save_cleanups (void *data)
-{
-  bfd *obfd = data;
-  char *pathname = xstrdup (bfd_get_filename (obfd));
+  pos = skip_spaces (*arg);
 
-  bfd_close (obfd);
-  unlink (pathname);
-  xfree (pathname);
+  if (!isdigit (*pos))
+    error (_("Expected positive number, got: %s."), pos);
+
+  return strtol (pos, arg, 10);
 }
 
-/* Save the execution log to a file.  We use a modified elf corefile
-   format, with an extra section for our data.  */
+/* Complain about junk at the end of an argument string.  */
 
 static void
-cmd_record_save (char *args, int from_tty)
+no_chunk (char *arg)
 {
-  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);
+  if (*arg != 0)
+    error (_("Junk after argument: %s."), arg);
+}
 
-  /* Open the output file.  */
-  obfd = create_gcore_bfd (recfilename);
-  old_cleanups = make_cleanup (record_save_cleanups, obfd);
+/* Read instruction-history modifiers from an argument string.  */
 
-  /* Save the current record entry to "cur_record_list".  */
-  cur_record_list = record_list;
+static int
+get_insn_history_modifiers (char **arg)
+{
+  int modifiers;
+  char *args;
 
-  /* Get the values of regcache and gdbarch.  */
-  regcache = get_current_regcache ();
-  gdbarch = get_regcache_arch (regcache);
+  modifiers = 0;
+  args = *arg;
 
-  /* Disable the GDB operation record.  */
-  set_cleanups = record_gdb_operation_disable_set ();
+  if (args == NULL)
+    return modifiers;
 
-  /* Reverse execute to the begin of record list.  */
-  while (1)
+  while (*args == '/')
     {
-      /* Check for beginning and end of log.  */
-      if (record_list == &record_first)
-        break;
-
-      record_exec_insn (regcache, gdbarch, record_list);
+      ++args;
 
-      if (record_list->prev)
-        record_list = record_list->prev;
-    }
+      if (*args == '\0')
+       error (_("Missing modifier."));
 
-  /* 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;
-    }
+      for (; *args; ++args)
+       {
+         if (isspace (*args))
+           break;
 
-  /* Reverse execute to cur_record_list.  */
-  while (1)
-    {
-      /* Check for beginning and end of log.  */
-      if (record_list == cur_record_list)
-        break;
+         if (*args == '/')
+           continue;
 
-      record_exec_insn (regcache, gdbarch, record_list);
+         switch (*args)
+           {
+           case 'm':
+           case 's':
+             modifiers |= DISASSEMBLY_SOURCE;
+             modifiers |= DISASSEMBLY_FILENAME;
+             break;
+           case 'r':
+             modifiers |= DISASSEMBLY_RAW_INSN;
+             break;
+           case 'f':
+             modifiers |= DISASSEMBLY_OMIT_FNAME;
+             break;
+           case 'p':
+             modifiers |= DISASSEMBLY_OMIT_PC;
+             break;
+           default:
+             error (_("Invalid modifier: %c."), *args);
+           }
+       }
 
-      if (record_list->prev)
-        record_list = record_list->prev;
+      args = skip_spaces (args);
     }
 
-  do_cleanups (set_cleanups);
-  bfd_close (obfd);
-  discard_cleanups (old_cleanups);
+  /* Update the argument string.  */
+  *arg = args;
 
-  /* Succeeded.  */
-  printf_filtered (_("Saved core file %s with execution log.\n"),
-                  recfilename);
+  return modifiers;
 }
 
-/* For "record pic" command.  */
-
-static struct cmd_list_element *set_record_pic_cmdlist,
-                               *show_record_pic_cmdlist;
+/* The "set record instruction-history-size / set record
+   function-call-history-size" commands are unsigned, with UINT_MAX
+   meaning unlimited.  The target interfaces works with signed int
+   though, to indicate direction, so map "unlimited" to INT_MAX, which
+   is about the same as unlimited in practice.  If the user does have
+   a log that huge, she can fetch it in chunks across several requests,
+   but she'll likely have other problems first...  */
 
-static void
-set_record_pic_command (char *args, int from_tty)
+static int
+command_size_to_target_size (unsigned int size)
 {
-  printf_unfiltered (_("\
-\"set record pic\" must be followed by an apporpriate subcommand.\n"));
-  help_list (set_record_cmdlist, "set record pic ", all_commands, gdb_stdout);
-}
+  gdb_assert (size <= INT_MAX || size == UINT_MAX);
 
-static void
-show_record_pic_command (char *args, int from_tty)
-{
-  cmd_show_list (show_record_pic_cmdlist, from_tty, "");
+  if (size == UINT_MAX)
+    return INT_MAX;
+  else
+    return size;
 }
 
-static const char record_pic_function[] = "function";
-static const char record_pic_line[] = "line";
-static const char *record_pic_enum[] =
-{
-  record_pic_function,
-  record_pic_line,
-  NULL,
-};
-static const char *set_record_pic_type = record_pic_line;
-
-static int record_pic_hide_nofunction = 1;
-static int record_pic_hide_nosource = 1;
-static int  record_pic_hide_same = 1;
+/* The "record instruction-history" command.  */
 
 static void
-record_pic_fputs (FILE *fp, const char *buf)
+cmd_record_insn_history (char *arg, int from_tty)
 {
-  if (fputs (buf, fp) == EOF)
-    error (_("Write to file error."));
-}
+  int flags, size;
 
-struct function_list
-{
-  struct function_list *next;
-  CORE_ADDR addr;
-  int fid;
-};
-struct node_list
-{
-  struct node_list *next;
-  int count;
-  CORE_ADDR addr;
-  int showall;
-  struct symtab *symtab;
-  int line;
-  struct minimal_symbol *function;
-  int fid;
-};
-struct edge_list
-{
-  struct edge_list *next;
-  int count;
-  struct node_list *s;
-  struct node_list *t;
-  int frame_diff;
-  int is_return;
-};
-struct function_list *function_list = NULL;
-struct node_list *node_list = NULL;
-struct edge_list *edge_list = NULL;
+  require_record_target ();
 
-static void
-record_pic_cleanups (void *data)
-{
-  FILE *fp = data;
-  struct function_list *fl, *fl2;
-  struct node_list *nl, *nl2;
-  struct edge_list *el, *el2;
+  flags = get_insn_history_modifiers (&arg);
 
-  fl = function_list;
-  while (fl)
-    {
-      fl2 = fl;
-      fl = fl->next;
-      xfree (fl2);
-    }
-  function_list = NULL;
+  size = command_size_to_target_size (record_insn_history_size);
 
-  nl = node_list;
-  while (nl)
+  if (arg == NULL || *arg == 0 || strcmp (arg, "+") == 0)
+    target_insn_history (size, flags);
+  else if (strcmp (arg, "-") == 0)
+    target_insn_history (-size, flags);
+  else
     {
-      nl2 = nl;
-      nl = nl->next;
-      xfree (nl2);
-    }
-  node_list = NULL;
+      ULONGEST begin, end;
 
-  el = edge_list;
-  while (el)
-    {
-      el2 = el;
-      el = el->next;
-      xfree (el2);
-    }
-  edge_list = NULL;
+      begin = get_insn_number (&arg);
 
-  fclose (fp);
-}
+      if (*arg == ',')
+       {
+         arg = skip_spaces (++arg);
 
-static void
-record_pic_node (char *buf, int buf_max, struct gdbarch *gdbarch,
-                 const char *type, struct node_list *nlp)
-{
-  if (type == record_pic_function)
-    {
-      snprintf (buf, buf_max, "%s %s %s",
-               (nlp->symtab) ? nlp->symtab->filename : "",
-                (nlp->function) ? SYMBOL_LINKAGE_NAME (nlp->function) : "",
-                (!nlp->function) ? paddress (gdbarch, nlp->addr) : "");
-    }
-  else
-    {
-      if (nlp->showall)
-        {
-         snprintf (buf, buf_max, "%s:%d %s %s", nlp->symtab->filename,
-                    nlp->line,
-                    (nlp->function) ? SYMBOL_LINKAGE_NAME (nlp->function) : "",
-                    paddress (gdbarch, nlp->addr));
-        }
+         if (*arg == '+')
+           {
+             arg += 1;
+             size = get_context_size (&arg);
+
+             no_chunk (arg);
+
+             target_insn_history_from (begin, size, flags);
+           }
+         else if (*arg == '-')
+           {
+             arg += 1;
+             size = get_context_size (&arg);
+
+             no_chunk (arg);
+
+             target_insn_history_from (begin, -size, flags);
+           }
+         else
+           {
+             end = get_insn_number (&arg);
+
+             no_chunk (arg);
+
+             target_insn_history_range (begin, end, flags);
+           }
+       }
       else
-        {
-          if (nlp->symtab)
-           snprintf (buf, buf_max, "%s %d %s",
-                      (nlp->function) ? SYMBOL_LINKAGE_NAME (nlp->function) : "",
-                      nlp->line, paddress (gdbarch, nlp->addr));
-          else
-            snprintf (buf, buf_max, "%s %s",
-                      (nlp->function) ? SYMBOL_LINKAGE_NAME (nlp->function) : "",
-                      paddress (gdbarch, nlp->addr));
-        }
+       {
+         no_chunk (arg);
+
+         target_insn_history_from (begin, size, flags);
+       }
+
+      dont_repeat ();
     }
 }
 
-static void
-record_pic_edge (char *buf, int buf_max, struct edge_list *elp,
-                char *node, char *prev_node)
+/* Read function-call-history modifiers from an argument string.  */
+
+static int
+get_call_history_modifiers (char **arg)
 {
-  if (elp->frame_diff)
+  int modifiers;
+  char *args;
+
+  modifiers = 0;
+  args = *arg;
+
+  if (args == NULL)
+    return modifiers;
+
+  while (*args == '/')
     {
-      if (elp->is_return)
-        snprintf (buf, buf_max, "edge: {color:blue sourcename: \"%s\" "
-                                "targetname: \"%s\"",
-                 prev_node, node);
-      else
-        snprintf (buf, buf_max, "edge: {color:red sourcename: \"%s\" "
-                                "targetname: \"%s\"",
-                 prev_node, node);
+      ++args;
+
+      if (*args == '\0')
+       error (_("Missing modifier."));
+
+      for (; *args; ++args)
+       {
+         if (isspace (*args))
+           break;
+
+         if (*args == '/')
+           continue;
+
+         switch (*args)
+           {
+           case 'l':
+             modifiers |= RECORD_PRINT_SRC_LINE;
+             break;
+           case 'i':
+             modifiers |= RECORD_PRINT_INSN_RANGE;
+             break;
+           case 'c':
+             modifiers |= RECORD_PRINT_INDENT_CALLS;
+             break;
+           default:
+             error (_("Invalid modifier: %c."), *args);
+           }
+       }
+
+      args = skip_spaces (args);
     }
-  else
-    snprintf (buf, buf_max,
-              "edge: {sourcename: \"%s\" targetname: \"%s\"",
-              prev_node, node);
+
+  /* Update the argument string.  */
+  *arg = args;
+
+  return modifiers;
 }
 
-/* Save the execution log to a vcg file.  */
+/* The "record function-call-history" command.  */
 
 static void
-cmd_record_pic (char *args, int from_tty)
+cmd_record_call_history (char *arg, int from_tty)
 {
-  char *recfilename, recfilename_buffer[40];
-  FILE *fp;
-  struct cleanup *old_cleanups, *set_cleanups;
-  struct regcache *regcache;
-  struct gdbarch *gdbarch;
-  struct record_entry *cur_record_list;
-  char prev_node[256], line[256];
-  CORE_ADDR prev_addr;
-  struct frame_id fi, caller_fi, prev_fi, prev_caller_fi;
-  struct function_list *function_list_tail, *function_list_prev;
-  struct edge_list *edge_list_tail = NULL;
-  struct node_list *node_list_tail = NULL;
-  struct symtab_and_line sal, prev_sal;
-  struct node_list *prev_nlp;
-  struct node_list prev_nlp_real;
-  int fid_count = 1;
-
-  /* Check if record target is running.  */
-  if (current_target.to_stratum != record_stratum)
-    error (_("This command can only be used with target 'record' \
-or target 'record-core'."));
-
-  if (args && *args)
-    recfilename = args;
+  int flags, size;
+
+  require_record_target ();
+
+  flags = get_call_history_modifiers (&arg);
+
+  size = command_size_to_target_size (record_call_history_size);
+
+  if (arg == NULL || *arg == 0 || strcmp (arg, "+") == 0)
+    target_call_history (size, flags);
+  else if (strcmp (arg, "-") == 0)
+    target_call_history (-size, flags);
   else
     {
-      /* Default recfile name is "gdb_record_PID.vcg".  */
-      snprintf (recfilename_buffer, sizeof (recfilename_buffer),
-                "gdb_record_%d.vcg", PIDGET (inferior_ptid));
-      recfilename = recfilename_buffer;
-    }
+      ULONGEST begin, end;
 
-  /* Open the output file.  */
-  fp = fopen (recfilename, "wb");
-  if (!fp)
-    error (_("Unable to open file '%s'"), recfilename);
+      begin = get_insn_number (&arg);
 
-  old_cleanups = make_cleanup (record_pic_cleanups, fp);
+      if (*arg == ',')
+       {
+         arg = skip_spaces (++arg);
 
-  /* Save the current record entry to "cur_record_list".  */
-  cur_record_list = record_list;
+         if (*arg == '+')
+           {
+             arg += 1;
+             size = get_context_size (&arg);
 
-  /* Get the values of regcache and gdbarch.  */
-  regcache = get_current_regcache ();
-  gdbarch = get_regcache_arch (regcache);
+             no_chunk (arg);
 
-  /* Disable the GDB operation record.  */
-  set_cleanups = record_gdb_operation_disable_set ();
+             target_call_history_from (begin, size, flags);
+           }
+         else if (*arg == '-')
+           {
+             arg += 1;
+             size = get_context_size (&arg);
 
-  /* Reverse execute to the begin of record list.  */
-  while (1)
-    {
-      /* Check for beginning and end of log.  */
-      if (record_list == &record_first)
-        break;
+             no_chunk (arg);
 
-      record_exec_insn (regcache, gdbarch, record_list);
+             target_call_history_from (begin, -size, flags);
+           }
+         else
+           {
+             end = get_insn_number (&arg);
 
-      if (record_list->prev)
-        record_list = record_list->prev;
-    }
+             no_chunk (arg);
 
-  /* Write out the record log.  */
-  /* Write the head.  */
-  record_pic_fputs (fp, "graph: {title: \"GDB process record\"\n");
-
-  /* Write the first node.  */
-  record_pic_fputs (fp, "node: {title: \"[BEGIN]\" vertical_order:0}\n");
-
-  /* Initialization.  */
-  snprintf (prev_node, 256, "[BEGIN]");
-  prev_fi = null_frame_id;
-  prev_caller_fi = null_frame_id;
-  prev_addr = 0;
-  prev_sal.symtab = NULL;
-  prev_sal.pc = 0;
-  prev_sal.end = 0;
-  prev_nlp_real.addr = 0;
-  prev_nlp = &prev_nlp_real;
-
-  /* Create first entry for function_list.  */
-  function_list = xmalloc (sizeof (struct function_list));
-  function_list->next = NULL;
-  function_list->addr = 0;
-  function_list->fid = -1;
-  function_list_tail = function_list;
-  function_list_prev = function_list;
-
-  /* Save the entries to fp and forward execute to the end of
-     record list.  */
-  record_list = &record_first;
-  while (1)
-    {
-      if (record_list->type == record_end)
-        {
-          int frame_diff = 0;
-          CORE_ADDR addr = regcache_read_pc (regcache);
-
-          /* Check if the ADDR is stil in the same line with the
-             prev cycle.  */
-          if (prev_sal.symtab
-              && addr >= prev_sal.pc && addr < prev_sal.end)
-            goto exec;
-          sal = find_pc_line (addr, 0);
-
-          if (record_pic_hide_nosource && !sal.symtab)
-            goto exec;
-
-          /* Check if the inferior is in same frame with prev cycle.
-             Check both the current fi and caller fi because the last
-             addr of function is different with current function.  */
-          reinit_frame_cache ();
-          fi = get_frame_id (get_current_frame ());
-          caller_fi = frame_unwind_caller_id (get_current_frame ());
-          if (!frame_id_eq (prev_fi, fi)
-              && !frame_id_eq (prev_caller_fi, caller_fi))
-            frame_diff = 1;
-
-          if (set_record_pic_type == record_pic_line || frame_diff)
-            {
-              int is_return = 0;
-              struct node_list *nlp = NULL;
-              struct edge_list *elp = NULL;
-              char node[256];
-              struct minimal_symbol *function;
-
-             /* Get the node addr.  */
-              if (set_record_pic_type == record_pic_function)
-                {
-                  /* Get the start addr of function.  */
-                  addr = get_pc_function_start (addr);
-                  if (addr == 0)
-                    {
-                      if (record_pic_hide_nofunction)
-                        goto exec;
-                      addr = regcache_read_pc (regcache);
-                    }
-                }
-              else
-                {
-                  /* Get the start addr of line.  */
-                  if (sal.symtab)
-                    addr = sal.pc;
-                }
-
-              function = lookup_minimal_symbol_by_pc (addr);
-              if (!function && record_pic_hide_nofunction)
-                goto exec;
-
-              if (frame_id_eq (fi, prev_caller_fi))
-                is_return = 1;
-
-              if (record_pic_hide_same)
-                {
-                  /* Check if addr in node_list.  */
-                  for (nlp = node_list; nlp; nlp = nlp->next)
-                    {
-                      if (nlp->addr == addr)
-                        {
-                         if (!is_return
-                             || set_record_pic_type != record_pic_function)
-                            nlp->count ++;
-                          break;
-                        }
-                    }
-
-                  /* Check if prev_addr and addr in edge_list.  */
-                 if (nlp)
-                   {
-                      for (elp = edge_list; elp; elp = elp->next)
-                        {
-                          if (elp->s->addr == prev_addr && elp->t->addr == addr)
-                            {
-                              elp->count ++;
-                              break;
-                            }
-                        }
-                   }
-                }
-
-              if (!nlp)
-                {
-                  struct node_list nl;
-                  CORE_ADDR function_addr;
-                  struct function_list *flp;
-
-                  nl.addr = addr;
-                  if (frame_diff && sal.symtab)
-                    nl.showall = 1;
-                  else
-                    nl.showall = 0;
-                  nl.symtab = sal.symtab;
-                  nl.line = sal.line;
-                  nl.function = function;
-
-                  /* Get the fid of the nl.  */
-                  if (set_record_pic_type != record_pic_function)
-                    function_addr = get_pc_function_start (addr);
-                  else
-                    function_addr = addr;
-                  if (function_list_prev->addr == function_addr)
-                    nl.fid = function_list_prev->fid;
-                  else
-                    {
-                      for (flp = function_list; flp; flp = flp->next)
-                        {
-                          if (flp->addr == function_addr)
-                            {
-                              nl.fid = flp->fid;
-                              break;
-                            }
-                        }
-                      if (flp == NULL)
-                        {
-                          /* Creat a new entry to function_list.  */
-                          nl.fid = fid_count ++;
-                          flp = xmalloc (sizeof (struct function_list));
-                          flp->addr = function_addr;
-                          flp->fid = nl.fid;
-                          flp->next = NULL;
-                         function_list_tail->next = flp;
-                          function_list_tail = flp;
-                        }
-                      function_list_prev = flp;
-                    }
-
-                  if (record_pic_hide_same)
-                    {
-                      nlp = xmalloc (sizeof (struct node_list));
-                      *nlp = nl;
-                      nlp->count = 1;
-
-                      /* Add node to node_list.  */
-                      nlp->next = NULL;
-                     if (node_list_tail)
-                        node_list_tail->next = nlp;
-                     if (node_list == NULL)
-                       node_list = nlp;
-                      node_list_tail = nlp;
-                    }
-                  else
-                    {
-                      /* Draw the node.  */
-                      record_pic_node (node, 256, gdbarch,
-                                       set_record_pic_type, &nl);
-                     snprintf (line, 256, "%s i:%s", node,
-                               pulongest (record_list->u.end.insn_num));
-                     strcpy (node, line);
-                      snprintf (line, 256, "node: {title: \"%s\" "
-                                           "vertical_order: %d}\n",
-                                node, nl.fid);
-                      record_pic_fputs (fp, line);
-                    }
-                }
-
-              if (!elp)
-                {
-                  struct edge_list el;
-
-                  el.is_return = is_return;
-                  el.frame_diff = frame_diff;
-
-                  if (record_pic_hide_same)
-                    {
-                      elp = xmalloc (sizeof (struct edge_list));
-                      *elp = el;
-                     elp->s = prev_nlp;
-                      elp->t = nlp;
-                      elp->count = 1;
-
-                      /* Add edge to edge_list.  */
-                      elp->next = NULL;
-                     if (edge_list_tail)
-                        edge_list_tail->next = elp;
-                     if (edge_list == NULL)
-                       edge_list = elp;
-                      edge_list_tail = elp;
-                    }
-                  else
-                    {
-                      /* Draw the edge.  */
-                      record_pic_edge (line, 256, &el, node, prev_node);
-                      record_pic_fputs (fp, line);
-                     record_pic_fputs (fp, " }\n");
-                    }
-                }
-
-              if (record_pic_hide_same)
-                prev_nlp = nlp;
-              else
-                snprintf (prev_node, 256, "%s", node);
-              prev_addr = addr;
-            }
-
-          prev_sal = sal;
-          prev_fi = fi;
-          prev_caller_fi = caller_fi;
-        }
-
-exec:
-      /* Execute entry.  */
-      record_exec_insn (regcache, gdbarch, record_list);
-
-      if (record_list->next)
-        record_list = record_list->next;
+             target_call_history_range (begin, end, flags);
+           }
+       }
       else
-        break;
-    }
+       {
+         no_chunk (arg);
 
-  if (record_pic_hide_same)
-    {
-      struct node_list *nlp = NULL;
-      struct edge_list *elp = NULL;
-      char node[256];
-
-      for (nlp = node_list; nlp; nlp = nlp->next)
-        {
-          /* Draw the node.  */
-          record_pic_node (node, 256, gdbarch, set_record_pic_type, nlp);
-          snprintf (line, 256, "node: {title: \"%s c:%d\" "
-                               "vertical_order: %d}\n", node,
-                   nlp->count, nlp->fid);
-          record_pic_fputs (fp, line);
+         target_call_history_from (begin, size, flags);
        }
 
-      record_pic_node (node, 256, gdbarch, set_record_pic_type, edge_list->t);
-      snprintf (line, 256,
-               "edge: {color:red sourcename: \"[BEGIN]\" targetname: \"%s c:%d\"}\n",
-               node, edge_list->count);
-      record_pic_fputs (fp, line);
-      for (elp = edge_list->next; elp; elp = elp->next)
-        {
-          /* Draw the edge.  */
-         record_pic_node (prev_node, 256, gdbarch, set_record_pic_type,
-                          elp->s);
-         snprintf (line, 256, "%s c:%d", prev_node, elp->s->count);
-         strcpy (prev_node, line);
-         record_pic_node (node, 256, gdbarch, set_record_pic_type,
-                          elp->t);
-         snprintf (line, 256, "%s c:%d", node, elp->t->count);
-         strcpy (node, line);
-          record_pic_edge (line, 256, elp, node, prev_node);
-          record_pic_fputs (fp, line);
-          snprintf (line, 256, " label: \"c:%d\"}\n", elp->count);
-         record_pic_fputs (fp, line);
-        }
+      dont_repeat ();
     }
+}
 
-  /* Write the last node.  */
-  snprintf (line, 256, "node: {title: \"[END]\" vertical_order: %d}\n",
-            fid_count);
-  record_pic_fputs (fp, line);
-  snprintf (line, 256,
-           "edge: {color:red sourcename: \"%s\" targetname: \"[END]\" }\n",
-           prev_node);
-  record_pic_fputs (fp, line);
-
-  /* Write the tail.  */
-  record_pic_fputs (fp, "}\n");
+/* Helper for "set record instruction-history-size" and "set record
+   function-call-history-size" input validation.  COMMAND_VAR is the
+   variable registered in the command as control variable.  *SETTING
+   is the real setting the command allows changing.  */
 
-  /* Reverse execute to cur_record_list.  */
-  while (1)
+static void
+validate_history_size (unsigned int *command_var, unsigned int *setting)
+{
+  if (*command_var != UINT_MAX && *command_var > INT_MAX)
     {
-      /* Check for beginning and end of log.  */
-      if (record_list == cur_record_list)
-        break;
-
-      record_exec_insn (regcache, gdbarch, record_list);
+      unsigned int new_value = *command_var;
 
-      if (record_list->prev)
-        record_list = record_list->prev;
+      /* Restore previous value.  */
+      *command_var = *setting;
+      error (_("integer %u out of range"), new_value);
     }
 
-  do_cleanups (set_cleanups);
-  do_cleanups (old_cleanups);
-
-  /* Succeeded.  */
-  printf_filtered (_("Saved file %s with execution log.\n"),
-                  recfilename);
+  /* Commit new value.  */
+  *setting = *command_var;
 }
 
-/* record_goto_insn -- rewind the record log (forward or backward,
-   depending on DIR) to the given entry, changing the program state
-   correspondingly.  */
+/* Called by do_setshow_command.  We only want values in the
+   [0..INT_MAX] range, while the command's machinery accepts
+   [0..UINT_MAX].  See command_size_to_target_size.  */
 
 static void
-record_goto_insn (struct record_entry *entry,
-                 enum exec_direction_kind dir)
+set_record_insn_history_size (char *args, int from_tty,
+                             struct cmd_list_element *c)
 {
-  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);
+  validate_history_size (&record_insn_history_size_setshow_var,
+                        &record_insn_history_size);
 }
 
-/* "record goto" command.  Argument is an instruction number,
-   as given by "info record".
-
-   Rewinds the recording (forward or backward) to the given instruction.  */
+/* Called by do_setshow_command.  We only want values in the
+   [0..INT_MAX] range, while the command's machinery accepts
+   [0..UINT_MAX].  See command_size_to_target_size.  */
 
 static void
-cmd_record_goto (char *arg, int from_tty)
+set_record_call_history_size (char *args, int from_tty,
+                             struct cmd_list_element *c)
 {
-  struct record_entry *p = NULL;
-  ULONGEST target_insn = 0;
-
-  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;
-    }
-  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;
-    }
-  else
-    {
-      /* General case.  Find designated insn.  */
-      target_insn = parse_and_eval_long (arg);
-
-      for (p = &record_first; p != NULL; p = p->next)
-       if (p->type == record_end && p->u.end.insn_num == target_insn)
-         break;
-    }
-
-  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);
+  validate_history_size (&record_call_history_size_setshow_var,
+                        &record_call_history_size);
 }
 
+/* Provide a prototype to silence -Wmissing-prototypes.  */
+extern initialize_file_ftype _initialize_record;
+
 void
 _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_zinteger_cmd ("record", no_class, &record_debug,
-                           _("Set debugging of record/replay feature."),
-                           _("Show debugging of record/replay feature."),
-                           _("When enabled, debugging output for "
-                             "record/replay feature is displayed."),
-                           NULL, show_record_debug, &setdebuglist,
-                           &showdebuglist);
+  add_setshow_zuinteger_cmd ("record", no_class, &record_debug,
+                            _("Set debugging of record/replay feature."),
+                            _("Show debugging of record/replay feature."),
+                            _("When enabled, debugging output for "
+                              "record/replay feature is displayed."),
+                            NULL, show_record_debug, &setdebuglist,
+                            &showdebuglist);
+
+  add_setshow_uinteger_cmd ("instruction-history-size", no_class,
+                           &record_insn_history_size_setshow_var, _("\
+Set number of instructions to print in \"record instruction-history\"."), _("\
+Show number of instructions to print in \"record instruction-history\"."), _("\
+A size of \"unlimited\" means unlimited instructions.  The default is 10."),
+                           set_record_insn_history_size, NULL,
+                           &set_record_cmdlist, &show_record_cmdlist);
+
+  add_setshow_uinteger_cmd ("function-call-history-size", no_class,
+                           &record_call_history_size_setshow_var, _("\
+Set number of function to print in \"record function-call-history\"."), _("\
+Show number of functions to print in \"record function-call-history\"."), _("\
+A size of \"unlimited\" means unlimited lines.  The default is 10."),
+                           set_record_call_history_size, NULL,
+                           &set_record_cmdlist, &show_record_cmdlist);
 
   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);
 
@@ -3242,12 +791,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);
@@ -3259,100 +802,61 @@ 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, _("\
+  add_prefix_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);
-
-  /* For "record pic" command.  */
-  c = add_cmd ("pic", class_obscure, cmd_record_pic,
-              _("Save the execution log to a vcg file.\n\
-Argument is optional filename.\n\
-Default filename is 'gdb_record_<process_id>.vcg'."),
-              &record_cmdlist);
-  set_cmd_completer (c, filename_completer);
-  add_prefix_cmd ("pic", class_support, set_record_pic_command,
-                 _("Set record pic options"), &set_record_pic_cmdlist,
-                 "set record pic ", 0, &set_record_cmdlist);
-  add_prefix_cmd ("pic", class_support, show_record_pic_command,
-                 _("Show record pic options"), &show_record_pic_cmdlist,
-                 "show record pic ", 0, &show_record_cmdlist);
-  add_setshow_enum_cmd ("type", no_class,
-                       record_pic_enum, &set_record_pic_type, _("\
-Set the type of the nodes that record pic command saved."), _("\
-Show the type of the nodes that record pic command saved."), _("\
-When LINE, each node of vcg file that command record pic saved\n\
-will be a line of the inferior.\n\
-When FUNCTION, each node of vcg file that command record pic saved\n\
-will be a function of the inferior."),
-                       NULL, NULL,
-                       &set_record_pic_cmdlist, &show_record_pic_cmdlist);
-  add_setshow_boolean_cmd ("hide-nofunction", no_class,
-                          &record_pic_hide_nofunction, _("\
-Set whether record pic command hide the nodes that don't have the function name."), _("\
-Show whether record pic command hide the nodes that don't have the function name."), _("\
-Default is ON.\n\
-When ON, record pic command will hide the nodes that don't have\n\
-the function name.\n\
-When OFF, record pic command will show the nodes that don't have\n\
-the function name."),
-                          NULL, NULL,
-                          &set_record_pic_cmdlist, &show_record_pic_cmdlist);
-  add_setshow_boolean_cmd ("hide-nosource", no_class,
-                          &record_pic_hide_nosource, _("\
-Set whether record pic command hide the nodes that don't have the source message."), _("\
-Show whether record pic command hide the nodes that don't have the source message."), _("\
-Default is ON.\n\
-When ON, record pic command will hide the nodes that don't have\n\
-the source message.\n\
-When OFF, record pic command will show the nodes that don't have\n\
-the source message."),
-                          NULL, NULL,
-                          &set_record_pic_cmdlist, &show_record_pic_cmdlist);
-  add_setshow_boolean_cmd ("hide-sameaddr", no_class,
-                          &record_pic_hide_same, _("\
-Set whether record pic command hide the nodes that have the same address node in vcg file."), _("\
-Show whether record pic command hide the nodes that have the same address node in vcg file."), _("\
-Default is ON.\n\
-When ON, record pic command will hide the nodes that have\n\
-the same address node in vcg file.\n\
-And record pic will show the execute count number of this line\n\
-in format \"c:number\".\n\
-When OFF, record pic command will show the nodes that have\n\
-the same address node in vcg file.\n\
-And record pic show the instruction number in format \"i:number\"\n\
-that \"record goto\" support."),
-                          NULL, NULL,
-                          &set_record_pic_cmdlist, &show_record_pic_cmdlist);
+                 &record_goto_cmdlist, "record goto ", 1, &record_cmdlist);
+
+  add_cmd ("begin", class_obscure, cmd_record_goto_begin,
+          _("Go to the beginning of the execution log."),
+          &record_goto_cmdlist);
+  add_alias_cmd ("start", "begin", class_obscure, 1, &record_goto_cmdlist);
+
+  add_cmd ("end", class_obscure, cmd_record_goto_end,
+          _("Go to the end of the execution log."),
+          &record_goto_cmdlist);
+
+  add_cmd ("instruction-history", class_obscure, cmd_record_insn_history, _("\
+Print disassembled instructions stored in the execution log.\n\
+With a /m or /s modifier, source lines are included (if available).\n\
+With a /r modifier, raw instructions in hex are included.\n\
+With a /f modifier, function names are omitted.\n\
+With a /p modifier, current position markers are omitted.\n\
+With no argument, disassembles ten more instructions after the previous \
+disassembly.\n\
+\"record instruction-history -\" disassembles ten instructions before a \
+previous disassembly.\n\
+One argument specifies an instruction number as shown by 'info record', and \
+ten instructions are disassembled after that instruction.\n\
+Two arguments with comma between them specify starting and ending instruction \
+numbers to disassemble.\n\
+If the second argument is preceded by '+' or '-', it specifies the distance \
+from the first argument.\n\
+The number of instructions to disassemble can be defined with \"set record \
+instruction-history-size\"."),
+           &record_cmdlist);
+
+  add_cmd ("function-call-history", class_obscure, cmd_record_call_history, _("\
+Prints the execution history at function granularity.\n\
+It prints one line for each sequence of instructions that belong to the same \
+function.\n\
+Without modifiers, it prints the function name.\n\
+With a /l modifier, the source file and line number range is included.\n\
+With a /i modifier, the instruction number range is included.\n\
+With a /c modifier, the output is indented based on the call stack depth.\n\
+With no argument, prints ten more lines after the previous ten-line print.\n\
+\"record function-call-history -\" prints ten lines before a previous ten-line \
+print.\n\
+One argument specifies a function number as shown by 'info record', and \
+ten lines are printed after that function.\n\
+Two arguments with comma between them specify a range of functions to print.\n\
+If the second argument is preceded by '+' or '-', it specifies the distance \
+from the first argument.\n\
+The number of functions to print can be defined with \"set record \
+function-call-history-size\"."),
+           &record_cmdlist);
+
+  /* Sync command control variables.  */
+  record_insn_history_size_setshow_var = record_insn_history_size;
+  record_call_history_size_setshow_var = record_call_history_size;
 }