daily update
[deliverable/binutils-gdb.git] / gdb / record.c
index 3d423a5e63cf00b0404e30f228a2b3e11dd5e773..60de0853a8e5bc5bddf8136f73770fa8fc8ea801 100644 (file)
@@ -1,6 +1,6 @@
 /* Process record and replay target for GDB, the GNU debugger.
 
-   Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -30,6 +30,8 @@
 #include "record.h"
 #include "elf-bfd.h"
 #include "gcore.h"
+#include "event-loop.h"
+#include "inf-loop.h"
 
 #include <signal.h>
 
@@ -231,6 +233,7 @@ static int (*record_beneath_to_remove_breakpoint) (struct gdbarch *,
 static int (*record_beneath_to_stopped_by_watchpoint) (void);
 static int (*record_beneath_to_stopped_data_address) (struct target_ops *,
                                                      CORE_ADDR *);
+static void (*record_beneath_to_async) (void (*) (enum inferior_event_type, void *), void *);
 
 /* Alloc and free functions for record_reg, record_mem, and record_end 
    entries.  */
@@ -495,7 +498,7 @@ record_arch_list_add_mem (CORE_ADDR addr, int len)
                        "record list.\n",
                        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;
 
   rec = record_mem_alloc (addr, len);
@@ -739,8 +742,8 @@ record_exec_insn (struct regcache *regcache, struct gdbarch *gdbarch,
               {
                 entry->u.mem.mem_entry_not_accessible = 1;
                 if (record_debug)
-                  warning ("Process record: error reading memory at "
-                          "addr = %s len = %d.",
+                  warning (_("Process record: error reading memory at "
+                            "addr = %s len = %d."),
                            paddress (gdbarch, entry->u.mem.addr),
                            entry->u.mem.len);
               }
@@ -752,8 +755,8 @@ record_exec_insn (struct regcache *regcache, struct gdbarch *gdbarch,
                   {
                     entry->u.mem.mem_entry_not_accessible = 1;
                     if (record_debug)
-                      warning ("Process record: error writing memory at "
-                              "addr = %s len = %d.",
+                      warning (_("Process record: error writing memory at "
+                                "addr = %s len = %d."),
                                paddress (gdbarch, entry->u.mem.addr),
                                entry->u.mem.len);
                   }
@@ -806,9 +809,22 @@ static int (*tmp_to_remove_breakpoint) (struct gdbarch *,
                                        struct bp_target_info *);
 static int (*tmp_to_stopped_by_watchpoint) (void);
 static int (*tmp_to_stopped_data_address) (struct target_ops *, CORE_ADDR *);
+static int (*tmp_to_stopped_data_address) (struct target_ops *, CORE_ADDR *);
+static void (*tmp_to_async) (void (*) (enum inferior_event_type, void *), void *);
 
 static void record_restore (void);
 
+/* Asynchronous signal handle registered as event loop source for when
+   we have pending events ready to be passed to the core.  */
+
+static struct async_event_handler *record_async_inferior_event_token;
+
+static void
+record_async_inferior_event_handler (gdb_client_data data)
+{
+  inferior_event_handler (INF_REG_EVENT, NULL);
+}
+
 /* Open the process record target.  */
 
 static void
@@ -852,9 +868,6 @@ record_open_1 (char *name, int from_tty)
   if (non_stop)
     error (_("Process record target can't debug inferior in non-stop mode "
             "(non-stop)."));
-  if (target_async_permitted)
-    error (_("Process record target can't debug inferior in asynchronous "
-            "mode (target-async)."));
 
   if (!gdbarch_process_record_p (target_gdbarch))
     error (_("Process record: the current architecture doesn't support "
@@ -865,15 +878,20 @@ record_open_1 (char *name, int from_tty)
   if (!tmp_to_wait)
     error (_("Could not find 'to_wait' method on the target stack."));
   if (!tmp_to_store_registers)
-    error (_("Could not find 'to_store_registers' method on the target stack."));
+    error (_("Could not find 'to_store_registers' "
+            "method on the target stack."));
   if (!tmp_to_insert_breakpoint)
-    error (_("Could not find 'to_insert_breakpoint' method on the target stack."));
+    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."));
+    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."));
+    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."));
+    error (_("Could not find 'to_stopped_data_address' "
+            "method on the target stack."));
 
   push_target (&record_ops);
 }
@@ -906,6 +924,7 @@ record_open (char *name, int from_tty)
   tmp_to_remove_breakpoint = NULL;
   tmp_to_stopped_by_watchpoint = NULL;
   tmp_to_stopped_data_address = NULL;
+  tmp_to_async = NULL;
 
   /* Set the beneath function pointers.  */
   for (t = current_target.beneath; t != NULL; t = t->beneath)
@@ -938,6 +957,8 @@ record_open (char *name, int from_tty)
        tmp_to_stopped_by_watchpoint = t->to_stopped_by_watchpoint;
       if (!tmp_to_stopped_data_address)
        tmp_to_stopped_data_address = t->to_stopped_data_address;
+      if (!tmp_to_async)
+       tmp_to_async = t->to_async;
     }
   if (!tmp_to_xfer_partial)
     error (_("Could not find 'to_xfer_partial' method on the target stack."));
@@ -961,11 +982,17 @@ record_open (char *name, int from_tty)
   record_beneath_to_remove_breakpoint = tmp_to_remove_breakpoint;
   record_beneath_to_stopped_by_watchpoint = tmp_to_stopped_by_watchpoint;
   record_beneath_to_stopped_data_address = tmp_to_stopped_data_address;
+  record_beneath_to_async = tmp_to_async;
 
   if (core_bfd)
     record_core_open_1 (name, from_tty);
   else
     record_open_1 (name, from_tty);
+
+  /* Register extra event sources in the event loop.  */
+  record_async_inferior_event_token
+    = create_async_event_handler (record_async_inferior_event_handler,
+                                 NULL);
 }
 
 /* "to_close" target method.  Close the process record target.  */
@@ -997,10 +1024,34 @@ record_close (int quitting)
        }
       record_core_buf_list = NULL;
     }
+
+  if (record_async_inferior_event_token)
+    delete_async_event_handler (&record_async_inferior_event_token);
 }
 
 static int record_resume_step = 0;
 
