OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-old/] [gdb-7.1/] [gdb/] [record.c] - Diff between revs 834 and 842

Go to most recent revision | Only display areas with differences | Details | Blame | View Log

Rev 834 Rev 842
/* Process record and replay target for GDB, the GNU debugger.
/* Process record and replay target for GDB, the GNU debugger.
 
 
   Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
   Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
 
 
   This file is part of GDB.
   This file is part of GDB.
 
 
   This program is free software; you can redistribute it and/or modify
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.
   (at your option) any later version.
 
 
   This program is distributed in the hope that it will be useful,
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   GNU General Public License for more details.
 
 
   You should have received a copy of the GNU General Public License
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 
#include "defs.h"
#include "defs.h"
#include "gdbcmd.h"
#include "gdbcmd.h"
#include "regcache.h"
#include "regcache.h"
#include "gdbthread.h"
#include "gdbthread.h"
#include "event-top.h"
#include "event-top.h"
#include "exceptions.h"
#include "exceptions.h"
#include "completer.h"
#include "completer.h"
#include "arch-utils.h"
#include "arch-utils.h"
#include "gdbcore.h"
#include "gdbcore.h"
#include "exec.h"
#include "exec.h"
#include "record.h"
#include "record.h"
#include "elf-bfd.h"
#include "elf-bfd.h"
#include "gcore.h"
#include "gcore.h"
 
 
#include <signal.h>
#include <signal.h>
 
 
/* This module implements "target record", also known as "process
/* This module implements "target record", also known as "process
   record and replay".  This target sits on top of a "normal" target
   record and replay".  This target sits on top of a "normal" target
   (a target that "has execution"), and provides a record and replay
   (a target that "has execution"), and provides a record and replay
   functionality, including reverse debugging.
   functionality, including reverse debugging.
 
 
   Target record has two modes: recording, and replaying.
   Target record has two modes: recording, and replaying.
 
 
   In record mode, we intercept the to_resume and to_wait methods.
   In record mode, we intercept the to_resume and to_wait methods.
   Whenever gdb resumes the target, we run the target in single step
   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
   mode, and we build up an execution log in which, for each executed
   instruction, we record all changes in memory and register state.
   instruction, we record all changes in memory and register state.
   This is invisible to the user, to whom it just looks like an
   This is invisible to the user, to whom it just looks like an
   ordinary debugging session (except for performance degredation).
   ordinary debugging session (except for performance degredation).
 
 
   In replay mode, instead of actually letting the inferior run as a
   In replay mode, instead of actually letting the inferior run as a
   process, we simulate its execution by playing back the recorded
   process, we simulate its execution by playing back the recorded
   execution log.  For each instruction in the log, we simulate the
   execution log.  For each instruction in the log, we simulate the
   instruction's side effects by duplicating the changes that it would
   instruction's side effects by duplicating the changes that it would
   have made on memory and registers.  */
   have made on memory and registers.  */
 
 
#define DEFAULT_RECORD_INSN_MAX_NUM     200000
#define DEFAULT_RECORD_INSN_MAX_NUM     200000
 
 
#define RECORD_IS_REPLAY \
#define RECORD_IS_REPLAY \
     (record_list->next || execution_direction == EXEC_REVERSE)
     (record_list->next || execution_direction == EXEC_REVERSE)
 
 
#define RECORD_FILE_MAGIC       netorder32(0x20091016)
#define RECORD_FILE_MAGIC       netorder32(0x20091016)
 
 
/* These are the core structs of the process record functionality.
/* These are the core structs of the process record functionality.
 
 
   A record_entry is a record of the value change of a register
   A record_entry is a record of the value change of a register
   ("record_reg") or a part of memory ("record_mem").  And each
   ("record_reg") or a part of memory ("record_mem").  And each
   instruction must have a struct record_entry ("record_end") that
   instruction must have a struct record_entry ("record_end") that
   indicates that this is the last struct record_entry of this
   indicates that this is the last struct record_entry of this
   instruction.
   instruction.
 
 
   Each struct record_entry is linked to "record_list" by "prev" and
   Each struct record_entry is linked to "record_list" by "prev" and
   "next" pointers.  */
   "next" pointers.  */
 
 
struct record_mem_entry
struct record_mem_entry
{
{
  CORE_ADDR addr;
  CORE_ADDR addr;
  int len;
  int len;
  /* Set this flag if target memory for this entry
  /* Set this flag if target memory for this entry
     can no longer be accessed.  */
     can no longer be accessed.  */
  int mem_entry_not_accessible;
  int mem_entry_not_accessible;
  union
  union
  {
  {
    gdb_byte *ptr;
    gdb_byte *ptr;
    gdb_byte buf[sizeof (gdb_byte *)];
    gdb_byte buf[sizeof (gdb_byte *)];
  } u;
  } u;
};
};
 
 
struct record_reg_entry
struct record_reg_entry
{
{
  unsigned short num;
  unsigned short num;
  unsigned short len;
  unsigned short len;
  union
  union
  {
  {
    gdb_byte *ptr;
    gdb_byte *ptr;
    gdb_byte buf[2 * sizeof (gdb_byte *)];
    gdb_byte buf[2 * sizeof (gdb_byte *)];
  } u;
  } u;
};
};
 
 
struct record_end_entry
struct record_end_entry
{
{
  enum target_signal sigval;
  enum target_signal sigval;
  ULONGEST insn_num;
  ULONGEST insn_num;
};
};
 
 
enum record_type
enum record_type
{
{
  record_end = 0,
  record_end = 0,
  record_reg,
  record_reg,
  record_mem
  record_mem
};
};
 
 
/* This is the data structure that makes up the execution log.
/* This is the data structure that makes up the execution log.
 
 
   The execution log consists of a single linked list of entries
   The execution log consists of a single linked list of entries
   of type "struct record_entry".  It is doubly linked so that it
   of type "struct record_entry".  It is doubly linked so that it
   can be traversed in either direction.
   can be traversed in either direction.
 
 
   The start of the list is anchored by a struct called
   The start of the list is anchored by a struct called
   "record_first".  The pointer "record_list" either points to the
   "record_first".  The pointer "record_list" either points to the
   last entry that was added to the list (in record mode), or 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).
   next entry in the list that will be executed (in replay mode).
 
 
   Each list element (struct record_entry), in addition to next and
   Each list element (struct record_entry), in addition to next and
   prev pointers, consists of a union of three entry types: mem, reg,
   prev pointers, consists of a union of three entry types: mem, reg,
   and end.  A field called "type" determines which entry type is
   and end.  A field called "type" determines which entry type is
   represented by a given list element.
   represented by a given list element.
 
 
   Each instruction that is added to the execution log is represented
   Each instruction that is added to the execution log is represented
   by a variable number of list elements ('entries').  The instruction
   by a variable number of list elements ('entries').  The instruction
   will have one "reg" entry for each register that is changed by
   will have one "reg" entry for each register that is changed by
   executing the instruction (including the PC in every case).  It
   executing the instruction (including the PC in every case).  It
   will also have one "mem" entry for each memory change.  Finally,
   will also have one "mem" entry for each memory change.  Finally,
   each instruction will have an "end" entry that separates it from
   each instruction will have an "end" entry that separates it from
   the changes associated with the next instruction.  */
   the changes associated with the next instruction.  */
 
 
struct record_entry
struct record_entry
{
{
  struct record_entry *prev;
  struct record_entry *prev;
  struct record_entry *next;
  struct record_entry *next;
  enum record_type type;
  enum record_type type;
  union
  union
  {
  {
    /* reg */
    /* reg */
    struct record_reg_entry reg;
    struct record_reg_entry reg;
    /* mem */
    /* mem */
    struct record_mem_entry mem;
    struct record_mem_entry mem;
    /* end */
    /* end */
    struct record_end_entry end;
    struct record_end_entry end;
  } u;
  } u;
};
};
 
 
/* This is the debug switch for process record.  */
/* This is the debug switch for process record.  */
int record_debug = 0;
int record_debug = 0;
 
 
struct record_core_buf_entry
struct record_core_buf_entry
{
{
  struct record_core_buf_entry *prev;
  struct record_core_buf_entry *prev;
  struct target_section *p;
  struct target_section *p;
  bfd_byte *buf;
  bfd_byte *buf;
};
};
 
 
/* Record buf with core target.  */
/* Record buf with core target.  */
static gdb_byte *record_core_regbuf = NULL;
static gdb_byte *record_core_regbuf = NULL;
static struct target_section *record_core_start;
static struct target_section *record_core_start;
static struct target_section *record_core_end;
static struct target_section *record_core_end;
static struct record_core_buf_entry *record_core_buf_list = NULL;
static struct record_core_buf_entry *record_core_buf_list = NULL;
 
 
/* The following variables are used for managing the linked list that
/* The following variables are used for managing the linked list that
   represents the execution log.
   represents the execution log.
 
 
   record_first is the anchor that holds down the beginning of the list.
   record_first is the anchor that holds down the beginning of the list.
 
 
   record_list serves two functions:
   record_list serves two functions:
     1) In record mode, it anchors the end of the list.
     1) In record mode, it anchors the end of the list.
     2) In replay mode, it traverses the list and points to
     2) In replay mode, it traverses the list and points to
        the next instruction that must be emulated.
        the next instruction that must be emulated.
 
 
   record_arch_list_head and record_arch_list_tail are used to manage
   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
   a separate list, which is used to build up the change elements of
   the currently executing instruction during record mode.  When this
   the currently executing instruction during record mode.  When this
   instruction has been completely annotated in the "arch list", it
   instruction has been completely annotated in the "arch list", it
   will be appended to the main execution log.  */
   will be appended to the main execution log.  */
 
 
static struct record_entry record_first;
static struct record_entry record_first;
static struct record_entry *record_list = &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_head = NULL;
static struct record_entry *record_arch_list_tail = NULL;
static struct record_entry *record_arch_list_tail = NULL;
 
 
/* 1 ask user. 0 auto delete the last struct record_entry.  */
/* 1 ask user. 0 auto delete the last struct record_entry.  */
static int record_stop_at_limit = 1;
static int record_stop_at_limit = 1;
/* Maximum allowed number of insns in execution log.  */
/* Maximum allowed number of insns in execution log.  */
static unsigned int record_insn_max_num = DEFAULT_RECORD_INSN_MAX_NUM;
static unsigned int record_insn_max_num = DEFAULT_RECORD_INSN_MAX_NUM;
/* Actual count of insns presently in execution log.  */
/* Actual count of insns presently in execution log.  */
static int record_insn_num = 0;
static int record_insn_num = 0;
/* Count of insns logged so far (may be larger
/* Count of insns logged so far (may be larger
   than count of insns presently in execution log).  */
   than count of insns presently in execution log).  */
static ULONGEST record_insn_count;
static ULONGEST record_insn_count;
 
 
/* The target_ops of process record.  */
/* The target_ops of process record.  */
static struct target_ops record_ops;
static struct target_ops record_ops;
static struct target_ops record_core_ops;
static struct target_ops record_core_ops;
 
 
/* The beneath function pointers.  */
/* The beneath function pointers.  */
static struct target_ops *record_beneath_to_resume_ops;
static struct target_ops *record_beneath_to_resume_ops;
static void (*record_beneath_to_resume) (struct target_ops *, ptid_t, int,
static void (*record_beneath_to_resume) (struct target_ops *, ptid_t, int,
                                         enum target_signal);
                                         enum target_signal);
static struct target_ops *record_beneath_to_wait_ops;
static struct target_ops *record_beneath_to_wait_ops;
static ptid_t (*record_beneath_to_wait) (struct target_ops *, ptid_t,
static ptid_t (*record_beneath_to_wait) (struct target_ops *, ptid_t,
                                         struct target_waitstatus *,
                                         struct target_waitstatus *,
                                         int);
                                         int);
static struct target_ops *record_beneath_to_store_registers_ops;
static struct target_ops *record_beneath_to_store_registers_ops;
static void (*record_beneath_to_store_registers) (struct target_ops *,
static void (*record_beneath_to_store_registers) (struct target_ops *,
                                                  struct regcache *,
                                                  struct regcache *,
                                                  int regno);
                                                  int regno);
static struct target_ops *record_beneath_to_xfer_partial_ops;
static struct target_ops *record_beneath_to_xfer_partial_ops;
static LONGEST (*record_beneath_to_xfer_partial) (struct target_ops *ops,
static LONGEST (*record_beneath_to_xfer_partial) (struct target_ops *ops,
                                                  enum target_object object,
                                                  enum target_object object,
                                                  const char *annex,
                                                  const char *annex,
                                                  gdb_byte *readbuf,
                                                  gdb_byte *readbuf,
                                                  const gdb_byte *writebuf,
                                                  const gdb_byte *writebuf,
                                                  ULONGEST offset,
                                                  ULONGEST offset,
                                                  LONGEST len);
                                                  LONGEST len);
static int (*record_beneath_to_insert_breakpoint) (struct gdbarch *,
static int (*record_beneath_to_insert_breakpoint) (struct gdbarch *,
                                                   struct bp_target_info *);
                                                   struct bp_target_info *);
static int (*record_beneath_to_remove_breakpoint) (struct gdbarch *,
static int (*record_beneath_to_remove_breakpoint) (struct gdbarch *,
                                                   struct bp_target_info *);
                                                   struct bp_target_info *);
static int (*record_beneath_to_stopped_by_watchpoint) (void);
static int (*record_beneath_to_stopped_by_watchpoint) (void);
static int (*record_beneath_to_stopped_data_address) (struct target_ops *,
static int (*record_beneath_to_stopped_data_address) (struct target_ops *,
                                                      CORE_ADDR *);
                                                      CORE_ADDR *);
 
 
/* Alloc and free functions for record_reg, record_mem, and record_end
/* Alloc and free functions for record_reg, record_mem, and record_end
   entries.  */
   entries.  */
 
 
/* Alloc a record_reg record entry.  */
/* Alloc a record_reg record entry.  */
 
 
static inline struct record_entry *
static inline struct record_entry *
record_reg_alloc (struct regcache *regcache, int regnum)
record_reg_alloc (struct regcache *regcache, int regnum)
{
{
  struct record_entry *rec;
  struct record_entry *rec;
  struct gdbarch *gdbarch = get_regcache_arch (regcache);
  struct gdbarch *gdbarch = get_regcache_arch (regcache);
 
 
  rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry));
  rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry));
  rec->type = record_reg;
  rec->type = record_reg;
  rec->u.reg.num = regnum;
  rec->u.reg.num = regnum;
  rec->u.reg.len = register_size (gdbarch, regnum);
  rec->u.reg.len = register_size (gdbarch, regnum);
  if (rec->u.reg.len > sizeof (rec->u.reg.u.buf))
  if (rec->u.reg.len > sizeof (rec->u.reg.u.buf))
    rec->u.reg.u.ptr = (gdb_byte *) xmalloc (rec->u.reg.len);
    rec->u.reg.u.ptr = (gdb_byte *) xmalloc (rec->u.reg.len);
 
 
  return rec;
  return rec;
}
}
 
 
/* Free a record_reg record entry.  */
/* Free a record_reg record entry.  */
 
 
static inline void
static inline void
record_reg_release (struct record_entry *rec)
record_reg_release (struct record_entry *rec)
{
{
  gdb_assert (rec->type == record_reg);
  gdb_assert (rec->type == record_reg);
  if (rec->u.reg.len > sizeof (rec->u.reg.u.buf))
  if (rec->u.reg.len > sizeof (rec->u.reg.u.buf))
    xfree (rec->u.reg.u.ptr);
    xfree (rec->u.reg.u.ptr);
  xfree (rec);
  xfree (rec);
}
}
 
 
/* Alloc a record_mem record entry.  */
/* Alloc a record_mem record entry.  */
 
 
static inline struct record_entry *
static inline struct record_entry *
record_mem_alloc (CORE_ADDR addr, int len)
record_mem_alloc (CORE_ADDR addr, int len)
{
{
  struct record_entry *rec;
  struct record_entry *rec;
 
 
  rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry));
  rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry));
  rec->type = record_mem;
  rec->type = record_mem;
  rec->u.mem.addr = addr;
  rec->u.mem.addr = addr;
  rec->u.mem.len = len;
  rec->u.mem.len = len;
  if (rec->u.mem.len > sizeof (rec->u.mem.u.buf))
  if (rec->u.mem.len > sizeof (rec->u.mem.u.buf))
    rec->u.mem.u.ptr = (gdb_byte *) xmalloc (len);
    rec->u.mem.u.ptr = (gdb_byte *) xmalloc (len);
 
 
  return rec;
  return rec;
}
}
 
 
/* Free a record_mem record entry.  */
/* Free a record_mem record entry.  */
 
 
static inline void
static inline void
record_mem_release (struct record_entry *rec)
record_mem_release (struct record_entry *rec)
{
{
  gdb_assert (rec->type == record_mem);
  gdb_assert (rec->type == record_mem);
  if (rec->u.mem.len > sizeof (rec->u.mem.u.buf))
  if (rec->u.mem.len > sizeof (rec->u.mem.u.buf))
    xfree (rec->u.mem.u.ptr);
    xfree (rec->u.mem.u.ptr);
  xfree (rec);
  xfree (rec);
}
}
 
 
/* Alloc a record_end record entry.  */
/* Alloc a record_end record entry.  */
 
 
static inline struct record_entry *
static inline struct record_entry *
record_end_alloc (void)
record_end_alloc (void)
{
{
  struct record_entry *rec;
  struct record_entry *rec;
 
 
  rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry));
  rec = (struct record_entry *) xcalloc (1, sizeof (struct record_entry));
  rec->type = record_end;
  rec->type = record_end;
 
 
  return rec;
  return rec;
}
}
 
 
/* Free a record_end record entry.  */
/* Free a record_end record entry.  */
 
 
static inline void
static inline void
record_end_release (struct record_entry *rec)
record_end_release (struct record_entry *rec)
{
{
  xfree (rec);
  xfree (rec);
}
}
 
 
/* Free one record entry, any type.
/* Free one record entry, any type.
   Return entry->type, in case caller wants to know.  */
   Return entry->type, in case caller wants to know.  */
 
 
static inline enum record_type
static inline enum record_type
record_entry_release (struct record_entry *rec)
record_entry_release (struct record_entry *rec)
{
{
  enum record_type type = rec->type;
  enum record_type type = rec->type;
 
 
  switch (type) {
  switch (type) {
  case record_reg:
  case record_reg:
    record_reg_release (rec);
    record_reg_release (rec);
    break;
    break;
  case record_mem:
  case record_mem:
    record_mem_release (rec);
    record_mem_release (rec);
    break;
    break;
  case record_end:
  case record_end:
    record_end_release (rec);
    record_end_release (rec);
    break;
    break;
  }
  }
  return type;
  return type;
}
}
 
 
/* Free all record entries in list pointed to by REC.  */
/* Free all record entries in list pointed to by REC.  */
 
 
static void
static void
record_list_release (struct record_entry *rec)
record_list_release (struct record_entry *rec)
{
{
  if (!rec)
  if (!rec)
    return;
    return;
 
 
  while (rec->next)
  while (rec->next)
    rec = rec->next;
    rec = rec->next;
 
 
  while (rec->prev)
  while (rec->prev)
    {
    {
      rec = rec->prev;
      rec = rec->prev;
      record_entry_release (rec->next);
      record_entry_release (rec->next);
    }
    }
 
 
  if (rec == &record_first)
  if (rec == &record_first)
    {
    {
      record_insn_num = 0;
      record_insn_num = 0;
      record_first.next = NULL;
      record_first.next = NULL;
    }
    }
  else
  else
    record_entry_release (rec);
    record_entry_release (rec);
}
}
 
 
/* Free all record entries forward of the given list position.  */
/* Free all record entries forward of the given list position.  */
 
 
static void
static void
record_list_release_following (struct record_entry *rec)
record_list_release_following (struct record_entry *rec)
{
{
  struct record_entry *tmp = rec->next;
  struct record_entry *tmp = rec->next;
 
 
  rec->next = NULL;
  rec->next = NULL;
  while (tmp)
  while (tmp)
    {
    {
      rec = tmp->next;
      rec = tmp->next;
      if (record_entry_release (tmp) == record_end)
      if (record_entry_release (tmp) == record_end)
        {
        {
          record_insn_num--;
          record_insn_num--;
          record_insn_count--;
          record_insn_count--;
        }
        }
      tmp = rec;
      tmp = rec;
    }
    }
}
}
 
 
/* Delete the first instruction from the beginning of the log, to make
/* Delete the first instruction from the beginning of the log, to make
   room for adding a new instruction at the end of the log.
   room for adding a new instruction at the end of the log.
 
 
   Note -- this function does not modify record_insn_num.  */
   Note -- this function does not modify record_insn_num.  */
 
 
