2010-03-08 Jan Kratochvil <jan.kratochvil@redhat.com>
[deliverable/binutils-gdb.git] / gdb / record.c
index 1cb2dab7f1b89bdb6825add2a6b79c42ad8d136e..fa6cfbbee89d99364cc4a15c86ba9c273e93f0bc 100644 (file)
@@ -1,6 +1,6 @@
 /* Process record and replay target for GDB, the GNU debugger.
 
-   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -31,7 +31,6 @@
 #include "elf-bfd.h"
 #include "gcore.h"
 
-#include <byteswap.h>
 #include <signal.h>
 
 /* This module implements "target record", also known as "process
@@ -225,6 +224,9 @@ static int (*record_beneath_to_insert_breakpoint) (struct gdbarch *,
                                                   struct bp_target_info *);
 static int (*record_beneath_to_remove_breakpoint) (struct gdbarch *,
                                                   struct bp_target_info *);
+static int (*record_beneath_to_stopped_by_watchpoint) (void);
+static int (*record_beneath_to_stopped_data_address) (struct target_ops *,
+                                                     CORE_ADDR *);
 
 /* Alloc and free functions for record_reg, record_mem, and record_end 
    entries.  */
@@ -570,17 +572,11 @@ record_arch_list_cleanups (void *ignore)
    record the running message of inferior and set them to
    record_arch_list, and add it to record_list.  */
 
-struct record_message_args {
-  struct regcache *regcache;
-  enum target_signal signal;
-};
-
 static int
-record_message (void *args)
+record_message (struct regcache *regcache, enum target_signal signal)
 {
   int ret;
-  struct record_message_args *myargs = args;
-  struct gdbarch *gdbarch = get_regcache_arch (myargs->regcache);
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
   struct cleanup *old_cleanups = make_cleanup (record_arch_list_cleanups, 0);
 
   record_arch_list_head = NULL;
@@ -614,18 +610,18 @@ record_message (void *args)
   if (record_list != &record_first)    /* FIXME better way to check */
     {
       gdb_assert (record_list->type == record_end);
-      record_list->u.end.sigval = myargs->signal;
+      record_list->u.end.sigval = signal;
     }
 
-  if (myargs->signal == TARGET_SIGNAL_0
+  if (signal == TARGET_SIGNAL_0
       || !gdbarch_process_record_signal_p (gdbarch))
     ret = gdbarch_process_record (gdbarch,
-                                 myargs->regcache,
-                                 regcache_read_pc (myargs->regcache));
+                                 regcache,
+                                 regcache_read_pc (regcache));
   else
     ret = gdbarch_process_record_signal (gdbarch,
-                                        myargs->regcache,
-                                        myargs->signal);
+                                        regcache,
+                                        signal);
 
   if (ret > 0)
     error (_("Process record: inferior program stopped."));
@@ -646,15 +642,29 @@ record_message (void *args)
   return 1;
 }
 
+struct record_message_args {
+  struct regcache *regcache;
+  enum target_signal signal;
+};
+
+static int
+record_message_wrapper (void *args)
+{
+  struct record_message_args *record_args = args;
+
+  return record_message (record_args->regcache, record_args->signal);
+}
+
 static int
-do_record_message (struct regcache *regcache,
-                  enum target_signal signal)
+record_message_wrapper_safe (struct regcache *regcache,
+                             enum target_signal signal)
 {
   struct record_message_args args;
 
   args.regcache = regcache;
   args.signal = signal;
-  return catch_errors (record_message, &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
@@ -674,6 +684,9 @@ record_gdb_operation_disable_set (void)
   return old_cleanups;
 }
 
+/* Flag set to TRUE for target_stopped_by_watchpoint.  */
+static int record_hw_watchpoint = 0;
+
 /* Execute one instruction from the record log.  Each instruction in
    the log will be represented by an arbitrary sequence of register
    entries and memory entries, followed by an 'end' entry.  */
@@ -740,7 +753,22 @@ record_exec_insn (struct regcache *regcache, struct gdbarch *gdbarch,
                                entry->u.mem.len);
                   }
                 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
