Merge async and sync code paths some more

This patch makes the execution control code use largely the same
mechanisms in both sync- and async-capable targets.  This means using
continuations and use the event loop to react to target events on sync
targets as well.  The trick is to immediately mark infrun's event loop
source after resume instead of calling wait_for_inferior.  Then
fetch_inferior_event is adjusted to do a blocking wait on sync
targets.

Tested on x86_64 Fedora 20, native and gdbserver, with and without
"maint set target-async off".

gdb/ChangeLog:
2015-09-09  Pedro Alves  <palves@redhat.com>

	* breakpoint.c (bpstat_do_actions_1, until_break_command): Don't
	check whether the target can async.
	* inf-loop.c (inferior_event_handler): Only call target_async if
	the target can async.
	* infcall.c: Include top.h and interps.h.
	(run_inferior_call): For the interpreter to sync mode while
	running the infcall.  Call wait_sync_command_done instead of
	wait_for_inferior plus normal_stop.
	* infcmd.c (prepare_execution_command): Don't check whether the
	target can async when running in the foreground.
	(step_1): Delete synchronous case handling.
	(step_once): Always install a continuation, even in sync mode.
	(until_next_command, finish_forward): Don't check whether the
	target can async.
	(attach_command_post_wait, notice_new_inferior): Always install a
	continuation, even in sync mode.
	* infrun.c (mark_infrun_async_event_handler): New function.
	(proceed): In sync mode, mark infrun's event source instead of
	waiting for events here.
	(fetch_inferior_event): If the target can't async, do a blocking
	wait.
	(prepare_to_wait): In sync mode, mark infrun's event source.
	(infrun_async_inferior_event_handler): No longer bail out if the
	target can't async.
	* infrun.h (mark_infrun_async_event_handler): New declaration.
	* linux-nat.c (linux_nat_wait_1): Remove calls to
	set_sigint_trap/clear_sigint_trap.
	(linux_nat_terminal_inferior): No longer check whether the target
	can async.
	* mi/mi-interp.c (mi_on_sync_execution_done): Update and simplify
	comment.
	(mi_execute_command_input_handler): No longer check whether the
	target is async.  Update and simplify comment.
	* target.c (default_target_wait): New function.
	* target.h (struct target_ops) <to_wait>: Now defaults to
	default_target_wait.
	(default_target_wait): Declare.
	* top.c (wait_sync_command_done): New function, factored out from
	...
	(maybe_wait_sync_command_done): ... this.
	* top.h (wait_sync_command_done): Declare.
	* target-delegates.c: Regenerate.
This commit is contained in:
Pedro Alves 2015-09-09 18:23:23 +01:00
parent ea4a7f9986
commit 0b333c5e7d
14 changed files with 173 additions and 198 deletions

View File

@ -1,3 +1,48 @@
2015-09-09 Pedro Alves <palves@redhat.com>
* breakpoint.c (bpstat_do_actions_1, until_break_command): Don't
check whether the target can async.
* inf-loop.c (inferior_event_handler): Only call target_async if
the target can async.
* infcall.c: Include top.h and interps.h.
(run_inferior_call): For the interpreter to sync mode while
running the infcall. Call wait_sync_command_done instead of
wait_for_inferior plus normal_stop.
* infcmd.c (prepare_execution_command): Don't check whether the
target can async when running in the foreground.
(step_1): Delete synchronous case handling.
(step_once): Always install a continuation, even in sync mode.
(until_next_command, finish_forward): Don't check whether the
target can async.
(attach_command_post_wait, notice_new_inferior): Always install a
continuation, even in sync mode.
* infrun.c (mark_infrun_async_event_handler): New function.
(proceed): In sync mode, mark infrun's event source instead of
waiting for events here.
(fetch_inferior_event): If the target can't async, do a blocking
wait.
(prepare_to_wait): In sync mode, mark infrun's event source.
(infrun_async_inferior_event_handler): No longer bail out if the
target can't async.
* infrun.h (mark_infrun_async_event_handler): New declaration.
* linux-nat.c (linux_nat_wait_1): Remove calls to
set_sigint_trap/clear_sigint_trap.
(linux_nat_terminal_inferior): No longer check whether the target
can async.
* mi/mi-interp.c (mi_on_sync_execution_done): Update and simplify
comment.
(mi_execute_command_input_handler): No longer check whether the
target is async. Update and simplify comment.
* target.c (default_target_wait): New function.
* target.h (struct target_ops) <to_wait>: Now defaults to
default_target_wait.
(default_target_wait): Declare.
* top.c (wait_sync_command_done): New function, factored out from
...
(maybe_wait_sync_command_done): ... this.
* top.h (wait_sync_command_done): Declare.
* target-delegates.c: Regenerate.
2015-09-09 Markus Metzger <markus.t.metzger@intel.com>
* nat/linux-btrace.h (struct btrace_target_info) <ptr_bits>: Remove.