static void
static void
record_list_release_first (void)
record_list_release_first (void)
{
{
  struct record_entry *tmp;
  struct record_entry *tmp;
 
 
  if (!record_first.next)
  if (!record_first.next)
    return;
    return;
 
 
  /* Loop until a record_end.  */
  /* Loop until a record_end.  */
  while (1)
  while (1)
    {
    {
      /* Cut record_first.next out of the linked list.  */
      /* Cut record_first.next out of the linked list.  */
      tmp = record_first.next;
      tmp = record_first.next;
      record_first.next = tmp->next;
      record_first.next = tmp->next;
      tmp->next->prev = &record_first;
      tmp->next->prev = &record_first;
 
 
      /* tmp is now isolated, and can be deleted.  */
      /* tmp is now isolated, and can be deleted.  */
      if (record_entry_release (tmp) == record_end)
      if (record_entry_release (tmp) == record_end)
        break;  /* End loop at first record_end.  */
        break;  /* End loop at first record_end.  */
 
 
      if (!record_first.next)
      if (!record_first.next)
        {
        {
          gdb_assert (record_insn_num == 1);
          gdb_assert (record_insn_num == 1);
          break;        /* End loop when list is empty.  */
          break;        /* End loop when list is empty.  */
        }
        }
    }
    }
}
}
 
 
/* Add a struct record_entry to record_arch_list.  */
/* Add a struct record_entry to record_arch_list.  */
 
 
static void
static void
record_arch_list_add (struct record_entry *rec)
record_arch_list_add (struct record_entry *rec)
{
{
  if (record_debug > 1)
  if (record_debug > 1)
    fprintf_unfiltered (gdb_stdlog,
    fprintf_unfiltered (gdb_stdlog,
                        "Process record: record_arch_list_add %s.\n",
                        "Process record: record_arch_list_add %s.\n",
                        host_address_to_string (rec));
                        host_address_to_string (rec));
 
 
  if (record_arch_list_tail)
  if (record_arch_list_tail)
    {
    {
      record_arch_list_tail->next = rec;
      record_arch_list_tail->next = rec;
      rec->prev = record_arch_list_tail;
      rec->prev = record_arch_list_tail;
      record_arch_list_tail = rec;
      record_arch_list_tail = rec;
    }
    }
  else
  else
    {
    {
      record_arch_list_head = rec;
      record_arch_list_head = rec;
      record_arch_list_tail = rec;
      record_arch_list_tail = rec;
    }
    }
}
}
 
 
/* Return the value storage location of a record entry.  */
/* Return the value storage location of a record entry.  */
static inline gdb_byte *
static inline gdb_byte *
record_get_loc (struct record_entry *rec)
record_get_loc (struct record_entry *rec)
{
{
  switch (rec->type) {
  switch (rec->type) {
  case record_mem:
  case record_mem:
    if (rec->u.mem.len > sizeof (rec->u.mem.u.buf))
    if (rec->u.mem.len > sizeof (rec->u.mem.u.buf))
      return rec->u.mem.u.ptr;
      return rec->u.mem.u.ptr;
    else
    else
      return rec->u.mem.u.buf;
      return rec->u.mem.u.buf;
  case record_reg:
  case record_reg:
    if (rec->u.reg.len > sizeof (rec->u.reg.u.buf))
    if (rec->u.reg.len > sizeof (rec->u.reg.u.buf))
      return rec->u.reg.u.ptr;
      return rec->u.reg.u.ptr;
    else
    else
      return rec->u.reg.u.buf;
      return rec->u.reg.u.buf;
  case record_end:
  case record_end:
  default:
  default:
    gdb_assert (0);
    gdb_assert (0);
    return NULL;
    return NULL;
  }
  }
}
}
 
 
/* Record the value of a register NUM to record_arch_list.  */
/* Record the value of a register NUM to record_arch_list.  */
 
 
int
int
record_arch_list_add_reg (struct regcache *regcache, int regnum)
record_arch_list_add_reg (struct regcache *regcache, int regnum)
{
{
  struct record_entry *rec;
  struct record_entry *rec;
 
 
  if (record_debug > 1)
  if (record_debug > 1)
    fprintf_unfiltered (gdb_stdlog,
    fprintf_unfiltered (gdb_stdlog,
                        "Process record: add register num = %d to "
                        "Process record: add register num = %d to "
                        "record list.\n",
                        "record list.\n",
                        regnum);
                        regnum);
 
 
  rec = record_reg_alloc (regcache, regnum);
  rec = record_reg_alloc (regcache, regnum);
 
 
  regcache_raw_read (regcache, regnum, record_get_loc (rec));
  regcache_raw_read (regcache, regnum, record_get_loc (rec));
 
 
  record_arch_list_add (rec);
  record_arch_list_add (rec);
 
 
  return 0;
  return 0;
}
}
 
 
/* Record the value of a region of memory whose address is ADDR and
/* Record the value of a region of memory whose address is ADDR and
   length is LEN to record_arch_list.  */
   length is LEN to record_arch_list.  */
 
 
int
int
record_arch_list_add_mem (CORE_ADDR addr, int len)
record_arch_list_add_mem (CORE_ADDR addr, int len)
{
{
  struct record_entry *rec;
  struct record_entry *rec;
 
 
  if (record_debug > 1)
  if (record_debug > 1)
    fprintf_unfiltered (gdb_stdlog,
    fprintf_unfiltered (gdb_stdlog,
                        "Process record: add mem addr = %s len = %d to "
                        "Process record: add mem addr = %s len = %d to "
                        "record list.\n",
                        "record list.\n",
                        paddress (target_gdbarch, addr), len);
                        paddress (target_gdbarch, addr), len);
 
 
  if (!addr)    /* FIXME: Why?  Some arch must permit it... */
  if (!addr)    /* FIXME: Why?  Some arch must permit it... */
    return 0;
    return 0;
 
 
  rec = record_mem_alloc (addr, len);
  rec = record_mem_alloc (addr, len);
 
 
  if (target_read_memory (addr, record_get_loc (rec), len))
  if (target_read_memory (addr, record_get_loc (rec), len))
    {
    {
      if (record_debug)
      if (record_debug)
        fprintf_unfiltered (gdb_stdlog,
        fprintf_unfiltered (gdb_stdlog,
                            "Process record: error reading memory at "
                            "Process record: error reading memory at "
                            "addr = %s len = %d.\n",
                            "addr = %s len = %d.\n",
                            paddress (target_gdbarch, addr), len);
                            paddress (target_gdbarch, addr), len);
      record_mem_release (rec);
      record_mem_release (rec);
      return -1;
      return -1;
    }
    }
 
 
  record_arch_list_add (rec);
  record_arch_list_add (rec);
 
 
  return 0;
  return 0;
}
}
 
 
/* Add a record_end type struct record_entry to record_arch_list.  */
/* Add a record_end type struct record_entry to record_arch_list.  */
 
 
int
int
record_arch_list_add_end (void)
record_arch_list_add_end (void)
{
{
  struct record_entry *rec;
  struct record_entry *rec;
 
 
  if (record_debug > 1)
  if (record_debug > 1)
    fprintf_unfiltered (gdb_stdlog,
    fprintf_unfiltered (gdb_stdlog,
                        "Process record: add end to arch list.\n");
                        "Process record: add end to arch list.\n");
 
 
  rec = record_end_alloc ();
  rec = record_end_alloc ();
  rec->u.end.sigval = TARGET_SIGNAL_0;
  rec->u.end.sigval = TARGET_SIGNAL_0;
  rec->u.end.insn_num = ++record_insn_count;
  rec->u.end.insn_num = ++record_insn_count;
 
 
  record_arch_list_add (rec);
  record_arch_list_add (rec);
 
 
  return 0;
  return 0;
}
}
 
 
static void
static void
record_check_insn_num (int set_terminal)
record_check_insn_num (int set_terminal)
{
{
  if (record_insn_max_num)
  if (record_insn_max_num)
    {
    {
      gdb_assert (record_insn_num <= record_insn_max_num);
      gdb_assert (record_insn_num <= record_insn_max_num);
      if (record_insn_num == record_insn_max_num)
      if (record_insn_num == record_insn_max_num)
        {
        {
          /* Ask user what to do.  */
          /* Ask user what to do.  */
          if (record_stop_at_limit)
          if (record_stop_at_limit)
            {
            {
              int q;
              int q;
              if (set_terminal)
              if (set_terminal)
                target_terminal_ours ();
                target_terminal_ours ();
              q = yquery (_("Do you want to auto delete previous execution "
              q = yquery (_("Do you want to auto delete previous execution "
                            "log entries when record/replay buffer becomes "
                            "log entries when record/replay buffer becomes "
                            "full (record stop-at-limit)?"));
                            "full (record stop-at-limit)?"));
              if (set_terminal)
              if (set_terminal)
                target_terminal_inferior ();
                target_terminal_inferior ();
              if (q)
              if (q)
                record_stop_at_limit = 0;
                record_stop_at_limit = 0;
              else
              else
                error (_("Process record: stopped by user."));
                error (_("Process record: stopped by user."));
            }
            }
        }
        }
    }
    }
}
}
 
 
static void
static void
record_arch_list_cleanups (void *ignore)
record_arch_list_cleanups (void *ignore)
{
{
  record_list_release (record_arch_list_tail);
  record_list_release (record_arch_list_tail);
}
}
 
 
/* Before inferior step (when GDB record the running message, inferior
/* Before inferior step (when GDB record the running message, inferior
   only can step), GDB will call this function to record the values to
   only can step), GDB will call this function to record the values to
   record_list.  This function will call gdbarch_process_record to
   record_list.  This function will call gdbarch_process_record to
   record the running message of inferior and set them to
   record the running message of inferior and set them to
   record_arch_list, and add it to record_list.  */
   record_arch_list, and add it to record_list.  */
 
 
static int
static int
record_message (struct regcache *regcache, enum target_signal signal)
record_message (struct regcache *regcache, enum target_signal signal)
{
{
  int ret;
  int ret;
  struct gdbarch *gdbarch = get_regcache_arch (regcache);
  struct gdbarch *gdbarch = get_regcache_arch (regcache);
  struct cleanup *old_cleanups = make_cleanup (record_arch_list_cleanups, 0);
  struct cleanup *old_cleanups = make_cleanup (record_arch_list_cleanups, 0);
 
 
  record_arch_list_head = NULL;
  record_arch_list_head = NULL;
  record_arch_list_tail = NULL;
  record_arch_list_tail = NULL;
 
 
  /* Check record_insn_num.  */
  /* Check record_insn_num.  */
  record_check_insn_num (1);
  record_check_insn_num (1);
 
 
  /* If gdb sends a signal value to target_resume,
  /* If gdb sends a signal value to target_resume,
     save it in the 'end' field of the previous instruction.
     save it in the 'end' field of the previous instruction.
 
 
     Maybe process record should record what really happened,
     Maybe process record should record what really happened,
     rather than what gdb pretends has happened.
     rather than what gdb pretends has happened.
 
 
     So if Linux delivered the signal to the child process during
     So if Linux delivered the signal to the child process during
     the record mode, we will record it and deliver it again in
     the record mode, we will record it and deliver it again in
     the replay mode.
     the replay mode.
 
 
     If user says "ignore this signal" during the record mode, then
     If user says "ignore this signal" during the record mode, then
     it will be ignored again during the replay mode (no matter if
     it will be ignored again during the replay mode (no matter if
     the user says something different, like "deliver this signal"
     the user says something different, like "deliver this signal"
     during the replay mode).
     during the replay mode).
 
 
     User should understand that nothing he does during the replay
     User should understand that nothing he does during the replay
     mode will change the behavior of the child.  If he tries,
     mode will change the behavior of the child.  If he tries,
     then that is a user error.
     then that is a user error.
 
 
     But we should still deliver the signal to gdb during the replay,
     But we should still deliver the signal to gdb during the replay,
     if we delivered it during the recording.  Therefore we should
     if we delivered it during the recording.  Therefore we should
     record the signal during record_wait, not record_resume.  */
     record the signal during record_wait, not record_resume.  */
  if (record_list != &record_first)    /* FIXME better way to check */
  if (record_list != &record_first)    /* FIXME better way to check */
    {
    {
      gdb_assert (record_list->type == record_end);
      gdb_assert (record_list->type == record_end);
      record_list->u.end.sigval = signal;
      record_list->u.end.sigval = signal;
    }
    }
 
 
  if (signal == TARGET_SIGNAL_0
  if (signal == TARGET_SIGNAL_0
      || !gdbarch_process_record_signal_p (gdbarch))
      || !gdbarch_process_record_signal_p (gdbarch))
    ret = gdbarch_process_record (gdbarch,
    ret = gdbarch_process_record (gdbarch,
                                  regcache,
                                  regcache,
                                  regcache_read_pc (regcache));
                                  regcache_read_pc (regcache));
  else
  else
    ret = gdbarch_process_record_signal (gdbarch,
    ret = gdbarch_process_record_signal (gdbarch,
                                         regcache,
                                         regcache,
                                         signal);
                                         signal);
 
 
  if (ret > 0)
  if (ret > 0)
    error (_("Process record: inferior program stopped."));
    error (_("Process record: inferior program stopped."));
  if (ret < 0)
  if (ret < 0)
    error (_("Process record: failed to record execution log."));
    error (_("Process record: failed to record execution log."));
 
 
  discard_cleanups (old_cleanups);
  discard_cleanups (old_cleanups);
 
 
  record_list->next = record_arch_list_head;
  record_list->next = record_arch_list_head;
  record_arch_list_head->prev = record_list;
  record_arch_list_head->prev = record_list;
  record_list = record_arch_list_tail;
  record_list = record_arch_list_tail;
 
 
  if (record_insn_num == record_insn_max_num && record_insn_max_num)
  if (record_insn_num == record_insn_max_num && record_insn_max_num)
    record_list_release_first ();
    record_list_release_first ();
  else
  else
    record_insn_num++;
    record_insn_num++;
 
 
  return 1;
  return 1;
}
}
 
 
struct record_message_args {
struct record_message_args {
  struct regcache *regcache;
  struct regcache *regcache;
  enum target_signal signal;
  enum target_signal signal;
};
};
 
 
static int
static int
record_message_wrapper (void *args)
record_message_wrapper (void *args)
{
{
  struct record_message_args *record_args = args;
  struct record_message_args *record_args = args;
 
 
  return record_message (record_args->regcache, record_args->signal);
  return record_message (record_args->regcache, record_args->signal);
}
}
 
 
static int
static int
record_message_wrapper_safe (struct regcache *regcache,
record_message_wrapper_safe (struct regcache *regcache,
                             enum target_signal signal)
                             enum target_signal signal)
{
{
  struct record_message_args args;
  struct record_message_args args;
 
 
  args.regcache = regcache;
  args.regcache = regcache;
  args.signal = signal;
  args.signal = signal;
 
 
  return catch_errors (record_message_wrapper, &args, NULL, RETURN_MASK_ALL);
  return catch_errors (record_message_wrapper, &args, NULL, RETURN_MASK_ALL);
}
}
 
 
/* Set to 1 if record_store_registers and record_xfer_partial
/* Set to 1 if record_store_registers and record_xfer_partial
   doesn't need record.  */
   doesn't need record.  */
 
 
static int record_gdb_operation_disable = 0;
static int record_gdb_operation_disable = 0;
 
 
struct cleanup *
struct cleanup *
record_gdb_operation_disable_set (void)
record_gdb_operation_disable_set (void)
{
{
  struct cleanup *old_cleanups = NULL;
  struct cleanup *old_cleanups = NULL;
 
 
  old_cleanups =
  old_cleanups =
    make_cleanup_restore_integer (&record_gdb_operation_disable);
    make_cleanup_restore_integer (&record_gdb_operation_disable);
  record_gdb_operation_disable = 1;
  record_gdb_operation_disable = 1;
 
 
  return old_cleanups;
  return old_cleanups;
}
}
 
 
/* Flag set to TRUE for target_stopped_by_watchpoint.  */
/* Flag set to TRUE for target_stopped_by_watchpoint.  */
static int record_hw_watchpoint = 0;
static int record_hw_watchpoint = 0;
 
 
/* Execute one instruction from the record log.  Each instruction in
/* Execute one instruction from the record log.  Each instruction in
   the log will be represented by an arbitrary sequence of register
   the log will be represented by an arbitrary sequence of register
   entries and memory entries, followed by an 'end' entry.  */
   entries and memory entries, followed by an 'end' entry.  */
 
 
static inline void
static inline void
record_exec_insn (struct regcache *regcache, struct gdbarch *gdbarch,
record_exec_insn (struct regcache *regcache, struct gdbarch *gdbarch,
                  struct record_entry *entry)
                  struct record_entry *entry)
{
{
  switch (entry->type)
  switch (entry->type)
    {
    {
    case record_reg: /* reg */
    case record_reg: /* reg */
      {
      {
        gdb_byte reg[MAX_REGISTER_SIZE];
        gdb_byte reg[MAX_REGISTER_SIZE];
 
 
        if (record_debug > 1)
        if (record_debug > 1)
          fprintf_unfiltered (gdb_stdlog,
          fprintf_unfiltered (gdb_stdlog,
                              "Process record: record_reg %s to "
                              "Process record: record_reg %s to "
                              "inferior num = %d.\n",
                              "inferior num = %d.\n",
                              host_address_to_string (entry),
                              host_address_to_string (entry),
                              entry->u.reg.num);
                              entry->u.reg.num);
 
 
        regcache_cooked_read (regcache, entry->u.reg.num, reg);
        regcache_cooked_read (regcache, entry->u.reg.num, reg);
        regcache_cooked_write (regcache, entry->u.reg.num,
        regcache_cooked_write (regcache, entry->u.reg.num,
                               record_get_loc (entry));
                               record_get_loc (entry));
        memcpy (record_get_loc (entry), reg, entry->u.reg.len);
        memcpy (record_get_loc (entry), reg, entry->u.reg.len);
      }
      }
      break;
      break;
 
 
    case record_mem: /* mem */
    case record_mem: /* mem */
      {
      {
        /* Nothing to do if the entry is flagged not_accessible.  */
        /* Nothing to do if the entry is flagged not_accessible.  */
        if (!entry->u.mem.mem_entry_not_accessible)
        if (!entry->u.mem.mem_entry_not_accessible)
          {
          {
            gdb_byte *mem = alloca (entry->u.mem.len);
            gdb_byte *mem = alloca (entry->u.mem.len);
 
 
            if (record_debug > 1)
            if (record_debug > 1)
              fprintf_unfiltered (gdb_stdlog,
              fprintf_unfiltered (gdb_stdlog,
                                  "Process record: record_mem %s to "
                                  "Process record: record_mem %s to "
                                  "inferior addr = %s len = %d.\n",
                                  "inferior addr = %s len = %d.\n",
                                  host_address_to_string (entry),
                                  host_address_to_string (entry),
                                  paddress (gdbarch, entry->u.mem.addr),
                                  paddress (gdbarch, entry->u.mem.addr),
                                  entry->u.mem.len);
                                  entry->u.mem.len);
 
 
            if (target_read_memory (entry->u.mem.addr, mem, 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;
                entry->u.mem.mem_entry_not_accessible = 1;
                if (record_debug)
                if (record_debug)
                  warning ("Process record: error reading memory at "
                  warning ("Process record: error reading memory at "
                           "addr = %s len = %d.",
                           "addr = %s len = %d.",
                           paddress (gdbarch, entry->u.mem.addr),
                           paddress (gdbarch, entry->u.mem.addr),
                           entry->u.mem.len);
                           entry->u.mem.len);
              }
              }
            else
            else
              {
              {
                if (target_write_memory (entry->u.mem.addr,
                if (target_write_memory (entry->u.mem.addr,
                                         record_get_loc (entry),
                                         record_get_loc (entry),
                                         entry->u.mem.len))
                                         entry->u.mem.len))
                  {
                  {
                    entry->u.mem.mem_entry_not_accessible = 1;
                    entry->u.mem.mem_entry_not_accessible = 1;
                    if (record_debug)
                    if (record_debug)
                      warning ("Process record: error writing memory at "
                      warning ("Process record: error writing memory at "
                               "addr = %s len = %d.",
                               "addr = %s len = %d.",
                               paddress (gdbarch, entry->u.mem.addr),
                               paddress (gdbarch, entry->u.mem.addr),
                               entry->u.mem.len);
                               entry->u.mem.len);
                  }
                  }
                else
                else
                  {
                  {
                    memcpy (record_get_loc (entry), mem, entry->u.mem.len);
                    memcpy (record_get_loc (entry), mem, entry->u.mem.len);
 
 
                    /* We've changed memory --- check if a hardware
                    /* We've changed memory --- check if a hardware
                       watchpoint should trap.  Note that this
                       watchpoint should trap.  Note that this
                       presently assumes the target beneath supports
                       presently assumes the target beneath supports
                       continuable watchpoints.  On non-continuable
                       continuable watchpoints.  On non-continuable
                       watchpoints target, we'll want to check this
                       watchpoints target, we'll want to check this
                       _before_ actually doing the memory change, and
                       _before_ actually doing the memory change, and
                       not doing the change at all if the watchpoint
                       not doing the change at all if the watchpoint
                       traps.  */
                       traps.  */
                    if (hardware_watchpoint_inserted_in_range
                    if (hardware_watchpoint_inserted_in_range
                        (get_regcache_aspace (regcache),
                        (get_regcache_aspace (regcache),
                         entry->u.mem.addr, entry->u.mem.len))
                         entry->u.mem.addr, entry->u.mem.len))
                      record_hw_watchpoint = 1;
                      record_hw_watchpoint = 1;
                  }
                  }
              }
              }
          }
          }
      }
      }
      break;
      break;
    }
    }
}
}
 
 
static struct target_ops *tmp_to_resume_ops;
static struct target_ops *tmp_to_resume_ops;
static void (*tmp_to_resume) (struct target_ops *, ptid_t, int,
static void (*tmp_to_resume) (struct target_ops *, ptid_t, int,
                              enum target_signal);
                              enum target_signal);