+                      watchpoint should trap.  Note that this
+                      presently assumes the target beneath supports
+                      continuable watchpoints.  On non-continuable
+                      watchpoints target, we'll want to check this
+                      _before_ actually doing the memory change, and
+                      not doing the change at all if the watchpoint
+                      traps.  */
+                   if (hardware_watchpoint_inserted_in_range
+                       (get_regcache_aspace (regcache),
+                        entry->u.mem.addr, entry->u.mem.len))
+                     record_hw_watchpoint = 1;
+                 }
               }
           }
       }
@@ -771,6 +799,8 @@ static int (*tmp_to_insert_breakpoint) (struct gdbarch *,
                                        struct bp_target_info *);
 static int (*tmp_to_remove_breakpoint) (struct gdbarch *,
                                        struct bp_target_info *);
+static int (*tmp_to_stopped_by_watchpoint) (void);
+static int (*tmp_to_stopped_data_address) (struct target_ops *, CORE_ADDR *);
 
 static void record_restore (void);
 
@@ -837,6 +867,10 @@ record_open_1 (char *name, int from_tty)
     error (_("Could not find 'to_insert_breakpoint' method on the target stack."));
   if (!tmp_to_remove_breakpoint)
     error (_("Could not find 'to_remove_breakpoint' method on the target stack."));
+  if (!tmp_to_stopped_by_watchpoint)
+    error (_("Could not find 'to_stopped_by_watchpoint' method on the target stack."));
+  if (!tmp_to_stopped_data_address)
+    error (_("Could not find 'to_stopped_data_address' method on the target stack."));
 
   push_target (&record_ops);
 }
@@ -867,6 +901,8 @@ record_open (char *name, int from_tty)
   tmp_to_xfer_partial = NULL;
   tmp_to_insert_breakpoint = NULL;
   tmp_to_remove_breakpoint = NULL;
+  tmp_to_stopped_by_watchpoint = NULL;
+  tmp_to_stopped_data_address = NULL;
 
   /* Set the beneath function pointers.  */
   for (t = current_target.beneath; t != NULL; t = t->beneath)
@@ -895,6 +931,10 @@ record_open (char *name, int from_tty)
        tmp_to_insert_breakpoint = t->to_insert_breakpoint;
       if (!tmp_to_remove_breakpoint)
        tmp_to_remove_breakpoint = t->to_remove_breakpoint;
+      if (!tmp_to_stopped_by_watchpoint)
+       tmp_to_stopped_by_watchpoint = t->to_stopped_by_watchpoint;
+      if (!tmp_to_stopped_data_address)
+       tmp_to_stopped_data_address = t->to_stopped_data_address;
     }
   if (!tmp_to_xfer_partial)
     error (_("Could not find 'to_xfer_partial' method on the target stack."));
@@ -916,6 +956,8 @@ record_open (char *name, int from_tty)
   record_beneath_to_xfer_partial = tmp_to_xfer_partial;
   record_beneath_to_insert_breakpoint = tmp_to_insert_breakpoint;
   record_beneath_to_remove_breakpoint = tmp_to_remove_breakpoint;
+  record_beneath_to_stopped_by_watchpoint = tmp_to_stopped_by_watchpoint;
+  record_beneath_to_stopped_data_address = tmp_to_stopped_data_address;
 
   if (current_target.to_stratum == core_stratum)
     record_core_open_1 (name, from_tty);
@@ -955,7 +997,6 @@ record_close (int quitting)
 }
 
 static int record_resume_step = 0;
-static int record_resume_error;
 
 /* "to_resume" target method.  Resume the process record target.  */
 
