diff options
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/ChangeLog | 34 | ||||
-rw-r--r-- | gdb/inferior.h | 5 | ||||
-rw-r--r-- | gdb/infrun.c | 5 | ||||
-rw-r--r-- | gdb/record.c | 190 | ||||
-rw-r--r-- | gdb/target.c | 15 | ||||
-rw-r--r-- | gdb/target.h | 8 |
6 files changed, 246 insertions, 11 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 2f6ae97..31b34e5 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,39 @@ 2011-05-26 Pedro Alves <pedro@codesourcery.com> + * 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. + +2011-05-26 Pedro Alves <pedro@codesourcery.com> + * infcall.c (call_function_by_hand): Don't allow calling functions in reverse execution mode. diff --git a/gdb/inferior.h b/gdb/inferior.h index ae2363d..9949136 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -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. diff --git a/gdb/infrun.c b/gdb/infrun.c index 67acdc4..8502e69 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -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; diff --git a/gdb/record.c b/gdb/record.c index 65eaa11..9a4fbba 100644 --- a/gdb/record.c +++ b/gdb/record.c @@ -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. */ @@ -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 " @@ -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, 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) { @@ -1054,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; @@ -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, int from_tty) 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 *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. */ @@ -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; } diff --git a/gdb/target.c b/gdb/target.c index ed2da34..0b7e61e 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -544,6 +544,18 @@ default_get_ada_task_ptid (long lwp, long tid) 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 diff --git a/gdb/target.h b/gdb/target.h index fd58bd95..21e1450 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -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'. */ |