static struct target_ops *tmp_to_wait_ops;
static struct target_ops *tmp_to_wait_ops;
static ptid_t (*tmp_to_wait) (struct target_ops *, ptid_t,
static ptid_t (*tmp_to_wait) (struct target_ops *, ptid_t,
                              struct target_waitstatus *,
                              struct target_waitstatus *,
                              int);
                              int);
static struct target_ops *tmp_to_store_registers_ops;
static struct target_ops *tmp_to_store_registers_ops;
static void (*tmp_to_store_registers) (struct target_ops *,
static void (*tmp_to_store_registers) (struct target_ops *,
                                       struct regcache *,
                                       struct regcache *,
                                       int regno);
                                       int regno);
static struct target_ops *tmp_to_xfer_partial_ops;
static struct target_ops *tmp_to_xfer_partial_ops;
static LONGEST (*tmp_to_xfer_partial) (struct target_ops *ops,
static LONGEST (*tmp_to_xfer_partial) (struct target_ops *ops,
                                       enum target_object object,
                                       enum target_object object,
                                       const char *annex,
                                       const char *annex,
                                       gdb_byte *readbuf,
                                       gdb_byte *readbuf,
                                       const gdb_byte *writebuf,
                                       const gdb_byte *writebuf,
                                       ULONGEST offset,
                                       ULONGEST offset,
                                       LONGEST len);
                                       LONGEST len);
static int (*tmp_to_insert_breakpoint) (struct gdbarch *,
static int (*tmp_to_insert_breakpoint) (struct gdbarch *,
                                        struct bp_target_info *);
                                        struct bp_target_info *);
static int (*tmp_to_remove_breakpoint) (struct gdbarch *,
static int (*tmp_to_remove_breakpoint) (struct gdbarch *,
                                        struct bp_target_info *);
                                        struct bp_target_info *);
static int (*tmp_to_stopped_by_watchpoint) (void);
static int (*tmp_to_stopped_by_watchpoint) (void);
static int (*tmp_to_stopped_data_address) (struct target_ops *, CORE_ADDR *);
static int (*tmp_to_stopped_data_address) (struct target_ops *, CORE_ADDR *);
 
 
static void record_restore (void);
static void record_restore (void);
 
 
/* Open the process record target.  */
/* Open the process record target.  */
 
 
static void
static void
record_core_open_1 (char *name, int from_tty)
record_core_open_1 (char *name, int from_tty)
{
{
  struct regcache *regcache = get_current_regcache ();
  struct regcache *regcache = get_current_regcache ();
  int regnum = gdbarch_num_regs (get_regcache_arch (regcache));
  int regnum = gdbarch_num_regs (get_regcache_arch (regcache));
  int i;
  int i;
 
 
  /* Get record_core_regbuf.  */
  /* Get record_core_regbuf.  */
  target_fetch_registers (regcache, -1);
  target_fetch_registers (regcache, -1);
  record_core_regbuf = xmalloc (MAX_REGISTER_SIZE * regnum);
  record_core_regbuf = xmalloc (MAX_REGISTER_SIZE * regnum);
  for (i = 0; i < regnum; i ++)
  for (i = 0; i < regnum; i ++)
    regcache_raw_collect (regcache, i,
    regcache_raw_collect (regcache, i,
                          record_core_regbuf + MAX_REGISTER_SIZE * i);
                          record_core_regbuf + MAX_REGISTER_SIZE * i);
 
 
  /* Get record_core_start and record_core_end.  */
  /* Get record_core_start and record_core_end.  */
  if (build_section_table (core_bfd, &record_core_start, &record_core_end))
  if (build_section_table (core_bfd, &record_core_start, &record_core_end))
    {
    {
      xfree (record_core_regbuf);
      xfree (record_core_regbuf);
      record_core_regbuf = NULL;
      record_core_regbuf = NULL;
      error (_("\"%s\": Can't find sections: %s"),
      error (_("\"%s\": Can't find sections: %s"),
             bfd_get_filename (core_bfd), bfd_errmsg (bfd_get_error ()));
             bfd_get_filename (core_bfd), bfd_errmsg (bfd_get_error ()));
    }
    }
 
 
  push_target (&record_core_ops);
  push_target (&record_core_ops);
  record_restore ();
  record_restore ();
}
}
 
 
/* "to_open" target method for 'live' processes.  */
/* "to_open" target method for 'live' processes.  */
 
 
static void
static void
record_open_1 (char *name, int from_tty)
record_open_1 (char *name, int from_tty)
{
{
  struct target_ops *t;
  struct target_ops *t;
 
 
  if (record_debug)
  if (record_debug)
    fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n");
    fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n");
 
 
  /* check exec */
  /* check exec */
  if (!target_has_execution)
  if (!target_has_execution)
    error (_("Process record: the program is not being run."));
    error (_("Process record: the program is not being run."));
  if (non_stop)
  if (non_stop)
    error (_("Process record target can't debug inferior in non-stop mode "
    error (_("Process record target can't debug inferior in non-stop mode "
             "(non-stop)."));
             "(non-stop)."));
  if (target_async_permitted)
  if (target_async_permitted)
    error (_("Process record target can't debug inferior in asynchronous "
    error (_("Process record target can't debug inferior in asynchronous "
             "mode (target-async)."));
             "mode (target-async)."));
 
 
  if (!gdbarch_process_record_p (target_gdbarch))
  if (!gdbarch_process_record_p (target_gdbarch))
    error (_("Process record: the current architecture doesn't support "
    error (_("Process record: the current architecture doesn't support "
             "record function."));
             "record function."));
 
 
  if (!tmp_to_resume)
  if (!tmp_to_resume)
    error (_("Could not find 'to_resume' method on the target stack."));
    error (_("Could not find 'to_resume' method on the target stack."));
  if (!tmp_to_wait)
  if (!tmp_to_wait)
    error (_("Could not find 'to_wait' method on the target stack."));
    error (_("Could not find 'to_wait' method on the target stack."));
  if (!tmp_to_store_registers)
  if (!tmp_to_store_registers)
    error (_("Could not find 'to_store_registers' method on the target stack."));
    error (_("Could not find 'to_store_registers' method on the target stack."));
  if (!tmp_to_insert_breakpoint)
  if (!tmp_to_insert_breakpoint)
    error (_("Could not find 'to_insert_breakpoint' method on the target stack."));
    error (_("Could not find 'to_insert_breakpoint' method on the target stack."));
  if (!tmp_to_remove_breakpoint)
  if (!tmp_to_remove_breakpoint)
    error (_("Could not find 'to_remove_breakpoint' method on the target stack."));
    error (_("Could not find 'to_remove_breakpoint' method on the target stack."));
 
 
  push_target (&record_ops);
  push_target (&record_ops);
}
}
 
 
/* "to_open" target method.  Open the process record target.  */
/* "to_open" target method.  Open the process record target.  */
 
 
static void
static void
record_open (char *name, int from_tty)
record_open (char *name, int from_tty)
{
{
  struct target_ops *t;
  struct target_ops *t;
 
 
  if (record_debug)
  if (record_debug)
    fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n");
    fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n");
 
 
  /* Check if record target is already running.  */
  /* Check if record target is already running.  */
  if (current_target.to_stratum == record_stratum)
  if (current_target.to_stratum == record_stratum)
    error (_("Process record target already running.  Use \"record stop\" to "
    error (_("Process record target already running.  Use \"record stop\" to "
             "stop record target first."));
             "stop record target first."));
 
 
  /* Reset the tmp beneath pointers.  */
  /* Reset the tmp beneath pointers.  */
  tmp_to_resume_ops = NULL;
  tmp_to_resume_ops = NULL;
  tmp_to_resume = NULL;
  tmp_to_resume = NULL;
  tmp_to_wait_ops = NULL;
  tmp_to_wait_ops = NULL;
  tmp_to_wait = NULL;
  tmp_to_wait = NULL;
  tmp_to_store_registers_ops = NULL;
  tmp_to_store_registers_ops = NULL;
  tmp_to_store_registers = NULL;
  tmp_to_store_registers = NULL;
  tmp_to_xfer_partial_ops = NULL;
  tmp_to_xfer_partial_ops = NULL;
  tmp_to_xfer_partial = NULL;
  tmp_to_xfer_partial = NULL;
  tmp_to_insert_breakpoint = NULL;
  tmp_to_insert_breakpoint = NULL;
  tmp_to_remove_breakpoint = NULL;
  tmp_to_remove_breakpoint = NULL;
 
 
  /* Set the beneath function pointers.  */
  /* Set the beneath function pointers.  */
  for (t = current_target.beneath; t != NULL; t = t->beneath)
  for (t = current_target.beneath; t != NULL; t = t->beneath)
    {
    {
      if (!tmp_to_resume)
      if (!tmp_to_resume)
        {
        {
          tmp_to_resume = t->to_resume;
          tmp_to_resume = t->to_resume;
          tmp_to_resume_ops = t;
          tmp_to_resume_ops = t;
        }
        }
      if (!tmp_to_wait)
      if (!tmp_to_wait)
        {
        {
          tmp_to_wait = t->to_wait;
          tmp_to_wait = t->to_wait;
          tmp_to_wait_ops = t;
          tmp_to_wait_ops = t;
        }
        }
      if (!tmp_to_store_registers)
      if (!tmp_to_store_registers)
        {
        {
          tmp_to_store_registers = t->to_store_registers;
          tmp_to_store_registers = t->to_store_registers;
          tmp_to_store_registers_ops = t;
          tmp_to_store_registers_ops = t;
        }
        }
      if (!tmp_to_xfer_partial)
      if (!tmp_to_xfer_partial)
        {
        {
          tmp_to_xfer_partial = t->to_xfer_partial;
          tmp_to_xfer_partial = t->to_xfer_partial;
          tmp_to_xfer_partial_ops = t;
          tmp_to_xfer_partial_ops = t;
        }
        }
      if (!tmp_to_insert_breakpoint)
      if (!tmp_to_insert_breakpoint)
        tmp_to_insert_breakpoint = t->to_insert_breakpoint;
        tmp_to_insert_breakpoint = t->to_insert_breakpoint;
      if (!tmp_to_remove_breakpoint)
      if (!tmp_to_remove_breakpoint)
        tmp_to_remove_breakpoint = t->to_remove_breakpoint;
        tmp_to_remove_breakpoint = t->to_remove_breakpoint;
      if (!tmp_to_stopped_by_watchpoint)
      if (!tmp_to_stopped_by_watchpoint)
        tmp_to_stopped_by_watchpoint = t->to_stopped_by_watchpoint;
        tmp_to_stopped_by_watchpoint = t->to_stopped_by_watchpoint;
      if (!tmp_to_stopped_data_address)
      if (!tmp_to_stopped_data_address)
        tmp_to_stopped_data_address = t->to_stopped_data_address;
        tmp_to_stopped_data_address = t->to_stopped_data_address;
    }
    }
  if (!tmp_to_xfer_partial)
  if (!tmp_to_xfer_partial)
    error (_("Could not find 'to_xfer_partial' method on the target stack."));
    error (_("Could not find 'to_xfer_partial' method on the target stack."));
 
 
  /* Reset */
  /* Reset */
  record_insn_num = 0;
  record_insn_num = 0;
  record_insn_count = 0;
  record_insn_count = 0;
  record_list = &record_first;
  record_list = &record_first;
  record_list->next = NULL;
  record_list->next = NULL;
 
 
  /* Set the tmp beneath pointers to beneath pointers.  */
  /* Set the tmp beneath pointers to beneath pointers.  */
  record_beneath_to_resume_ops = tmp_to_resume_ops;
  record_beneath_to_resume_ops = tmp_to_resume_ops;
  record_beneath_to_resume = tmp_to_resume;
  record_beneath_to_resume = tmp_to_resume;
  record_beneath_to_wait_ops = tmp_to_wait_ops;
  record_beneath_to_wait_ops = tmp_to_wait_ops;
  record_beneath_to_wait = tmp_to_wait;
  record_beneath_to_wait = tmp_to_wait;
  record_beneath_to_store_registers_ops = tmp_to_store_registers_ops;
  record_beneath_to_store_registers_ops = tmp_to_store_registers_ops;
  record_beneath_to_store_registers = tmp_to_store_registers;
  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_ops = tmp_to_xfer_partial_ops;
  record_beneath_to_xfer_partial = tmp_to_xfer_partial;
  record_beneath_to_xfer_partial = tmp_to_xfer_partial;
  record_beneath_to_insert_breakpoint = tmp_to_insert_breakpoint;
  record_beneath_to_insert_breakpoint = tmp_to_insert_breakpoint;
  record_beneath_to_remove_breakpoint = tmp_to_remove_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_by_watchpoint = tmp_to_stopped_by_watchpoint;
  record_beneath_to_stopped_data_address = tmp_to_stopped_data_address;
  record_beneath_to_stopped_data_address = tmp_to_stopped_data_address;
 
 
  if (current_target.to_stratum == core_stratum)
  if (current_target.to_stratum == core_stratum)
    record_core_open_1 (name, from_tty);
    record_core_open_1 (name, from_tty);
  else
  else
    record_open_1 (name, from_tty);
    record_open_1 (name, from_tty);
}
}
 
 
/* "to_close" target method.  Close the process record target.  */
/* "to_close" target method.  Close the process record target.  */
 
 
static void
static void
record_close (int quitting)
record_close (int quitting)
{
{
  struct record_core_buf_entry *entry;
  struct record_core_buf_entry *entry;
 
 
  if (record_debug)
  if (record_debug)
    fprintf_unfiltered (gdb_stdlog, "Process record: record_close\n");
    fprintf_unfiltered (gdb_stdlog, "Process record: record_close\n");
 
 
  record_list_release (record_list);
  record_list_release (record_list);
 
 
  /* Release record_core_regbuf.  */
  /* Release record_core_regbuf.  */
  if (record_core_regbuf)
  if (record_core_regbuf)
    {
    {
      xfree (record_core_regbuf);
      xfree (record_core_regbuf);
      record_core_regbuf = NULL;
      record_core_regbuf = NULL;
    }
    }
 
 
  /* Release record_core_buf_list.  */
  /* Release record_core_buf_list.  */
  if (record_core_buf_list)
  if (record_core_buf_list)
    {
    {
      for (entry = record_core_buf_list->prev; entry; entry = entry->prev)
      for (entry = record_core_buf_list->prev; entry; entry = entry->prev)
        {
        {
          xfree (record_core_buf_list);
          xfree (record_core_buf_list);
          record_core_buf_list = entry;
          record_core_buf_list = entry;
        }
        }
      record_core_buf_list = NULL;
      record_core_buf_list = NULL;
    }
    }
}
}
 
 
static int record_resume_step = 0;
static int record_resume_step = 0;
 
 
/* "to_resume" target method.  Resume the process record target.  */
/* "to_resume" target method.  Resume the process record target.  */
 
 
static void
static void
record_resume (struct target_ops *ops, ptid_t ptid, int step,
record_resume (struct target_ops *ops, ptid_t ptid, int step,
               enum target_signal signal)
               enum target_signal signal)
{
{
  record_resume_step = step;
  record_resume_step = step;
 
 
  if (!RECORD_IS_REPLAY)
  if (!RECORD_IS_REPLAY)
    {
    {
      record_message (get_current_regcache (), signal);
      record_message (get_current_regcache (), signal);
      record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1,
      record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1,
                                signal);
                                signal);
    }
    }
}
}
 
 
static int record_get_sig = 0;
static int record_get_sig = 0;
 
 
/* SIGINT signal handler, registered by "to_wait" method.  */
/* SIGINT signal handler, registered by "to_wait" method.  */
 
 
static void
static void
record_sig_handler (int signo)
record_sig_handler (int signo)
{
{
  if (record_debug)
  if (record_debug)
    fprintf_unfiltered (gdb_stdlog, "Process record: get a signal\n");
    fprintf_unfiltered (gdb_stdlog, "Process record: get a signal\n");
 
 
  /* It will break the running inferior in replay mode.  */
  /* It will break the running inferior in replay mode.  */
  record_resume_step = 1;
  record_resume_step = 1;
 
 
  /* It will let record_wait set inferior status to get the signal
  /* It will let record_wait set inferior status to get the signal
     SIGINT.  */
     SIGINT.  */
  record_get_sig = 1;
  record_get_sig = 1;
}
}
 
 
static void
static void
record_wait_cleanups (void *ignore)
record_wait_cleanups (void *ignore)
{
{
  if (execution_direction == EXEC_REVERSE)
  if (execution_direction == EXEC_REVERSE)
    {
    {
      if (record_list->next)
      if (record_list->next)
        record_list = record_list->next;
        record_list = record_list->next;
    }
    }
  else
  else
    record_list = record_list->prev;
    record_list = record_list->prev;
}
}
 
 
/* "to_wait" target method for process record target.
/* "to_wait" target method for process record target.
 
 
   In record mode, the target is always run in singlestep mode
   In record mode, the target is always run in singlestep mode
   (even when gdb says to continue).  The to_wait method intercepts
   (even when gdb says to continue).  The to_wait method intercepts
   the stop events and determines which ones are to be passed on to
   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
   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
   to know about, so the to_wait method just records them and keeps
   singlestepping.
   singlestepping.
 
 
   In replay mode, this function emulates the recorded execution log,
   In replay mode, this function emulates the recorded execution log,
   one instruction at a time (forward or backward), and determines
   one instruction at a time (forward or backward), and determines
   where to stop.  */
   where to stop.  */
 
 
