/* Target-struct-independent code to start (run) and stop an inferior
process.
- Copyright (C) 1986-2016 Free Software Foundation, Inc.
+ Copyright (C) 1986-2017 Free Software Foundation, Inc.
This file is part of GDB.
static struct step_over_info step_over_info;
/* Record the address of the breakpoint/instruction we're currently
- stepping over. */
+ stepping over.
+ N.B. We record the aspace and address now, instead of say just the thread,
+ because when we need the info later the thread may be running. */
static void
set_step_over_info (struct address_space *aspace, CORE_ADDR address,
step_over_what step_what;
int must_be_in_line;
+ gdb_assert (!tp->stop_requested);
+
next = thread_step_over_chain_next (tp);
/* If this inferior already has a displaced step in process,
{
struct thread_info *tp = inferior_thread ();
+ gdb_assert (!tp->stop_requested);
+
/* Install inferior's terminal modes. */
target_terminal_inferior ();
single-step). */
int step;
+ gdb_assert (!tp->stop_requested);
gdb_assert (!thread_is_in_step_over_chain (tp));
QUIT;
static void process_event_stop_test (struct execution_control_state *ecs);
static int switch_back_to_stepped_thread (struct execution_control_state *ecs);
-/* Callback for iterate over threads. If the thread is stopped, but
- the user/frontend doesn't know about that yet, go through
- normal_stop, as if the thread had just stopped now. ARG points at
- a ptid. If PTID is MINUS_ONE_PTID, applies to all threads. If
- ptid_is_pid(PTID) is true, applies to all threads of the process
- pointed at by PTID. Otherwise, apply only to the thread pointed by
- PTID. */
-
-static int
-infrun_thread_stop_requested_callback (struct thread_info *info, void *arg)
-{
- ptid_t ptid = * (ptid_t *) arg;
-
- if ((ptid_equal (info->ptid, ptid)
- || ptid_equal (minus_one_ptid, ptid)
- || (ptid_is_pid (ptid)
- && ptid_get_pid (ptid) == ptid_get_pid (info->ptid)))
- && is_running (info->ptid)
- && !is_executing (info->ptid))
- {
- struct cleanup *old_chain;
- struct execution_control_state ecss;
- struct execution_control_state *ecs = &ecss;
-
- memset (ecs, 0, sizeof (*ecs));
-
- old_chain = make_cleanup_restore_current_thread ();
-
- overlay_cache_invalid = 1;
- /* Flush target cache before starting to handle each event.
- Target was running and cache could be stale. This is just a
- heuristic. Running threads may modify target memory, but we
- don't get any event. */
- target_dcache_invalidate ();
-
- /* Go through handle_inferior_event/normal_stop, so we always
- have consistent output as if the stop event had been
- reported. */
- ecs->ptid = info->ptid;
- ecs->event_thread = info;
- ecs->ws.kind = TARGET_WAITKIND_STOPPED;
- ecs->ws.value.sig = GDB_SIGNAL_0;
-
- handle_inferior_event (ecs);
-
- if (!ecs->wait_some_more)
- {
- /* Cancel any running execution command. */
- thread_cancel_execution_command (info);
-
- normal_stop ();
- }
-
- do_cleanups (old_chain);
- }
-
- return 0;
-}
-
/* This function is attached as a "thread_stop_requested" observer.
Cleanup local state that assumed the PTID was to be resumed, and
report the stop to the frontend. */
{
struct thread_info *tp;
- /* PTID was requested to stop. Remove matching threads from the
- step-over queue, so we don't try to resume them
- automatically. */
+ /* PTID was requested to stop. If the thread was already stopped,
+ but the user/frontend doesn't know about that yet (e.g., the
+ thread had been temporarily paused for some step-over), set up
+ for reporting the stop now. */
ALL_NON_EXITED_THREADS (tp)
if (ptid_match (tp->ptid, ptid))
{
+ if (tp->state != THREAD_RUNNING)
+ continue;
+ if (tp->executing)
+ continue;
+
+ /* Remove matching threads from the step-over queue, so
+ start_step_over doesn't try to resume them
+ automatically. */
if (thread_is_in_step_over_chain (tp))
thread_step_over_chain_remove (tp);
- }
- iterate_over_threads (infrun_thread_stop_requested_callback, &ptid);
+ /* If the thread is stopped, but the user/frontend doesn't
+ know about that yet, queue a pending event, as if the
+ thread had just stopped now. Unless the thread already had
+ a pending event. */
+ if (!tp->suspend.waitstatus_pending_p)
+ {
+ tp->suspend.waitstatus_pending_p = 1;
+ tp->suspend.waitstatus.kind = TARGET_WAITKIND_STOPPED;
+ tp->suspend.waitstatus.value.sig = GDB_SIGNAL_0;
+ }
+
+ /* Clear the inline-frame state, since we're re-processing the
+ stop. */
+ clear_inline_frame_state (tp->ptid);
+
+ /* If this thread was paused because some other thread was
+ doing an inline-step over, let that finish first. Once
+ that happens, we'll restart all threads and consume pending
+ stop events then. */
+ if (step_over_info_valid_p ())
+ continue;
+
+ /* Otherwise we can process the (new) pending event now. Set
+ it so this pending event is considered by
+ do_target_wait. */
+ tp->resumed = 1;
+ }
}
static void
const struct target_waitstatus *ws)
{
char *status_string = target_waitstatus_to_string (ws);
- struct ui_file *tmp_stream = mem_fileopen ();
+ string_file stb;
/* The text is split over several lines because it was getting too long.
Call fprintf_unfiltered (gdb_stdlog) once so that the text is still
output as a unit; we want only one timestamp printed if debug_timestamp
is set. */
- fprintf_unfiltered (tmp_stream,
- "infrun: target_wait (%d.%ld.%ld",
- ptid_get_pid (waiton_ptid),
- ptid_get_lwp (waiton_ptid),
- ptid_get_tid (waiton_ptid));
+ stb.printf ("infrun: target_wait (%d.%ld.%ld",
+ ptid_get_pid (waiton_ptid),
+ ptid_get_lwp (waiton_ptid),
+ ptid_get_tid (waiton_ptid));
if (ptid_get_pid (waiton_ptid) != -1)
- fprintf_unfiltered (tmp_stream,
- " [%s]", target_pid_to_str (waiton_ptid));
- fprintf_unfiltered (tmp_stream, ", status) =\n");
- fprintf_unfiltered (tmp_stream,
- "infrun: %d.%ld.%ld [%s],\n",
- ptid_get_pid (result_ptid),
- ptid_get_lwp (result_ptid),
- ptid_get_tid (result_ptid),
- target_pid_to_str (result_ptid));
- fprintf_unfiltered (tmp_stream,
- "infrun: %s\n",
- status_string);
-
- std::string text = ui_file_as_string (tmp_stream);
+ stb.printf (" [%s]", target_pid_to_str (waiton_ptid));
+ stb.printf (", status) =\n");
+ stb.printf ("infrun: %d.%ld.%ld [%s],\n",
+ ptid_get_pid (result_ptid),
+ ptid_get_lwp (result_ptid),
+ ptid_get_tid (result_ptid),
+ target_pid_to_str (result_ptid));
+ stb.printf ("infrun: %s\n", status_string);
/* This uses %s in part to handle %'s in the text, but also to avoid
a gcc error: the format attribute requires a string literal. */
- fprintf_unfiltered (gdb_stdlog, "%s", text.c_str ());
+ fprintf_unfiltered (gdb_stdlog, "%s", stb.c_str ());
xfree (status_string);
- ui_file_delete (tmp_stream);
}
/* Select a thread at random, out of those which are resumed and have
return 0;
}
+/* If the event thread has the stop requested flag set, pretend it
+ stopped for a GDB_SIGNAL_0 (i.e., as if it stopped due to
+ target_stop). */
+
+static bool
+handle_stop_requested (struct execution_control_state *ecs)
+{
+ if (ecs->event_thread->stop_requested)
+ {
+ ecs->ws.kind = TARGET_WAITKIND_STOPPED;
+ ecs->ws.value.sig = GDB_SIGNAL_0;
+ handle_signal_stop (ecs);
+ return true;
+ }
+ return false;
+}
+
/* Auxiliary function that handles syscall entry/return events.
It returns 1 if the inferior should keep going (and GDB
should ignore the event), or 0 if the event deserves to be
= bpstat_stop_status (get_regcache_aspace (regcache),
stop_pc, ecs->ptid, &ecs->ws);
+ if (handle_stop_requested (ecs))
+ return 0;
+
if (bpstat_causes_stop (ecs->event_thread->control.stop_bpstat))
{
/* Catchpoint hit. */
}
}
+ if (handle_stop_requested (ecs))
+ return 0;
+
/* If no catchpoint triggered for this, then keep going. */
keep_going (ecs);
return 1;
= bpstat_stop_status (get_regcache_aspace (regcache),
stop_pc, ecs->ptid, &ecs->ws);
+ if (handle_stop_requested (ecs))
+ return;
+
if (bpstat_causes_stop (ecs->event_thread->control.stop_bpstat))
{
/* A catchpoint triggered. */
case TARGET_WAITKIND_SPURIOUS:
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SPURIOUS\n");
+ if (handle_stop_requested (ecs))
+ return;
if (!ptid_equal (ecs->ptid, inferior_ptid))
context_switch (ecs->ptid);
resume (GDB_SIGNAL_0);
case TARGET_WAITKIND_THREAD_CREATED:
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_THREAD_CREATED\n");
+ if (handle_stop_requested (ecs))
+ return;
if (!ptid_equal (ecs->ptid, inferior_ptid))
context_switch (ecs->ptid);
if (!switch_back_to_stepped_thread (ecs))
= bpstat_stop_status (get_regcache_aspace (get_current_regcache ()),
stop_pc, ecs->ptid, &ecs->ws);
+ if (handle_stop_requested (ecs))
+ return;
+
/* If no catchpoint triggered for this, then keep going. Note
that we're interested in knowing the bpstat actually causes a
stop, not just if it may explain the signal. Software
current_inferior ()->waiting_for_vfork_done = 0;
current_inferior ()->pspace->breakpoints_not_allowed = 0;
+
+ if (handle_stop_requested (ecs))
+ return;
+
/* This also takes care of reinserting breakpoints in the
previously locked inferior. */
keep_going (ecs);
xfree (ecs->ws.value.execd_pathname);
ecs->ws.value.execd_pathname = NULL;
+ if (handle_stop_requested (ecs))
+ return;
+
/* If no catchpoint triggered for this, then keep going. */
if (!bpstat_causes_stop (ecs->event_thread->control.stop_bpstat))
{
case TARGET_WAITKIND_STOPPED:
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_STOPPED\n");
- ecs->event_thread->suspend.stop_signal = ecs->ws.value.sig;
handle_signal_stop (ecs);
return;
delete_just_stopped_threads_single_step_breakpoints ();
stop_pc = regcache_read_pc (get_thread_regcache (inferior_ptid));
+
+ if (handle_stop_requested (ecs))
+ return;
+
observer_notify_no_history ();
stop_waiting (ecs);
return;
continue;
}
+ gdb_assert (!tp->stop_requested);
+
/* If some thread needs to start a step-over at this point, it
should still be in the step-over queue, and thus skipped
above. */
back an event. */
gdb_assert (ecs->event_thread->control.trap_expected);
- if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP)
- clear_step_over_info ();
+ clear_step_over_info ();
}
if (!target_is_non_stop_p ())
gdb_assert (ecs->ws.kind == TARGET_WAITKIND_STOPPED);
+ ecs->event_thread->suspend.stop_signal = ecs->ws.value.sig;
+
/* Do we need to clean up the state of a thread that has
completed a displaced single-step? (Doing so usually affects
the PC, so do it here, before we set stop_pc.) */
if (random_signal)
random_signal = !stopped_by_watchpoint;
+ /* Always stop if the user explicitly requested this thread to
+ remain stopped. */
+ if (ecs->event_thread->stop_requested)
+ {
+ random_signal = 1;
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "infrun: user-requested stop\n");
+ }
+
/* For the program's own signals, act according to
the signal handling tables. */
&& ecs->event_thread->control.trap_expected
&& ecs->event_thread->control.step_resume_breakpoint == NULL)
{
- int was_in_line;
-
/* We were just starting a new sequence, attempting to
single-step off of a breakpoint and expecting a SIGTRAP.
Instead this signal arrives. This signal will take us out
"infrun: signal arrived while stepping over "
"breakpoint\n");
- was_in_line = step_over_info_valid_p ();
- clear_step_over_info ();
insert_hp_step_resume_breakpoint_at_frame (frame);
ecs->event_thread->step_after_step_resume_breakpoint = 1;
/* Reset trap_expected to ensure breakpoints are re-inserted. */
ecs->event_thread->control.trap_expected = 0;
- if (target_is_non_stop_p ())
- {
- /* Either "set non-stop" is "on", or the target is
- always in non-stop mode. In this case, we have a bit
- more work to do. Resume the current thread, and if
- we had paused all threads, restart them while the
- signal handler runs. */
- keep_going (ecs);
-
- if (was_in_line)
- {
- restart_threads (ecs->event_thread);
- }
- else if (debug_infrun)
- {
- fprintf_unfiltered (gdb_stdlog,
- "infrun: no need to restart threads\n");
- }
- return;
- }
-
/* If we were nexting/stepping some other thread, switch to
it, so that we don't continue it, losing control. */
if (!switch_back_to_stepped_thread (ecs))
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: stop_waiting\n");
- clear_step_over_info ();
-
/* Let callers know we don't want to wait for the inferior anymore. */
ecs->wait_some_more = 0;