#include "target-dcache.h"
#include "terminal.h"
#include "solist.h"
-#include "event-loop.h"
+#include "gdbsupport/event-loop.h"
#include "thread-fsm.h"
#include "gdbsupport/enum-flags.h"
#include "progspace-and-thread.h"
#include "arch-utils.h"
#include "gdbsupport/scope-exit.h"
#include "gdbsupport/forward-scope-exit.h"
-#include "gdb_select.h"
+#include "gdbsupport/gdb_select.h"
#include <unordered_map>
+#include "async-event.h"
/* Prototypes for local functions */
static void sig_print_header (void);
-static int follow_fork (void);
-
-static int follow_fork_inferior (int follow_child, int detach_fork);
-
static void follow_inferior_reset_breakpoints (void);
static int currently_stepping (struct thread_info *tp);
the fork parent. At return inferior_ptid is the ptid of the
followed inferior. */
-static int
-follow_fork_inferior (int follow_child, int detach_fork)
+static bool
+follow_fork_inferior (bool follow_child, bool detach_fork)
{
int has_vforked;
ptid_t parent_ptid, child_ptid;
if the inferior should be resumed; false, if the target for some
reason decided it's best not to resume. */
-static int
-follow_fork (void)
+static bool
+follow_fork ()
{
- int follow_child = (follow_fork_mode_string == follow_fork_mode_child);
- int should_resume = 1;
+ bool follow_child = (follow_fork_mode_string == follow_fork_mode_child);
+ bool should_resume = true;
struct thread_info *tp;
/* Copy user stepping state to the new inferior thread. FIXME: the
struct breakpoint *exception_resume_breakpoint = NULL;
CORE_ADDR step_range_start = 0;
CORE_ADDR step_range_end = 0;
+ int current_line = 0;
+ symtab *current_symtab = NULL;
struct frame_id step_frame_id = { 0 };
struct thread_fsm *thread_fsm = NULL;
happened. */
thread_info *wait_thread = find_thread_ptid (wait_target, wait_ptid);
switch_to_thread (wait_thread);
- should_resume = 0;
+ should_resume = false;
}
}
(tp->control.step_resume_breakpoint);
step_range_start = tp->control.step_range_start;
step_range_end = tp->control.step_range_end;
+ current_line = tp->current_line;
+ current_symtab = tp->current_symtab;
step_frame_id = tp->control.step_frame_id;
exception_resume_breakpoint
= clone_momentary_breakpoint (tp->control.exception_resume_breakpoint);
= step_resume_breakpoint;
tp->control.step_range_start = step_range_start;
tp->control.step_range_end = step_range_end;
+ tp->current_line = current_line;
+ tp->current_symtab = current_symtab;
tp->control.step_frame_id = step_frame_id;
tp->control.exception_resume_breakpoint
= exception_resume_breakpoint;
doesn't support it, GDB will instead use the traditional
hold-and-step approach. If AUTO (which is the default), GDB will
decide which technique to use to step over breakpoints depending on
- which of all-stop or non-stop mode is active --- displaced stepping
- in non-stop mode; hold-and-step in all-stop mode. */
+ whether the target works in a non-stop way (see use_displaced_stepping). */
static enum auto_boolean can_use_displaced_stepping = AUTO_BOOLEAN_AUTO;
"to step over breakpoints is %s.\n"), value);
}
+/* Return true if the gdbarch implements the required methods to use
+ displaced stepping. */
+
+static bool
+gdbarch_supports_displaced_stepping (gdbarch *arch)
+{
+ /* Only check for the presence of step_copy_insn. Other required methods
+ are checked by the gdbarch validation. */
+ return gdbarch_displaced_step_copy_insn_p (arch);
+}
+
/* Return non-zero if displaced stepping can/should be used to step
over breakpoints of thread TP. */
-static int
-use_displaced_stepping (struct thread_info *tp)
+static bool
+use_displaced_stepping (thread_info *tp)
{
- struct regcache *regcache = get_thread_regcache (tp);
- struct gdbarch *gdbarch = regcache->arch ();
+ /* If the user disabled it explicitly, don't use displaced stepping. */
+ if (can_use_displaced_stepping == AUTO_BOOLEAN_FALSE)
+ return false;
+
+ /* If "auto", only use displaced stepping if the target operates in a non-stop
+ way. */
+ if (can_use_displaced_stepping == AUTO_BOOLEAN_AUTO
+ && !target_is_non_stop_p ())
+ return false;
+
+ gdbarch *gdbarch = get_thread_regcache (tp)->arch ();
+
+ /* If the architecture doesn't implement displaced stepping, don't use
+ it. */
+ if (!gdbarch_supports_displaced_stepping (gdbarch))
+ return false;
+
+ /* If recording, don't use displaced stepping. */
+ if (find_record_target () != nullptr)
+ return false;
+
displaced_step_inferior_state *displaced_state
= get_displaced_stepping_state (tp->inf);
- return (((can_use_displaced_stepping == AUTO_BOOLEAN_AUTO
- && target_is_non_stop_p ())
- || can_use_displaced_stepping == AUTO_BOOLEAN_TRUE)
- && gdbarch_displaced_step_copy_insn_p (gdbarch)
- && find_record_target () == NULL
- && !displaced_state->failed_before);
+ /* If displaced stepping failed before for this inferior, don't bother trying
+ again. */
+ if (displaced_state->failed_before)
+ return false;
+
+ return true;
}
/* Simple function wrapper around displaced_step_inferior_state::reset. */
/* We should never reach this function if the architecture does not
support displaced stepping. */
- gdb_assert (gdbarch_displaced_step_copy_insn_p (gdbarch));
+ gdb_assert (gdbarch_supports_displaced_stepping (gdbarch));
/* Nor if the thread isn't meant to step over a breakpoint. */
gdb_assert (tp->control.trap_expected);
if (displaced->step_thread != event_thread)
return 0;
- displaced_step_reset_cleanup cleanup (displaced);
-
- displaced_step_restore (displaced, displaced->step_thread->ptid);
-
/* Fixup may need to read memory/registers. Switch to the thread
that we're fixing up. Also, target_stopped_by_watchpoint checks
- the current thread. */
+ the current thread, and displaced_step_restore performs ptid-dependent
+ memory accesses using current_inferior() and current_top_target(). */
switch_to_thread (event_thread);
+ displaced_step_reset_cleanup cleanup (displaced);
+
+ displaced_step_restore (displaced, displaced->step_thread->ptid);
+
/* Did the instruction complete successfully? */
if (signal == GDB_SIGNAL_TRAP
&& !(target_stopped_by_watchpoint ()
struct regcache *regcache = get_current_regcache ();
struct gdbarch *gdbarch = regcache->arch ();
struct thread_info *tp = inferior_thread ();
- CORE_ADDR pc = regcache_read_pc (regcache);
const address_space *aspace = regcache->aspace ();
ptid_t resume_ptid;
/* This represents the user's step vs continue request. When
step = 0;
}
+ CORE_ADDR pc = regcache_read_pc (regcache);
+
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog,
"infrun: resume (step=%d, signal=%s), "
gdbarch = regcache->arch ();
const address_space *aspace = regcache->aspace ();
- pc = regcache_read_pc (regcache);
+ pc = regcache_read_pc_protected (regcache);
+
thread_info *cur_thr = inferior_thread ();
/* Fill in with reasonable starting values. */
advanced. Must do this before resuming any thread, as in
all-stop/remote, once we resume we can't send any other packet
until the target stops again. */
- cur_thr->prev_pc = regcache_read_pc (regcache);
+ cur_thr->prev_pc = regcache_read_pc_protected (regcache);
{
scoped_restore save_defer_tc = make_scoped_defer_target_commit_resume ();
ptid_t event_ptid;
struct thread_info *tp;
+ /* We know that we are looking for an event in the target of inferior
+ INF, but we don't know which thread the event might come from. As
+ such we want to make sure that INFERIOR_PTID is reset so that none of
+ the wait code relies on it - doing so is always a mistake. */
+ switch_to_inferior_no_thread (inf);
+
/* First check if there is a resumed thread with a wait status
pending. */
if (ptid == minus_one_ptid || ptid.is_pid ())
auto do_wait = [&] (inferior *inf)
{
- switch_to_inferior_no_thread (inf);
-
ecs->ptid = do_target_wait_1 (inf, wait_ptid, &ecs->ws, options);
ecs->target = inf->process_target ();
return (ecs->ws.kind != TARGET_WAITKIND_IGNORE);
printf_unfiltered (_("completed.\n"));
}
-/* Record the frame and location we're currently stepping through. */
+/* See infrun.h. */
+
void
-set_step_info (struct frame_info *frame, struct symtab_and_line sal)
+set_step_info (thread_info *tp, struct frame_info *frame,
+ struct symtab_and_line sal)
{
- struct thread_info *tp = inferior_thread ();
+ /* This can be removed once this function no longer implicitly relies on the
+ inferior_ptid value. */
+ gdb_assert (inferior_ptid == tp->ptid);
tp->control.step_frame_id = get_frame_id (frame);
tp->control.step_stack_frame_id = get_stack_frame_id (frame);
}
}
+/* Mark the non-executing threads accordingly. In all-stop, all
+ threads of all processes are stopped when we get any event
+ reported. In non-stop mode, only the event thread stops. */
+
+static void
+mark_non_executing_threads (process_stratum_target *target,
+ ptid_t event_ptid,
+ struct target_waitstatus ws)
+{
+ ptid_t mark_ptid;
+
+ if (!target_is_non_stop_p ())
+ mark_ptid = minus_one_ptid;
+ else if (ws.kind == TARGET_WAITKIND_SIGNALLED
+ || ws.kind == TARGET_WAITKIND_EXITED)
+ {
+ /* If we're handling a process exit in non-stop mode, even
+ though threads haven't been deleted yet, one would think
+ that there is nothing to do, as threads of the dead process
+ will be soon deleted, and threads of any other process were
+ left running. However, on some targets, threads survive a
+ process exit event. E.g., for the "checkpoint" command,
+ when the current checkpoint/fork exits, linux-fork.c
+ automatically switches to another fork from within
+ target_mourn_inferior, by associating the same
+ inferior/thread to another fork. We haven't mourned yet at
+ this point, but we must mark any threads left in the
+ process as not-executing so that finish_thread_state marks
+ them stopped (in the user's perspective) if/when we present
+ the stop to the user. */
+ mark_ptid = ptid_t (event_ptid.pid ());
+ }
+ else
+ mark_ptid = event_ptid;
+
+ set_executing (target, mark_ptid, false);
+
+ /* Likewise the resumed flag. */
+ set_resumed (target, mark_ptid, false);
+}
+
/* See infrun.h. */
void
int pass;
int iterations = 0;
- gdb_assert (target_is_non_stop_p ());
+ gdb_assert (exists_non_stop_target ());
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: stop_all_threads\n");
scoped_restore_current_thread restore_thread;
- target_thread_events (1);
- SCOPE_EXIT { target_thread_events (0); };
+ /* Enable thread events of all targets. */
+ for (auto *target : all_non_exited_process_targets ())
+ {
+ switch_to_target_no_thread (target);
+ target_thread_events (true);
+ }
+
+ SCOPE_EXIT
+ {
+ /* Disable thread events of all targets. */
+ for (auto *target : all_non_exited_process_targets ())
+ {
+ switch_to_target_no_thread (target);
+ target_thread_events (false);
+ }
+
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "infrun: stop_all_threads done\n");
+ };
/* Request threads to stop, and then wait for the stops. Because
threads we already know about can spawn more threads while we're
"iterations=%d\n", pass, iterations);
while (1)
{
- int need_wait = 0;
+ int waits_needed = 0;
- update_thread_list ();
+ for (auto *target : all_non_exited_process_targets ())
+ {
+ switch_to_target_no_thread (target);
+ update_thread_list ();
+ }
/* Go through all threads looking for threads that we need
to tell the target to stop. */
for (thread_info *t : all_non_exited_threads ())
{
+ /* For a single-target setting with an all-stop target,
+ we would not even arrive here. For a multi-target
+ setting, until GDB is able to handle a mixture of
+ all-stop and non-stop targets, simply skip all-stop
+ targets' threads. This should be fine due to the
+ protection of 'check_multi_target_resumption'. */
+
+ switch_to_thread_no_regs (t);
+ if (!target_is_non_stop_p ())
+ continue;
+
if (t->executing)
{
/* If already stopping, don't request a stop again.
"infrun: %s executing, "
"need stop\n",
target_pid_to_str (t->ptid).c_str ());
- switch_to_thread_no_regs (t);
target_stop (t->ptid);
t->stop_requested = 1;
}
}
if (t->stop_requested)
- need_wait = 1;
+ waits_needed++;
}
else
{
}
}
- if (!need_wait)
+ if (waits_needed == 0)
break;
/* If we find new threads on the second iteration, restart
if (pass > 0)
pass = -1;
- wait_one_event event = wait_one ();
-
- if (debug_infrun)
+ for (int i = 0; i < waits_needed; i++)
{
- fprintf_unfiltered (gdb_stdlog,
- "infrun: stop_all_threads %s %s\n",
- target_waitstatus_to_string (&event.ws).c_str (),
- target_pid_to_str (event.ptid).c_str ());
- }
+ wait_one_event event = wait_one ();
- if (event.ws.kind == TARGET_WAITKIND_NO_RESUMED
- || event.ws.kind == TARGET_WAITKIND_THREAD_EXITED
- || event.ws.kind == TARGET_WAITKIND_EXITED
- || event.ws.kind == TARGET_WAITKIND_SIGNALLED)
- {
- /* All resumed threads exited
- or one thread/process exited/signalled. */
- }
- else
- {
- thread_info *t = find_thread_ptid (event.target, event.ptid);
- if (t == NULL)
- t = add_thread (event.target, event.ptid);
-
- t->stop_requested = 0;
- t->executing = 0;
- t->resumed = false;
- t->control.may_range_step = 0;
-
- /* This may be the first time we see the inferior report
- a stop. */
- inferior *inf = find_inferior_ptid (event.target, event.ptid);
- if (inf->needs_setup)
+ if (debug_infrun)
{
- switch_to_thread_no_regs (t);
- setup_inferior (0);
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: stop_all_threads %s %s\n",
+ target_waitstatus_to_string (&event.ws).c_str (),
+ target_pid_to_str (event.ptid).c_str ());
}
- if (event.ws.kind == TARGET_WAITKIND_STOPPED
- && event.ws.value.sig == GDB_SIGNAL_0)
+ if (event.ws.kind == TARGET_WAITKIND_NO_RESUMED)
{
- /* We caught the event that we intended to catch, so
- there's no event pending. */
- t->suspend.waitstatus.kind = TARGET_WAITKIND_IGNORE;
- t->suspend.waitstatus_pending_p = 0;
+ /* All resumed threads exited. */
+ break;
+ }
+ else if (event.ws.kind == TARGET_WAITKIND_THREAD_EXITED
+ || event.ws.kind == TARGET_WAITKIND_EXITED
+ || event.ws.kind == TARGET_WAITKIND_SIGNALLED)
+ {
+ /* One thread/process exited/signalled. */
- if (displaced_step_fixup (t, GDB_SIGNAL_0) < 0)
+ thread_info *t = nullptr;
+
+ /* The target may have reported just a pid. If so, try
+ the first non-exited thread. */
+ if (event.ptid.is_pid ())
{
- /* Add it back to the step-over queue. */
- if (debug_infrun)
+ int pid = event.ptid.pid ();
+ inferior *inf = find_inferior_pid (event.target, pid);
+ for (thread_info *tp : inf->non_exited_threads ())
{
- fprintf_unfiltered (gdb_stdlog,
- "infrun: displaced-step of %s "
- "canceled: adding back to the "
- "step-over queue\n",
- target_pid_to_str (t->ptid).c_str ());
+ t = tp;
+ break;
}
- t->control.trap_expected = 0;
- thread_step_over_chain_enqueue (t);
+
+ /* If there is no available thread, the event would
+ have to be appended to a per-inferior event list,
+ which does not exist (and if it did, we'd have
+ to adjust run control command to be able to
+ resume such an inferior). We assert here instead
+ of going into an infinite loop. */
+ gdb_assert (t != nullptr);
+
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: stop_all_threads, using %s\n",
+ target_pid_to_str (t->ptid).c_str ());
+ }
+ else
+ {
+ t = find_thread_ptid (event.target, event.ptid);
+ /* Check if this is the first time we see this thread.
+ Don't bother adding if it individually exited. */
+ if (t == nullptr
+ && event.ws.kind != TARGET_WAITKIND_THREAD_EXITED)
+ t = add_thread (event.target, event.ptid);
+ }
+
+ if (t != nullptr)
+ {
+ /* Set the threads as non-executing to avoid
+ another stop attempt on them. */
+ switch_to_thread_no_regs (t);
+ mark_non_executing_threads (event.target, event.ptid,
+ event.ws);
+ save_waitstatus (t, &event.ws);
+ t->stop_requested = false;
}
}
else
{
- enum gdb_signal sig;
- struct regcache *regcache;
+ thread_info *t = find_thread_ptid (event.target, event.ptid);
+ if (t == NULL)
+ t = add_thread (event.target, event.ptid);
- if (debug_infrun)
+ t->stop_requested = 0;
+ t->executing = 0;
+ t->resumed = false;
+ t->control.may_range_step = 0;
+
+ /* This may be the first time we see the inferior report
+ a stop. */
+ inferior *inf = find_inferior_ptid (event.target, event.ptid);
+ if (inf->needs_setup)
{
- std::string statstr = target_waitstatus_to_string (&event.ws);
-
- fprintf_unfiltered (gdb_stdlog,
- "infrun: target_wait %s, saving "
- "status for %d.%ld.%ld\n",
- statstr.c_str (),
- t->ptid.pid (),
- t->ptid.lwp (),
- t->ptid.tid ());
+ switch_to_thread_no_regs (t);
+ setup_inferior (0);
}
- /* Record for later. */
- save_waitstatus (t, &event.ws);
-
- sig = (event.ws.kind == TARGET_WAITKIND_STOPPED
- ? event.ws.value.sig : GDB_SIGNAL_0);
-
- if (displaced_step_fixup (t, sig) < 0)
+ if (event.ws.kind == TARGET_WAITKIND_STOPPED
+ && event.ws.value.sig == GDB_SIGNAL_0)
{
- /* Add it back to the step-over queue. */
- t->control.trap_expected = 0;
- thread_step_over_chain_enqueue (t);
+ /* We caught the event that we intended to catch, so
+ there's no event pending. */
+ t->suspend.waitstatus.kind = TARGET_WAITKIND_IGNORE;
+ t->suspend.waitstatus_pending_p = 0;
+
+ if (displaced_step_fixup (t, GDB_SIGNAL_0) < 0)
+ {
+ /* Add it back to the step-over queue. */
+ if (debug_infrun)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: displaced-step of %s "
+ "canceled: adding back to the "
+ "step-over queue\n",
+ target_pid_to_str (t->ptid).c_str ());
+ }
+ t->control.trap_expected = 0;
+ thread_step_over_chain_enqueue (t);
+ }
}
+ else
+ {
+ enum gdb_signal sig;
+ struct regcache *regcache;
- regcache = get_thread_regcache (t);
- t->suspend.stop_pc = regcache_read_pc (regcache);
+ if (debug_infrun)
+ {
+ std::string statstr = target_waitstatus_to_string (&event.ws);
- if (debug_infrun)
- {
- fprintf_unfiltered (gdb_stdlog,
- "infrun: saved stop_pc=%s for %s "
- "(currently_stepping=%d)\n",
- paddress (target_gdbarch (),
- t->suspend.stop_pc),
- target_pid_to_str (t->ptid).c_str (),
- currently_stepping (t));
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: target_wait %s, saving "
+ "status for %d.%ld.%ld\n",
+ statstr.c_str (),
+ t->ptid.pid (),
+ t->ptid.lwp (),
+ t->ptid.tid ());
+ }
+
+ /* Record for later. */
+ save_waitstatus (t, &event.ws);
+
+ sig = (event.ws.kind == TARGET_WAITKIND_STOPPED
+ ? event.ws.value.sig : GDB_SIGNAL_0);
+
+ if (displaced_step_fixup (t, sig) < 0)
+ {
+ /* Add it back to the step-over queue. */
+ t->control.trap_expected = 0;
+ thread_step_over_chain_enqueue (t);
+ }
+
+ regcache = get_thread_regcache (t);
+ t->suspend.stop_pc = regcache_read_pc (regcache);
+
+ if (debug_infrun)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: saved stop_pc=%s for %s "
+ "(currently_stepping=%d)\n",
+ paddress (target_gdbarch (),
+ t->suspend.stop_pc),
+ target_pid_to_str (t->ptid).c_str (),
+ currently_stepping (t));
+ }
}
}
}
}
}
-
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "infrun: stop_all_threads done\n");
}
/* Handle a TARGET_WAITKIND_NO_RESUMED event. */
{
if (target_can_async_p ())
{
- struct ui *ui;
int any_sync = 0;
- ALL_UIS (ui)
+ for (ui *ui : all_uis ())
{
if (ui->prompt_state == PROMPT_BLOCKED)
{
}
}
- /* Note however that we may find no resumed thread because the whole
- process exited meanwhile (thus updating the thread list results
- in an empty thread list). In this case we know we'll be getting
- a process exit event shortly. */
- for (inferior *inf : all_non_exited_inferiors (ecs->target))
- {
- thread_info *thread = any_live_thread_of_inferior (inf);
- if (thread == NULL)
- {
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog,
- "infrun: TARGET_WAITKIND_NO_RESUMED "
- "(expect process exit)\n");
- prepare_to_wait (ecs);
- return 1;
- }
- }
-
/* Go ahead and report the event. */
return 0;
}
}
}
- /* Mark the non-executing threads accordingly. In all-stop, all
- threads of all processes are stopped when we get any event
- reported. In non-stop mode, only the event thread stops. */
- {
- ptid_t mark_ptid;
-
- if (!target_is_non_stop_p ())
- mark_ptid = minus_one_ptid;
- else if (ecs->ws.kind == TARGET_WAITKIND_SIGNALLED
- || ecs->ws.kind == TARGET_WAITKIND_EXITED)
- {
- /* If we're handling a process exit in non-stop mode, even
- though threads haven't been deleted yet, one would think
- that there is nothing to do, as threads of the dead process
- will be soon deleted, and threads of any other process were
- left running. However, on some targets, threads survive a
- process exit event. E.g., for the "checkpoint" command,
- when the current checkpoint/fork exits, linux-fork.c
- automatically switches to another fork from within
- target_mourn_inferior, by associating the same
- inferior/thread to another fork. We haven't mourned yet at
- this point, but we must mark any threads left in the
- process as not-executing so that finish_thread_state marks
- them stopped (in the user's perspective) if/when we present
- the stop to the user. */
- mark_ptid = ptid_t (ecs->ptid.pid ());
- }
- else
- mark_ptid = ecs->ptid;
-
- set_executing (ecs->target, mark_ptid, false);
-
- /* Likewise the resumed flag. */
- set_resumed (ecs->target, mark_ptid, false);
- }
+ mark_non_executing_threads (ecs->target, ecs->ptid, ecs->ws);
switch (ecs->ws.kind)
{
watchpoints, for example, always appear in the bpstat. */
if (!bpstat_causes_stop (ecs->event_thread->control.stop_bpstat))
{
- int should_resume;
- int follow_child
+ bool follow_child
= (follow_fork_mode_string == follow_fork_mode_child);
ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0;
process_stratum_target *targ
= ecs->event_thread->inf->process_target ();
- should_resume = follow_fork ();
+ bool should_resume = follow_fork ();
/* Note that one of these may be an invalid pointer,
depending on detach_fork. */
}
}
+ /* This always returns the sal for the inner-most frame when we are in a
+ stack of inlined frames, even if GDB actually believes that it is in a
+ more outer frame. This is checked for below by calls to
+ inline_skipped_frames. */
stop_pc_sal = find_pc_line (ecs->event_thread->suspend.stop_pc, 0);
/* NOTE: tausq/2004-05-24: This if block used to be done before all
return;
}
+ bool refresh_step_info = true;
if ((ecs->event_thread->suspend.stop_pc == stop_pc_sal.pc)
&& (ecs->event_thread->current_line != stop_pc_sal.line
|| ecs->event_thread->current_symtab != stop_pc_sal.symtab))
{
- /* We are at the start of a different line. So stop. Note that
- we don't stop if we step into the middle of a different line.
- That is said to make things like for (;;) statements work
- better. */
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog,
- "infrun: stepped to a different line\n");
- end_stepping_range (ecs);
- return;
+ if (stop_pc_sal.is_stmt)
+ {
+ /* We are at the start of a different line. So stop. Note that
+ we don't stop if we step into the middle of a different line.
+ That is said to make things like for (;;) statements work
+ better. */
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: stepped to a different line\n");
+ end_stepping_range (ecs);
+ return;
+ }
+ else if (frame_id_eq (get_frame_id (get_current_frame ()),
+ ecs->event_thread->control.step_frame_id))
+ {
+ /* We are at the start of a different line, however, this line is
+ not marked as a statement, and we have not changed frame. We
+ ignore this line table entry, and continue stepping forward,
+ looking for a better place to stop. */
+ refresh_step_info = false;
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: stepped to a different line, but "
+ "it's not the start of a statement\n");
+ }
}
/* We aren't done stepping.
Optimize by setting the stepping range to the line.
(We might not be in the original line, but if we entered a
new line in mid-statement, we continue stepping. This makes
- things like for(;;) statements work better.) */
+ things like for(;;) statements work better.)
+
+ If we entered a SAL that indicates a non-statement line table entry,
+ then we update the stepping range, but we don't update the step info,
+ which includes things like the line number we are stepping away from.
+ This means we will stop when we find a line table entry that is marked
+ as is-statement, even if it matches the non-statement one we just
+ stepped into. */
ecs->event_thread->control.step_range_start = stop_pc_sal.pc;
ecs->event_thread->control.step_range_end = stop_pc_sal.end;
ecs->event_thread->control.may_range_step = 1;
- set_step_info (frame, stop_pc_sal);
+ if (refresh_step_info)
+ set_step_info (ecs->event_thread, frame, stop_pc_sal);
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: keep going\n");
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog,
"infrun: exception resume at %s\n",
- paddress (get_objfile_arch (probe->objfile),
+ paddress (probe->objfile->arch (),
handler));
bp = set_momentary_breakpoint_at_pc (get_frame_arch (frame),
/* Let callers know we don't want to wait for the inferior anymore. */
ecs->wait_some_more = 0;
- /* If all-stop, but the target is always in non-stop mode, stop all
+ /* If all-stop, but there exists a non-stop target, stop all
threads now that we're presenting the stop to the user. */
- if (!non_stop && target_is_non_stop_p ())
+ if (!non_stop && exists_non_stop_target ())
stop_all_threads ();
}
/* Save the pc before execution, to compare with pc after stop. */
ecs->event_thread->prev_pc
- = regcache_read_pc (get_thread_regcache (ecs->event_thread));
+ = regcache_read_pc_protected (get_thread_regcache (ecs->event_thread));
if (ecs->event_thread->control.trap_expected)
{