static ptid_t
static ptid_t
record_wait (struct target_ops *ops,
record_wait (struct target_ops *ops,
             ptid_t ptid, struct target_waitstatus *status,
             ptid_t ptid, struct target_waitstatus *status,
             int options)
             int options)
{
{
  struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
  struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
 
 
  if (record_debug)
  if (record_debug)
    fprintf_unfiltered (gdb_stdlog,
    fprintf_unfiltered (gdb_stdlog,
                        "Process record: record_wait "
                        "Process record: record_wait "
                        "record_resume_step = %d\n",
                        "record_resume_step = %d\n",
                        record_resume_step);
                        record_resume_step);
 
 
  if (!RECORD_IS_REPLAY && ops != &record_core_ops)
  if (!RECORD_IS_REPLAY && ops != &record_core_ops)
    {
    {
      if (record_resume_step)
      if (record_resume_step)
        {
        {
          /* This is a single step.  */
          /* This is a single step.  */
          return record_beneath_to_wait (record_beneath_to_wait_ops,
          return record_beneath_to_wait (record_beneath_to_wait_ops,
                                         ptid, status, options);
                                         ptid, status, options);
        }
        }
      else
      else
        {
        {
          /* This is not a single step.  */
          /* This is not a single step.  */
          ptid_t ret;
          ptid_t ret;
          CORE_ADDR tmp_pc;
          CORE_ADDR tmp_pc;
 
 
          while (1)
          while (1)
            {
            {
              ret = record_beneath_to_wait (record_beneath_to_wait_ops,
              ret = record_beneath_to_wait (record_beneath_to_wait_ops,
                                            ptid, status, options);
                                            ptid, status, options);
 
 
              /* Is this a SIGTRAP?  */
              /* Is this a SIGTRAP?  */
              if (status->kind == TARGET_WAITKIND_STOPPED
              if (status->kind == TARGET_WAITKIND_STOPPED
                  && status->value.sig == TARGET_SIGNAL_TRAP)
                  && status->value.sig == TARGET_SIGNAL_TRAP)
                {
                {
                  struct regcache *regcache;
                  struct regcache *regcache;
                  struct address_space *aspace;
                  struct address_space *aspace;
 
 
                  /* Yes -- this is likely our single-step finishing,
                  /* Yes -- this is likely our single-step finishing,
                     but check if there's any reason the core would be
                     but check if there's any reason the core would be
                     interested in the event.  */
                     interested in the event.  */
 
 
                  registers_changed ();
                  registers_changed ();
                  regcache = get_current_regcache ();
                  regcache = get_current_regcache ();
                  tmp_pc = regcache_read_pc (regcache);
                  tmp_pc = regcache_read_pc (regcache);
                  aspace = get_regcache_aspace (regcache);
                  aspace = get_regcache_aspace (regcache);
 
 
                  if (target_stopped_by_watchpoint ())
                  if (target_stopped_by_watchpoint ())
                    {
                    {
                      /* Always interested in watchpoints.  */
                      /* Always interested in watchpoints.  */
                    }
                    }
                  else if (breakpoint_inserted_here_p (aspace, tmp_pc))
                  else if (breakpoint_inserted_here_p (aspace, tmp_pc))
                    {
                    {
                      /* There is a breakpoint here.  Let the core
                      /* There is a breakpoint here.  Let the core
                         handle it.  */
                         handle it.  */
                      if (software_breakpoint_inserted_here_p (aspace, tmp_pc))
                      if (software_breakpoint_inserted_here_p (aspace, tmp_pc))
                        {
                        {
                          struct gdbarch *gdbarch = get_regcache_arch (regcache);
                          struct gdbarch *gdbarch = get_regcache_arch (regcache);
                          CORE_ADDR decr_pc_after_break
                          CORE_ADDR decr_pc_after_break
                            = gdbarch_decr_pc_after_break (gdbarch);
                            = gdbarch_decr_pc_after_break (gdbarch);
                          if (decr_pc_after_break)
                          if (decr_pc_after_break)
                            regcache_write_pc (regcache,
                            regcache_write_pc (regcache,
                                               tmp_pc + decr_pc_after_break);
                                               tmp_pc + decr_pc_after_break);
                        }
                        }
                    }
                    }
                  else
                  else
                    {
                    {
                      /* This must be a single-step trap.  Record the
                      /* This must be a single-step trap.  Record the
                         insn and issue another step.  */
                         insn and issue another step.  */
                      if (!record_message_wrapper_safe (regcache,
                      if (!record_message_wrapper_safe (regcache,
                                                        TARGET_SIGNAL_0))
                                                        TARGET_SIGNAL_0))
                        {
                        {
                           status->kind = TARGET_WAITKIND_STOPPED;
                           status->kind = TARGET_WAITKIND_STOPPED;
                           status->value.sig = TARGET_SIGNAL_0;
                           status->value.sig = TARGET_SIGNAL_0;
                           break;
                           break;
                        }
                        }
 
 
                      record_beneath_to_resume (record_beneath_to_resume_ops,
                      record_beneath_to_resume (record_beneath_to_resume_ops,
                                                ptid, 1,
                                                ptid, 1,
                                                TARGET_SIGNAL_0);
                                                TARGET_SIGNAL_0);
                      continue;
                      continue;
                    }
                    }
                }
                }
 
 
              /* The inferior is broken by a breakpoint or a signal.  */
              /* The inferior is broken by a breakpoint or a signal.  */
              break;
              break;
            }
            }
 
 
          return ret;
          return ret;
        }
        }
    }
    }
  else
  else
    {
    {
      struct regcache *regcache = get_current_regcache ();
      struct regcache *regcache = get_current_regcache ();
      struct gdbarch *gdbarch = get_regcache_arch (regcache);
      struct gdbarch *gdbarch = get_regcache_arch (regcache);
      struct address_space *aspace = get_regcache_aspace (regcache);
      struct address_space *aspace = get_regcache_aspace (regcache);
      int continue_flag = 1;
      int continue_flag = 1;
      int first_record_end = 1;
      int first_record_end = 1;
      struct cleanup *old_cleanups = make_cleanup (record_wait_cleanups, 0);
      struct cleanup *old_cleanups = make_cleanup (record_wait_cleanups, 0);
      CORE_ADDR tmp_pc;
      CORE_ADDR tmp_pc;
 
 
      record_hw_watchpoint = 0;
      record_hw_watchpoint = 0;
      status->kind = TARGET_WAITKIND_STOPPED;
      status->kind = TARGET_WAITKIND_STOPPED;
 
 
      /* Check breakpoint when forward execute.  */
      /* Check breakpoint when forward execute.  */
      if (execution_direction == EXEC_FORWARD)
      if (execution_direction == EXEC_FORWARD)
        {
        {
          tmp_pc = regcache_read_pc (regcache);
          tmp_pc = regcache_read_pc (regcache);
          if (breakpoint_inserted_here_p (aspace, tmp_pc))
          if (breakpoint_inserted_here_p (aspace, tmp_pc))
            {
            {
              int decr_pc_after_break = gdbarch_decr_pc_after_break (gdbarch);
              int decr_pc_after_break = gdbarch_decr_pc_after_break (gdbarch);
 
 
              if (record_debug)
              if (record_debug)
                fprintf_unfiltered (gdb_stdlog,
                fprintf_unfiltered (gdb_stdlog,
                                    "Process record: break at %s.\n",
                                    "Process record: break at %s.\n",
                                    paddress (gdbarch, tmp_pc));
                                    paddress (gdbarch, tmp_pc));
 
 
              if (decr_pc_after_break
              if (decr_pc_after_break
                  && !record_resume_step
                  && !record_resume_step
                  && software_breakpoint_inserted_here_p (aspace, tmp_pc))
                  && software_breakpoint_inserted_here_p (aspace, tmp_pc))
                regcache_write_pc (regcache,
                regcache_write_pc (regcache,
                                   tmp_pc + decr_pc_after_break);
                                   tmp_pc + decr_pc_after_break);
              goto replay_out;
              goto replay_out;
            }
            }
        }
        }
 
 
      record_get_sig = 0;
      record_get_sig = 0;
      signal (SIGINT, record_sig_handler);
      signal (SIGINT, record_sig_handler);
      /* If GDB is in terminal_inferior mode, it will not get the signal.
      /* 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
         And in GDB replay mode, GDB doesn't need to be in terminal_inferior
         mode, because inferior will not executed.
         mode, because inferior will not executed.
         Then set it to terminal_ours to make GDB get the signal.  */
         Then set it to terminal_ours to make GDB get the signal.  */
      target_terminal_ours ();
      target_terminal_ours ();
 
 
      /* In EXEC_FORWARD mode, record_list points to the tail of prev
      /* In EXEC_FORWARD mode, record_list points to the tail of prev
         instruction.  */
         instruction.  */
      if (execution_direction == EXEC_FORWARD && record_list->next)
      if (execution_direction == EXEC_FORWARD && record_list->next)
        record_list = record_list->next;
        record_list = record_list->next;
 
 
      /* Loop over the record_list, looking for the next place to
      /* Loop over the record_list, looking for the next place to
         stop.  */
         stop.  */
      do
      do
        {
        {
          /* Check for beginning and end of log.  */
          /* Check for beginning and end of log.  */
          if (execution_direction == EXEC_REVERSE
          if (execution_direction == EXEC_REVERSE
              && record_list == &record_first)
              && record_list == &record_first)
            {
            {
              /* Hit beginning of record log in reverse.  */
              /* Hit beginning of record log in reverse.  */
              status->kind = TARGET_WAITKIND_NO_HISTORY;
              status->kind = TARGET_WAITKIND_NO_HISTORY;
              break;
              break;
            }
            }
          if (execution_direction != EXEC_REVERSE && !record_list->next)
          if (execution_direction != EXEC_REVERSE && !record_list->next)
            {
            {
              /* Hit end of record log going forward.  */
              /* Hit end of record log going forward.  */
              status->kind = TARGET_WAITKIND_NO_HISTORY;
              status->kind = TARGET_WAITKIND_NO_HISTORY;
              break;
              break;
            }
            }
 
 
          record_exec_insn (regcache, gdbarch, record_list);
          record_exec_insn (regcache, gdbarch, record_list);
 
 
          if (record_list->type == record_end)
          if (record_list->type == record_end)
            {
            {
              if (record_debug > 1)
              if (record_debug > 1)
                fprintf_unfiltered (gdb_stdlog,
                fprintf_unfiltered (gdb_stdlog,
                                    "Process record: record_end %s to "
                                    "Process record: record_end %s to "
                                    "inferior.\n",
                                    "inferior.\n",
                                    host_address_to_string (record_list));
                                    host_address_to_string (record_list));
 
 
              if (first_record_end && execution_direction == EXEC_REVERSE)
              if (first_record_end && execution_direction == EXEC_REVERSE)
                {
                {
                  /* When reverse excute, the first record_end is the part of
                  /* When reverse excute, the first record_end is the part of
                     current instruction.  */
                     current instruction.  */
                  first_record_end = 0;
                  first_record_end = 0;
                }
                }
              else
              else
                {
                {
                  /* In EXEC_REVERSE mode, this is the record_end of prev
                  /* In EXEC_REVERSE mode, this is the record_end of prev
                     instruction.
                     instruction.
                     In EXEC_FORWARD mode, this is the record_end of current
                     In EXEC_FORWARD mode, this is the record_end of current
                     instruction.  */
                     instruction.  */
                  /* step */
                  /* step */
                  if (record_resume_step)
                  if (record_resume_step)
                    {
                    {
                      if (record_debug > 1)
                      if (record_debug > 1)
                        fprintf_unfiltered (gdb_stdlog,
                        fprintf_unfiltered (gdb_stdlog,
                                            "Process record: step.\n");
                                            "Process record: step.\n");
                      continue_flag = 0;
                      continue_flag = 0;
                    }
                    }
 
 
                  /* check breakpoint */
                  /* check breakpoint */
                  tmp_pc = regcache_read_pc (regcache);
                  tmp_pc = regcache_read_pc (regcache);
                  if (breakpoint_inserted_here_p (aspace, tmp_pc))
                  if (breakpoint_inserted_here_p (aspace, tmp_pc))
                    {
                    {
                      int decr_pc_after_break
                      int decr_pc_after_break
                        = gdbarch_decr_pc_after_break (gdbarch);
                        = gdbarch_decr_pc_after_break (gdbarch);
 
 
                      if (record_debug)
                      if (record_debug)
                        fprintf_unfiltered (gdb_stdlog,
                        fprintf_unfiltered (gdb_stdlog,
                                            "Process record: break "
                                            "Process record: break "
                                            "at %s.\n",
                                            "at %s.\n",
                                            paddress (gdbarch, tmp_pc));
                                            paddress (gdbarch, tmp_pc));
                      if (decr_pc_after_break
                      if (decr_pc_after_break
                          && execution_direction == EXEC_FORWARD
                          && execution_direction == EXEC_FORWARD
                          && !record_resume_step
                          && !record_resume_step
                          && software_breakpoint_inserted_here_p (aspace,
                          && software_breakpoint_inserted_here_p (aspace,
                                                                  tmp_pc))
                                                                  tmp_pc))
                        regcache_write_pc (regcache,
                        regcache_write_pc (regcache,
                                           tmp_pc + decr_pc_after_break);
                                           tmp_pc + decr_pc_after_break);
                      continue_flag = 0;
                      continue_flag = 0;
                    }
                    }
 
 
                  if (record_hw_watchpoint)
                  if (record_hw_watchpoint)
                    {
                    {
                      if (record_debug)
                      if (record_debug)
                        fprintf_unfiltered (gdb_stdlog, "\
                        fprintf_unfiltered (gdb_stdlog, "\
Process record: hit hw watchpoint.\n");
Process record: hit hw watchpoint.\n");
                      continue_flag = 0;
                      continue_flag = 0;
                    }
                    }
                  /* Check target signal */
                  /* Check target signal */
                  if (record_list->u.end.sigval != TARGET_SIGNAL_0)
                  if (record_list->u.end.sigval != TARGET_SIGNAL_0)
                    /* FIXME: better way to check */
                    /* FIXME: better way to check */
                    continue_flag = 0;
                    continue_flag = 0;
                }
                }
            }
            }
 
 
          if (continue_flag)
          if (continue_flag)
            {
            {
              if (execution_direction == EXEC_REVERSE)
              if (execution_direction == EXEC_REVERSE)
                {
                {
                  if (record_list->prev)
                  if (record_list->prev)
                    record_list = record_list->prev;
                    record_list = record_list->prev;
                }
                }
              else
              else
                {
                {
                  if (record_list->next)
                  if (record_list->next)
                    record_list = record_list->next;
                    record_list = record_list->next;
                }
                }
            }
            }
        }
        }
      while (continue_flag);
      while (continue_flag);
 
 
      signal (SIGINT, handle_sigint);
      signal (SIGINT, handle_sigint);
 
 
replay_out:
replay_out:
      if (record_get_sig)
      if (record_get_sig)
        status->value.sig = TARGET_SIGNAL_INT;
        status->value.sig = TARGET_SIGNAL_INT;
      else if (record_list->u.end.sigval != TARGET_SIGNAL_0)
      else if (record_list->u.end.sigval != TARGET_SIGNAL_0)
        /* FIXME: better way to check */
        /* FIXME: better way to check */
        status->value.sig = record_list->u.end.sigval;
        status->value.sig = record_list->u.end.sigval;
      else
      else
        status->value.sig = TARGET_SIGNAL_TRAP;
        status->value.sig = TARGET_SIGNAL_TRAP;
 
 
      discard_cleanups (old_cleanups);
      discard_cleanups (old_cleanups);
    }
    }
 
 
  do_cleanups (set_cleanups);
  do_cleanups (set_cleanups);
  return inferior_ptid;
  return inferior_ptid;
}
}
 
 
static int
static int
record_stopped_by_watchpoint (void)
record_stopped_by_watchpoint (void)
{
{
  if (RECORD_IS_REPLAY)
  if (RECORD_IS_REPLAY)
    return record_hw_watchpoint;
    return record_hw_watchpoint;
  else
  else
    return record_beneath_to_stopped_by_watchpoint ();
    return record_beneath_to_stopped_by_watchpoint ();
}
}
 
 
static int
static int
record_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p)
record_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p)
{
{
  if (RECORD_IS_REPLAY)
  if (RECORD_IS_REPLAY)
    return 0;
    return 0;
  else
  else
    return record_beneath_to_stopped_data_address (ops, addr_p);
    return record_beneath_to_stopped_data_address (ops, addr_p);
}
}
 
 
/* "to_disconnect" method for process record target.  */
/* "to_disconnect" method for process record target.  */
 
 
static void
static void
record_disconnect (struct target_ops *target, char *args, int from_tty)
record_disconnect (struct target_ops *target, char *args, int from_tty)
{
{
  if (record_debug)
  if (record_debug)
    fprintf_unfiltered (gdb_stdlog, "Process record: record_disconnect\n");
    fprintf_unfiltered (gdb_stdlog, "Process record: record_disconnect\n");
 
 
  unpush_target (&record_ops);
  unpush_target (&record_ops);
  target_disconnect (args, from_tty);
  target_disconnect (args, from_tty);
}
}
 
 
/* "to_detach" method for process record target.  */
/* "to_detach" method for process record target.  */
 
 
static void
static void
record_detach (struct target_ops *ops, char *args, int from_tty)
record_detach (struct target_ops *ops, char *args, int from_tty)
{
{
  if (record_debug)
  if (record_debug)
    fprintf_unfiltered (gdb_stdlog, "Process record: record_detach\n");
    fprintf_unfiltered (gdb_stdlog, "Process record: record_detach\n");
 
 
  unpush_target (&record_ops);
  unpush_target (&record_ops);
  target_detach (args, from_tty);
  target_detach (args, from_tty);
}
}
 
 
/* "to_mourn_inferior" method for process record target.  */
/* "to_mourn_inferior" method for process record target.  */
 
 
static void
static void
record_mourn_inferior (struct target_ops *ops)
record_mourn_inferior (struct target_ops *ops)
{
{
  if (record_debug)
  if (record_debug)
    fprintf_unfiltered (gdb_stdlog, "Process record: "
    fprintf_unfiltered (gdb_stdlog, "Process record: "
                                    "record_mourn_inferior\n");
                                    "record_mourn_inferior\n");
 
 
  unpush_target (&record_ops);
  unpush_target (&record_ops);
  target_mourn_inferior ();
  target_mourn_inferior ();
}
}
 
 
/* Close process record target before killing the inferior process.  */
/* Close process record target before killing the inferior process.  */
 
 
static void
static void
record_kill (struct target_ops *ops)
record_kill (struct target_ops *ops)
{
{
  if (record_debug)
  if (record_debug)
    fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n");
    fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n");
 
 
  unpush_target (&record_ops);
  unpush_target (&record_ops);
  target_kill ();
  target_kill ();
}
}
 
 
/* Record registers change (by user or by GDB) to list as an instruction.  */
/* Record registers change (by user or by GDB) to list as an instruction.  */
 
 
static void
static void
record_registers_change (struct regcache *regcache, int regnum)
record_registers_change (struct regcache *regcache, int regnum)
{
{
  /* Check record_insn_num.  */
  /* Check record_insn_num.  */
  record_check_insn_num (0);
  record_check_insn_num (0);
 
 
  record_arch_list_head = NULL;
  record_arch_list_head = NULL;
  record_arch_list_tail = NULL;
  record_arch_list_tail = NULL;
 
 
  if (regnum < 0)
  if (regnum < 0)
    {
    {
      int i;
      int i;
      for (i = 0; i < gdbarch_num_regs (get_regcache_arch (regcache)); i++)
      for (i = 0; i < gdbarch_num_regs (get_regcache_arch (regcache)); i++)
        {
        {
          if (record_arch_list_add_reg (regcache, i))
          if (record_arch_list_add_reg (regcache, i))
            {
            {
              record_list_release (record_arch_list_tail);
              record_list_release (record_arch_list_tail);
              error (_("Process record: failed to record execution log."));
              error (_("Process record: failed to record execution log."));
            }
            }
        }
        }
    }
    }
  else
  else
    {
    {
      if (record_arch_list_add_reg (regcache, regnum))
      if (record_arch_list_add_reg (regcache, regnum))
        {
        {
          record_list_release (record_arch_list_tail);
          record_list_release (record_arch_list_tail);
          error (_("Process record: failed to record execution log."));
          error (_("Process record: failed to record execution log."));
        }
        }
    }
    }
  if (record_arch_list_add_end ())
  if (record_arch_list_add_end ())
    {
    {
      record_list_release (record_arch_list_tail);
      record_list_release (record_arch_list_tail);
      error (_("Process record: failed to record execution log."));
      error (_("Process record: failed to record execution log."));
    }
    }
  record_list->next = record_arch_list_head;
  record_list->next = record_arch_list_head;
  record_arch_list_head->prev = record_list;
  record_arch_list_head->prev = record_list;
  record_list = record_arch_list_tail;
  record_list = record_arch_list_tail;
 
 
  if (record_insn_num == record_insn_max_num && record_insn_max_num)
  if (record_insn_num == record_insn_max_num && record_insn_max_num)
    record_list_release_first ();
    record_list_release_first ();
  else
  else
    record_insn_num++;
    record_insn_num++;
}
}
 
 
/* "to_store_registers" method for process record target.  */
/* "to_store_registers" method for process record target.  */
 
 
static void
static void
record_store_registers (struct target_ops *ops, struct regcache *regcache,
record_store_registers (struct target_ops *ops, struct regcache *regcache,
                        int regno)
                        int regno)
{
{
  if (!record_gdb_operation_disable)
  if (!record_gdb_operation_disable)
    {
    {
      if (RECORD_IS_REPLAY)
      if (RECORD_IS_REPLAY)
        {
        {
          int n;
          int n;
 
 
          /* Let user choose if he wants to write register or not.  */
          /* Let user choose if he wants to write register or not.  */
          if (regno < 0)
          if (regno < 0)
            n =
            n =
              query (_("Because GDB is in replay mode, changing the "
              query (_("Because GDB is in replay mode, changing the "
                       "value of a register will make the execution "
                       "value of a register will make the execution "
                       "log unusable from this point onward.  "
                       "log unusable from this point onward.  "
                       "Change all registers?"));
                       "Change all registers?"));
          else
          else
            n =
            n =
              query (_("Because GDB is in replay mode, changing the value "
              query (_("Because GDB is in replay mode, changing the value "
                       "of a register will make the execution log unusable "
                       "of a register will make the execution log unusable "
                       "from this point onward.  Change register %s?"),
                       "from this point onward.  Change register %s?"),
                      gdbarch_register_name (get_regcache_arch (regcache),
                      gdbarch_register_name (get_regcache_arch (regcache),
                                               regno));
                                               regno));
 
 
          if (!n)
          if (!n)
            {
            {
              /* Invalidate the value of regcache that was set in function
              /* Invalidate the value of regcache that was set in function
                 "regcache_raw_write".  */
                 "regcache_raw_write".  */
              if (regno < 0)
              if (regno < 0)
                {
                {
                  int i;
                  int i;
                  for (i = 0;
                  for (i = 0;
                       i < gdbarch_num_regs (get_regcache_arch (regcache));
                       i < gdbarch_num_regs (get_regcache_arch (regcache));
                       i++)
                       i++)
                    regcache_invalidate (regcache, i);
                    regcache_invalidate (regcache, i);
                }
                }
              else
              else
                regcache_invalidate (regcache, regno);
                regcache_invalidate (regcache, regno);
 
 
              error (_("Process record canceled the operation."));
              error (_("Process record canceled the operation."));
            }
            }
 
 
          /* Destroy the record from here forward.  */
          /* Destroy the record from here forward.  */
          record_list_release_following (record_list);
          record_list_release_following (record_list);
        }
        }
 
 
      record_registers_change (regcache, regno);
      record_registers_change (regcache, regno);
    }
    }
  record_beneath_to_store_registers (record_beneath_to_store_registers_ops,
  record_beneath_to_store_registers (record_beneath_to_store_registers_ops,
                                     regcache, regno);
                                     regcache, regno);
}
}
 
 
/* "to_xfer_partial" method.  Behavior is conditional on RECORD_IS_REPLAY.
/* "to_xfer_partial" method.  Behavior is conditional on RECORD_IS_REPLAY.
   In replay mode, we cannot write memory unles we are willing to
   In replay mode, we cannot write memory unles we are willing to
   invalidate the record/replay log from this point forward.  */
   invalidate the record/replay log from this point forward.  */
 
 
