This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

teach the record target about async mode


Tested on x86_64-linux + precord, and applied.

The gdb.reverse/ tests look as good in async as they do
in sync.  Same for gdb.mi/mi-reverse.exp.

Not much to say here...

- Add an async event source in the event loop, and handle
  TARGET_WNOHANG.  Most of the magic replay/record magic
  is/was done at target_wait time, that doesn't change.

- Something needs to record the execution direction the target
  was last resumed with, and whenever the event loop
  fetches an event from the target, it needs to
  know the direction the target going, as it
  may not be the same as currently set globally.
  E.g., all the internal intermediate steps for
  "reverse-step", while the global execution direction
  is set to forward (set exec-direction forward, the default).

Pedro Alves

2011-05-26  Pedro Alves  <pedro@codesourcery.com>

	gdb/
	* record.c: Include event-loop.h, inf-loop.h.
	(record_beneath_to_async): New global.
	(tmp_to_async): New global.
	(record_async_inferior_event_token): New global.
	(record_open_1): Don't error out if async is enabled.
	(record_open): Handle to_async.  Create an async event source in
	the event loop.
	(record_close): Delete the async event source.
	(record_resumed): New global.
	(record_execution_dir): New global.
	(record_resume, record_core_resume): Set them.  Register the
	target on the event loop.
	(record_wait): Rename to ...
	(record_wait_1): ... this.  Add more debug output.  Handle
	TARGET_WNOHANG, and the target beneath returning
	TARGET_WAITKIND_IGNORE.
	(record_wait): Reimplement on top of record_wait_1.
	(record_async_mask_value): New global.
	(record_async, record_async_mask, record_can_async_p)
	(record_is_async_p, record_execution_direction): New functions.
	(init_record_ops, init_record_core_ops): Install new methods.
	* infrun.c (fetch_inferior_event): Temporarily switch the global
	execution direction to the direction the target was going.
	(execution_direction): Change type to int.
	* target.c (default_execution_direction): New function.
	(update_current_target): Inherit and de_fault
	to_execution_direction.
	* target.h (struct target_ops) <to_execution_direction>: New
	field.
	(target_execution_direction): New macro.
	* inferior.h (execution_direction): Change type to int.

---
 gdb/inferior.h |    5 +
 gdb/infrun.c   |    5 +
 gdb/record.c   |  190 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 gdb/target.c   |   15 ++++
 gdb/target.h   |    8 ++
 5 files changed, 212 insertions(+), 11 deletions(-)

Index: src/gdb/record.c
===================================================================
--- src.orig/gdb/record.c	2011-05-26 17:19:24.961253737 +0100
+++ src/gdb/record.c	2011-05-26 18:59:54.491251655 +0100
@@ -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_br
 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.  */