View File

@ -4662,7 +4662,7 @@ bpstat_do_actions_1 (bpstat *bsp)
if (breakpoint_proceeded)
{
if (interpreter_async && target_can_async_p ())
if (interpreter_async)
/* If we are in async mode, then the target might be still
running, not stopped at any breakpoint, so nothing for
us to do here -- just return to the event loop. */
@ -11588,7 +11588,7 @@ until_break_command (char *arg, int from_tty, int anywhere)
be deleted when the target stops. Otherwise, we're already
stopped and delete breakpoints via cleanup chain. */
if (target_can_async_p () && is_running (inferior_ptid))
if (is_running (inferior_ptid))
{
struct until_break_command_continuation_args *args =
XNEW (struct until_break_command_continuation_args);

View File

@ -73,7 +73,7 @@ inferior_event_handler (enum inferior_event_type event_type,
/* Unregister the inferior from the event loop. This is done
so that when the inferior is not running we don't get
distracted by spurious inferior output. */
if (target_has_execution)
if (target_has_execution && target_can_async_p ())
target_async (0);
}

View File

@ -36,6 +36,8 @@
#include "gdbthread.h"
#include "event-top.h"
#include "observer.h"
#include "top.h"
#include "interps.h"
/* If we can't find a function's name from its address,
we print this instead. */
@ -388,10 +390,13 @@ run_inferior_call (struct thread_info *call_thread, CORE_ADDR real_pc)
ptid_t call_thread_ptid = call_thread->ptid;
int saved_sync_execution = sync_execution;
int was_running = call_thread->state == THREAD_RUNNING;
int saved_interpreter_async = interpreter_async;
/* Infcalls run synchronously, in the foreground. */
if (target_can_async_p ())
sync_execution = 1;
sync_execution = 1;
/* So that we don't print the prompt prematurely in
fetch_inferior_event. */
interpreter_async = 0;
call_thread->control.in_infcall = 1;
@ -404,25 +409,11 @@ run_inferior_call (struct thread_info *call_thread, CORE_ADDR real_pc)
TRY
{
int was_sync = sync_execution;
proceed (real_pc, GDB_SIGNAL_0);
/* Inferior function calls are always synchronous, even if the
target supports asynchronous execution. Do here what
`proceed' itself does in sync mode. */
if (target_can_async_p ())
{
wait_for_inferior ();
normal_stop ();
/* If GDB was previously in sync execution mode, then ensure
that it remains so. normal_stop calls
async_enable_stdin, so reset it again here. In other
cases, stdin will be re-enabled by
inferior_event_handler, when an exception is thrown. */
if (was_sync)
async_disable_stdin ();
}
target supports asynchronous execution. */
wait_sync_command_done ();
}
CATCH (e, RETURN_MASK_ALL)
{
@ -430,6 +421,13 @@ run_inferior_call (struct thread_info *call_thread, CORE_ADDR real_pc)
}
END_CATCH
/* If GDB was previously in sync execution mode, then ensure that it
remains so. normal_stop calls async_enable_stdin, so reset it
again here. In other cases, stdin will be re-enabled by
inferior_event_handler, when an exception is thrown. */
sync_execution = saved_sync_execution;
interpreter_async = saved_interpreter_async;
/* At this point the current thread may have changed. Refresh
CALL_THREAD as it could be invalid if its thread has exited. */
call_thread = find_thread_ptid (call_thread_ptid);
@ -470,8 +468,6 @@ run_inferior_call (struct thread_info *call_thread, CORE_ADDR real_pc)
if (call_thread != NULL)
call_thread->control.in_infcall = saved_in_infcall;
sync_execution = saved_sync_execution;
return caught_error;
}

View File

@ -504,13 +504,12 @@ prepare_execution_command (struct target_ops *target, int background)
if (background && !target->to_can_async_p (target))
error (_("Asynchronous execution not supported on this target."));
/* If we don't get a request of running in the bg, then we need
to simulate synchronous (fg) execution. */
if (!background && target->to_can_async_p (target))
if (!background)
{
/* Simulate synchronous execution. Note no cleanup is necessary
for this. stdin is re-enabled whenever an error reaches the
top level. */
/* If we get a request for running in the fg, then we need to
simulate synchronous (fg) execution. Note no cleanup is
necessary for this. stdin is re-enabled whenever an error
reaches the top level. */
async_disable_stdin ();
}
}
@ -937,44 +936,15 @@ step_1 (int skip_subroutines, int single_inst, char *count_string)
make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
}
/* In synchronous case, all is well; each step_once call will step once. */
if (!target_can_async_p ())
{
for (; count > 0; count--)
{
step_once (skip_subroutines, single_inst, count, thread);
/* Do only one step for now, before returning control to the event
loop. Let the continuation figure out how many other steps we
need to do, and handle them one at the time, through
step_once. */
step_once (skip_subroutines, single_inst, count, thread);
if (!target_has_execution)
break;
else
{
struct thread_info *tp = inferior_thread ();
if (!tp->control.stop_step || !tp->step_multi)
{
/* If we stopped for some reason that is not stepping
there are no further steps to make. */
tp->step_multi = 0;
break;
}
}
}
do_cleanups (cleanups);
}
else
{
/* In the case of an asynchronous target things get complicated;
do only one step for now, before returning control to the
event loop. Let the continuation figure out how many other
steps we need to do, and handle them one at the time, through
step_once. */
step_once (skip_subroutines, single_inst, count, thread);
/* We are running, and the continuation is installed. It will
disable the longjmp breakpoint as appropriate. */
discard_cleanups (cleanups);
}
/* We are running, and the continuation is installed. It will
disable the longjmp breakpoint as appropriate. */
discard_cleanups (cleanups);
}
struct step_1_continuation_args
@ -1033,6 +1003,7 @@ step_once (int skip_subroutines, int single_inst, int count, int thread)
if (count > 0)
{
struct step_1_continuation_args *args;
/* Don't assume THREAD is a valid thread id. It is set to -1 if
the longjmp breakpoint was not required. Use the
INFERIOR_PTID thread instead, which is the same thread when
@ -1116,21 +1087,15 @@ step_once (int skip_subroutines, int single_inst, int count, int thread)
tp->control.stepping_command = 1;
proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT);
/* For async targets, register a continuation to do any
additional steps. For sync targets, the caller will handle
further stepping. */
if (target_can_async_p ())
{
struct step_1_continuation_args *args =
XNEW (struct step_1_continuation_args);
/* Register a continuation to do any additional steps. */
args = XNEW (struct step_1_continuation_args);
args = xmalloc (sizeof (*args));
args->skip_subroutines = skip_subroutines;
args->single_inst = single_inst;
args->count = count;
args->thread = thread;
args->skip_subroutines = skip_subroutines;
args->single_inst = single_inst;
args->count = count;
args->thread = thread;
add_intermediate_continuation (tp, step_1_continuation, args, xfree);
}
add_intermediate_continuation (tp, step_1_continuation, args, xfree);
}
}
@ -1442,7 +1407,7 @@ until_next_command (int from_tty)
proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT);
if (target_can_async_p () && is_running (inferior_ptid))
if (is_running (inferior_ptid))
{
struct until_next_continuation_args *cont_args;
@ -1801,8 +1766,6 @@ finish_forward (struct symbol *function, struct frame_info *frame)
proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT);
discard_cleanups (old_chain);
if (!target_can_async_p ())
do_all_continuations (0);
}
/* "finish": Set a temporary breakpoint at the place the selected
@ -2532,8 +2495,7 @@ attach_command_post_wait (char *args, int from_tty, int async_exec)
/* The user requested a plain `attach', so be sure to leave
the inferior stopped. */
if (target_can_async_p ())
async_enable_stdin ();
async_enable_stdin ();
/* At least the current thread is already stopped. */
@ -2663,6 +2625,7 @@ attach_command (char *args, int from_tty)
E.g. Mach 3 or GNU hurd. */
if (!target_attach_no_wait)
{
struct attach_command_continuation_args *a;
struct inferior *inferior = current_inferior ();
/* Careful here. See comments in inferior.h. Basically some
@ -2672,25 +2635,19 @@ attach_command (char *args, int from_tty)
STOP_QUIETLY_NO_SIGSTOP is for. */
inferior->control.stop_soon = STOP_QUIETLY_NO_SIGSTOP;
if (target_can_async_p ())
{
/* sync_execution mode. Wait for stop. */
struct attach_command_continuation_args *a;
/* sync_execution mode. Wait for stop. */
a = XNEW (struct attach_command_continuation_args);
a->args = xstrdup (args);
a->from_tty = from_tty;
a->async_exec = async_exec;
add_inferior_continuation (attach_command_continuation, a,
attach_command_continuation_free_args);
/* Done with ARGS. */
do_cleanups (args_chain);
a = XNEW (struct attach_command_continuation_args);
a->args = xstrdup (args);
a->from_tty = from_tty;
a->async_exec = async_exec;
add_inferior_continuation (attach_command_continuation, a,
attach_command_continuation_free_args);
/* Done with ARGS. */
do_cleanups (args_chain);
return;
}
wait_for_inferior ();
if (!target_is_async_p ())
mark_infrun_async_event_handler ();
return;
}
/* Done with ARGS. */
@ -2731,6 +2688,7 @@ notice_new_inferior (ptid_t ptid, int leave_running, int from_tty)
if (is_executing (inferior_ptid))
{
struct attach_command_continuation_args *a;
struct inferior *inferior = current_inferior ();
/* We're going to install breakpoints, and poke at memory,
@ -2741,22 +2699,15 @@ notice_new_inferior (ptid_t ptid, int leave_running, int from_tty)
inferior->control.stop_soon = STOP_QUIETLY_REMOTE;
/* Wait for stop before proceeding. */
if (target_can_async_p ())
{
struct attach_command_continuation_args *a;
a = XNEW (struct attach_command_continuation_args);
a->args = xstrdup ("");
a->from_tty = from_tty;
a->async_exec = async_exec;
add_inferior_continuation (attach_command_continuation, a,
attach_command_continuation_free_args);
a = XNEW (struct attach_command_continuation_args);
a->args = xstrdup ("");
a->from_tty = from_tty;
a->async_exec = async_exec;
add_inferior_continuation (attach_command_continuation, a,
attach_command_continuation_free_args);
do_cleanups (old_chain);
return;
}
else
wait_for_inferior ();
do_cleanups (old_chain);
return;
}
async_exec = leave_running;