static LONGEST
static LONGEST
record_xfer_partial (struct target_ops *ops, enum target_object object,
record_xfer_partial (struct target_ops *ops, enum target_object object,
                     const char *annex, gdb_byte *readbuf,
                     const char *annex, gdb_byte *readbuf,
                     const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
                     const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
{
{
  if (!record_gdb_operation_disable
  if (!record_gdb_operation_disable
      && (object == TARGET_OBJECT_MEMORY
      && (object == TARGET_OBJECT_MEMORY
          || object == TARGET_OBJECT_RAW_MEMORY) && writebuf)
          || object == TARGET_OBJECT_RAW_MEMORY) && writebuf)
    {
    {
      if (RECORD_IS_REPLAY)
      if (RECORD_IS_REPLAY)
        {
        {
          /* Let user choose if he wants to write memory or not.  */
          /* Let user choose if he wants to write memory or not.  */
          if (!query (_("Because GDB is in replay mode, writing to memory "
          if (!query (_("Because GDB is in replay mode, writing to memory "
                        "will make the execution log unusable from this "
                        "will make the execution log unusable from this "
                        "point onward.  Write memory at address %s?"),
                        "point onward.  Write memory at address %s?"),
                       paddress (target_gdbarch, offset)))
                       paddress (target_gdbarch, offset)))
            error (_("Process record canceled the operation."));
            error (_("Process record canceled the operation."));
 
 
          /* Destroy the record from here forward.  */
          /* Destroy the record from here forward.  */
          record_list_release_following (record_list);
          record_list_release_following (record_list);
        }
        }
 
 
      /* Check record_insn_num */
      /* Check record_insn_num */
      record_check_insn_num (0);
      record_check_insn_num (0);
 
 
      /* Record registers change to list as an instruction.  */
      /* Record registers change to list as an instruction.  */
      record_arch_list_head = NULL;
      record_arch_list_head = NULL;
      record_arch_list_tail = NULL;
      record_arch_list_tail = NULL;
      if (record_arch_list_add_mem (offset, len))
      if (record_arch_list_add_mem (offset, len))
        {
        {
          record_list_release (record_arch_list_tail);
          record_list_release (record_arch_list_tail);
          if (record_debug)
          if (record_debug)
            fprintf_unfiltered (gdb_stdlog,
            fprintf_unfiltered (gdb_stdlog,
                                "Process record: failed to record "
                                "Process record: failed to record "
                                "execution log.");
                                "execution log.");
          return -1;
          return -1;
        }
        }
      if (record_arch_list_add_end ())
      if (record_arch_list_add_end ())
        {
        {
          record_list_release (record_arch_list_tail);
          record_list_release (record_arch_list_tail);
          if (record_debug)
          if (record_debug)
            fprintf_unfiltered (gdb_stdlog,
            fprintf_unfiltered (gdb_stdlog,
                                "Process record: failed to record "
                                "Process record: failed to record "
                                "execution log.");
                                "execution log.");
          return -1;
          return -1;
        }
        }
      record_list->next = record_arch_list_head;
      record_list->next = record_arch_list_head;
      record_arch_list_head->prev = record_list;
      record_arch_list_head->prev = record_list;
      record_list = record_arch_list_tail;
      record_list = record_arch_list_tail;
 
 
      if (record_insn_num == record_insn_max_num && record_insn_max_num)
      if (record_insn_num == record_insn_max_num && record_insn_max_num)
        record_list_release_first ();
        record_list_release_first ();
      else
      else
        record_insn_num++;
        record_insn_num++;
    }
    }
 
 
  return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops,
  return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops,
                                         object, annex, readbuf, writebuf,
                                         object, annex, readbuf, writebuf,
                                         offset, len);
                                         offset, len);
}
}
 
 
/* Behavior is conditional on RECORD_IS_REPLAY.
/* Behavior is conditional on RECORD_IS_REPLAY.
   We will not actually insert or remove breakpoints when replaying,
   We will not actually insert or remove breakpoints when replaying,
   nor when recording.  */
   nor when recording.  */
 
 