@@ -806,9 +809,22 @@ static int (*tmp_to_remove_breakpoint) (
 					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 "
@@ -911,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)
@@ -943,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."));
@@ -966,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.  */
@@ -1002,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
@@ -1013,6 +1059,8 @@ record_resume (struct target_ops *ops, p
                enum target_signal signal)
 {
   record_resume_step = step;
+  record_resumed = 1;
+  record_execution_dir = execution_direction;
 
   if (!RECORD_IS_REPLAY)
     {
@@ -1054,6 +1102,16 @@ record_resume (struct target_ops *ops, p
       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;
@@ -1100,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);
@@ -1134,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
@@ -1204,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);
@@ -1385,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)
 {
@@ -1719,6 +1817,58 @@ record_goto_bookmark (gdb_byte *bookmark
   return;
 }
 
+static int record_async_mask_value = 1;
+
+static void
+record_async (void (*callback) (enum inferior_event_type event_type,
+				void *context), void *context)
+{
+  if (record_async_mask_value == 0)
+    internal_error (__FILE__, __LINE__,
+		    _("Calling record_async when async is masked"));
+
+  /* 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_async_mask (int new_mask)
+{
+  int curr_mask = record_async_mask_value;
+
+  record_async_mask_value = new_mask;
+  return curr_mask;
+}
+
+static int
+record_can_async_p (void)
+{
+  /* We only enable async when the user specifically asks for it.  */
+  if (!target_async_permitted)
+    return 0;
+
+  return record_async_mask_value;
+}
+
+static int
+record_is_async_p (void)
+{
+  /* We only enable async when the user specifically asks for it.  */
+  if (!target_async_permitted)
+    return 0;
+
+  return record_async_mask_value;
+}
+
+static enum exec_direction_kind
+record_execution_direction (void)
+{
+  return record_execution_dir;
+}
+
 static void
 init_record_ops (void)
 {
@@ -1746,6 +1896,11 @@ 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_async_mask = record_async_mask;
+  record_ops.to_execution_direction = record_execution_direction;
   record_ops.to_magic = OPS_MAGIC;
 }
 
@@ -1756,6 +1911,18 @@ record_core_resume (struct target_ops *o
                     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.  */
@@ -1955,6 +2122,11 @@ 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_async_mask = record_async_mask;
+  record_core_ops.to_execution_direction = record_execution_direction;
   record_core_ops.to_magic = OPS_MAGIC;
 }
 
Index: src/gdb/infrun.c
===================================================================
--- src.orig/gdb/infrun.c	2011-05-26 17:19:24.961253737 +0100
+++ src/gdb/infrun.c	2011-05-26 18:42:20.281252019 +0100
@@ -2742,6 +2742,9 @@ fetch_inferior_event (void *client_data)
   overlay_cache_invalid = 1;
   registers_changed ();
 
+  make_cleanup_restore_integer (&execution_direction);
+  execution_direction = target_execution_direction ();
+
   if (deprecated_target_wait_hook)
     ecs->ptid =
       deprecated_target_wait_hook (waiton_ptid, &ecs->ws, TARGET_WNOHANG);
@@ -6878,7 +6881,7 @@ save_inferior_ptid (void)
    Set exec-direction / show exec-direction commands
    (returns error unless target implements to_set_exec_direction method).  */
 
-enum exec_direction_kind execution_direction = EXEC_FORWARD;
+int execution_direction = EXEC_FORWARD;
 static const char exec_forward[] = "forward";
 static const char exec_reverse[] = "reverse";
 static const char *exec_direction = exec_forward;
Index: src/gdb/target.c
===================================================================
--- src.orig/gdb/target.c	2011-05-26 17:19:24.961253737 +0100
+++ src/gdb/target.c	2011-05-26 18:42:00.711252026 +0100
@@ -544,6 +544,18 @@ default_get_ada_task_ptid (long lwp, lon
   return ptid_build (ptid_get_pid (inferior_ptid), lwp, tid);
 }
 
+static enum exec_direction_kind
+default_execution_direction (void)
+{
+  if (!target_can_execute_reverse)
+    return EXEC_FORWARD;
+  else if (!target_can_async_p ())
+    return EXEC_FORWARD;
+  else
+    gdb_assert_not_reached ("\
+to_execution_direction must be implemented for reverse async");
+}
+
 /* Go through the target stack from top to bottom, copying over zero
    entries in current_target, then filling in still empty entries.  In
    effect, we are doing class inheritance through the pushed target
@@ -654,6 +666,7 @@ update_current_target (void)
       INHERIT (to_goto_bookmark, t);
       /* Do not inherit to_get_thread_local_address.  */
       INHERIT (to_can_execute_reverse, t);
+      INHERIT (to_execution_direction, t);
       INHERIT (to_thread_architecture, t);
       /* Do not inherit to_read_description.  */
       INHERIT (to_get_ada_task_ptid, t);
@@ -897,6 +910,8 @@ update_current_target (void)
   de_fault (to_traceframe_info,
 	    (struct traceframe_info * (*) (void))
 	    tcomplain);
+  de_fault (to_execution_direction, default_execution_direction);
+
 #undef de_fault
 
   /* Finally, position the target-stack beneath the squashed
Index: src/gdb/target.h
===================================================================
--- src.orig/gdb/target.h	2011-05-26 17:19:24.961253737 +0100
+++ src/gdb/target.h	2011-05-26 18:34:22.061252184 +0100
@@ -641,6 +641,11 @@ struct target_ops
     /* Can target execute in reverse?  */
     int (*to_can_execute_reverse) (void);
 
+    /* The direction the target is currently executing.  Must be
+       implemented on targets that support reverse execution and async
+       mode.  The default simply returns forward execution.  */
+    enum exec_direction_kind (*to_execution_direction) (void);
+
     /* Does this target support debugging multiple processes
        simultaneously?  */
     int (*to_supports_multi_process) (void);
@@ -1271,6 +1276,9 @@ int target_supports_non_stop (void);
 #define target_async_mask(MASK)	\
   (current_target.to_async_mask (MASK))
 
+#define target_execution_direction() \
+  (current_target.to_execution_direction ())
+
 /* Converts a process id to a string.  Usually, the string just contains
    `process xyz', but on some systems it may contain
    `process xyz thread abc'.  */
Index: src/gdb/inferior.h
===================================================================
--- src.orig/gdb/inferior.h	2011-05-26 16:17:21.000000000 +0100
+++ src/gdb/inferior.h	2011-05-26 18:56:33.441251724 +0100
@@ -356,7 +356,10 @@ enum exec_direction_kind
     EXEC_ERROR
   };
 
-extern enum exec_direction_kind execution_direction;
+/* The current execution direction.  This should only be set to enum
+   exec_direction_kind values.  It is only an int to make it
+   compatible with make_cleanup_restore_integer.  */
+extern int execution_direction;
 
 /* Save register contents here when executing a "finish" command or are
    about to pop a stack dummy frame, if-and-only-if proceed_to_finish is set.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]