View File

@ -131,6 +131,14 @@ infrun_async (int enable)
}
}
/* See infrun.h. */
void
mark_infrun_async_event_handler (void)
{
mark_async_event_handler (infrun_async_inferior_event_token);
}
/* When set, stop the 'step' command if we enter a function which has
no line number information. The normal behavior is that we step
over such function. */
@ -3094,15 +3102,11 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
discard_cleanups (old_chain);
/* Wait for it to stop (if not standalone)
and in any case decode why it stopped, and act accordingly. */
/* Do this only if we are not using the event loop, or if the target
does not support asynchronous execution. */
/* Tell the event loop to wait for it to stop. If the target
supports asynchronous execution, it'll do this from within
target_resume. */
if (!target_can_async_p ())
{
wait_for_inferior ();
normal_stop ();
}
mark_async_event_handler (infrun_async_inferior_event_token);
}
@ -3766,7 +3770,8 @@ fetch_inferior_event (void *client_data)
make_cleanup_restore_integer (&execution_direction);
execution_direction = target_execution_direction ();
ecs->ptid = do_target_wait (waiton_ptid, &ecs->ws, TARGET_WNOHANG);
ecs->ptid = do_target_wait (waiton_ptid, &ecs->ws,
target_can_async_p () ? TARGET_WNOHANG : 0);
if (debug_infrun)
print_target_wait_results (waiton_ptid, ecs->ptid, &ecs->ws);
@ -7520,10 +7525,10 @@ prepare_to_wait (struct execution_control_state *ecs)
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: prepare_to_wait\n");
/* This is the old end of the while loop. Let everybody know we
want to wait for the inferior some more and get called again
soon. */
ecs->wait_some_more = 1;
if (!target_is_async_p ())
mark_infrun_async_event_handler ();
}
/* We are done with the step range of a step/next/si/ni command.
@ -8816,14 +8821,6 @@ static const struct internalvar_funcs siginfo_funcs =
static void
infrun_async_inferior_event_handler (gdb_client_data data)
{
/* If the target is closed while this event source is marked, we
will reach here without execution, or a target to call
target_wait on, which is an error. Instead of tracking whether
the target has been popped already, or whether we do have threads
with pending statutes, simply ignore the event. */
if (!target_is_async_p ())
return;
inferior_event_handler (INF_REG_EVENT, NULL);
}