static int
static int
record_insert_breakpoint (struct gdbarch *gdbarch,
record_insert_breakpoint (struct gdbarch *gdbarch,
                          struct bp_target_info *bp_tgt)
                          struct bp_target_info *bp_tgt)
{
{
  if (!RECORD_IS_REPLAY)
  if (!RECORD_IS_REPLAY)
    {
    {
      struct cleanup *old_cleanups = record_gdb_operation_disable_set ();
      struct cleanup *old_cleanups = record_gdb_operation_disable_set ();
      int ret = record_beneath_to_insert_breakpoint (gdbarch, bp_tgt);
      int ret = record_beneath_to_insert_breakpoint (gdbarch, bp_tgt);
 
 
      do_cleanups (old_cleanups);
      do_cleanups (old_cleanups);
 
 
      return ret;
      return ret;
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 
/* "to_remove_breakpoint" method for process record target.  */
/* "to_remove_breakpoint" method for process record target.  */
 
 
static int
static int
record_remove_breakpoint (struct gdbarch *gdbarch,
record_remove_breakpoint (struct gdbarch *gdbarch,
                          struct bp_target_info *bp_tgt)
                          struct bp_target_info *bp_tgt)
{
{
  if (!RECORD_IS_REPLAY)
  if (!RECORD_IS_REPLAY)
    {
    {
      struct cleanup *old_cleanups = record_gdb_operation_disable_set ();
      struct cleanup *old_cleanups = record_gdb_operation_disable_set ();
      int ret = record_beneath_to_remove_breakpoint (gdbarch, bp_tgt);
      int ret = record_beneath_to_remove_breakpoint (gdbarch, bp_tgt);
 
 
      do_cleanups (old_cleanups);
      do_cleanups (old_cleanups);
 
 
      return ret;
      return ret;
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 
/* "to_can_execute_reverse" method for process record target.  */
/* "to_can_execute_reverse" method for process record target.  */
 
 
static int
static int
record_can_execute_reverse (void)
record_can_execute_reverse (void)
{
{
  return 1;
  return 1;
}
}
 
 
/* "to_get_bookmark" method for process record and prec over core.  */
/* "to_get_bookmark" method for process record and prec over core.  */
 
 
static gdb_byte *
static gdb_byte *
record_get_bookmark (char *args, int from_tty)
record_get_bookmark (char *args, int from_tty)
{
{
  gdb_byte *ret = NULL;
  gdb_byte *ret = NULL;
 
 
  /* Return stringified form of instruction count.  */
  /* Return stringified form of instruction count.  */
  if (record_list && record_list->type == record_end)
  if (record_list && record_list->type == record_end)
    ret = xstrdup (pulongest (record_list->u.end.insn_num));
    ret = xstrdup (pulongest (record_list->u.end.insn_num));
 
 
  if (record_debug)
  if (record_debug)
    {
    {
      if (ret)
      if (ret)
        fprintf_unfiltered (gdb_stdlog,
        fprintf_unfiltered (gdb_stdlog,
                            "record_get_bookmark returns %s\n", ret);
                            "record_get_bookmark returns %s\n", ret);
      else
      else
        fprintf_unfiltered (gdb_stdlog,
        fprintf_unfiltered (gdb_stdlog,
                            "record_get_bookmark returns NULL\n");
                            "record_get_bookmark returns NULL\n");
    }
    }
  return ret;
  return ret;
}
}
 
 
/* The implementation of the command "record goto".  */
/* The implementation of the command "record goto".  */
static void cmd_record_goto (char *, int);
static void cmd_record_goto (char *, int);
 
 
/* "to_goto_bookmark" method for process record and prec over core.  */
/* "to_goto_bookmark" method for process record and prec over core.  */
 
 
static void
static void
record_goto_bookmark (gdb_byte *bookmark, int from_tty)
record_goto_bookmark (gdb_byte *bookmark, int from_tty)
{
{
  if (record_debug)
  if (record_debug)
    fprintf_unfiltered (gdb_stdlog,
    fprintf_unfiltered (gdb_stdlog,
                        "record_goto_bookmark receives %s\n", bookmark);
                        "record_goto_bookmark receives %s\n", bookmark);
 
 
  if (bookmark[0] == '\'' || bookmark[0] == '\"')
  if (bookmark[0] == '\'' || bookmark[0] == '\"')
    {
    {
      if (bookmark[strlen (bookmark) - 1] != bookmark[0])
      if (bookmark[strlen (bookmark) - 1] != bookmark[0])
        error (_("Unbalanced quotes: %s"), bookmark);
        error (_("Unbalanced quotes: %s"), bookmark);
 
 
      /* Strip trailing quote.  */
      /* Strip trailing quote.  */
      bookmark[strlen (bookmark) - 1] = '\0';
      bookmark[strlen (bookmark) - 1] = '\0';
      /* Strip leading quote.  */
      /* Strip leading quote.  */
      bookmark++;
      bookmark++;
      /* Pass along to cmd_record_goto.  */
      /* Pass along to cmd_record_goto.  */
    }
    }
 
 
  cmd_record_goto ((char *) bookmark, from_tty);
  cmd_record_goto ((char *) bookmark, from_tty);
  return;
  return;
}
}
 
 
static void
static void
init_record_ops (void)
init_record_ops (void)
{
{
  record_ops.to_shortname = "record";
  record_ops.to_shortname = "record";
  record_ops.to_longname = "Process record and replay target";
  record_ops.to_longname = "Process record and replay target";
  record_ops.to_doc =
  record_ops.to_doc =
    "Log program while executing and replay execution from log.";
    "Log program while executing and replay execution from log.";
  record_ops.to_open = record_open;
  record_ops.to_open = record_open;
  record_ops.to_close = record_close;
  record_ops.to_close = record_close;
  record_ops.to_resume = record_resume;
  record_ops.to_resume = record_resume;
  record_ops.to_wait = record_wait;
  record_ops.to_wait = record_wait;
  record_ops.to_disconnect = record_disconnect;
  record_ops.to_disconnect = record_disconnect;
  record_ops.to_detach = record_detach;
  record_ops.to_detach = record_detach;
  record_ops.to_mourn_inferior = record_mourn_inferior;
  record_ops.to_mourn_inferior = record_mourn_inferior;
  record_ops.to_kill = record_kill;
  record_ops.to_kill = record_kill;
  record_ops.to_create_inferior = find_default_create_inferior;
  record_ops.to_create_inferior = find_default_create_inferior;
  record_ops.to_store_registers = record_store_registers;
  record_ops.to_store_registers = record_store_registers;
  record_ops.to_xfer_partial = record_xfer_partial;
  record_ops.to_xfer_partial = record_xfer_partial;
  record_ops.to_insert_breakpoint = record_insert_breakpoint;
  record_ops.to_insert_breakpoint = record_insert_breakpoint;
  record_ops.to_remove_breakpoint = record_remove_breakpoint;
  record_ops.to_remove_breakpoint = record_remove_breakpoint;
  record_ops.to_stopped_by_watchpoint = record_stopped_by_watchpoint;
  record_ops.to_stopped_by_watchpoint = record_stopped_by_watchpoint;
  record_ops.to_stopped_data_address = record_stopped_data_address;
  record_ops.to_stopped_data_address = record_stopped_data_address;
  record_ops.to_can_execute_reverse = record_can_execute_reverse;
  record_ops.to_can_execute_reverse = record_can_execute_reverse;
  record_ops.to_stratum = record_stratum;
  record_ops.to_stratum = record_stratum;
  /* Add bookmark target methods.  */
  /* Add bookmark target methods.  */
  record_ops.to_get_bookmark = record_get_bookmark;
  record_ops.to_get_bookmark = record_get_bookmark;
  record_ops.to_goto_bookmark = record_goto_bookmark;
  record_ops.to_goto_bookmark = record_goto_bookmark;
  record_ops.to_magic = OPS_MAGIC;
  record_ops.to_magic = OPS_MAGIC;
}
}
 
 
/* "to_resume" method for prec over corefile.  */
/* "to_resume" method for prec over corefile.  */
 
 
static void
static void
record_core_resume (struct target_ops *ops, ptid_t ptid, int step,
record_core_resume (struct target_ops *ops, ptid_t ptid, int step,
                    enum target_signal signal)
                    enum target_signal signal)
{
{
  record_resume_step = step;
  record_resume_step = step;
}
}
 
 
/* "to_kill" method for prec over corefile.  */
/* "to_kill" method for prec over corefile.  */
 
 
static void
static void
record_core_kill (struct target_ops *ops)
record_core_kill (struct target_ops *ops)
{
{
  if (record_debug)
  if (record_debug)
    fprintf_unfiltered (gdb_stdlog, "Process record: record_core_kill\n");
    fprintf_unfiltered (gdb_stdlog, "Process record: record_core_kill\n");
 
 
  unpush_target (&record_core_ops);
  unpush_target (&record_core_ops);
}
}
 
 
/* "to_fetch_registers" method for prec over corefile.  */
/* "to_fetch_registers" method for prec over corefile.  */
 
 
static void
static void
record_core_fetch_registers (struct target_ops *ops,
record_core_fetch_registers (struct target_ops *ops,
                             struct regcache *regcache,
                             struct regcache *regcache,
                             int regno)
                             int regno)
{
{
  if (regno < 0)
  if (regno < 0)
    {
    {
      int num = gdbarch_num_regs (get_regcache_arch (regcache));
      int num = gdbarch_num_regs (get_regcache_arch (regcache));
      int i;
      int i;
 
 
      for (i = 0; i < num; i ++)
      for (i = 0; i < num; i ++)
        regcache_raw_supply (regcache, i,
        regcache_raw_supply (regcache, i,
                             record_core_regbuf + MAX_REGISTER_SIZE * i);
                             record_core_regbuf + MAX_REGISTER_SIZE * i);
    }
    }
  else
  else
    regcache_raw_supply (regcache, regno,
    regcache_raw_supply (regcache, regno,
                         record_core_regbuf + MAX_REGISTER_SIZE * regno);
                         record_core_regbuf + MAX_REGISTER_SIZE * regno);
}
}
 
 
/* "to_prepare_to_store" method for prec over corefile.  */
/* "to_prepare_to_store" method for prec over corefile.  */
 
 
static void
static void
record_core_prepare_to_store (struct regcache *regcache)
record_core_prepare_to_store (struct regcache *regcache)
{
{
}
}
 
 
/* "to_store_registers" method for prec over corefile.  */
/* "to_store_registers" method for prec over corefile.  */
 
 
static void
static void
record_core_store_registers (struct target_ops *ops,
record_core_store_registers (struct target_ops *ops,
                             struct regcache *regcache,
                             struct regcache *regcache,
                             int regno)
                             int regno)
{
{
  if (record_gdb_operation_disable)
  if (record_gdb_operation_disable)
    regcache_raw_collect (regcache, regno,
    regcache_raw_collect (regcache, regno,
                          record_core_regbuf + MAX_REGISTER_SIZE * regno);
                          record_core_regbuf + MAX_REGISTER_SIZE * regno);
  else
  else
    error (_("You can't do that without a process to debug."));
    error (_("You can't do that without a process to debug."));
}
}
 
 
/* "to_xfer_partial" method for prec over corefile.  */
/* "to_xfer_partial" method for prec over corefile.  */
 
 
static LONGEST
static LONGEST
record_core_xfer_partial (struct target_ops *ops, enum target_object object,
record_core_xfer_partial (struct target_ops *ops, enum target_object object,
                          const char *annex, gdb_byte *readbuf,
                          const char *annex, gdb_byte *readbuf,
                          const gdb_byte *writebuf, ULONGEST offset,
                          const gdb_byte *writebuf, ULONGEST offset,
                          LONGEST len)
                          LONGEST len)
{
{
   if (object == TARGET_OBJECT_MEMORY)
   if (object == TARGET_OBJECT_MEMORY)
     {
     {
       if (record_gdb_operation_disable || !writebuf)
       if (record_gdb_operation_disable || !writebuf)
         {
         {
           struct target_section *p;
           struct target_section *p;
           for (p = record_core_start; p < record_core_end; p++)
           for (p = record_core_start; p < record_core_end; p++)
             {
             {
               if (offset >= p->addr)
               if (offset >= p->addr)
                 {
                 {
                   struct record_core_buf_entry *entry;
                   struct record_core_buf_entry *entry;
                   ULONGEST sec_offset;
                   ULONGEST sec_offset;
 
 
                   if (offset >= p->endaddr)
                   if (offset >= p->endaddr)
                     continue;
                     continue;
 
 
                   if (offset + len > p->endaddr)
                   if (offset + len > p->endaddr)
                     len = p->endaddr - offset;
                     len = p->endaddr - offset;
 
 
                   sec_offset = offset - p->addr;
                   sec_offset = offset - p->addr;
 
 
                   /* Read readbuf or write writebuf p, offset, len.  */
                   /* Read readbuf or write writebuf p, offset, len.  */
                   /* Check flags.  */
                   /* Check flags.  */
                   if (p->the_bfd_section->flags & SEC_CONSTRUCTOR
                   if (p->the_bfd_section->flags & SEC_CONSTRUCTOR
                       || (p->the_bfd_section->flags & SEC_HAS_CONTENTS) == 0)
                       || (p->the_bfd_section->flags & SEC_HAS_CONTENTS) == 0)
                     {
                     {
                       if (readbuf)
                       if (readbuf)
                         memset (readbuf, 0, len);
                         memset (readbuf, 0, len);
                       return len;
                       return len;
                     }
                     }
                   /* Get record_core_buf_entry.  */
                   /* Get record_core_buf_entry.  */
                   for (entry = record_core_buf_list; entry;
                   for (entry = record_core_buf_list; entry;
                        entry = entry->prev)
                        entry = entry->prev)
                     if (entry->p == p)
                     if (entry->p == p)
                       break;
                       break;
                   if (writebuf)
                   if (writebuf)
                     {
                     {
                       if (!entry)
                       if (!entry)
                         {
                         {
                           /* Add a new entry.  */
                           /* Add a new entry.  */
                           entry
                           entry
                             = (struct record_core_buf_entry *)
                             = (struct record_core_buf_entry *)
                                 xmalloc
                                 xmalloc
                                   (sizeof (struct record_core_buf_entry));
                                   (sizeof (struct record_core_buf_entry));
                           entry->p = p;
                           entry->p = p;
                           if (!bfd_malloc_and_get_section (p->bfd,
                           if (!bfd_malloc_and_get_section (p->bfd,
                                                            p->the_bfd_section,
                                                            p->the_bfd_section,
                                                            &entry->buf))
                                                            &entry->buf))
                             {
                             {
                               xfree (entry);
                               xfree (entry);
                               return 0;
                               return 0;
                             }
                             }
                           entry->prev = record_core_buf_list;
                           entry->prev = record_core_buf_list;
                           record_core_buf_list = entry;
                           record_core_buf_list = entry;
                         }
                         }
 
 
                        memcpy (entry->buf + sec_offset, writebuf,
                        memcpy (entry->buf + sec_offset, writebuf,
                                (size_t) len);
                                (size_t) len);
                     }
                     }
                   else
                   else
                     {
                     {
                       if (!entry)
                       if (!entry)
                         return record_beneath_to_xfer_partial
                         return record_beneath_to_xfer_partial
                                  (record_beneath_to_xfer_partial_ops,
                                  (record_beneath_to_xfer_partial_ops,
                                   object, annex, readbuf, writebuf,
                                   object, annex, readbuf, writebuf,
                                   offset, len);
                                   offset, len);
 
 
                       memcpy (readbuf, entry->buf + sec_offset,
                       memcpy (readbuf, entry->buf + sec_offset,
                               (size_t) len);
                               (size_t) len);
                     }
                     }
 
 
                   return len;
                   return len;
                 }
                 }
             }
             }
 
 
           return -1;
           return -1;
         }
         }
       else
       else
         error (_("You can't do that without a process to debug."));
         error (_("You can't do that without a process to debug."));
     }
     }
 
 
  return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops,
  return record_beneath_to_xfer_partial (record_beneath_to_xfer_partial_ops,
                                         object, annex, readbuf, writebuf,
                                         object, annex, readbuf, writebuf,
                                         offset, len);
                                         offset, len);
}
}
 
 
/* "to_insert_breakpoint" method for prec over corefile.  */
/* "to_insert_breakpoint" method for prec over corefile.  */
 
 
static int
static int
record_core_insert_breakpoint (struct gdbarch *gdbarch,
record_core_insert_breakpoint (struct gdbarch *gdbarch,
                               struct bp_target_info *bp_tgt)
                               struct bp_target_info *bp_tgt)
{
{
  return 0;
  return 0;
}
}
 
 
/* "to_remove_breakpoint" method for prec over corefile.  */
/* "to_remove_breakpoint" method for prec over corefile.  */
 
 
static int
static int
record_core_remove_breakpoint (struct gdbarch *gdbarch,
record_core_remove_breakpoint (struct gdbarch *gdbarch,
                               struct bp_target_info *bp_tgt)
                               struct bp_target_info *bp_tgt)
{
{
  return 0;
  return 0;
}
}
 
 
/* "to_has_execution" method for prec over corefile.  */
/* "to_has_execution" method for prec over corefile.  */
 
 
int
int
record_core_has_execution (struct target_ops *ops)
record_core_has_execution (struct target_ops *ops)
{
{
  return 1;
  return 1;
}
}
 
 
static void
static void
init_record_core_ops (void)
init_record_core_ops (void)
{
{
  record_core_ops.to_shortname = "record_core";
  record_core_ops.to_shortname = "record_core";
  record_core_ops.to_longname = "Process record and replay target";
  record_core_ops.to_longname = "Process record and replay target";
  record_core_ops.to_doc =
  record_core_ops.to_doc =
    "Log program while executing and replay execution from log.";
    "Log program while executing and replay execution from log.";
  record_core_ops.to_open = record_open;
  record_core_ops.to_open = record_open;
  record_core_ops.to_close = record_close;
  record_core_ops.to_close = record_close;
  record_core_ops.to_resume = record_core_resume;
  record_core_ops.to_resume = record_core_resume;
  record_core_ops.to_wait = record_wait;
  record_core_ops.to_wait = record_wait;
  record_core_ops.to_kill = record_core_kill;
  record_core_ops.to_kill = record_core_kill;
  record_core_ops.to_fetch_registers = record_core_fetch_registers;
  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_prepare_to_store = record_core_prepare_to_store;
  record_core_ops.to_store_registers = record_core_store_registers;
  record_core_ops.to_store_registers = record_core_store_registers;
  record_core_ops.to_xfer_partial = record_core_xfer_partial;
  record_core_ops.to_xfer_partial = record_core_xfer_partial;
  record_core_ops.to_insert_breakpoint = record_core_insert_breakpoint;
  record_core_ops.to_insert_breakpoint = record_core_insert_breakpoint;
  record_core_ops.to_remove_breakpoint = record_core_remove_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_by_watchpoint = record_stopped_by_watchpoint;
  record_core_ops.to_stopped_data_address = record_stopped_data_address;
  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_can_execute_reverse = record_can_execute_reverse;
  record_core_ops.to_has_execution = record_core_has_execution;
  record_core_ops.to_has_execution = record_core_has_execution;
  record_core_ops.to_stratum = record_stratum;
  record_core_ops.to_stratum = record_stratum;
  /* Add bookmark target methods.  */
  /* Add bookmark target methods.  */
  record_core_ops.to_get_bookmark = record_get_bookmark;
  record_core_ops.to_get_bookmark = record_get_bookmark;
  record_core_ops.to_goto_bookmark = record_goto_bookmark;
  record_core_ops.to_goto_bookmark = record_goto_bookmark;
  record_core_ops.to_magic = OPS_MAGIC;
  record_core_ops.to_magic = OPS_MAGIC;
}
}
 
 
/* Implement "show record debug" command.  */
/* Implement "show record debug" command.  */
 
 
static void
static void
show_record_debug (struct ui_file *file, int from_tty,
show_record_debug (struct ui_file *file, int from_tty,
                   struct cmd_list_element *c, const char *value)
                   struct cmd_list_element *c, const char *value)
{
{
  fprintf_filtered (file, _("Debugging of process record target is %s.\n"),
  fprintf_filtered (file, _("Debugging of process record target is %s.\n"),
                    value);
                    value);
}
}
 
 
/* Alias for "target record".  */
/* Alias for "target record".  */
 
 
static void
static void
cmd_record_start (char *args, int from_tty)
cmd_record_start (char *args, int from_tty)
{
{
  execute_command ("target record", from_tty);
  execute_command ("target record", from_tty);
}
}
 
 
/* Truncate the record log from the present point
/* Truncate the record log from the present point
   of replay until the end.  */
   of replay until the end.  */
 
 
static void
static void
cmd_record_delete (char *args, int from_tty)
cmd_record_delete (char *args, int from_tty)
{
{
  if (current_target.to_stratum == record_stratum)
  if (current_target.to_stratum == record_stratum)
    {
    {
      if (RECORD_IS_REPLAY)
      if (RECORD_IS_REPLAY)
        {
        {
          if (!from_tty || query (_("Delete the log from this point forward "
          if (!from_tty || query (_("Delete the log from this point forward "
                                    "and begin to record the running message "
                                    "and begin to record the running message "
                                    "at current PC?")))
                                    "at current PC?")))
            record_list_release_following (record_list);
            record_list_release_following (record_list);
        }
        }
      else
      else
          printf_unfiltered (_("Already at end of record list.\n"));
          printf_unfiltered (_("Already at end of record list.\n"));
 
 
    }
    }
  else
  else
    printf_unfiltered (_("Process record is not started.\n"));
    printf_unfiltered (_("Process record is not started.\n"));
}
}
 
 
/* Implement the "stoprecord" or "record stop" command.  */
/* Implement the "stoprecord" or "record stop" command.  */
 
 
static void
static void
cmd_record_stop (char *args, int from_tty)
cmd_record_stop (char *args, int from_tty)
{
{
  if (current_target.to_stratum == record_stratum)
  if (current_target.to_stratum == record_stratum)
    {
    {
      unpush_target (&record_ops);
      unpush_target (&record_ops);
      printf_unfiltered (_("Process record is stopped and all execution "
      printf_unfiltered (_("Process record is stopped and all execution "
                           "logs are deleted.\n"));
                           "logs are deleted.\n"));
    }
    }
  else
  else
    printf_unfiltered (_("Process record is not started.\n"));
    printf_unfiltered (_("Process record is not started.\n"));
}
}
 
 
/* Set upper limit of record log size.  */
/* Set upper limit of record log size.  */
 
 
static void
static void
set_record_insn_max_num (char *args, int from_tty, struct cmd_list_element *c)
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)
  if (record_insn_num > record_insn_max_num && record_insn_max_num)
    {
    {
      /* Count down record_insn_num while releasing records from list.  */
      /* Count down record_insn_num while releasing records from list.  */
      while (record_insn_num > record_insn_max_num)
      while (record_insn_num > record_insn_max_num)
        {
        {
          record_list_release_first ();
          record_list_release_first ();
          record_insn_num--;
          record_insn_num--;
        }
        }
    }
    }
}
}
 
 
static struct cmd_list_element *record_cmdlist, *set_record_cmdlist,
static struct cmd_list_element *record_cmdlist, *set_record_cmdlist,
                               *show_record_cmdlist, *info_record_cmdlist;
                               *show_record_cmdlist, *info_record_cmdlist;
 
 
static void
static void
set_record_command (char *args, int from_tty)
set_record_command (char *args, int from_tty)
{
{
  printf_unfiltered (_("\
  printf_unfiltered (_("\
\"set record\" must be followed by an apporpriate subcommand.\n"));
\"set record\" must be followed by an apporpriate subcommand.\n"));
  help_list (set_record_cmdlist, "set record ", all_commands, gdb_stdout);
  help_list (set_record_cmdlist, "set record ", all_commands, gdb_stdout);
}
}
 
 
static void
static void
show_record_command (char *args, int from_tty)
show_record_command (char *args, int from_tty)
{
{
  cmd_show_list (show_record_cmdlist, from_tty, "");
  cmd_show_list (show_record_cmdlist, from_tty, "");
}
}
 
 
/* Display some statistics about the execution log.  */
/* Display some statistics about the execution log.  */
 
 
static void
static void
info_record_command (char *args, int from_tty)
info_record_command (char *args, int from_tty)
{
{
  struct record_entry *p;
  struct record_entry *p;
 
 
  if (current_target.to_stratum == record_stratum)
  if (current_target.to_stratum == record_stratum)
    {
    {
      if (RECORD_IS_REPLAY)
      if (RECORD_IS_REPLAY)
        printf_filtered (_("Replay mode:\n"));
        printf_filtered (_("Replay mode:\n"));
      else
      else
        printf_filtered (_("Record mode:\n"));
        printf_filtered (_("Record mode:\n"));
 
 
      /* Find entry for first actual instruction in the log.  */
      /* Find entry for first actual instruction in the log.  */
      for (p = record_first.next;
      for (p = record_first.next;
           p != NULL && p->type != record_end;
           p != NULL && p->type != record_end;
           p = p->next)
           p = p->next)
        ;
        ;
 
 
      /* Do we have a log at all?  */
      /* Do we have a log at all?  */
      if (p != NULL && p->type == record_end)
      if (p != NULL && p->type == record_end)
        {
        {
          /* Display instruction number for first instruction in the log.  */
          /* Display instruction number for first instruction in the log.  */
          printf_filtered (_("Lowest recorded instruction number is %s.\n"),
          printf_filtered (_("Lowest recorded instruction number is %s.\n"),
                           pulongest (p->u.end.insn_num));
                           pulongest (p->u.end.insn_num));
 
 
          /* If in replay mode, display where we are in the log.  */
          /* If in replay mode, display where we are in the log.  */
          if (RECORD_IS_REPLAY)
          if (RECORD_IS_REPLAY)
            printf_filtered (_("Current instruction number is %s.\n"),
            printf_filtered (_("Current instruction number is %s.\n"),
                             pulongest (record_list->u.end.insn_num));
                             pulongest (record_list->u.end.insn_num));
 
 
          /* Display instruction number for last instruction in the log.  */
          /* Display instruction number for last instruction in the log.  */
          printf_filtered (_("Highest recorded instruction number is %s.\n"),
          printf_filtered (_("Highest recorded instruction number is %s.\n"),
                           pulongest (record_insn_count));
                           pulongest (record_insn_count));
 
 
          /* Display log count.  */
          /* Display log count.  */
          printf_filtered (_("Log contains %d instructions.\n"),
          printf_filtered (_("Log contains %d instructions.\n"),
                           record_insn_num);
                           record_insn_num);
        }
        }
      else
      else
        {
        {
          printf_filtered (_("No instructions have been logged.\n"));
          printf_filtered (_("No instructions have been logged.\n"));
        }
        }
    }
    }
  else
  else
    {
    {
      printf_filtered (_("target record is not active.\n"));
      printf_filtered (_("target record is not active.\n"));
    }
    }
 
 
  /* Display max log size.  */
  /* Display max log size.  */
  printf_filtered (_("Max logged instructions is %d.\n"),
  printf_filtered (_("Max logged instructions is %d.\n"),
                   record_insn_max_num);
                   record_insn_max_num);
}
}
 
 
/* Record log save-file format
/* Record log save-file format
   Version 1 (never released)
   Version 1 (never released)
 
 
   Header:
   Header:
     4 bytes: magic number htonl(0x20090829).
     4 bytes: magic number htonl(0x20090829).
       NOTE: be sure to change whenever this file format changes!
       NOTE: be sure to change whenever this file format changes!
 
 
   Records:
   Records:
     record_end:
     record_end:
       1 byte:  record type (record_end, see enum record_type).
       1 byte:  record type (record_end, see enum record_type).
     record_reg:
     record_reg:
       1 byte:  record type (record_reg, see enum record_type).
       1 byte:  record type (record_reg, see enum record_type).
       8 bytes: register id (network byte order).
       8 bytes: register id (network byte order).
       MAX_REGISTER_SIZE bytes: register value.
       MAX_REGISTER_SIZE bytes: register value.
     record_mem:
     record_mem:
       1 byte:  record type (record_mem, see enum record_type).
       1 byte:  record type (record_mem, see enum record_type).
       8 bytes: memory length (network byte order).
       8 bytes: memory length (network byte order).
       8 bytes: memory address (network byte order).
       8 bytes: memory address (network byte order).
       n bytes: memory value (n == memory length).
       n bytes: memory value (n == memory length).
 
 
   Version 2
   Version 2
     4 bytes: magic number netorder32(0x20091016).
     4 bytes: magic number netorder32(0x20091016).
       NOTE: be sure to change whenever this file format changes!
       NOTE: be sure to change whenever this file format changes!
 
 
   Records:
   Records:
     record_end:
     record_end:
       1 byte:  record type (record_end, see enum record_type).
       1 byte:  record type (record_end, see enum record_type).
       4 bytes: signal
       4 bytes: signal
       4 bytes: instruction count
       4 bytes: instruction count
     record_reg:
     record_reg:
       1 byte:  record type (record_reg, see enum record_type).
       1 byte:  record type (record_reg, see enum record_type).
       4 bytes: register id (network byte order).
       4 bytes: register id (network byte order).
       n bytes: register value (n == actual register size).
       n bytes: register value (n == actual register size).
                (eg. 4 bytes for x86 general registers).
                (eg. 4 bytes for x86 general registers).
     record_mem:
     record_mem:
       1 byte:  record type (record_mem, see enum record_type).
       1 byte:  record type (record_mem, see enum record_type).
       4 bytes: memory length (network byte order).
       4 bytes: memory length (network byte order).
       8 bytes: memory address (network byte order).
       8 bytes: memory address (network byte order).
       n bytes: memory value (n == memory length).
       n bytes: memory value (n == memory length).
 
 
*/
*/
 
 
/* bfdcore_read -- read bytes from a core file section.  */
/* bfdcore_read -- read bytes from a core file section.  */
 
 
static inline void
static inline void
bfdcore_read (bfd *obfd, asection *osec, void *buf, int len, int *offset)
bfdcore_read (bfd *obfd, asection *osec, void *buf, int len, int *offset)
{
{
  int ret = bfd_get_section_contents (obfd, osec, buf, *offset, len);
  int ret = bfd_get_section_contents (obfd, osec, buf, *offset, len);
 
 
  if (ret)
  if (ret)
    *offset += len;
    *offset += len;
  else
  else
    error (_("Failed to read %d bytes from core file %s ('%s').\n"),
    error (_("Failed to read %d bytes from core file %s ('%s').\n"),
           len, bfd_get_filename (obfd),
           len, bfd_get_filename (obfd),
           bfd_errmsg (bfd_get_error ()));
           bfd_errmsg (bfd_get_error ()));
}
}
 
 
static inline uint64_t
static inline uint64_t
netorder64 (uint64_t input)
netorder64 (uint64_t input)
{
{
  uint64_t ret;
  uint64_t ret;
 
 
  store_unsigned_integer ((gdb_byte *) &ret, sizeof (ret),
  store_unsigned_integer ((gdb_byte *) &ret, sizeof (ret),
                          BFD_ENDIAN_BIG, input);
                          BFD_ENDIAN_BIG, input);
  return ret;
  return ret;
}
}
 
 
static inline uint32_t
static inline uint32_t
netorder32 (uint32_t input)
netorder32 (uint32_t input)
{
{
  uint32_t ret;
  uint32_t ret;
 
 
  store_unsigned_integer ((gdb_byte *) &ret, sizeof (ret),
  store_unsigned_integer ((gdb_byte *) &ret, sizeof (ret),
                          BFD_ENDIAN_BIG, input);
                          BFD_ENDIAN_BIG, input);
  return ret;
  return ret;
}
}
 
 
static inline uint16_t
static inline uint16_t
netorder16 (uint16_t input)
netorder16 (uint16_t input)
{
{
  uint16_t ret;
  uint16_t ret;
 
 
  store_unsigned_integer ((gdb_byte *) &ret, sizeof (ret),
  store_unsigned_integer ((gdb_byte *) &ret, sizeof (ret),
                          BFD_ENDIAN_BIG, input);
                          BFD_ENDIAN_BIG, input);
  return ret;
  return ret;
}
}
 
 
/* Restore the execution log from a core_bfd file.  */
/* Restore the execution log from a core_bfd file.  */
static void
static void
record_restore (void)
record_restore (void)
{
{
  uint32_t magic;
  uint32_t magic;
  struct cleanup *old_cleanups;
  struct cleanup *old_cleanups;
  struct record_entry *rec;
  struct record_entry *rec;
  asection *osec;
  asection *osec;
  uint32_t osec_size;
  uint32_t osec_size;
  int bfd_offset = 0;
  int bfd_offset = 0;
  struct regcache *regcache;
  struct regcache *regcache;
 
 
  /* We restore the execution log from the open core bfd,
  /* We restore the execution log from the open core bfd,
     if there is one.  */
     if there is one.  */
  if (core_bfd == NULL)
  if (core_bfd == NULL)
    return;
    return;
 
 
  /* "record_restore" can only be called when record list is empty.  */
  /* "record_restore" can only be called when record list is empty.  */
  gdb_assert (record_first.next == NULL);
  gdb_assert (record_first.next == NULL);
 
 
  if (record_debug)
  if (record_debug)
    fprintf_unfiltered (gdb_stdlog, "Restoring recording from core file.\n");
    fprintf_unfiltered (gdb_stdlog, "Restoring recording from core file.\n");
 
 
  /* Now need to find our special note section.  */
  /* Now need to find our special note section.  */
  osec = bfd_get_section_by_name (core_bfd, "null0");
  osec = bfd_get_section_by_name (core_bfd, "null0");
  osec_size = bfd_section_size (core_bfd, osec);
  osec_size = bfd_section_size (core_bfd, osec);
  if (record_debug)
  if (record_debug)
    fprintf_unfiltered (gdb_stdlog, "Find precord section %s.\n",
    fprintf_unfiltered (gdb_stdlog, "Find precord section %s.\n",
                        osec ? "succeeded" : "failed");
                        osec ? "succeeded" : "failed");
  if (osec == NULL)
  if (osec == NULL)
    return;
    return;
  if (record_debug)
  if (record_debug)
    fprintf_unfiltered (gdb_stdlog, "%s", bfd_section_name (core_bfd, osec));
    fprintf_unfiltered (gdb_stdlog, "%s", bfd_section_name (core_bfd, osec));
 
 
  /* Check the magic code.  */
  /* Check the magic code.  */
  bfdcore_read (core_bfd, osec, &magic, sizeof (magic), &bfd_offset);
  bfdcore_read (core_bfd, osec, &magic, sizeof (magic), &bfd_offset);
  if (magic != RECORD_FILE_MAGIC)
  if (magic != RECORD_FILE_MAGIC)
    error (_("Version mis-match or file format error in core file %s."),
    error (_("Version mis-match or file format error in core file %s."),
           bfd_get_filename (core_bfd));
           bfd_get_filename (core_bfd));
  if (record_debug)
  if (record_debug)
    fprintf_unfiltered (gdb_stdlog, "\
    fprintf_unfiltered (gdb_stdlog, "\
  Reading 4-byte magic cookie RECORD_FILE_MAGIC (0x%s)\n",
  Reading 4-byte magic cookie RECORD_FILE_MAGIC (0x%s)\n",
                        phex_nz (netorder32 (magic), 4));
                        phex_nz (netorder32 (magic), 4));
 
 
  /* Restore the entries in recfd into record_arch_list_head and
  /* Restore the entries in recfd into record_arch_list_head and
     record_arch_list_tail.  */
     record_arch_list_tail.  */
  record_arch_list_head = NULL;
  record_arch_list_head = NULL;
  record_arch_list_tail = NULL;
  record_arch_list_tail = NULL;
  record_insn_num = 0;
  record_insn_num = 0;
  old_cleanups = make_cleanup (record_arch_list_cleanups, 0);
  old_cleanups = make_cleanup (record_arch_list_cleanups, 0);
  regcache = get_current_regcache ();
  regcache = get_current_regcache ();
 
 
  while (1)
  while (1)
    {
    {
      int ret;
      int ret;
      uint8_t tmpu8;
      uint8_t tmpu8;
      uint32_t regnum, len, signal, count;
      uint32_t regnum, len, signal, count;
      uint64_t addr;
      uint64_t addr;
 
 
      /* We are finished when offset reaches osec_size.  */
      /* We are finished when offset reaches osec_size.  */
      if (bfd_offset >= osec_size)
      if (bfd_offset >= osec_size)
        break;
        break;
      bfdcore_read (core_bfd, osec, &tmpu8, sizeof (tmpu8), &bfd_offset);
      bfdcore_read (core_bfd, osec, &tmpu8, sizeof (tmpu8), &bfd_offset);
 
 
      switch (tmpu8)
      switch (tmpu8)
        {
        {
        case record_reg: /* reg */
        case record_reg: /* reg */
          /* Get register number to regnum.  */
          /* Get register number to regnum.  */
          bfdcore_read (core_bfd, osec, &regnum,
          bfdcore_read (core_bfd, osec, &regnum,
                        sizeof (regnum), &bfd_offset);
                        sizeof (regnum), &bfd_offset);
          regnum = netorder32 (regnum);
          regnum = netorder32 (regnum);
 
 
          rec = record_reg_alloc (regcache, regnum);
          rec = record_reg_alloc (regcache, regnum);
 
 
          /* Get val.  */
          /* Get val.  */
          bfdcore_read (core_bfd, osec, record_get_loc (rec),
          bfdcore_read (core_bfd, osec, record_get_loc (rec),
                        rec->u.reg.len, &bfd_offset);
                        rec->u.reg.len, &bfd_offset);
 
 
          if (record_debug)
          if (record_debug)
            fprintf_unfiltered (gdb_stdlog, "\
            fprintf_unfiltered (gdb_stdlog, "\
  Reading register %d (1 plus %lu plus %d bytes)\n",
  Reading register %d (1 plus %lu plus %d bytes)\n",
                                rec->u.reg.num,
                                rec->u.reg.num,
                                (unsigned long) sizeof (regnum),
                                (unsigned long) sizeof (regnum),
                                rec->u.reg.len);
                                rec->u.reg.len);
          break;
          break;
 
 
        case record_mem: /* mem */
        case record_mem: /* mem */
          /* Get len.  */
          /* Get len.  */
          bfdcore_read (core_bfd, osec, &len,
          bfdcore_read (core_bfd, osec, &len,
                        sizeof (len), &bfd_offset);
                        sizeof (len), &bfd_offset);
          len = netorder32 (len);
          len = netorder32 (len);
 
 
          /* Get addr.  */
          /* Get addr.  */
          bfdcore_read (core_bfd, osec, &addr,
          bfdcore_read (core_bfd, osec, &addr,
                        sizeof (addr), &bfd_offset);
                        sizeof (addr), &bfd_offset);
          addr = netorder64 (addr);
          addr = netorder64 (addr);
 
 
          rec = record_mem_alloc (addr, len);
          rec = record_mem_alloc (addr, len);
 
 
          /* Get val.  */
          /* Get val.  */
          bfdcore_read (core_bfd, osec, record_get_loc (rec),
          bfdcore_read (core_bfd, osec, record_get_loc (rec),
                        rec->u.mem.len, &bfd_offset);
                        rec->u.mem.len, &bfd_offset);
 
 
          if (record_debug)
          if (record_debug)
            fprintf_unfiltered (gdb_stdlog, "\
            fprintf_unfiltered (gdb_stdlog, "\
  Reading memory %s (1 plus %lu plus %lu plus %d bytes)\n",
  Reading memory %s (1 plus %lu plus %lu plus %d bytes)\n",
                                paddress (get_current_arch (),
                                paddress (get_current_arch (),
                                          rec->u.mem.addr),
                                          rec->u.mem.addr),
                                (unsigned long) sizeof (addr),
                                (unsigned long) sizeof (addr),
                                (unsigned long) sizeof (len),
                                (unsigned long) sizeof (len),
                                rec->u.mem.len);
                                rec->u.mem.len);
          break;
          break;
 
 
        case record_end: /* end */
        case record_end: /* end */
          rec = record_end_alloc ();
          rec = record_end_alloc ();
          record_insn_num ++;
          record_insn_num ++;
 
 
          /* Get signal value.  */
          /* Get signal value.  */
          bfdcore_read (core_bfd, osec, &signal,
          bfdcore_read (core_bfd, osec, &signal,
                        sizeof (signal), &bfd_offset);
                        sizeof (signal), &bfd_offset);
          signal = netorder32 (signal);
          signal = netorder32 (signal);
          rec->u.end.sigval = signal;
          rec->u.end.sigval = signal;
 
 
          /* Get insn count.  */
          /* Get insn count.  */
          bfdcore_read (core_bfd, osec, &count,
          bfdcore_read (core_bfd, osec, &count,
                        sizeof (count), &bfd_offset);
                        sizeof (count), &bfd_offset);
          count = netorder32 (count);
          count = netorder32 (count);
          rec->u.end.insn_num = count;
          rec->u.end.insn_num = count;
          record_insn_count = count + 1;
          record_insn_count = count + 1;
          if (record_debug)
          if (record_debug)
            fprintf_unfiltered (gdb_stdlog, "\
            fprintf_unfiltered (gdb_stdlog, "\
  Reading record_end (1 + %lu + %lu bytes), offset == %s\n",
  Reading record_end (1 + %lu + %lu bytes), offset == %s\n",
                                (unsigned long) sizeof (signal),
                                (unsigned long) sizeof (signal),
                                (unsigned long) sizeof (count),
                                (unsigned long) sizeof (count),
                                paddress (get_current_arch (),
                                paddress (get_current_arch (),
                                          bfd_offset));
                                          bfd_offset));
          break;
          break;
 
 
        default:
        default:
          error (_("Bad entry type in core file %s."),
          error (_("Bad entry type in core file %s."),
                 bfd_get_filename (core_bfd));
                 bfd_get_filename (core_bfd));
          break;
          break;
        }
        }
 
 
      /* Add rec to record arch list.  */
      /* Add rec to record arch list.  */
      record_arch_list_add (rec);
      record_arch_list_add (rec);
    }
    }
 
 
  discard_cleanups (old_cleanups);
  discard_cleanups (old_cleanups);
 
 
  /* Add record_arch_list_head to the end of record list.  */
  /* Add record_arch_list_head to the end of record list.  */
  record_first.next = record_arch_list_head;
  record_first.next = record_arch_list_head;
  record_arch_list_head->prev = &record_first;
  record_arch_list_head->prev = &record_first;
  record_arch_list_tail->next = NULL;
  record_arch_list_tail->next = NULL;
  record_list = &record_first;
  record_list = &record_first;
 
 
  /* Update record_insn_max_num.  */
  /* Update record_insn_max_num.  */
  if (record_insn_num > record_insn_max_num)
  if (record_insn_num > record_insn_max_num)
    {
    {
      record_insn_max_num = record_insn_num;
      record_insn_max_num = record_insn_num;
      warning (_("Auto increase record/replay buffer limit to %d."),
      warning (_("Auto increase record/replay buffer limit to %d."),
               record_insn_max_num);
               record_insn_max_num);
    }
    }
 
 
  /* Succeeded.  */
  /* Succeeded.  */
  printf_filtered (_("Restored records from core file %s.\n"),
  printf_filtered (_("Restored records from core file %s.\n"),
                   bfd_get_filename (core_bfd));
                   bfd_get_filename (core_bfd));
 
 
  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
}
}
 
 
/* bfdcore_write -- write bytes into a core file section.  */
/* bfdcore_write -- write bytes into a core file section.  */
 
 
static inline void
static inline void
bfdcore_write (bfd *obfd, asection *osec, void *buf, int len, int *offset)
bfdcore_write (bfd *obfd, asection *osec, void *buf, int len, int *offset)
{
{
  int ret = bfd_set_section_contents (obfd, osec, buf, *offset, len);
  int ret = bfd_set_section_contents (obfd, osec, buf, *offset, len);
 
 
  if (ret)
  if (ret)
    *offset += len;
    *offset += len;
  else
  else
    error (_("Failed to write %d bytes to core file %s ('%s').\n"),
    error (_("Failed to write %d bytes to core file %s ('%s').\n"),
           len, bfd_get_filename (obfd),
           len, bfd_get_filename (obfd),
           bfd_errmsg (bfd_get_error ()));
           bfd_errmsg (bfd_get_error ()));
}
}
 
 
/* Restore the execution log from a file.  We use a modified elf
/* Restore the execution log from a file.  We use a modified elf
   corefile format, with an extra section for our data.  */
   corefile format, with an extra section for our data.  */
 
 