@@ -967,15 +1008,7 @@ record_resume (struct target_ops *ops, ptid_t ptid, int step,
 
   if (!RECORD_IS_REPLAY)
     {
-      if (do_record_message (get_current_regcache (), signal))
-        {
-          record_resume_error = 0;
-        }
-      else
-        {
-          record_resume_error = 1;
-          return;
-        }
+      record_message (get_current_regcache (), signal);
       record_beneath_to_resume (record_beneath_to_resume_ops, ptid, 1,
                                 signal);
     }
@@ -1039,14 +1072,6 @@ record_wait (struct target_ops *ops,
 
   if (!RECORD_IS_REPLAY && ops != &record_core_ops)
     {
-      if (record_resume_error)
-       {
-         /* If record_resume get error, return directly.  */
-         status->kind = TARGET_WAITKIND_STOPPED;
-         status->value.sig = TARGET_SIGNAL_ABRT;
-         return inferior_ptid;
-       }
-
       if (record_resume_step)
        {
          /* This is a single step.  */
@@ -1069,30 +1094,46 @@ record_wait (struct target_ops *ops,
                  && status->value.sig == TARGET_SIGNAL_TRAP)
                {
                  struct regcache *regcache;
+                 struct address_space *aspace;
+
+                 /* Yes -- this is likely our single-step finishing,
+                    but check if there's any reason the core would be
+                    interested in the event.  */
 
-                 /* Yes -- check if there is a breakpoint.  */
                  registers_changed ();
                  regcache = get_current_regcache ();
                  tmp_pc = regcache_read_pc (regcache);
-                 if (breakpoint_inserted_here_p (get_regcache_aspace (regcache),
-                                                 tmp_pc))
+                 aspace = get_regcache_aspace (regcache);
+
+                 if (target_stopped_by_watchpoint ())
                    {
-                     /* There is a breakpoint.  GDB will want to stop.  */
-                     struct gdbarch *gdbarch = get_regcache_arch (regcache);
-                     CORE_ADDR decr_pc_after_break
-                       = gdbarch_decr_pc_after_break (gdbarch);
-                     if (decr_pc_after_break)
-                       regcache_write_pc (regcache,
-                                          tmp_pc + decr_pc_after_break);
+                     /* Always interested in watchpoints.  */
+                   }
+                 else if (breakpoint_inserted_here_p (aspace, tmp_pc))
+                   {
+                     /* There is a breakpoint here.  Let the core
+                        handle it.  */
+                     if (software_breakpoint_inserted_here_p (aspace, tmp_pc))
+                       {
+                         struct gdbarch *gdbarch = get_regcache_arch (regcache);
+                         CORE_ADDR decr_pc_after_break
+                           = gdbarch_decr_pc_after_break (gdbarch);
+                         if (decr_pc_after_break)
+                           regcache_write_pc (regcache,
+                                              tmp_pc + decr_pc_after_break);
+                       }
                    }
                  else
                    {
-                     /* There is not a breakpoint, and gdb is not
-                        stepping, therefore gdb will not stop.
-                        Therefore we will not return to gdb.
-                        Record the insn and resume.  */
-                     if (!do_record_message (regcache, TARGET_SIGNAL_0))
-                       break;
+                     /* This must be a single-step trap.  Record the
+                        insn and issue another step.  */
+                     if (!record_message_wrapper_safe (regcache,
+                                                        TARGET_SIGNAL_0))
+                       {
+                           status->kind = TARGET_WAITKIND_STOPPED;
+                           status->value.sig = TARGET_SIGNAL_0;
+                           break;
+                       }
 
                      record_beneath_to_resume (record_beneath_to_resume_ops,
                                                ptid, 1,
@@ -1112,29 +1153,33 @@ record_wait (struct target_ops *ops,
     {
       struct regcache *regcache = get_current_regcache ();
       struct gdbarch *gdbarch = get_regcache_arch (regcache);
+      struct address_space *aspace = get_regcache_aspace (regcache);
       int continue_flag = 1;
       int first_record_end = 1;
       struct cleanup *old_cleanups = make_cleanup (record_wait_cleanups, 0);
       CORE_ADDR tmp_pc;
 
+      record_hw_watchpoint = 0;
       status->kind = TARGET_WAITKIND_STOPPED;
 
       /* Check breakpoint when forward execute.  */
       if (execution_direction == EXEC_FORWARD)
        {
          tmp_pc = regcache_read_pc (regcache);
-         if (breakpoint_inserted_here_p (get_regcache_aspace (regcache),
-                                         tmp_pc))
+         if (breakpoint_inserted_here_p (aspace, tmp_pc))
            {
+             int decr_pc_after_break = gdbarch_decr_pc_after_break (gdbarch);
+
              if (record_debug)
                fprintf_unfiltered (gdb_stdlog,
                                    "Process record: break at %s.\n",
                                    paddress (gdbarch, tmp_pc));
-             if (gdbarch_decr_pc_after_break (gdbarch)
-                 && !record_resume_step)
+
+             if (decr_pc_after_break
+                 && !record_resume_step
+                 && software_breakpoint_inserted_here_p (aspace, tmp_pc))
                regcache_write_pc (regcache,
-                                  tmp_pc +
-                                  gdbarch_decr_pc_after_break (gdbarch));
+                                  tmp_pc + decr_pc_after_break);
              goto replay_out;
            }
        }
@@ -1204,20 +1249,31 @@ record_wait (struct target_ops *ops,
 
                  /* check breakpoint */
                  tmp_pc = regcache_read_pc (regcache);
-                 if (breakpoint_inserted_here_p (get_regcache_aspace (regcache),
-                                                 tmp_pc))
+                 if (breakpoint_inserted_here_p (aspace, tmp_pc))
                    {
+                     int decr_pc_after_break
+                       = gdbarch_decr_pc_after_break (gdbarch);
+
                      if (record_debug)
                        fprintf_unfiltered (gdb_stdlog,
                                            "Process record: break "
                                            "at %s.\n",
                                            paddress (gdbarch, tmp_pc));
-                     if (gdbarch_decr_pc_after_break (gdbarch)
+                     if (decr_pc_after_break
                          && execution_direction == EXEC_FORWARD
-                         && !record_resume_step)
+                         && !record_resume_step
+                         && software_breakpoint_inserted_here_p (aspace,
+                                                                 tmp_pc))
                        regcache_write_pc (regcache,
-                                          tmp_pc +
-                                          gdbarch_decr_pc_after_break (gdbarch));
+                                          tmp_pc + decr_pc_after_break);
+                     continue_flag = 0;
+                   }
+
+                 if (record_hw_watchpoint)
+                   {
+                     if (record_debug)
+                       fprintf_unfiltered (gdb_stdlog, "\
+Process record: hit hw watchpoint.\n");
                      continue_flag = 0;
                    }
                  /* Check target signal */
@@ -1261,6 +1317,24 @@ replay_out:
   return inferior_ptid;
 }
 
+static int
+record_stopped_by_watchpoint (void)
+{
+  if (RECORD_IS_REPLAY)
+    return record_hw_watchpoint;
+  else
+    return record_beneath_to_stopped_by_watchpoint ();
+}
+
+static int
+record_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p)
+{
+  if (RECORD_IS_REPLAY)
+    return 0;
+  else
+    return record_beneath_to_stopped_data_address (ops, addr_p);
+}
+
 /* "to_disconnect" method for process record target.  */
 
 static void
@@ -1524,6 +1598,57 @@ record_can_execute_reverse (void)
   return 1;
 }
 
+/* "to_get_bookmark" method for process record and prec over core.  */
+
+static gdb_byte *
+record_get_bookmark (char *args, int from_tty)
+{
+  gdb_byte *ret = NULL;
+
+  /* Return stringified form of instruction count.  */
+  if (record_list && record_list->type == record_end)
+    ret = xstrdup (pulongest (record_list->u.end.insn_num));
+
+  if (record_debug)
+    {
+      if (ret)
+       fprintf_unfiltered (gdb_stdlog,
+                           "record_get_bookmark returns %s\n", ret);
+      else
+       fprintf_unfiltered (gdb_stdlog,
+                           "record_get_bookmark returns NULL\n");
+    }
+  return ret;
+}
+
+/* The implementation of the command "record goto".  */
+static void cmd_record_goto (char *, int);
+
+/* "to_goto_bookmark" method for process record and prec over core.  */
+
+static void
+record_goto_bookmark (gdb_byte *bookmark, int from_tty)
+{
+  if (record_debug)
+    fprintf_unfiltered (gdb_stdlog,
+                       "record_goto_bookmark receives %s\n", bookmark);
+
+  if (bookmark[0] == '\'' || bookmark[0] == '\"')
+    {
+      if (bookmark[strlen (bookmark) - 1] != bookmark[0])
+       error (_("Unbalanced quotes: %s"), bookmark);
+
+      /* Strip trailing quote.  */
+      bookmark[strlen (bookmark) - 1] = '\0';
+      /* Strip leading quote.  */
+      bookmark++;
+      /* Pass along to cmd_record_goto.  */
+    }
+
+  cmd_record_goto ((char *) bookmark, from_tty);
+  return;
+}
+
 static void
 init_record_ops (void)
 {
@@ -1544,8 +1669,13 @@ init_record_ops (void)
   record_ops.to_xfer_partial = record_xfer_partial;
   record_ops.to_insert_breakpoint = record_insert_breakpoint;
   record_ops.to_remove_breakpoint = record_remove_breakpoint;
+  record_ops.to_stopped_by_watchpoint = record_stopped_by_watchpoint;
+  record_ops.to_stopped_data_address = record_stopped_data_address;
   record_ops.to_can_execute_reverse = record_can_execute_reverse;
   record_ops.to_stratum = record_stratum;
+  /* Add bookmark target methods.  */
+  record_ops.to_get_bookmark = record_get_bookmark;
+  record_ops.to_goto_bookmark = record_goto_bookmark;
   record_ops.to_magic = OPS_MAGIC;
 }
 
@@ -1629,6 +1759,7 @@ record_core_xfer_partial (struct target_ops *ops, enum target_object object,
                if (offset >= p->addr)
                  {
                    struct record_core_buf_entry *entry;
+                  ULONGEST sec_offset;
 
                    if (offset >= p->endaddr)
                      continue;
@@ -1636,7 +1767,7 @@ record_core_xfer_partial (struct target_ops *ops, enum target_object object,
                    if (offset + len > p->endaddr)
                      len = p->endaddr - offset;
 
-                   offset -= p->addr;
+                   sec_offset = offset - p->addr;
 
                    /* Read readbuf or write writebuf p, offset, len.  */
                    /* Check flags.  */
@@ -1673,7 +1804,8 @@ record_core_xfer_partial (struct target_ops *ops, enum target_object object,
                            record_core_buf_list = entry;
                          }
 
-                        memcpy (entry->buf + offset, writebuf, (size_t) len);
+                        memcpy (entry->buf + sec_offset, writebuf,
+                               (size_t) len);
                      }
                    else
                      {
@@ -1683,7 +1815,8 @@ record_core_xfer_partial (struct target_ops *ops, enum target_object object,
                                    object, annex, readbuf, writebuf,
                                    offset, len);
 
-                       memcpy (readbuf, entry->buf + offset, (size_t) len);
+                       memcpy (readbuf, entry->buf + sec_offset,
+                              (size_t) len);
                      }
 
                    return len;
@@ -1745,9 +1878,14 @@ init_record_core_ops (void)
   record_core_ops.to_xfer_partial = record_core_xfer_partial;
   record_core_ops.to_insert_breakpoint = record_core_insert_breakpoint;
   record_core_ops.to_remove_breakpoint = record_core_remove_breakpoint;
+  record_core_ops.to_stopped_by_watchpoint = record_stopped_by_watchpoint;
+  record_core_ops.to_stopped_data_address = record_stopped_data_address;
   record_core_ops.to_can_execute_reverse = record_can_execute_reverse;
   record_core_ops.to_has_execution = record_core_has_execution;
   record_core_ops.to_stratum = record_stratum;
+  /* Add bookmark target methods.  */
+  record_core_ops.to_get_bookmark = record_get_bookmark;
+  record_core_ops.to_goto_bookmark = record_goto_bookmark;
   record_core_ops.to_magic = OPS_MAGIC;
 }
 
@@ -1953,27 +2091,33 @@ bfdcore_read (bfd *obfd, asection *osec, void *buf, int len, int *offset)
 }
 
 static inline uint64_t
-netorder64 (uint64_t fromfile)
+netorder64 (uint64_t input)
 {
-  return (BYTE_ORDER == LITTLE_ENDIAN) 
-    ? bswap_64 (fromfile) 
-    : fromfile;
+  uint64_t ret;
+
+  store_unsigned_integer ((gdb_byte *) &ret, sizeof (ret), 
+                         BFD_ENDIAN_BIG, input);
+  return ret;
 }
 
 static inline uint32_t
-netorder32 (uint32_t fromfile)
+netorder32 (uint32_t input)
 {
-  return (BYTE_ORDER == LITTLE_ENDIAN) 
-    ? bswap_32 (fromfile) 
-    : fromfile;
+  uint32_t ret;
+
+  store_unsigned_integer ((gdb_byte *) &ret, sizeof (ret), 
+                         BFD_ENDIAN_BIG, input);
+  return ret;
 }
 
 static inline uint16_t
-netorder16 (uint16_t fromfile)
+netorder16 (uint16_t input)
 {
-  return (BYTE_ORDER == LITTLE_ENDIAN) 
-    ? bswap_16 (fromfile) 
-    : fromfile;
+  uint16_t ret;
+
+  store_unsigned_integer ((gdb_byte *) &ret, sizeof (ret), 
+                         BFD_ENDIAN_BIG, input);
+  return ret;
 }
 
 /* Restore the execution log from a core_bfd file.  */
@@ -1997,18 +2141,18 @@ record_restore (void)
   gdb_assert (record_first.next == NULL);
  
   if (record_debug)
-    printf_filtered ("Restoring recording from core file.\n");
+    fprintf_unfiltered (gdb_stdlog, "Restoring recording from core file.\n");
 
   /* Now need to find our special note section.  */
   osec = bfd_get_section_by_name (core_bfd, "null0");
   osec_size = bfd_section_size (core_bfd, osec);
   if (record_debug)
-    printf_filtered ("Find precord section %s.\n",
-                    osec ? "succeeded" : "failed");
-  if (!osec)
+    fprintf_unfiltered (gdb_stdlog, "Find precord section %s.\n",
+                       osec ? "succeeded" : "failed");
+  if (osec == NULL)
     return;
   if (record_debug)
-    printf_filtered ("%s", bfd_section_name (core_bfd, osec));
+    fprintf_unfiltered (gdb_stdlog, "%s", bfd_section_name (core_bfd, osec));
 
   /* Check the magic code.  */
   bfdcore_read (core_bfd, osec, &magic, sizeof (magic), &bfd_offset);
@@ -2016,9 +2160,9 @@ record_restore (void)
     error (_("Version mis-match or file format error in core file %s."),
           bfd_get_filename (core_bfd));
   if (record_debug)
-    printf_filtered ("\
-  Reading 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n",
-                    netorder32 (magic));
+    fprintf_unfiltered (gdb_stdlog, "\
+  Reading 4-byte magic cookie RECORD_FILE_MAGIC (0x%s)\n",
+                       phex_nz (netorder32 (magic), 4));
 
   /* Restore the entries in recfd into record_arch_list_head and
      record_arch_list_tail.  */
@@ -2031,16 +2175,16 @@ record_restore (void)
   while (1)
     {
       int ret;
-      uint8_t tmpu8;
+      uint8_t rectype;
       uint32_t regnum, len, signal, count;
       uint64_t addr;
 
       /* We are finished when offset reaches osec_size.  */
       if (bfd_offset >= osec_size)
        break;
-      bfdcore_read (core_bfd, osec, &tmpu8, sizeof (tmpu8), &bfd_offset);
+      bfdcore_read (core_bfd, osec, &rectype, sizeof (rectype), &bfd_offset);
 
-      switch (tmpu8)
+      switch (rectype)
         {
         case record_reg: /* reg */
           /* Get register number to regnum.  */
@@ -2054,12 +2198,12 @@ record_restore (void)
           bfdcore_read (core_bfd, osec, record_get_loc (rec),
                        rec->u.reg.len, &bfd_offset);
 
-          if (record_debug)
-            printf_filtered ("\
+         if (record_debug)
+           fprintf_unfiltered (gdb_stdlog, "\
   Reading register %d (1 plus %lu plus %d bytes)\n",
-                            rec->u.reg.num,
-                            (unsigned long) sizeof (regnum),
-                            rec->u.reg.len);
+                               rec->u.reg.num,
+                               (unsigned long) sizeof (regnum),
+                               rec->u.reg.len);
           break;
 
         case record_mem: /* mem */
@@ -2079,14 +2223,14 @@ record_restore (void)
           bfdcore_read (core_bfd, osec, record_get_loc (rec),
                        rec->u.mem.len, &bfd_offset);
 
-          if (record_debug)
-            printf_filtered ("\
+         if (record_debug)
+           fprintf_unfiltered (gdb_stdlog, "\
   Reading memory %s (1 plus %lu plus %lu plus %d bytes)\n",
-                            paddress (get_current_arch (),
-                                      rec->u.mem.addr),
-                            (unsigned long) sizeof (addr),
-                            (unsigned long) sizeof (len),
-                            rec->u.mem.len);
+                               paddress (get_current_arch (),
+                                         rec->u.mem.addr),
+                               (unsigned long) sizeof (addr),
+                               (unsigned long) sizeof (len),
+                               rec->u.mem.len);
           break;
 
         case record_end: /* end */
@@ -2105,13 +2249,13 @@ record_restore (void)
          count = netorder32 (count);
          rec->u.end.insn_num = count;
          record_insn_count = count + 1;
-          if (record_debug)
-            printf_filtered ("\
+         if (record_debug)
+           fprintf_unfiltered (gdb_stdlog, "\
   Reading record_end (1 + %lu + %lu bytes), offset == %s\n",
-                            (unsigned long) sizeof (signal),
-                            (unsigned long) sizeof (count),
-                            paddress (get_current_arch (),
-                                      bfd_offset));
+                               (unsigned long) sizeof (signal),
+                               (unsigned long) sizeof (count),
+                               paddress (get_current_arch (),
+                                         bfd_offset));
           break;
 
         default:
@@ -2217,7 +2361,8 @@ cmd_record_save (char *args, int from_tty)
 
   /* Open the save file.  */
   if (record_debug)
-    printf_filtered ("Saving execution log to core file '%s'\n", recfilename);
+    fprintf_unfiltered (gdb_stdlog, "Saving execution log to core file '%s'\n",
+                       recfilename);
 
   /* Open the output file.  */
   obfd = create_gcore_bfd (recfilename);
@@ -2283,9 +2428,9 @@ cmd_record_save (char *args, int from_tty)
   /* Write the magic code.  */
   magic = RECORD_FILE_MAGIC;
   if (record_debug)
-    printf_filtered ("\
-  Writing 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n",
-                    magic);
+    fprintf_unfiltered (gdb_stdlog, "\
+  Writing 4-byte magic cookie RECORD_FILE_MAGIC (0x%s)\n",
+                     phex_nz (magic, 4));
   bfdcore_write (obfd, osec, &magic, sizeof (magic), &bfd_offset);
 
   /* Save the entries to recfd and forward execute to the end of
@@ -2306,12 +2451,12 @@ cmd_record_save (char *args, int from_tty)
           switch (record_list->type)
             {
             case record_reg: /* reg */
-              if (record_debug)
-               printf_filtered ("\
+             if (record_debug)
+               fprintf_unfiltered (gdb_stdlog, "\
   Writing register %d (1 plus %lu plus %d bytes)\n",
-                                record_list->u.reg.num,
-                                (unsigned long) sizeof (regnum),
-                                record_list->u.reg.len);
+                                   record_list->u.reg.num,
+                                   (unsigned long) sizeof (regnum),
+                                   record_list->u.reg.len);
 
               /* Write regnum.  */
               regnum = netorder32 (record_list->u.reg.num);
@@ -2325,13 +2470,13 @@ cmd_record_save (char *args, int from_tty)
 
             case record_mem: /* mem */
              if (record_debug)
-               printf_filtered ("\
+               fprintf_unfiltered (gdb_stdlog, "\
   Writing memory %s (1 plus %lu plus %lu plus %d bytes)\n",
-                                paddress (gdbarch,
-                                          record_list->u.mem.addr),
-                                (unsigned long) sizeof (addr),
-                                (unsigned long) sizeof (len),
-                                record_list->u.mem.len);
+                                   paddress (gdbarch,
+                                             record_list->u.mem.addr),
+                                   (unsigned long) sizeof (addr),
+                                   (unsigned long) sizeof (len),
+                                   record_list->u.mem.len);
 
              /* Write memlen.  */
              len = netorder32 (record_list->u.mem.len);
@@ -2348,11 +2493,11 @@ cmd_record_save (char *args, int from_tty)
               break;
 
               case record_end:
-                if (record_debug)
-                  printf_filtered ("\
+               if (record_debug)
+                 fprintf_unfiltered (gdb_stdlog, "\
   Writing record_end (1 + %lu + %lu bytes)\n", 
-                                  (unsigned long) sizeof (signal),
-                                  (unsigned long) sizeof (count));
+                                     (unsigned long) sizeof (signal),
+                                     (unsigned long) sizeof (count));
                /* Write signal value.  */
                signal = netorder32 (record_list->u.end.sigval);
                bfdcore_write (obfd, osec, &signal,
@@ -2397,6 +2542,101 @@ cmd_record_save (char *args, int from_tty)
                   recfilename);
 }
 
+/* record_goto_insn -- rewind the record log (forward or backward,
+   depending on DIR) to the given entry, changing the program state
+   correspondingly.  */
+
+static void
+record_goto_insn (struct record_entry *entry,
+                 enum exec_direction_kind dir)
+{
+  struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
+  struct regcache *regcache = get_current_regcache ();
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+
+  /* Assume everything is valid: we will hit the entry,
+     and we will not hit the end of the recording.  */
+
+  if (dir == EXEC_FORWARD)
+    record_list = record_list->next;
+
+  do
+    {
+      record_exec_insn (regcache, gdbarch, record_list);
+      if (dir == EXEC_REVERSE)
+       record_list = record_list->prev;
+      else
+       record_list = record_list->next;
+    } while (record_list != entry);
+  do_cleanups (set_cleanups);
+}
+
+/* "record goto" command.  Argument is an instruction number,
+   as given by "info record".
+
+   Rewinds the recording (forward or backward) to the given instruction.  */
+
+static void
+cmd_record_goto (char *arg, int from_tty)
+{
+  struct record_entry *p = NULL;
+  ULONGEST target_insn = 0;
+
+  if (arg == NULL || *arg == '\0')
+    error (_("Command requires an argument (insn number to go to)."));
+
+  if (strncmp (arg, "start", strlen ("start")) == 0
+      || strncmp (arg, "begin", strlen ("begin")) == 0)
+    {
+      /* Special case.  Find first insn.  */
+      for (p = &record_first; p != NULL; p = p->next)
+       if (p->type == record_end)
+         break;
+      if (p)
+       target_insn = p->u.end.insn_num;
+    }
+  else if (strncmp (arg, "end", strlen ("end")) == 0)
+    {
+      /* Special case.  Find last insn.  */
+      for (p = record_list; p->next != NULL; p = p->next)
+       ;
+      for (; p!= NULL; p = p->prev)
+       if (p->type == record_end)
+         break;
+      if (p)
+       target_insn = p->u.end.insn_num;
+    }
+  else
+    {
+      /* General case.  Find designated insn.  */
+      target_insn = parse_and_eval_long (arg);
+
+      for (p = &record_first; p != NULL; p = p->next)
+       if (p->type == record_end && p->u.end.insn_num == target_insn)
+         break;
+    }
+
+  if (p == NULL)
+    error (_("Target insn '%s' not found."), arg);
+  else if (p == record_list)
+    error (_("Already at insn '%s'."), arg);
+  else if (p->u.end.insn_num > record_list->u.end.insn_num)
+    {
+      printf_filtered (_("Go forward to insn number %s\n"),
+                      pulongest (target_insn));
+      record_goto_insn (p, EXEC_FORWARD);
+    }
+  else
+    {
+      printf_filtered (_("Go backward to insn number %s\n"),
+                      pulongest (target_insn));
+      record_goto_insn (p, EXEC_REVERSE);
+    }
+  registers_changed ();
+  reinit_frame_cache ();
+  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
+}
+
 void
 _initialize_record (void)
 {
@@ -2482,4 +2722,9 @@ Set the maximum number of instructions to be stored in the\n\
 record/replay buffer.  Zero means unlimited.  Default is 200000."),
                            set_record_insn_max_num,
                            NULL, &set_record_cmdlist, &show_record_cmdlist);
+
+  add_cmd ("goto", class_obscure, cmd_record_goto, _("\
+Restore the program to its state at instruction number N.\n\
+Argument is instruction number, as shown by 'info record'."),
+          &record_cmdlist);
 }
This page took 0.037423 seconds and 4 git commands to generate.