+/* True if we've been resumed, and so each record_wait call should
+   advance execution.  If this is false, record_wait will return a
+   TARGET_WAITKIND_IGNORE.  */
+static int record_resumed = 0;
+
+/* The execution direction of the last resume we got.  This is
+   necessary for async mode.  Vis (order is not strictly accurate):
+
+   1. user has the global execution direction set to forward
+   2. user does a reverse-step command
+   3. record_resume is called with global execution direction
+      temporarily switched to reverse
+   4. GDB's execution direction is reverted back to forward
+   5. target record notifies event loop there's an event to handle
+   6. infrun asks the target which direction was it going, and switches
+      the global execution direction accordingly (to reverse)
+   7. infrun polls an event out of the record target, and handles it
+   8. GDB goes back to the event loop, and goto #4.
+*/
+static enum exec_direction_kind record_execution_dir = EXEC_FORWARD;
+
 /* "to_resume" target method.  Resume the process record target.  */
 
 static void
@@ -1008,6 +1059,8 @@ record_resume (struct target_ops *ops, ptid_t ptid, int step,
                enum target_signal signal)
 {
   record_resume_step = step;
+  record_resumed = 1;
+  record_execution_dir = execution_direction;
 
   if (!RECORD_IS_REPLAY)
     {
@@ -1049,6 +1102,16 @@ record_resume (struct target_ops *ops, ptid_t ptid, int step,
       record_beneath_to_resume (record_beneath_to_resume_ops,
                                 ptid, step, signal);
     }
+
+  /* We are about to start executing the inferior (or simulate it),
+     let's register it with the event loop.  */
+  if (target_can_async_p ())
+    {
+      target_async (inferior_event_handler, 0);
+      /* Notify the event loop there's an event to wait for.  We do
+        most of the work in record_wait.  */
+      mark_async_event_handler (record_async_inferior_event_token);
+    }
 }
 
 static int record_get_sig = 0;
@@ -1095,17 +1158,27 @@ record_wait_cleanups (void *ignore)
    where to stop.  */
 
 static ptid_t
-record_wait (struct target_ops *ops,
-            ptid_t ptid, struct target_waitstatus *status,
-            int options)
+record_wait_1 (struct target_ops *ops,
+              ptid_t ptid, struct target_waitstatus *status,
+              int options)
 {
   struct cleanup *set_cleanups = record_gdb_operation_disable_set ();
 
   if (record_debug)
     fprintf_unfiltered (gdb_stdlog,
                        "Process record: record_wait "
-                       "record_resume_step = %d\n",
-                       record_resume_step);
+                       "record_resume_step = %d, record_resumed = %d, direction=%s\n",
+                       record_resume_step, record_resumed,
+                       record_execution_dir == EXEC_FORWARD ? "forward" : "reverse");
+
+  if (!record_resumed)
+    {
+      gdb_assert ((options & TARGET_WNOHANG) != 0);
+
+      /* No interesting event.  */
+      status->kind = TARGET_WAITKIND_IGNORE;
+      return minus_one_ptid;
+    }
 
   record_get_sig = 0;
   signal (SIGINT, record_sig_handler);
@@ -1129,12 +1202,20 @@ record_wait (struct target_ops *ops,
            {
              ret = record_beneath_to_wait (record_beneath_to_wait_ops,
                                            ptid, status, options);
+             if (status->kind == TARGET_WAITKIND_IGNORE)
+               {
+                 if (record_debug)
+                   fprintf_unfiltered (gdb_stdlog,
+                                       "Process record: record_wait "
+                                       "target beneath not done yet\n");
+                 return ret;
+               }
 
               if (single_step_breakpoints_inserted ())
                 remove_single_step_breakpoints ();
 
              if (record_resume_step)
-               return ret;
+               return ret;
 
              /* Is this a SIGTRAP?  */
              if (status->kind == TARGET_WAITKIND_STOPPED
@@ -1162,7 +1243,8 @@ record_wait (struct target_ops *ops,
                         handle it.  */
                      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
                            = gdbarch_decr_pc_after_break (gdbarch);
                          if (decr_pc_after_break)
@@ -1198,6 +1280,10 @@ record_wait (struct target_ops *ops,
                          set_executing (inferior_ptid, 1);
                        }
 
+                     if (record_debug)
+                       fprintf_unfiltered (gdb_stdlog,
+                                           "Process record: record_wait "
+                                           "issuing one more step in the target beneath\n");
                      record_beneath_to_resume (record_beneath_to_resume_ops,
                                                ptid, step,
                                                TARGET_SIGNAL_0);
@@ -1333,8 +1419,9 @@ record_wait (struct target_ops *ops,
                  if (record_hw_watchpoint)
                    {
                      if (record_debug)
-                       fprintf_unfiltered (gdb_stdlog, "\
-Process record: hit hw watchpoint.\n");
+                       fprintf_unfiltered (gdb_stdlog,
+                                           "Process record: hit hw "
+                                           "watchpoint.\n");
                      continue_flag = 0;
                    }
                  /* Check target signal */
@@ -1378,6 +1465,24 @@ replay_out:
   return inferior_ptid;
 }
 
+static ptid_t
+record_wait (struct target_ops *ops,
+            ptid_t ptid, struct target_waitstatus *status,
+            int options)
+{
+  ptid_t return_ptid;
+
+  return_ptid = record_wait_1 (ops, ptid, status, options);
+  if (status->kind != TARGET_WAITKIND_IGNORE)
+    {
+      /* We're reporting a stop.  Make sure any spurious
+        target_wait(WNOHANG) doesn't advance the target until the
+        core wants us resumed again.  */
+      record_resumed = 0;
+    }
+  return return_ptid;
+}
+
 static int
 record_stopped_by_watchpoint (void)
 {
@@ -1712,6 +1817,37 @@ record_goto_bookmark (gdb_byte *bookmark, int from_tty)
   return;
 }
 
+static void
+record_async (void (*callback) (enum inferior_event_type event_type,
+                               void *context), void *context)
+{
+  /* If we're on top of a line target (e.g., linux-nat, remote), then
+     set it to async mode as well.  Will be NULL if we're sitting on
+     top of the core target, for "record restore".  */
+  if (record_beneath_to_async != NULL)
+    record_beneath_to_async (callback, context);
+}
+
+static int
+record_can_async_p (void)
+{
+  /* We only enable async when the user specifically asks for it.  */
+  return target_async_permitted;
+}
+
+static int
+record_is_async_p (void)
+{
+  /* We only enable async when the user specifically asks for it.  */
+  return target_async_permitted;
+}
+
+static enum exec_direction_kind
+record_execution_direction (void)
+{
+  return record_execution_dir;
+}
+
 static void
 init_record_ops (void)
 {
@@ -1739,6 +1875,10 @@ init_record_ops (void)
   /* Add bookmark target methods.  */
   record_ops.to_get_bookmark = record_get_bookmark;
   record_ops.to_goto_bookmark = record_goto_bookmark;
+  record_ops.to_async = record_async;
+  record_ops.to_can_async_p = record_can_async_p;
+  record_ops.to_is_async_p = record_is_async_p;
+  record_ops.to_execution_direction = record_execution_direction;
   record_ops.to_magic = OPS_MAGIC;
 }
 
@@ -1749,6 +1889,18 @@ record_core_resume (struct target_ops *ops, ptid_t ptid, int step,
                     enum target_signal signal)
 {
   record_resume_step = step;
+  record_resumed = 1;
+  record_execution_dir = execution_direction;
+
+  /* We are about to start executing the inferior (or simulate it),
+     let's register it with the event loop.  */
+  if (target_can_async_p ())
+    {
+      target_async (inferior_event_handler, 0);
+
+      /* Notify the event loop there's an event to wait for.  */
+      mark_async_event_handler (record_async_inferior_event_token);
+    }
 }
 
 /* "to_kill" method for prec over corefile.  */
@@ -1916,8 +2068,8 @@ record_core_remove_breakpoint (struct gdbarch *gdbarch,
 
 /* "to_has_execution" method for prec over corefile.  */
 
-int
-record_core_has_execution (struct target_ops *ops)
+static int
+record_core_has_execution (struct target_ops *ops, ptid_t the_ptid)
 {
   return 1;
 }
@@ -1948,6 +2100,10 @@ init_record_core_ops (void)
   /* Add bookmark target methods.  */
   record_core_ops.to_get_bookmark = record_get_bookmark;
   record_core_ops.to_goto_bookmark = record_goto_bookmark;
+  record_core_ops.to_async = record_async;
+  record_core_ops.to_can_async_p = record_can_async_p;
+  record_core_ops.to_is_async_p = record_is_async_p;
+  record_core_ops.to_execution_direction = record_execution_direction;
   record_core_ops.to_magic = OPS_MAGIC;
 }
 
@@ -2029,8 +2185,8 @@ static struct cmd_list_element *record_cmdlist, *set_record_cmdlist,
 static void
 set_record_command (char *args, int from_tty)
 {
-  printf_unfiltered (_("\
-\"set record\" must be followed by an apporpriate subcommand.\n"));
+  printf_unfiltered (_("\"set record\" must be followed "
+                      "by an apporpriate subcommand.\n"));
   help_list (set_record_cmdlist, "set record ", all_commands, gdb_stdout);
 }
 
@@ -2147,7 +2303,7 @@ bfdcore_read (bfd *obfd, asection *osec, void *buf, int len, int *offset)
   if (ret)
     *offset += len;
   else
-    error (_("Failed to read %d bytes from core file %s ('%s').\n"),
+    error (_("Failed to read %d bytes from core file %s ('%s')."),
           len, bfd_get_filename (obfd),
           bfd_errmsg (bfd_get_error ()));
 }
@@ -2207,12 +2363,12 @@ record_restore (void)
 
   /* Now need to find our special note section.  */
   osec = bfd_get_section_by_name (core_bfd, "null0");
-  osec_size = bfd_section_size (core_bfd, osec);
   if (record_debug)
     fprintf_unfiltered (gdb_stdlog, "Find precord section %s.\n",
                        osec ? "succeeded" : "failed");
   if (osec == NULL)
     return;
+  osec_size = bfd_section_size (core_bfd, osec);
   if (record_debug)
     fprintf_unfiltered (gdb_stdlog, "%s", bfd_section_name (core_bfd, osec));
 
@@ -2222,8 +2378,9 @@ record_restore (void)
     error (_("Version mis-match or file format error in core file %s."),
           bfd_get_filename (core_bfd));
   if (record_debug)
-    fprintf_unfiltered (gdb_stdlog, "\
-  Reading 4-byte magic cookie RECORD_FILE_MAGIC (0x%s)\n",
+    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
@@ -2260,8 +2417,9 @@ record_restore (void)
                        rec->u.reg.len, &bfd_offset);
 
          if (record_debug)
-           fprintf_unfiltered (gdb_stdlog, "\
-  Reading register %d (1 plus %lu plus %d bytes)\n",
+           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);
@@ -2285,8 +2443,9 @@ record_restore (void)
                        rec->u.mem.len, &bfd_offset);
 
          if (record_debug)
-           fprintf_unfiltered (gdb_stdlog, "\
-  Reading memory %s (1 plus %lu plus %lu plus %d bytes)\n",
+           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),
@@ -2311,8 +2470,9 @@ record_restore (void)
          rec->u.end.insn_num = count;
          record_insn_count = count + 1;
          if (record_debug)
-           fprintf_unfiltered (gdb_stdlog, "\
-  Reading record_end (1 + %lu + %lu bytes), offset == %s\n",
+           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 (),
@@ -2362,7 +2522,7 @@ bfdcore_write (bfd *obfd, asection *osec, void *buf, int len, int *offset)
   if (ret)
     *offset += len;
   else
-    error (_("Failed to write %d bytes to core file %s ('%s').\n"),
+    error (_("Failed to write %d bytes to core file %s ('%s')."),
           len, bfd_get_filename (obfd),
           bfd_errmsg (bfd_get_error ()));
 }
@@ -2489,8 +2649,9 @@ cmd_record_save (char *args, int from_tty)
   /* Write the magic code.  */
   magic = RECORD_FILE_MAGIC;
   if (record_debug)
-    fprintf_unfiltered (gdb_stdlog, "\
-  Writing 4-byte magic cookie RECORD_FILE_MAGIC (0x%s)\n",
+    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);
 
@@ -2513,8 +2674,9 @@ cmd_record_save (char *args, int from_tty)
             {
             case record_reg: /* reg */
              if (record_debug)
-               fprintf_unfiltered (gdb_stdlog, "\
-  Writing register %d (1 plus %lu plus %d bytes)\n",
+               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);
@@ -2531,8 +2693,9 @@ cmd_record_save (char *args, int from_tty)
 
             case record_mem: /* mem */
              if (record_debug)
-               fprintf_unfiltered (gdb_stdlog, "\
-  Writing memory %s (1 plus %lu plus %lu plus %d bytes)\n",
+               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),
@@ -2555,8 +2718,9 @@ cmd_record_save (char *args, int from_tty)
 
               case record_end:
                if (record_debug)
-                 fprintf_unfiltered (gdb_stdlog, "\
-  Writing record_end (1 + %lu + %lu bytes)\n", 
+                 fprintf_unfiltered (gdb_stdlog,
+                                     "  Writing record_end (1 + "
+                                     "%lu + %lu bytes)\n", 
                                      (unsigned long) sizeof (signal),
                                      (unsigned long) sizeof (count));
                /* Write signal value.  */
@@ -2768,8 +2932,8 @@ Argument is filename.  File must be created with 'record save'."),
   add_setshow_boolean_cmd ("stop-at-limit", no_class,
                           &record_stop_at_limit, _("\
 Set whether record/replay stops when record/replay buffer becomes full."), _("\
-Show whether record/replay stops when record/replay buffer becomes full."), _("\
-Default is ON.\n\
+Show whether record/replay stops when record/replay buffer becomes full."),
+                          _("Default is ON.\n\
 When ON, if the record/replay buffer becomes full, ask user what to do.\n\
 When OFF, if the record/replay buffer becomes full,\n\
 delete the oldest recorded instruction to make room for each new one."),
This page took 0.030055 seconds and 4 git commands to generate.