static void
static void
cmd_record_restore (char *args, int from_tty)
cmd_record_restore (char *args, int from_tty)
{
{
  core_file_command (args, from_tty);
  core_file_command (args, from_tty);
  record_open (args, from_tty);
  record_open (args, from_tty);
}
}
 
 
static void
static void
record_save_cleanups (void *data)
record_save_cleanups (void *data)
{
{
  bfd *obfd = data;
  bfd *obfd = data;
  char *pathname = xstrdup (bfd_get_filename (obfd));
  char *pathname = xstrdup (bfd_get_filename (obfd));
  bfd_close (obfd);
  bfd_close (obfd);
  unlink (pathname);
  unlink (pathname);
  xfree (pathname);
  xfree (pathname);
}
}
 
 
/* Save the execution log to a file.  We use a modified elf corefile
/* Save the execution log to a file.  We use a modified elf corefile
   format, with an extra section for our data.  */
   format, with an extra section for our data.  */
 
 
static void
static void
cmd_record_save (char *args, int from_tty)
cmd_record_save (char *args, int from_tty)
{
{
  char *recfilename, recfilename_buffer[40];
  char *recfilename, recfilename_buffer[40];
  int recfd;
  int recfd;
  struct record_entry *cur_record_list;
  struct record_entry *cur_record_list;
  uint32_t magic;
  uint32_t magic;
  struct regcache *regcache;
  struct regcache *regcache;
  struct gdbarch *gdbarch;
  struct gdbarch *gdbarch;
  struct cleanup *old_cleanups;
  struct cleanup *old_cleanups;
  struct cleanup *set_cleanups;
  struct cleanup *set_cleanups;
  bfd *obfd;
  bfd *obfd;
  int save_size = 0;
  int save_size = 0;
  asection *osec = NULL;
  asection *osec = NULL;
  int bfd_offset = 0;
  int bfd_offset = 0;
 
 
  if (strcmp (current_target.to_shortname, "record") != 0)
  if (strcmp (current_target.to_shortname, "record") != 0)
    error (_("This command can only be used with target 'record'.\n"
    error (_("This command can only be used with target 'record'.\n"
             "Use 'target record' first.\n"));
             "Use 'target record' first.\n"));
 
 
  if (args && *args)
  if (args && *args)
    recfilename = args;
    recfilename = args;
  else
  else
    {
    {
      /* Default recfile name is "gdb_record.PID".  */
      /* Default recfile name is "gdb_record.PID".  */
      snprintf (recfilename_buffer, sizeof (recfilename_buffer),
      snprintf (recfilename_buffer, sizeof (recfilename_buffer),
                "gdb_record.%d", PIDGET (inferior_ptid));
                "gdb_record.%d", PIDGET (inferior_ptid));
      recfilename = recfilename_buffer;
      recfilename = recfilename_buffer;
    }
    }
 
 
  /* Open the save file.  */
  /* Open the save file.  */
  if (record_debug)
  if (record_debug)
    fprintf_unfiltered (gdb_stdlog, "Saving execution log to core file '%s'\n",
    fprintf_unfiltered (gdb_stdlog, "Saving execution log to core file '%s'\n",
                        recfilename);
                        recfilename);
 
 
  /* Open the output file.  */
  /* Open the output file.  */
  obfd = create_gcore_bfd (recfilename);
  obfd = create_gcore_bfd (recfilename);
  old_cleanups = make_cleanup (record_save_cleanups, obfd);
  old_cleanups = make_cleanup (record_save_cleanups, obfd);
 
 
  /* Save the current record entry to "cur_record_list".  */
  /* Save the current record entry to "cur_record_list".  */
  cur_record_list = record_list;
  cur_record_list = record_list;
 
 
  /* Get the values of regcache and gdbarch.  */
  /* Get the values of regcache and gdbarch.  */
  regcache = get_current_regcache ();
  regcache = get_current_regcache ();
  gdbarch = get_regcache_arch (regcache);
  gdbarch = get_regcache_arch (regcache);
 
 
  /* Disable the GDB operation record.  */
  /* Disable the GDB operation record.  */
  set_cleanups = record_gdb_operation_disable_set ();
  set_cleanups = record_gdb_operation_disable_set ();
 
 
  /* Reverse execute to the begin of record list.  */
  /* Reverse execute to the begin of record list.  */
  while (1)
  while (1)
    {
    {
      /* Check for beginning and end of log.  */
      /* Check for beginning and end of log.  */
      if (record_list == &record_first)
      if (record_list == &record_first)
        break;
        break;
 
 
      record_exec_insn (regcache, gdbarch, record_list);
      record_exec_insn (regcache, gdbarch, record_list);
 
 
      if (record_list->prev)
      if (record_list->prev)
        record_list = record_list->prev;
        record_list = record_list->prev;
    }
    }
 
 
  /* Compute the size needed for the extra bfd section.  */
  /* Compute the size needed for the extra bfd section.  */
  save_size = 4;        /* magic cookie */
  save_size = 4;        /* magic cookie */
  for (record_list = record_first.next; record_list;
  for (record_list = record_first.next; record_list;
       record_list = record_list->next)
       record_list = record_list->next)
    switch (record_list->type)
    switch (record_list->type)
      {
      {
      case record_end:
      case record_end:
        save_size += 1 + 4 + 4;
        save_size += 1 + 4 + 4;
        break;
        break;
      case record_reg:
      case record_reg:
        save_size += 1 + 4 + record_list->u.reg.len;
        save_size += 1 + 4 + record_list->u.reg.len;
        break;
        break;
      case record_mem:
      case record_mem:
        save_size += 1 + 4 + 8 + record_list->u.mem.len;
        save_size += 1 + 4 + 8 + record_list->u.mem.len;
        break;
        break;
      }
      }
 
 
  /* Make the new bfd section.  */
  /* Make the new bfd section.  */
  osec = bfd_make_section_anyway_with_flags (obfd, "precord",
  osec = bfd_make_section_anyway_with_flags (obfd, "precord",
                                             SEC_HAS_CONTENTS
                                             SEC_HAS_CONTENTS
                                             | SEC_READONLY);
                                             | SEC_READONLY);
  if (osec == NULL)
  if (osec == NULL)
    error (_("Failed to create 'precord' section for corefile %s: %s"),
    error (_("Failed to create 'precord' section for corefile %s: %s"),
           recfilename,
           recfilename,
           bfd_errmsg (bfd_get_error ()));
           bfd_errmsg (bfd_get_error ()));
  bfd_set_section_size (obfd, osec, save_size);
  bfd_set_section_size (obfd, osec, save_size);
  bfd_set_section_vma (obfd, osec, 0);
  bfd_set_section_vma (obfd, osec, 0);
  bfd_set_section_alignment (obfd, osec, 0);
  bfd_set_section_alignment (obfd, osec, 0);
  bfd_section_lma (obfd, osec) = 0;
  bfd_section_lma (obfd, osec) = 0;
 
 
  /* Save corefile state.  */
  /* Save corefile state.  */
  write_gcore_file (obfd);
  write_gcore_file (obfd);
 
 
  /* Write out the record log.  */
  /* Write out the record log.  */
  /* Write the magic code.  */
  /* Write the magic code.  */
  magic = RECORD_FILE_MAGIC;
  magic = RECORD_FILE_MAGIC;
  if (record_debug)
  if (record_debug)
    fprintf_unfiltered (gdb_stdlog, "\
    fprintf_unfiltered (gdb_stdlog, "\
  Writing 4-byte magic cookie RECORD_FILE_MAGIC (0x%s)\n",
  Writing 4-byte magic cookie RECORD_FILE_MAGIC (0x%s)\n",
                      phex_nz (magic, 4));
                      phex_nz (magic, 4));
  bfdcore_write (obfd, osec, &magic, sizeof (magic), &bfd_offset);
  bfdcore_write (obfd, osec, &magic, sizeof (magic), &bfd_offset);
 
 
  /* Save the entries to recfd and forward execute to the end of
  /* Save the entries to recfd and forward execute to the end of
     record list.  */
     record list.  */
  record_list = &record_first;
  record_list = &record_first;
  while (1)
  while (1)
    {
    {
      /* Save entry.  */
      /* Save entry.  */
      if (record_list != &record_first)
      if (record_list != &record_first)
        {
        {
          uint8_t type;
          uint8_t type;
          uint32_t regnum, len, signal, count;
          uint32_t regnum, len, signal, count;
          uint64_t addr;
          uint64_t addr;
 
 
          type = record_list->type;
          type = record_list->type;
          bfdcore_write (obfd, osec, &type, sizeof (type), &bfd_offset);
          bfdcore_write (obfd, osec, &type, sizeof (type), &bfd_offset);
 
 
          switch (record_list->type)
          switch (record_list->type)
            {
            {
            case record_reg: /* reg */
            case record_reg: /* reg */
              if (record_debug)
              if (record_debug)
                fprintf_unfiltered (gdb_stdlog, "\
                fprintf_unfiltered (gdb_stdlog, "\
  Writing register %d (1 plus %lu plus %d bytes)\n",
  Writing register %d (1 plus %lu plus %d bytes)\n",
                                    record_list->u.reg.num,
                                    record_list->u.reg.num,
                                    (unsigned long) sizeof (regnum),
                                    (unsigned long) sizeof (regnum),
                                    record_list->u.reg.len);
                                    record_list->u.reg.len);
 
 
              /* Write regnum.  */
              /* Write regnum.  */
              regnum = netorder32 (record_list->u.reg.num);
              regnum = netorder32 (record_list->u.reg.num);
              bfdcore_write (obfd, osec, &regnum,
              bfdcore_write (obfd, osec, &regnum,
                             sizeof (regnum), &bfd_offset);
                             sizeof (regnum), &bfd_offset);
 
 
              /* Write regval.  */
              /* Write regval.  */
              bfdcore_write (obfd, osec, record_get_loc (record_list),
              bfdcore_write (obfd, osec, record_get_loc (record_list),
                             record_list->u.reg.len, &bfd_offset);
                             record_list->u.reg.len, &bfd_offset);
              break;
              break;
 
 
            case record_mem: /* mem */
            case record_mem: /* mem */
              if (record_debug)
              if (record_debug)
                fprintf_unfiltered (gdb_stdlog, "\
                fprintf_unfiltered (gdb_stdlog, "\
  Writing memory %s (1 plus %lu plus %lu plus %d bytes)\n",
  Writing memory %s (1 plus %lu plus %lu plus %d bytes)\n",
                                    paddress (gdbarch,
                                    paddress (gdbarch,
                                              record_list->u.mem.addr),
                                              record_list->u.mem.addr),
                                    (unsigned long) sizeof (addr),
                                    (unsigned long) sizeof (addr),
                                    (unsigned long) sizeof (len),
                                    (unsigned long) sizeof (len),
                                    record_list->u.mem.len);
                                    record_list->u.mem.len);
 
 
              /* Write memlen.  */
              /* Write memlen.  */
              len = netorder32 (record_list->u.mem.len);
              len = netorder32 (record_list->u.mem.len);
              bfdcore_write (obfd, osec, &len, sizeof (len), &bfd_offset);
              bfdcore_write (obfd, osec, &len, sizeof (len), &bfd_offset);
 
 
              /* Write memaddr.  */
              /* Write memaddr.  */
              addr = netorder64 (record_list->u.mem.addr);
              addr = netorder64 (record_list->u.mem.addr);
              bfdcore_write (obfd, osec, &addr,
              bfdcore_write (obfd, osec, &addr,
                             sizeof (addr), &bfd_offset);
                             sizeof (addr), &bfd_offset);
 
 
              /* Write memval.  */
              /* Write memval.  */
              bfdcore_write (obfd, osec, record_get_loc (record_list),
              bfdcore_write (obfd, osec, record_get_loc (record_list),
                             record_list->u.mem.len, &bfd_offset);
                             record_list->u.mem.len, &bfd_offset);
              break;
              break;
 
 
              case record_end:
              case record_end:
                if (record_debug)
                if (record_debug)
                  fprintf_unfiltered (gdb_stdlog, "\
                  fprintf_unfiltered (gdb_stdlog, "\
  Writing record_end (1 + %lu + %lu bytes)\n",
  Writing record_end (1 + %lu + %lu bytes)\n",
                                      (unsigned long) sizeof (signal),
                                      (unsigned long) sizeof (signal),
                                      (unsigned long) sizeof (count));
                                      (unsigned long) sizeof (count));
                /* Write signal value.  */
                /* Write signal value.  */
                signal = netorder32 (record_list->u.end.sigval);
                signal = netorder32 (record_list->u.end.sigval);
                bfdcore_write (obfd, osec, &signal,
                bfdcore_write (obfd, osec, &signal,
                               sizeof (signal), &bfd_offset);
                               sizeof (signal), &bfd_offset);
 
 
                /* Write insn count.  */
                /* Write insn count.  */
                count = netorder32 (record_list->u.end.insn_num);
                count = netorder32 (record_list->u.end.insn_num);
                bfdcore_write (obfd, osec, &count,
                bfdcore_write (obfd, osec, &count,
                               sizeof (count), &bfd_offset);
                               sizeof (count), &bfd_offset);
                break;
                break;
            }
            }
        }
        }
 
 
      /* Execute entry.  */
      /* Execute entry.  */
      record_exec_insn (regcache, gdbarch, record_list);
      record_exec_insn (regcache, gdbarch, record_list);
 
 
      if (record_list->next)
      if (record_list->next)
        record_list = record_list->next;
        record_list = record_list->next;
      else
      else
        break;
        break;
    }
    }
 
 
  /* Reverse execute to cur_record_list.  */
  /* Reverse execute to cur_record_list.  */
  while (1)
  while (1)
    {
    {
      /* Check for beginning and end of log.  */
      /* Check for beginning and end of log.  */
      if (record_list == cur_record_list)
      if (record_list == cur_record_list)
        break;
        break;
 
 
      record_exec_insn (regcache, gdbarch, record_list);
      record_exec_insn (regcache, gdbarch, record_list);
 
 
      if (record_list->prev)
      if (record_list->prev)
        record_list = record_list->prev;
        record_list = record_list->prev;
    }
    }
 
 
  do_cleanups (set_cleanups);
  do_cleanups (set_cleanups);
  bfd_close (obfd);
  bfd_close (obfd);
  discard_cleanups (old_cleanups);
  discard_cleanups (old_cleanups);
 
 
  /* Succeeded.  */
  /* Succeeded.  */
  printf_filtered (_("Saved core file %s with execution log.\n"),
  printf_filtered (_("Saved core file %s with execution log.\n"),
                   recfilename);
                   recfilename);
}
}
 
 
/* record_goto_insn -- rewind the record log (forward or backward,
/* record_goto_insn -- rewind the record log (forward or backward,
   depending on DIR) to the given entry, changing the program state
   depending on DIR) to the given entry, changing the program state
   correspondingly.  */
   correspondingly.  */
 
 