View File

@ -197,6 +197,10 @@ enum gdb_signal gdb_signal_from_command (int num);
/* Enables/disables infrun's async event source in the event loop. */
extern void infrun_async (int enable);
/* Call infrun's event handler the next time through the event
loop. */
extern void mark_infrun_async_event_handler (void);
/* The global queue of threads that need to do a step-over operation
to get past e.g., a breakpoint. */
extern struct thread_info *step_over_queue_head;

View File

@ -3442,12 +3442,6 @@ linux_nat_wait_1 (struct target_ops *ops,
target_pid_to_str (lp->ptid));
}
if (!target_is_async_p ())
{
/* Causes SIGINT to be passed on to the attached process. */
set_sigint_trap ();
}
/* But if we don't find a pending event, we'll have to wait. Always
pull all events out of the kernel. We'll randomly select an
event LWP out of all that have events, to prevent starvation. */
@ -3518,9 +3512,6 @@ linux_nat_wait_1 (struct target_ops *ops,
ourstatus->kind = TARGET_WAITKIND_NO_RESUMED;
if (!target_is_async_p ())
clear_sigint_trap ();
restore_child_signals_mask (&prev_mask);
return minus_one_ptid;
}
@ -3546,9 +3537,6 @@ linux_nat_wait_1 (struct target_ops *ops,
sigsuspend (&suspend_mask);
}
if (!target_is_async_p ())
clear_sigint_trap ();
gdb_assert (lp);
status = lp->status;
@ -4629,17 +4617,6 @@ static int async_terminal_is_ours = 1;
static void
linux_nat_terminal_inferior (struct target_ops *self)
{
/* Like target_terminal_inferior, use target_can_async_p, not
target_is_async_p, since at this point the target is not async
yet. If it can async, then we know it will become async prior to
resume. */
if (!target_can_async_p ())
{
/* Async mode is disabled. */
child_terminal_inferior (self);
return;
}
child_terminal_inferior (self);
/* Calls to target_terminal_*() are meant to be idempotent. */

View File

@ -309,16 +309,8 @@ mi_execute_command_wrapper (const char *cmd)
static void
mi_on_sync_execution_done (void)
{
/* MI generally prints a prompt after a command, indicating it's
ready for further input. However, due to an historical wart, if
MI async, and a (CLI) synchronous command was issued, then we
will print the prompt right after printing "^running", even if we
cannot actually accept any input until the target stops. See
mi_on_resume. However, if the target is async but MI is sync,
then we need to output the MI prompt now, to replicate gdb's
behavior when neither the target nor MI are async. (Note this
observer is only called by the asynchronous target event handling
code.) */
/* If MI is sync, then output the MI prompt now, indicating we're
ready for further input. */
if (!mi_async_p ())
{
fputs_unfiltered ("(gdb) \n", raw_stdout);
@ -333,20 +325,12 @@ mi_execute_command_input_handler (char *cmd)
{
mi_execute_command_wrapper (cmd);
/* MI generally prints a prompt after a command, indicating it's
ready for further input. However, due to an historical wart, if
MI is async, and a synchronous command was issued, then we will
print the prompt right after printing "^running", even if we
cannot actually accept any input until the target stops. See
mi_on_resume.
If MI is not async, then we print the prompt when the command
finishes. If the target is sync, that means output the prompt
now, as in that case executing a command doesn't return until the
command is done. However, if the target is async, we go back to
the event loop and output the prompt in the
'synchronous_command_done' observer. */
if (!target_is_async_p () || !sync_execution)
/* Print a prompt, indicating we're ready for further input, unless
we just started a synchronous command. In that case, we're about
to go back to the event loop and will output the prompt in the
'synchronous_command_done' observer when the target next
stops. */
if (!sync_execution)
{
fputs_unfiltered ("(gdb) \n", raw_stdout);
gdb_flush (raw_stdout);

View File

@ -116,12 +116,6 @@ delegate_wait (struct target_ops *self, ptid_t arg1, struct target_waitstatus *a
return self->to_wait (self, arg1, arg2, arg3);
}
static ptid_t
tdefault_wait (struct target_ops *self, ptid_t arg1, struct target_waitstatus *arg2, int arg3)
{
noprocess ();
}
static ptid_t
debug_wait (struct target_ops *self, ptid_t arg1, struct target_waitstatus *arg2, int arg3)
{
@ -4251,7 +4245,7 @@ install_dummy_methods (struct target_ops *ops)
ops->to_detach = tdefault_detach;
ops->to_disconnect = tdefault_disconnect;
ops->to_resume = tdefault_resume;
ops->to_wait = tdefault_wait;
ops->to_wait = default_target_wait;
ops->to_fetch_registers = tdefault_fetch_registers;
ops->to_store_registers = tdefault_store_registers;
ops->to_prepare_to_store = tdefault_prepare_to_store;

View File

@ -2233,6 +2233,17 @@ target_wait (ptid_t ptid, struct target_waitstatus *status, int options)
return (current_target.to_wait) (&current_target, ptid, status, options);
}
/* See target.h. */
ptid_t
default_target_wait (struct target_ops *ops,
ptid_t ptid, struct target_waitstatus *status,
int options)
{
status->kind = TARGET_WAITKIND_IGNORE;
return minus_one_ptid;
}
char *
target_pid_to_str (ptid_t ptid)
{

View File

@ -471,7 +471,7 @@ struct target_ops
ptid_t (*to_wait) (struct target_ops *,
ptid_t, struct target_waitstatus *,
int TARGET_DEBUG_PRINTER (target_debug_print_options))
TARGET_DEFAULT_NORETURN (noprocess ());
TARGET_DEFAULT_FUNC (default_target_wait);
void (*to_fetch_registers) (struct target_ops *, struct regcache *, int)
TARGET_DEFAULT_IGNORE ();
void (*to_store_registers) (struct target_ops *, struct regcache *, int)
@ -1327,6 +1327,13 @@ extern void target_resume (ptid_t ptid, int step, enum gdb_signal signal);
extern ptid_t target_wait (ptid_t ptid, struct target_waitstatus *status,
int options);
/* The default target_ops::to_wait implementation. */
extern ptid_t default_target_wait (struct target_ops *ops,
ptid_t ptid,
struct target_waitstatus *status,
int options);
/* Fetch at least register REGNO, or all regs if regno == -1. No result. */
extern void target_fetch_registers (struct regcache *regcache, int regno);

View File

@ -367,6 +367,16 @@ check_frame_language_change (void)
/* See top.h. */
void
wait_sync_command_done (void)
{
while (gdb_do_one_event () >= 0)
if (!sync_execution)
break;
}
/* See top.h. */
void
maybe_wait_sync_command_done (int was_sync)
{
@ -375,11 +385,7 @@ maybe_wait_sync_command_done (int was_sync)
just ran a synchronous command that started the target, wait
for that command to end. */
if (!interpreter_async && !was_sync && sync_execution)
{
while (gdb_do_one_event () >= 0)
if (!sync_execution)
break;
}
wait_sync_command_done ();
}
/* Execute the line P as a command, in the current user context.

View File

@ -50,6 +50,9 @@ extern void execute_command (char *, int);
extern void maybe_wait_sync_command_done (int was_sync);
/* Wait for a synchronous execution command to end. */
extern void wait_sync_command_done (void);
extern void check_frame_language_change (void);
/* Prepare for execution of a command.