static void
static void
record_goto_insn (struct record_entry *entry,
record_goto_insn (struct record_entry *entry,
                  enum exec_direction_kind dir)
                  enum exec_direction_kind dir)
{
{
  struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
  struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
  struct regcache *regcache = get_current_regcache ();
  struct regcache *regcache = get_current_regcache ();
  struct gdbarch *gdbarch = get_regcache_arch (regcache);
  struct gdbarch *gdbarch = get_regcache_arch (regcache);
 
 
  /* Assume everything is valid: we will hit the entry,
  /* Assume everything is valid: we will hit the entry,
     and we will not hit the end of the recording.  */
     and we will not hit the end of the recording.  */
 
 
  if (dir == EXEC_FORWARD)
  if (dir == EXEC_FORWARD)
    record_list = record_list->next;
    record_list = record_list->next;
 
 
  do
  do
    {
    {
      record_exec_insn (regcache, gdbarch, record_list);
      record_exec_insn (regcache, gdbarch, record_list);
      if (dir == EXEC_REVERSE)
      if (dir == EXEC_REVERSE)
        record_list = record_list->prev;
        record_list = record_list->prev;
      else
      else
        record_list = record_list->next;
        record_list = record_list->next;
    } while (record_list != entry);
    } while (record_list != entry);
  do_cleanups (set_cleanups);
  do_cleanups (set_cleanups);
}
}
 
 
/* "record goto" command.  Argument is an instruction number,
/* "record goto" command.  Argument is an instruction number,
   as given by "info record".
   as given by "info record".
 
 
   Rewinds the recording (forward or backward) to the given instruction.  */
   Rewinds the recording (forward or backward) to the given instruction.  */
 
 
static void
static void
cmd_record_goto (char *arg, int from_tty)
cmd_record_goto (char *arg, int from_tty)
{
{
  struct record_entry *p = NULL;
  struct record_entry *p = NULL;
  ULONGEST target_insn = 0;
  ULONGEST target_insn = 0;
 
 
  if (arg == NULL || *arg == '\0')
  if (arg == NULL || *arg == '\0')
    error (_("Command requires an argument (insn number to go to)."));
    error (_("Command requires an argument (insn number to go to)."));
 
 
  if (strncmp (arg, "start", strlen ("start")) == 0
  if (strncmp (arg, "start", strlen ("start")) == 0
      || strncmp (arg, "begin", strlen ("begin")) == 0)
      || strncmp (arg, "begin", strlen ("begin")) == 0)
    {
    {
      /* Special case.  Find first insn.  */
      /* Special case.  Find first insn.  */
      for (p = &record_first; p != NULL; p = p->next)
      for (p = &record_first; p != NULL; p = p->next)
        if (p->type == record_end)
        if (p->type == record_end)
          break;
          break;
      if (p)
      if (p)
        target_insn = p->u.end.insn_num;
        target_insn = p->u.end.insn_num;
    }
    }
  else if (strncmp (arg, "end", strlen ("end")) == 0)
  else if (strncmp (arg, "end", strlen ("end")) == 0)
    {
    {
      /* Special case.  Find last insn.  */
      /* Special case.  Find last insn.  */
      for (p = record_list; p->next != NULL; p = p->next)
      for (p = record_list; p->next != NULL; p = p->next)
        ;
        ;
      for (; p!= NULL; p = p->prev)
      for (; p!= NULL; p = p->prev)
        if (p->type == record_end)
        if (p->type == record_end)
          break;
          break;
      if (p)
      if (p)
        target_insn = p->u.end.insn_num;
        target_insn = p->u.end.insn_num;
    }
    }
  else
  else
    {
    {
      /* General case.  Find designated insn.  */
      /* General case.  Find designated insn.  */
      target_insn = parse_and_eval_long (arg);
      target_insn = parse_and_eval_long (arg);
 
 
      for (p = &record_first; p != NULL; p = p->next)
      for (p = &record_first; p != NULL; p = p->next)
        if (p->type == record_end && p->u.end.insn_num == target_insn)
        if (p->type == record_end && p->u.end.insn_num == target_insn)
          break;
          break;
    }
    }
 
 
  if (p == NULL)
  if (p == NULL)
    error (_("Target insn '%s' not found."), arg);
    error (_("Target insn '%s' not found."), arg);
  else if (p == record_list)
  else if (p == record_list)
    error (_("Already at insn '%s'."), arg);
    error (_("Already at insn '%s'."), arg);
  else if (p->u.end.insn_num > record_list->u.end.insn_num)
  else if (p->u.end.insn_num > record_list->u.end.insn_num)
    {
    {
      printf_filtered (_("Go forward to insn number %s\n"),
      printf_filtered (_("Go forward to insn number %s\n"),
                       pulongest (target_insn));
                       pulongest (target_insn));
      record_goto_insn (p, EXEC_FORWARD);
      record_goto_insn (p, EXEC_FORWARD);
    }
    }
  else
  else
    {
    {
      printf_filtered (_("Go backward to insn number %s\n"),
      printf_filtered (_("Go backward to insn number %s\n"),
                       pulongest (target_insn));
                       pulongest (target_insn));
      record_goto_insn (p, EXEC_REVERSE);
      record_goto_insn (p, EXEC_REVERSE);
    }
    }
  registers_changed ();
  registers_changed ();
  reinit_frame_cache ();
  reinit_frame_cache ();
  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
}
}
 
 
void
void
_initialize_record (void)
_initialize_record (void)
{
{
  struct cmd_list_element *c;
  struct cmd_list_element *c;
 
 
  /* Init record_first.  */
  /* Init record_first.  */
  record_first.prev = NULL;
  record_first.prev = NULL;
  record_first.next = NULL;
  record_first.next = NULL;
  record_first.type = record_end;
  record_first.type = record_end;
 
 
  init_record_ops ();
  init_record_ops ();
  add_target (&record_ops);
  add_target (&record_ops);
  init_record_core_ops ();
  init_record_core_ops ();
  add_target (&record_core_ops);
  add_target (&record_core_ops);
 
 
  add_setshow_zinteger_cmd ("record", no_class, &record_debug,
  add_setshow_zinteger_cmd ("record", no_class, &record_debug,
                            _("Set debugging of record/replay feature."),
                            _("Set debugging of record/replay feature."),
                            _("Show debugging of record/replay feature."),
                            _("Show debugging of record/replay feature."),
                            _("When enabled, debugging output for "
                            _("When enabled, debugging output for "
                              "record/replay feature is displayed."),
                              "record/replay feature is displayed."),
                            NULL, show_record_debug, &setdebuglist,
                            NULL, show_record_debug, &setdebuglist,
                            &showdebuglist);
                            &showdebuglist);
 
 
  c = add_prefix_cmd ("record", class_obscure, cmd_record_start,
  c = add_prefix_cmd ("record", class_obscure, cmd_record_start,
                      _("Abbreviated form of \"target record\" command."),
                      _("Abbreviated form of \"target record\" command."),
                      &record_cmdlist, "record ", 0, &cmdlist);
                      &record_cmdlist, "record ", 0, &cmdlist);
  set_cmd_completer (c, filename_completer);
  set_cmd_completer (c, filename_completer);
 
 
  add_com_alias ("rec", "record", class_obscure, 1);
  add_com_alias ("rec", "record", class_obscure, 1);
  add_prefix_cmd ("record", class_support, set_record_command,
  add_prefix_cmd ("record", class_support, set_record_command,
                  _("Set record options"), &set_record_cmdlist,
                  _("Set record options"), &set_record_cmdlist,
                  "set record ", 0, &setlist);
                  "set record ", 0, &setlist);
  add_alias_cmd ("rec", "record", class_obscure, 1, &setlist);
  add_alias_cmd ("rec", "record", class_obscure, 1, &setlist);
  add_prefix_cmd ("record", class_support, show_record_command,
  add_prefix_cmd ("record", class_support, show_record_command,
                  _("Show record options"), &show_record_cmdlist,
                  _("Show record options"), &show_record_cmdlist,
                  "show record ", 0, &showlist);
                  "show record ", 0, &showlist);
  add_alias_cmd ("rec", "record", class_obscure, 1, &showlist);
  add_alias_cmd ("rec", "record", class_obscure, 1, &showlist);
  add_prefix_cmd ("record", class_support, info_record_command,
  add_prefix_cmd ("record", class_support, info_record_command,
                  _("Info record options"), &info_record_cmdlist,
                  _("Info record options"), &info_record_cmdlist,
                  "info record ", 0, &infolist);
                  "info record ", 0, &infolist);
  add_alias_cmd ("rec", "record", class_obscure, 1, &infolist);
  add_alias_cmd ("rec", "record", class_obscure, 1, &infolist);
 
 
  c = add_cmd ("save", class_obscure, cmd_record_save,
  c = add_cmd ("save", class_obscure, cmd_record_save,
               _("Save the execution log to a file.\n\
               _("Save the execution log to a file.\n\
Argument is optional filename.\n\
Argument is optional filename.\n\
Default filename is 'gdb_record.<process_id>'."),
Default filename is 'gdb_record.<process_id>'."),
               &record_cmdlist);
               &record_cmdlist);
  set_cmd_completer (c, filename_completer);
  set_cmd_completer (c, filename_completer);
 
 
  c = add_cmd ("restore", class_obscure, cmd_record_restore,
  c = add_cmd ("restore", class_obscure, cmd_record_restore,
               _("Restore the execution log from a file.\n\
               _("Restore the execution log from a file.\n\
Argument is filename.  File must be created with 'record save'."),
Argument is filename.  File must be created with 'record save'."),
               &record_cmdlist);
               &record_cmdlist);
  set_cmd_completer (c, filename_completer);
  set_cmd_completer (c, filename_completer);
 
 
  add_cmd ("delete", class_obscure, cmd_record_delete,
  add_cmd ("delete", class_obscure, cmd_record_delete,
           _("Delete the rest of execution log and start recording it anew."),
           _("Delete the rest of execution log and start recording it anew."),
           &record_cmdlist);
           &record_cmdlist);
  add_alias_cmd ("d", "delete", class_obscure, 1, &record_cmdlist);
  add_alias_cmd ("d", "delete", class_obscure, 1, &record_cmdlist);
  add_alias_cmd ("del", "delete", class_obscure, 1, &record_cmdlist);
  add_alias_cmd ("del", "delete", class_obscure, 1, &record_cmdlist);
 
 
  add_cmd ("stop", class_obscure, cmd_record_stop,
  add_cmd ("stop", class_obscure, cmd_record_stop,
           _("Stop the record/replay target."),
           _("Stop the record/replay target."),
           &record_cmdlist);
           &record_cmdlist);
  add_alias_cmd ("s", "stop", class_obscure, 1, &record_cmdlist);
  add_alias_cmd ("s", "stop", class_obscure, 1, &record_cmdlist);
 
 
  /* Record instructions number limit command.  */
  /* Record instructions number limit command.  */
  add_setshow_boolean_cmd ("stop-at-limit", no_class,
  add_setshow_boolean_cmd ("stop-at-limit", no_class,
                           &record_stop_at_limit, _("\
                           &record_stop_at_limit, _("\
Set whether record/replay stops when record/replay buffer becomes full."), _("\
Set whether record/replay stops when record/replay buffer becomes full."), _("\
Show 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\
Default is ON.\n\
When ON, if the record/replay buffer becomes full, ask user what to do.\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\
When OFF, if the record/replay buffer becomes full,\n\
delete the oldest recorded instruction to make room for each new one."),
delete the oldest recorded instruction to make room for each new one."),
                           NULL, NULL,
                           NULL, NULL,
                           &set_record_cmdlist, &show_record_cmdlist);
                           &set_record_cmdlist, &show_record_cmdlist);
  add_setshow_uinteger_cmd ("insn-number-max", no_class,
  add_setshow_uinteger_cmd ("insn-number-max", no_class,
                            &record_insn_max_num,
                            &record_insn_max_num,
                            _("Set record/replay buffer limit."),
                            _("Set record/replay buffer limit."),
                            _("Show record/replay buffer limit."), _("\
                            _("Show record/replay buffer limit."), _("\
Set the maximum number of instructions to be stored in the\n\
Set the maximum number of instructions to be stored in the\n\
record/replay buffer.  Zero means unlimited.  Default is 200000."),
record/replay buffer.  Zero means unlimited.  Default is 200000."),
                            set_record_insn_max_num,
                            set_record_insn_max_num,
                            NULL, &set_record_cmdlist, &show_record_cmdlist);
                            NULL, &set_record_cmdlist, &show_record_cmdlist);
 
 
  add_cmd ("goto", class_obscure, cmd_record_goto, _("\
  add_cmd ("goto", class_obscure, cmd_record_goto, _("\
Restore the program to its state at instruction number N.\n\
Restore the program to its state at instruction number N.\n\
Argument is instruction number, as shown by 'info record'."),
Argument is instruction number, as shown by 'info record'."),
           &record_cmdlist);
           &record_cmdlist);
}
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.