/* Target-struct-independent code to start (run) and stop an inferior
process.
- Copyright (C) 1986-2014 Free Software Foundation, Inc.
+ Copyright (C) 1986-2015 Free Software Foundation, Inc.
This file is part of GDB.
#include "defs.h"
#include "infrun.h"
-#include <string.h>
#include <ctype.h>
#include "symtab.h"
#include "frame.h"
#include "inferior.h"
-#include "exceptions.h"
#include "breakpoint.h"
#include "gdb_wait.h"
#include "gdbcore.h"
#include "main.h"
#include "dictionary.h"
#include "block.h"
-#include "gdb_assert.h"
#include "mi/mi-common.h"
#include "event-top.h"
#include "record.h"
#include "completer.h"
#include "target-descriptions.h"
#include "target-dcache.h"
+#include "terminal.h"
+#include "solist.h"
/* Prototypes for local functions */
static int follow_fork (void);
+static int follow_fork_inferior (int follow_child, int detach_fork);
+
+static void follow_inferior_reset_breakpoints (void);
+
static void set_schedlock_func (char *args, int from_tty,
struct cmd_list_element *c);
static int currently_stepping (struct thread_info *tp);
-static void xdb_handle_command (char *args, int from_tty);
-
void _initialize_infrun (void);
void nullify_last_target_wait_ptid (void);
static void insert_longjmp_resume_breakpoint (struct gdbarch *, CORE_ADDR);
+static int maybe_software_singlestep (struct gdbarch *gdbarch, CORE_ADDR pc);
+
/* 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. */
static struct cmd_list_element *stop_command;
-/* Function inferior was in as of last step command. */
-
-static struct symbol *step_start_function;
-
/* Nonzero if we want to give control to the user when we're notified
of shared library events by the dynamic linker. */
int stop_on_solib_events;
int stop_after_trap;
-/* 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.
- Thus this contains the return value from the called function (assuming
- values are returned in a register). */
-
-struct regcache *stop_registers;
-
/* Nonzero after stop if current stack frame should be printed. */
static int stop_print_frame;
void init_thread_stepping_state (struct thread_info *tss);
-static void init_infwait_state (void);
-
static const char follow_fork_mode_child[] = "child";
static const char follow_fork_mode_parent[] = "parent";
}
\f
+/* Handle changes to the inferior list based on the type of fork,
+ which process is being followed, and whether the other process
+ should be detached. On entry inferior_ptid must be the ptid of
+ 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)
+{
+ int has_vforked;
+ ptid_t parent_ptid, child_ptid;
+
+ has_vforked = (inferior_thread ()->pending_follow.kind
+ == TARGET_WAITKIND_VFORKED);
+ parent_ptid = inferior_ptid;
+ child_ptid = inferior_thread ()->pending_follow.value.related_pid;
+
+ if (has_vforked
+ && !non_stop /* Non-stop always resumes both branches. */
+ && (!target_is_async_p () || sync_execution)
+ && !(follow_child || detach_fork || sched_multi))
+ {
+ /* The parent stays blocked inside the vfork syscall until the
+ child execs or exits. If we don't let the child run, then
+ the parent stays blocked. If we're telling the parent to run
+ in the foreground, the user will not be able to ctrl-c to get
+ back the terminal, effectively hanging the debug session. */
+ fprintf_filtered (gdb_stderr, _("\
+Can not resume the parent process over vfork in the foreground while\n\
+holding the child stopped. Try \"set detach-on-fork\" or \
+\"set schedule-multiple\".\n"));
+ /* FIXME output string > 80 columns. */
+ return 1;
+ }
+
+ if (!follow_child)
+ {
+ /* Detach new forked process? */
+ if (detach_fork)
+ {
+ struct cleanup *old_chain;
+
+ /* Before detaching from the child, remove all breakpoints
+ from it. If we forked, then this has already been taken
+ care of by infrun.c. If we vforked however, any
+ breakpoint inserted in the parent is visible in the
+ child, even those added while stopped in a vfork
+ catchpoint. This will remove the breakpoints from the
+ parent also, but they'll be reinserted below. */
+ if (has_vforked)
+ {
+ /* Keep breakpoints list in sync. */
+ remove_breakpoints_pid (ptid_get_pid (inferior_ptid));
+ }
+
+ if (info_verbose || debug_infrun)
+ {
+ /* Ensure that we have a process ptid. */
+ ptid_t process_ptid = pid_to_ptid (ptid_get_pid (child_ptid));
+
+ target_terminal_ours_for_output ();
+ fprintf_filtered (gdb_stdlog,
+ _("Detaching after %s from child %s.\n"),
+ has_vforked ? "vfork" : "fork",
+ target_pid_to_str (process_ptid));
+ }
+ }
+ else
+ {
+ struct inferior *parent_inf, *child_inf;
+ struct cleanup *old_chain;
+
+ /* Add process to GDB's tables. */
+ child_inf = add_inferior (ptid_get_pid (child_ptid));
+
+ parent_inf = current_inferior ();
+ child_inf->attach_flag = parent_inf->attach_flag;
+ copy_terminal_info (child_inf, parent_inf);
+ child_inf->gdbarch = parent_inf->gdbarch;
+ copy_inferior_target_desc_info (child_inf, parent_inf);
+
+ old_chain = save_inferior_ptid ();
+ save_current_program_space ();
+
+ inferior_ptid = child_ptid;
+ add_thread (inferior_ptid);
+ child_inf->symfile_flags = SYMFILE_NO_READ;
+
+ /* If this is a vfork child, then the address-space is
+ shared with the parent. */
+ if (has_vforked)
+ {
+ child_inf->pspace = parent_inf->pspace;
+ child_inf->aspace = parent_inf->aspace;
+
+ /* The parent will be frozen until the child is done
+ with the shared region. Keep track of the
+ parent. */
+ child_inf->vfork_parent = parent_inf;
+ child_inf->pending_detach = 0;
+ parent_inf->vfork_child = child_inf;
+ parent_inf->pending_detach = 0;
+ }
+ else
+ {
+ child_inf->aspace = new_address_space ();
+ child_inf->pspace = add_program_space (child_inf->aspace);
+ child_inf->removable = 1;
+ set_current_program_space (child_inf->pspace);
+ clone_program_space (child_inf->pspace, parent_inf->pspace);
+
+ /* Let the shared library layer (e.g., solib-svr4) learn
+ about this new process, relocate the cloned exec, pull
+ in shared libraries, and install the solib event
+ breakpoint. If a "cloned-VM" event was propagated
+ better throughout the core, this wouldn't be
+ required. */
+ solib_create_inferior_hook (0);
+ }
+
+ do_cleanups (old_chain);
+ }
+
+ if (has_vforked)
+ {
+ struct inferior *parent_inf;
+
+ parent_inf = current_inferior ();
+
+ /* If we detached from the child, then we have to be careful
+ to not insert breakpoints in the parent until the child
+ is done with the shared memory region. However, if we're
+ staying attached to the child, then we can and should
+ insert breakpoints, so that we can debug it. A
+ subsequent child exec or exit is enough to know when does
+ the child stops using the parent's address space. */
+ parent_inf->waiting_for_vfork_done = detach_fork;
+ parent_inf->pspace->breakpoints_not_allowed = detach_fork;
+ }
+ }
+ else
+ {
+ /* Follow the child. */
+ struct inferior *parent_inf, *child_inf;
+ struct program_space *parent_pspace;
+
+ if (info_verbose || debug_infrun)
+ {
+ target_terminal_ours_for_output ();
+ fprintf_filtered (gdb_stdlog,
+ _("Attaching after %s %s to child %s.\n"),
+ target_pid_to_str (parent_ptid),
+ has_vforked ? "vfork" : "fork",
+ target_pid_to_str (child_ptid));
+ }
+
+ /* Add the new inferior first, so that the target_detach below
+ doesn't unpush the target. */
+
+ child_inf = add_inferior (ptid_get_pid (child_ptid));
+
+ parent_inf = current_inferior ();
+ child_inf->attach_flag = parent_inf->attach_flag;
+ copy_terminal_info (child_inf, parent_inf);
+ child_inf->gdbarch = parent_inf->gdbarch;
+ copy_inferior_target_desc_info (child_inf, parent_inf);
+
+ parent_pspace = parent_inf->pspace;
+
+ /* If we're vforking, we want to hold on to the parent until the
+ child exits or execs. At child exec or exit time we can
+ remove the old breakpoints from the parent and detach or
+ resume debugging it. Otherwise, detach the parent now; we'll
+ want to reuse it's program/address spaces, but we can't set
+ them to the child before removing breakpoints from the
+ parent, otherwise, the breakpoints module could decide to
+ remove breakpoints from the wrong process (since they'd be
+ assigned to the same address space). */
+
+ if (has_vforked)
+ {
+ gdb_assert (child_inf->vfork_parent == NULL);
+ gdb_assert (parent_inf->vfork_child == NULL);
+ child_inf->vfork_parent = parent_inf;
+ child_inf->pending_detach = 0;
+ parent_inf->vfork_child = child_inf;
+ parent_inf->pending_detach = detach_fork;
+ parent_inf->waiting_for_vfork_done = 0;
+ }
+ else if (detach_fork)
+ {
+ if (info_verbose || debug_infrun)
+ {
+ /* Ensure that we have a process ptid. */
+ ptid_t process_ptid = pid_to_ptid (ptid_get_pid (child_ptid));
+
+ target_terminal_ours_for_output ();
+ fprintf_filtered (gdb_stdlog,
+ _("Detaching after fork from "
+ "child %s.\n"),
+ target_pid_to_str (process_ptid));
+ }
+
+ target_detach (NULL, 0);
+ }
+
+ /* Note that the detach above makes PARENT_INF dangling. */
+
+ /* Add the child thread to the appropriate lists, and switch to
+ this new thread, before cloning the program space, and
+ informing the solib layer about this new process. */
+
+ inferior_ptid = child_ptid;
+ add_thread (inferior_ptid);
+
+ /* If this is a vfork child, then the address-space is shared
+ with the parent. If we detached from the parent, then we can
+ reuse the parent's program/address spaces. */
+ if (has_vforked || detach_fork)
+ {
+ child_inf->pspace = parent_pspace;
+ child_inf->aspace = child_inf->pspace->aspace;
+ }
+ else
+ {
+ child_inf->aspace = new_address_space ();
+ child_inf->pspace = add_program_space (child_inf->aspace);
+ child_inf->removable = 1;
+ child_inf->symfile_flags = SYMFILE_NO_READ;
+ set_current_program_space (child_inf->pspace);
+ clone_program_space (child_inf->pspace, parent_pspace);
+
+ /* Let the shared library layer (e.g., solib-svr4) learn
+ about this new process, relocate the cloned exec, pull in
+ shared libraries, and install the solib event breakpoint.
+ If a "cloned-VM" event was propagated better throughout
+ the core, this wouldn't be required. */
+ solib_create_inferior_hook (0);
+ }
+ }
+
+ return target_follow_fork (follow_child, detach_fork);
+}
+
/* Tell the target to follow the fork we're stopped at. Returns true
if the inferior should be resumed; false, if the target for some
reason decided it's best not to resume. */
parent = inferior_ptid;
child = tp->pending_follow.value.related_pid;
- /* Tell the target to do whatever is necessary to follow
- either parent or child. */
- if (target_follow_fork (follow_child, detach_fork))
+ /* Set up inferior(s) as specified by the caller, and tell the
+ target to do whatever is necessary to follow either parent
+ or child. */
+ if (follow_fork_inferior (follow_child, detach_fork))
{
/* Target refused to follow, or there's some other reason
we shouldn't resume. */
return should_resume;
}
-void
+static void
follow_inferior_reset_breakpoints (void)
{
struct thread_info *tp = inferior_thread ();
switch_to_thread (thread->ptid);
clear_proceed_status (0);
- proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT, 0);
+ proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT);
}
return 0;
if (debug_infrun || info_verbose)
{
- target_terminal_ours ();
+ target_terminal_ours_for_output ();
if (exec)
- fprintf_filtered (gdb_stdlog,
- "Detaching vfork parent process "
- "%d after child exec.\n",
- inf->vfork_parent->pid);
+ {
+ fprintf_filtered (gdb_stdlog,
+ _("Detaching vfork parent process "
+ "%d after child exec.\n"),
+ inf->vfork_parent->pid);
+ }
else
- fprintf_filtered (gdb_stdlog,
- "Detaching vfork parent process "
- "%d after child exit.\n",
- inf->vfork_parent->pid);
+ {
+ fprintf_filtered (gdb_stdlog,
+ _("Detaching vfork parent process "
+ "%d after child exit.\n"),
+ inf->vfork_parent->pid);
+ }
}
target_detach (NULL, 0);
/* EXECD_PATHNAME is assumed to be non-NULL. */
static void
-follow_exec (ptid_t pid, char *execd_pathname)
+follow_exec (ptid_t ptid, char *execd_pathname)
{
- struct thread_info *th = inferior_thread ();
+ struct thread_info *th, *tmp;
struct inferior *inf = current_inferior ();
+ int pid = ptid_get_pid (ptid);
/* This is an exec event that we actually wish to pay attention to.
Refresh our symbol table to the newly exec'd program, remove any
mark_breakpoints_out ();
- update_breakpoints_after_exec ();
-
- /* If there was one, it's gone now. We cannot truly step-to-next
- statement through an exec(). */
+ /* The target reports the exec event to the main thread, even if
+ some other thread does the exec, and even if the main thread was
+ stopped or already gone. We may still have non-leader threads of
+ the process on our list. E.g., on targets that don't have thread
+ exit events (like remote); or on native Linux in non-stop mode if
+ there were only two threads in the inferior and the non-leader
+ one is the one that execs (and nothing forces an update of the
+ thread list up to here). When debugging remotely, it's best to
+ avoid extra traffic, when possible, so avoid syncing the thread
+ list with the target, and instead go ahead and delete all threads
+ of the process but one that reported the event. Note this must
+ be done before calling update_breakpoints_after_exec, as
+ otherwise clearing the threads' resources would reference stale
+ thread breakpoints -- it may have been one of these threads that
+ stepped across the exec. We could just clear their stepping
+ states, but as long as we're iterating, might as well delete
+ them. Deleting them now rather than at the next user-visible
+ stop provides a nicer sequence of events for user and MI
+ notifications. */
+ ALL_THREADS_SAFE (th, tmp)
+ if (ptid_get_pid (th->ptid) == pid && !ptid_equal (th->ptid, ptid))
+ delete_thread (th->ptid);
+
+ /* We also need to clear any left over stale state for the
+ leader/event thread. E.g., if there was any step-resume
+ breakpoint or similar, it's gone now. We cannot truly
+ step-to-next statement through an exec(). */
+ th = inferior_thread ();
th->control.step_resume_breakpoint = NULL;
th->control.exception_resume_breakpoint = NULL;
+ th->control.single_step_breakpoints = NULL;
th->control.step_range_start = 0;
th->control.step_range_end = 0;
- /* The target reports the exec event to the main thread, even if
- some other thread does the exec, and even if the main thread was
- already stopped --- if debugging in non-stop mode, it's possible
- the user had the main thread held stopped in the previous image
- --- release it now. This is the same behavior as step-over-exec
- with scheduler-locking on in all-stop mode. */
+ /* The user may have had the main thread held stopped in the
+ previous image (e.g., schedlock on, or non-stop). Release
+ it now. */
th->stop_requested = 0;
+ update_breakpoints_after_exec ();
+
/* What is this a.out's name? */
printf_unfiltered (_("%s is executing new program: %s\n"),
target_pid_to_str (inferior_ptid),
breakpoint_init_inferior (inf_execd);
- if (gdb_sysroot && *gdb_sysroot)
+ if (*gdb_sysroot != '\0')
{
- char *name = alloca (strlen (gdb_sysroot)
- + strlen (execd_pathname)
- + 1);
+ char *name = exec_file_find (execd_pathname, NULL);
- strcpy (name, gdb_sysroot);
- strcat (name, execd_pathname);
- execd_pathname = name;
+ execd_pathname = alloca (strlen (name) + 1);
+ strcpy (execd_pathname, name);
+ xfree (name);
}
/* Reset the shared library package. This ensures that we get a
matically get reset there in the new process.). */
}
-/* Non-zero if we just simulating a single-step. This is needed
- because we cannot remove the breakpoints in the inferior process
- until after the `wait' in `wait_for_inferior'. */
-static int singlestep_breakpoints_inserted_p = 0;
-
-/* The thread we inserted single-step breakpoints for. */
-static ptid_t singlestep_ptid;
-
-/* PC when we started this single-step. */
-static CORE_ADDR singlestep_pc;
-
-/* Info about an instruction that is being stepped over. Invalid if
- ASPACE is NULL. */
+/* Info about an instruction that is being stepped over. */
struct step_over_info
{
- /* The instruction's address space. */
+ /* If we're stepping past a breakpoint, this is the address space
+ and address of the instruction the breakpoint is set at. We'll
+ skip inserting all breakpoints here. Valid iff ASPACE is
+ non-NULL. */
struct address_space *aspace;
-
- /* The instruction's address. */
CORE_ADDR address;
+
+ /* The instruction being stepped over triggers a nonsteppable
+ watchpoint. If true, we'll skip inserting watchpoints. */
+ int nonsteppable_watchpoint_p;
};
/* The step-over info of the location that is being stepped over.
stepping over. */
static void
-set_step_over_info (struct address_space *aspace, CORE_ADDR address)
+set_step_over_info (struct address_space *aspace, CORE_ADDR address,
+ int nonsteppable_watchpoint_p)
{
step_over_info.aspace = aspace;
step_over_info.address = address;
+ step_over_info.nonsteppable_watchpoint_p = nonsteppable_watchpoint_p;
}
/* Called when we're not longer stepping over a breakpoint / an
{
step_over_info.aspace = NULL;
step_over_info.address = 0;
+ step_over_info.nonsteppable_watchpoint_p = 0;
}
-/* See inferior.h. */
+/* See infrun.h. */
int
stepping_past_instruction_at (struct address_space *aspace,
step_over_info.address));
}
+/* See infrun.h. */
+
+int
+stepping_past_nonsteppable_watchpoint (void)
+{
+ return step_over_info.nonsteppable_watchpoint_p;
+}
+
+/* Returns true if step-over info is valid. */
+
+static int
+step_over_info_valid_p (void)
+{
+ return (step_over_info.aspace != NULL
+ || stepping_past_nonsteppable_watchpoint ());
+}
+
\f
/* Displaced stepping. */
return NULL;
}
+/* Return true if process PID has a thread doing a displaced step. */
+
+static int
+displaced_step_in_progress (int pid)
+{
+ struct displaced_step_inferior_state *displaced;
+
+ displaced = get_displaced_stepping_state (pid);
+ if (displaced != NULL && !ptid_equal (displaced->step_ptid, null_ptid))
+ return 1;
+
+ return 0;
+}
+
/* Add a new displaced stepping state for process PID to the displaced
stepping state list, or return a pointer to an already existing
entry, if it already exists. Never returns NULL. */
displaced_step_restore (displaced, displaced->step_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. */
+ switch_to_thread (event_ptid);
+
/* Did the instruction complete successfully? */
- if (signal == GDB_SIGNAL_TRAP)
+ if (signal == GDB_SIGNAL_TRAP
+ && !(target_stopped_by_watchpoint ()
+ && (gdbarch_have_nonsteppable_watchpoint (displaced->step_gdbarch)
+ || target_have_steppable_watchpoint)))
{
/* Fix up the resulting state. */
gdbarch_displaced_step_fixup (displaced->step_gdbarch,
regcache = get_thread_regcache (ptid);
actual_pc = regcache_read_pc (regcache);
aspace = get_regcache_aspace (regcache);
+ gdbarch = get_regcache_arch (regcache);
if (breakpoint_here_p (aspace, actual_pc))
{
displaced_step_prepare (ptid);
- gdbarch = get_regcache_arch (regcache);
-
if (debug_displaced)
{
CORE_ADDR actual_pc = regcache_read_pc (regcache);
/* Go back to what we were trying to do. */
step = currently_stepping (tp);
+ if (step)
+ step = maybe_software_singlestep (gdbarch, actual_pc);
+
if (debug_displaced)
fprintf_unfiltered (gdb_stdlog,
"displaced: breakpoint is gone: %s, step(%d)\n",
if (ptid_equal (inferior_ptid, old_ptid))
inferior_ptid = new_ptid;
- if (ptid_equal (singlestep_ptid, old_ptid))
- singlestep_ptid = new_ptid;
-
for (displaced = displaced_step_inferior_states;
displaced;
displaced = displaced->next)
static void
resume_cleanups (void *ignore)
{
+ if (!ptid_equal (inferior_ptid, null_ptid))
+ delete_single_step_breakpoints (inferior_thread ());
+
normal_stop ();
}
&& gdbarch_software_single_step (gdbarch, get_current_frame ()))
{
hw_step = 0;
- /* Do not pull these breakpoints until after a `wait' in
- `wait_for_inferior'. */
- singlestep_breakpoints_inserted_p = 1;
- singlestep_ptid = inferior_ptid;
- singlestep_pc = pc;
}
return hw_step;
}
+/* See infrun.h. */
+
ptid_t
user_visible_resume_ptid (int step)
{
- /* By default, resume all threads of all processes. */
- ptid_t resume_ptid = RESUME_ALL;
-
- /* Maybe resume only all threads of the current process. */
- if (!sched_multi && target_supports_multi_process ())
- {
- resume_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
- }
+ ptid_t resume_ptid;
- /* Maybe resume a single thread after all. */
if (non_stop)
{
/* With non-stop mode on, threads are always handled
resume_ptid = inferior_ptid;
}
else if ((scheduler_mode == schedlock_on)
- || (scheduler_mode == schedlock_step
- && (step || singlestep_breakpoints_inserted_p)))
+ || (scheduler_mode == schedlock_step && step))
{
- /* User-settable 'scheduler' mode requires solo thread resume. */
+ /* User-settable 'scheduler' mode requires solo thread
+ resume. */
resume_ptid = inferior_ptid;
}
+ else if (!sched_multi && target_supports_multi_process ())
+ {
+ /* Resume all threads of the current process (and none of other
+ processes). */
+ resume_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
+ }
+ else
+ {
+ /* Resume all threads of all processes. */
+ resume_ptid = RESUME_ALL;
+ }
- /* We may actually resume fewer threads at first, e.g., if a thread
- is stopped at a breakpoint that needs stepping-off, but that
- should not be visible to the user/frontend, and neither should
- the frontend/user be allowed to proceed any of the threads that
- happen to be stopped for internal run control handling, if a
- previous command wanted them resumed. */
return resume_ptid;
}
+/* Wrapper for target_resume, that handles infrun-specific
+ bookkeeping. */
+
+static void
+do_target_resume (ptid_t resume_ptid, int step, enum gdb_signal sig)
+{
+ struct thread_info *tp = inferior_thread ();
+
+ /* Install inferior's terminal modes. */
+ target_terminal_inferior ();
+
+ /* Avoid confusing the next resume, if the next stop/resume
+ happens to apply to another thread. */
+ tp->suspend.stop_signal = GDB_SIGNAL_0;
+
+ /* Advise target which signals may be handled silently.
+
+ If we have removed breakpoints because we are stepping over one
+ in-line (in any thread), we need to receive all signals to avoid
+ accidentally skipping a breakpoint during execution of a signal
+ handler.
+
+ Likewise if we're displaced stepping, otherwise a trap for a
+ breakpoint in a signal handler might be confused with the
+ displaced step finishing. We don't make the displaced_step_fixup
+ step distinguish the cases instead, because:
+
+ - a backtrace while stopped in the signal handler would show the
+ scratch pad as frame older than the signal handler, instead of
+ the real mainline code.
+
+ - when the thread is later resumed, the signal handler would
+ return to the scratch pad area, which would no longer be
+ valid. */
+ if (step_over_info_valid_p ()
+ || displaced_step_in_progress (ptid_get_pid (tp->ptid)))
+ target_pass_signals (0, NULL);
+ else
+ target_pass_signals ((int) GDB_SIGNAL_LAST, signal_pass);
+
+ target_resume (resume_ptid, step, sig);
+}
+
/* Resume the inferior, but allow a QUIT. This is useful if the user
wants to interrupt some lengthy single-stepping operation
(for child processes, the SIGINT goes to the inferior, and so
we get a SIGINT random_signal, but for remote debugging and perhaps
other targets, that's not true).
- STEP nonzero if we should step (zero to continue instead).
SIG is the signal to give the inferior (zero for none). */
void
-resume (int step, enum gdb_signal sig)
+resume (enum gdb_signal sig)
{
struct cleanup *old_cleanups = make_cleanup (resume_cleanups, 0);
struct regcache *regcache = get_current_regcache ();
CORE_ADDR pc = regcache_read_pc (regcache);
struct address_space *aspace = get_regcache_aspace (regcache);
ptid_t resume_ptid;
- /* From here on, this represents the caller's step vs continue
- request, while STEP represents what we'll actually request the
- target to do. STEP can decay from a step to a continue, if e.g.,
- we need to implement single-stepping with breakpoints (software
- single-step). When deciding whether "set scheduler-locking step"
- applies, it's the callers intention that counts. */
- const int entry_step = step;
+ /* This represents the user's step vs continue request. When
+ deciding whether "set scheduler-locking step" applies, it's the
+ user's intention that counts. */
+ const int user_step = tp->control.stepping_command;
+ /* This represents what we'll actually request the target to do.
+ This can decay from a step to a continue, if e.g., we need to
+ implement single-stepping with breakpoints (software
+ single-step). */
+ int step;
+
+ tp->stepped_breakpoint = 0;
QUIT;
+ /* Depends on stepped_breakpoint. */
+ step = currently_stepping (tp);
+
if (current_inferior ()->waiting_for_vfork_done)
{
/* Don't try to single-step a vfork parent that is waiting for
breakpoints can't be removed. So we have to test for it here. */
if (breakpoint_here_p (aspace, pc) == permanent_breakpoint_here)
{
- if (gdbarch_skip_permanent_breakpoint_p (gdbarch))
- gdbarch_skip_permanent_breakpoint (gdbarch, regcache);
+ if (sig != GDB_SIGNAL_0)
+ {
+ /* We have a signal to pass to the inferior. The resume
+ may, or may not take us to the signal handler. If this
+ is a step, we'll need to stop in the signal handler, if
+ there's one, (if the target supports stepping into
+ handlers), or in the next mainline instruction, if
+ there's no handler. If this is a continue, we need to be
+ sure to run the handler with all breakpoints inserted.
+ In all cases, set a breakpoint at the current address
+ (where the handler returns to), and once that breakpoint
+ is hit, resume skipping the permanent breakpoint. If
+ that breakpoint isn't hit, then we've stepped into the
+ signal handler (or hit some other event). We'll delete
+ the step-resume breakpoint then. */
+
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: resume: skipping permanent breakpoint, "
+ "deliver signal first\n");
+
+ clear_step_over_info ();
+ tp->control.trap_expected = 0;
+
+ if (tp->control.step_resume_breakpoint == NULL)
+ {
+ /* Set a "high-priority" step-resume, as we don't want
+ user breakpoints at PC to trigger (again) when this
+ hits. */
+ insert_hp_step_resume_breakpoint_at_frame (get_current_frame ());
+ gdb_assert (tp->control.step_resume_breakpoint->loc->permanent);
+
+ tp->step_after_step_resume_breakpoint = step;
+ }
+
+ insert_breakpoints ();
+ }
else
- error (_("\
-The program is stopped at a permanent breakpoint, but GDB does not know\n\
-how to step past a permanent breakpoint on this architecture. Try using\n\
-a command like `return' or `jump' to continue execution."));
+ {
+ /* There's no signal to pass, we can go ahead and skip the
+ permanent breakpoint manually. */
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: resume: skipping permanent breakpoint\n");
+ gdbarch_skip_permanent_breakpoint (gdbarch, regcache);
+ /* Update pc to reflect the new address from which we will
+ execute instructions. */
+ pc = regcache_read_pc (regcache);
+
+ if (step)
+ {
+ /* We've already advanced the PC, so the stepping part
+ is done. Now we need to arrange for a trap to be
+ reported to handle_inferior_event. Set a breakpoint
+ at the current PC, and run to it. Don't update
+ prev_pc, because if we end in
+ switch_back_to_stepped_thread, we want the "expected
+ thread advanced also" branch to be taken. IOW, we
+ don't want this thread to step further from PC
+ (overstep). */
+ gdb_assert (!step_over_info_valid_p ());
+ insert_single_step_breakpoint (gdbarch, aspace, pc);
+ insert_breakpoints ();
+
+ resume_ptid = user_visible_resume_ptid (user_step);
+ do_target_resume (resume_ptid, 0, GDB_SIGNAL_0);
+ discard_cleanups (old_cleanups);
+ return;
+ }
+ }
}
/* If we have a breakpoint to step over, make sure to do a single
event, displaced stepping breaks the vfork child similarly as single
step software breakpoint. */
if (use_displaced_stepping (gdbarch)
- && (tp->control.trap_expected
- || (step && gdbarch_software_single_step_p (gdbarch)))
+ && tp->control.trap_expected
+ && !step_over_info_valid_p ()
&& sig == GDB_SIGNAL_0
&& !current_inferior ()->waiting_for_vfork_done)
{
requests finish. The thread is not executing at this
point, and the call to set_executing will be made later.
But we need to call set_running here, since from the
- user/frontend's point of view, threads were set running.
- Unless we're calling an inferior function, as in that
- case we pretend the inferior doesn't run at all. */
- if (!tp->control.in_infcall)
- set_running (user_visible_resume_ptid (entry_step), 1);
+ user/frontend's point of view, threads were set running. */
+ set_running (user_visible_resume_ptid (user_step), 1);
discard_cleanups (old_cleanups);
return;
}
at the current address, deliver the signal without stepping, and
once we arrive back at the step-resume breakpoint, actually step
over the breakpoint we originally wanted to step over. */
- if (singlestep_breakpoints_inserted_p
- && tp->control.trap_expected && sig != GDB_SIGNAL_0)
+ if (thread_has_single_step_breakpoints_set (tp)
+ && sig != GDB_SIGNAL_0
+ && step_over_info_valid_p ())
{
/* If we have nested signals or a pending signal is delivered
immediately after a handler returns, might might already have
tp->step_after_step_resume_breakpoint = 1;
}
- remove_single_step_breakpoints ();
- singlestep_breakpoints_inserted_p = 0;
+ delete_single_step_breakpoints (tp);
clear_step_over_info ();
tp->control.trap_expected = 0;
/* If STEP is set, it's a request to use hardware stepping
facilities. But in that case, we should never
use singlestep breakpoint. */
- gdb_assert (!(singlestep_breakpoints_inserted_p && step));
+ gdb_assert (!(thread_has_single_step_breakpoints_set (tp) && step));
/* Decide the set of threads to ask the target to resume. Start
by assuming everything will be resumed, than narrow the set
by applying increasingly restricting conditions. */
- resume_ptid = user_visible_resume_ptid (entry_step);
+ resume_ptid = user_visible_resume_ptid (user_step);
/* Even if RESUME_PTID is a wildcard, and we end up resuming less
(e.g., we might need to step over a breakpoint), from the
user/frontend's point of view, all threads in RESUME_PTID are now
- running. Unless we're calling an inferior function, as in that
- case pretend we inferior doesn't run at all. */
- if (!tp->control.in_infcall)
- set_running (resume_ptid, 1);
+ running. */
+ set_running (resume_ptid, 1);
/* Maybe resume a single thread after all. */
- if ((step || singlestep_breakpoints_inserted_p)
+ if ((step || thread_has_single_step_breakpoints_set (tp))
&& tp->control.trap_expected)
{
/* We're allowing a thread to run past a breakpoint it has
resume_ptid = inferior_ptid;
}
- if (gdbarch_cannot_step_breakpoint (gdbarch))
- {
+ if (execution_direction != EXEC_REVERSE
+ && step && breakpoint_inserted_here_p (aspace, pc))
+ {
+ /* The only case we currently need to step a breakpoint
+ instruction is when we have a signal to deliver. See
+ handle_signal_stop where we handle random signals that could
+ take out us out of the stepping range. Normally, in that
+ case we end up continuing (instead of stepping) over the
+ signal handler with a breakpoint at PC, but there are cases
+ where we should _always_ single-step, even if we have a
+ step-resume breakpoint, like when a software watchpoint is
+ set. Assuming single-stepping and delivering a signal at the
+ same time would takes us to the signal handler, then we could
+ have removed the breakpoint at PC to step over it. However,
+ some hardware step targets (like e.g., Mac OS) can't step
+ into signal handlers, and for those, we need to leave the
+ breakpoint at PC inserted, as otherwise if the handler
+ recurses and executes PC again, it'll miss the breakpoint.
+ So we leave the breakpoint inserted anyway, but we need to
+ record that we tried to step a breakpoint instruction, so
+ that adjust_pc_after_break doesn't end up confused. */
+ gdb_assert (sig != GDB_SIGNAL_0);
+
+ tp->stepped_breakpoint = 1;
+
/* Most targets can step a breakpoint instruction, thus
executing it normally. But if this one cannot, just
continue and we will hit it anyway. */
- if (step && breakpoint_inserted_here_p (aspace, pc))
+ if (gdbarch_cannot_step_breakpoint (gdbarch))
step = 0;
}
if (debug_displaced
&& use_displaced_stepping (gdbarch)
- && tp->control.trap_expected)
+ && tp->control.trap_expected
+ && !step_over_info_valid_p ())
{
- struct regcache *resume_regcache = get_thread_regcache (resume_ptid);
+ struct regcache *resume_regcache = get_thread_regcache (tp->ptid);
struct gdbarch *resume_gdbarch = get_regcache_arch (resume_regcache);
CORE_ADDR actual_pc = regcache_read_pc (resume_regcache);
gdb_byte buf[4];
gdb_assert (pc_in_thread_step_range (pc, tp));
}
- /* Install inferior's terminal modes. */
- target_terminal_inferior ();
-
- /* Avoid confusing the next resume, if the next stop/resume
- happens to apply to another thread. */
- tp->suspend.stop_signal = GDB_SIGNAL_0;
-
- /* Advise target which signals may be handled silently. If we have
- removed breakpoints because we are stepping over one (which can
- happen only if we are not using displaced stepping), we need to
- receive all signals to avoid accidentally skipping a breakpoint
- during execution of a signal handler. */
- if ((step || singlestep_breakpoints_inserted_p)
- && tp->control.trap_expected
- && !use_displaced_stepping (gdbarch))
- target_pass_signals (0, NULL);
- else
- target_pass_signals ((int) GDB_SIGNAL_LAST, signal_pass);
-
- target_resume (resume_ptid, step, sig);
-
+ do_target_resume (resume_ptid, step, sig);
discard_cleanups (old_cleanups);
}
\f
tp->control.step_frame_id = null_frame_id;
tp->control.step_stack_frame_id = null_frame_id;
tp->control.step_over_calls = STEP_OVER_UNDEBUGGABLE;
+ tp->control.step_start_function = NULL;
tp->stop_requested = 0;
tp->control.stop_step = 0;
tp->control.proceed_to_finish = 0;
tp->control.command_interp = NULL;
+ tp->control.stepping_command = 0;
/* Discard any remaining commands or status from previous stop. */
bpstat_clear (&tp->control.stop_bpstat);
clear_step_over_info ();
observer_notify_about_to_proceed ();
-
- if (stop_registers)
- {
- regcache_xfree (stop_registers);
- stop_registers = NULL;
- }
}
/* Returns true if TP is still stopped at a breakpoint that needs
struct regcache *regcache = get_thread_regcache (tp->ptid);
if (breakpoint_here_p (get_regcache_aspace (regcache),
- regcache_read_pc (regcache)))
+ regcache_read_pc (regcache))
+ == ordinary_breakpoint_here)
return 1;
tp->stepping_over_breakpoint = 0;
we're about to do a step/next-like command to a thread. */
static int
-schedlock_applies (int step)
+schedlock_applies (struct thread_info *tp)
{
return (scheduler_mode == schedlock_on
|| (scheduler_mode == schedlock_step
- && step));
+ && tp->control.stepping_command));
}
/* Look a thread other than EXCEPT that has previously reported a
breakpoint event, and thus needs a step-over in order to make
- progress. Returns NULL is none is found. STEP indicates whether
- we're about to step the current thread, in order to decide whether
- "set scheduler-locking step" applies. */
+ progress. Returns NULL is none is found. */
static struct thread_info *
-find_thread_needs_step_over (int step, struct thread_info *except)
+find_thread_needs_step_over (struct thread_info *except)
{
struct thread_info *tp, *current;
/* If scheduler locking applies, we can avoid iterating over all
threads. */
- if (schedlock_applies (step))
+ if (schedlock_applies (except))
{
if (except != current
&& thread_still_needs_step_over (current))
You should call clear_proceed_status before calling proceed. */
void
-proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step)
+proceed (CORE_ADDR addr, enum gdb_signal siggnal)
{
struct regcache *regcache;
struct gdbarch *gdbarch;
pc = regcache_read_pc (regcache);
tp = inferior_thread ();
- if (step > 0)
- step_start_function = find_pc_function (pc);
- if (step < 0)
- stop_after_trap = 1;
-
/* Fill in with reasonable starting values. */
init_thread_stepping_state (tp);
if (addr == (CORE_ADDR) -1)
{
- if (pc == stop_pc && breakpoint_here_p (aspace, pc)
+ if (pc == stop_pc
+ && breakpoint_here_p (aspace, pc) == ordinary_breakpoint_here
&& execution_direction != EXEC_REVERSE)
/* There is a breakpoint at the address we will resume at,
step one instruction before inserting breakpoints so that
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog,
- "infrun: proceed (addr=%s, signal=%s, step=%d)\n",
+ "infrun: proceed (addr=%s, signal=%s)\n",
paddress (gdbarch, addr),
- gdb_signal_to_symbol_string (siggnal), step);
+ gdb_signal_to_symbol_string (siggnal));
if (non_stop)
/* In non-stop, each thread is handled individually. The context
Look for a thread other than the current (TP) that reported a
breakpoint hit and hasn't been resumed yet since. */
- step_over = find_thread_needs_step_over (step, tp);
+ step_over = find_thread_needs_step_over (tp);
if (step_over != NULL)
{
if (debug_infrun)
target_pid_to_str (step_over->ptid));
/* Store the prev_pc for the stepping thread too, needed by
- switch_back_to_stepping thread. */
+ switch_back_to_stepped_thread. */
tp->prev_pc = regcache_read_pc (get_current_regcache ());
switch_to_thread (step_over->ptid);
tp = step_over;
struct regcache *regcache = get_current_regcache ();
set_step_over_info (get_regcache_aspace (regcache),
- regcache_read_pc (regcache));
+ regcache_read_pc (regcache), 0);
}
else
clear_step_over_info ();
correctly when the inferior is stopped. */
tp->prev_pc = regcache_read_pc (get_current_regcache ());
- /* Reset to normal state. */
- init_infwait_state ();
-
/* Resume inferior. */
- resume (tp->control.trap_expected || step || bpstat_should_step (),
- tp->suspend.stop_signal);
+ resume (tp->suspend.stop_signal);
/* Wait for it to stop (if not standalone)
and in any case decode why it stopped, and act accordingly. */
target_last_wait_ptid = minus_one_ptid;
previous_inferior_ptid = inferior_ptid;
- init_infwait_state ();
/* Discard any skipped inlined frames. */
clear_inline_frame_state (minus_one_ptid);
-
- singlestep_ptid = null_ptid;
- singlestep_pc = 0;
}
\f
-/* This enum encodes possible reasons for doing a target_wait, so that
- wfi can call target_wait in one place. (Ultimately the call will be
- moved out of the infinite loop entirely.) */
-
-enum infwait_states
-{
- infwait_normal_state,
- infwait_step_watch_state,
- infwait_nonstep_watch_state
-};
-
-/* The PTID we'll do a target_wait on.*/
-ptid_t waiton_ptid;
-
-/* Current inferior wait state. */
-static enum infwait_states infwait_state;
-
/* Data to be passed around while handling an event. This data is
discarded between events. */
struct execution_control_state
const char *stop_func_name;
int wait_some_more;
- /* We were in infwait_step_watch_state or
- infwait_nonstep_watch_state state, and the thread reported an
- event. */
- int stepped_after_stopped_by_watchpoint;
-
/* True if the event thread hit the single-step breakpoint of
another thread. Thus the event doesn't cause a stop, the thread
needs to be single-stepped past the single-step breakpoint before
nullify_last_target_wait_ptid ();
}
-/* Callback for iterate_over_threads. */
+/* Delete the step resume, single-step and longjmp/exception resume
+ breakpoints of TP. */
-static int
-delete_step_resume_breakpoint_callback (struct thread_info *info, void *data)
+static void
+delete_thread_infrun_breakpoints (struct thread_info *tp)
{
- if (is_exited (info->ptid))
- return 0;
-
- delete_step_resume_breakpoint (info);
- delete_exception_resume_breakpoint (info);
- return 0;
+ delete_step_resume_breakpoint (tp);
+ delete_exception_resume_breakpoint (tp);
+ delete_single_step_breakpoints (tp);
}
-/* In all-stop, delete the step resume breakpoint of any thread that
- had one. In non-stop, delete the step resume breakpoint of the
- thread that just stopped. */
+/* If the target still has execution, call FUNC for each thread that
+ just stopped. In all-stop, that's all the non-exited threads; in
+ non-stop, that's the current thread, only. */
+
+typedef void (*for_each_just_stopped_thread_callback_func)
+ (struct thread_info *tp);
static void
-delete_step_thread_step_resume_breakpoint (void)
+for_each_just_stopped_thread (for_each_just_stopped_thread_callback_func func)
{
- if (!target_has_execution
- || ptid_equal (inferior_ptid, null_ptid))
- /* If the inferior has exited, we have already deleted the step
- resume breakpoints out of GDB's lists. */
+ if (!target_has_execution || ptid_equal (inferior_ptid, null_ptid))
return;
if (non_stop)
{
- /* If in non-stop mode, only delete the step-resume or
- longjmp-resume breakpoint of the thread that just stopped
- stepping. */
- struct thread_info *tp = inferior_thread ();
-
- delete_step_resume_breakpoint (tp);
- delete_exception_resume_breakpoint (tp);
+ /* If in non-stop mode, only the current thread stopped. */
+ func (inferior_thread ());
}
else
- /* In all-stop mode, delete all step-resume and longjmp-resume
- breakpoints of any thread that had them. */
- iterate_over_threads (delete_step_resume_breakpoint_callback, NULL);
+ {
+ struct thread_info *tp;
+
+ /* In all-stop mode, all threads have stopped. */
+ ALL_NON_EXITED_THREADS (tp)
+ {
+ func (tp);
+ }
+ }
+}
+
+/* Delete the step resume and longjmp/exception resume breakpoints of
+ the threads that just stopped. */
+
+static void
+delete_just_stopped_threads_infrun_breakpoints (void)
+{
+ for_each_just_stopped_thread (delete_thread_infrun_breakpoints);
+}
+
+/* Delete the single-step breakpoints of the threads that just
+ stopped. */
+
+static void
+delete_just_stopped_threads_single_step_breakpoints (void)
+{
+ for_each_just_stopped_thread (delete_single_step_breakpoints);
}
/* A cleanup wrapper. */
static void
-delete_step_thread_step_resume_breakpoint_cleanup (void *arg)
+delete_just_stopped_threads_infrun_breakpoints_cleanup (void *arg)
{
- delete_step_thread_step_resume_breakpoint ();
+ delete_just_stopped_threads_infrun_breakpoints ();
}
/* Pretty print the results of target_wait, for debugging purposes. */
is set. */
fprintf_unfiltered (tmp_stream,
- "infrun: target_wait (%d", ptid_get_pid (waiton_ptid));
+ "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 [%s],\n",
+ "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",
wait_for_inferior (void)
{
struct cleanup *old_cleanups;
+ struct cleanup *thread_state_chain;
if (debug_infrun)
fprintf_unfiltered
(gdb_stdlog, "infrun: wait_for_inferior ()\n");
- old_cleanups =
- make_cleanup (delete_step_thread_step_resume_breakpoint_cleanup, NULL);
+ old_cleanups
+ = make_cleanup (delete_just_stopped_threads_infrun_breakpoints_cleanup,
+ NULL);
+
+ /* If an error happens while handling the event, propagate GDB's
+ knowledge of the executing state to the frontend/user running
+ state. */
+ thread_state_chain = make_cleanup (finish_thread_state_cleanup, &minus_one_ptid);
while (1)
{
struct execution_control_state ecss;
struct execution_control_state *ecs = &ecss;
- struct cleanup *old_chain;
+ ptid_t waiton_ptid = minus_one_ptid;
memset (ecs, 0, sizeof (*ecs));
if (debug_infrun)
print_target_wait_results (waiton_ptid, ecs->ptid, &ecs->ws);
- /* If an error happens while handling the event, propagate GDB's
- knowledge of the executing state to the frontend/user running
- state. */
- old_chain = make_cleanup (finish_thread_state_cleanup, &minus_one_ptid);
-
/* Now figure out what to do with the result of the result. */
handle_inferior_event (ecs);
- /* No error, don't finish the state yet. */
- discard_cleanups (old_chain);
-
if (!ecs->wait_some_more)
break;
}
+ /* No error, don't finish the state yet. */
+ discard_cleanups (thread_state_chain);
+
do_cleanups (old_cleanups);
}
+/* Cleanup that reinstalls the readline callback handler, if the
+ target is running in the background. If while handling the target
+ event something triggered a secondary prompt, like e.g., a
+ pagination prompt, we'll have removed the callback handler (see
+ gdb_readline_wrapper_line). Need to do this as we go back to the
+ event loop, ready to process further input. Note this has no
+ effect if the handler hasn't actually been removed, because calling
+ rl_callback_handler_install resets the line buffer, thus losing
+ input. */
+
+static void
+reinstall_readline_callback_handler_cleanup (void *arg)
+{
+ if (!interpreter_async)
+ {
+ /* We're not going back to the top level event loop yet. Don't
+ install the readline callback, as it'd prep the terminal,
+ readline-style (raw, noecho) (e.g., --batch). We'll install
+ it the next time the prompt is displayed, when we're ready
+ for input. */
+ return;
+ }
+
+ if (async_command_editing_p && !sync_execution)
+ gdb_rl_callback_handler_reinstall ();
+}
+
/* Asynchronous version of wait_for_inferior. It is called by the
event loop whenever a change of state is detected on the file
descriptor corresponding to the target. It can be called more than
struct cleanup *ts_old_chain;
int was_sync = sync_execution;
int cmd_done = 0;
+ ptid_t waiton_ptid = minus_one_ptid;
memset (ecs, 0, sizeof (*ecs));
+ /* End up with readline processing input, if necessary. */
+ make_cleanup (reinstall_readline_callback_handler_cleanup, NULL);
+
/* We're handling a live event, so make sure we're doing live
debugging. If we're looking at traceframes while the target is
running, we're going to need to get back to that mode after
still for the thread which has thrown the exception. */
make_bpstat_clear_actions_cleanup ();
+ make_cleanup (delete_just_stopped_threads_infrun_breakpoints_cleanup, NULL);
+
/* Now figure out what to do with the result of the result. */
handle_inferior_event (ecs);
if (!ecs->wait_some_more)
{
- struct inferior *inf = find_inferior_pid (ptid_get_pid (ecs->ptid));
+ struct inferior *inf = find_inferior_ptid (ecs->ptid);
- delete_step_thread_step_resume_breakpoint ();
+ delete_just_stopped_threads_infrun_breakpoints ();
/* We may not find an inferior if this was a process exit. */
if (inf == NULL || inf->control.stop_soon == NO_STOP_QUIETLY)
void
init_thread_stepping_state (struct thread_info *tss)
{
+ tss->stepped_breakpoint = 0;
tss->stepping_over_breakpoint = 0;
+ tss->stepping_over_watchpoint = 0;
tss->step_after_step_resume_breakpoint = 0;
}
if (execution_direction == EXEC_REVERSE)
return;
+ /* If the target can tell whether the thread hit a SW breakpoint,
+ trust it. Targets that can tell also adjust the PC
+ themselves. */
+ if (target_supports_stopped_by_sw_breakpoint ())
+ return;
+
+ /* Note that relying on whether a breakpoint is planted in memory to
+ determine this can fail. E.g,. the breakpoint could have been
+ removed since. Or the thread could have been told to step an
+ instruction the size of a breakpoint instruction, and only
+ _after_ was a breakpoint inserted at its address. */
+
/* If this target does not decrement the PC after breakpoints, then
we have nothing to do. */
regcache = get_thread_regcache (ecs->ptid);
gdbarch = get_regcache_arch (regcache);
- decr_pc = target_decr_pc_after_break (gdbarch);
+ decr_pc = gdbarch_decr_pc_after_break (gdbarch);
if (decr_pc == 0)
return;
breakpoint would be. */
breakpoint_pc = regcache_read_pc (regcache) - decr_pc;
+ /* If the target can't tell whether a software breakpoint triggered,
+ fallback to figuring it out based on breakpoints we think were
+ inserted in the target, and on whether the thread was stepped or
+ continued. */
+
/* Check whether there actually is a software breakpoint inserted at
that location.
removed a breakpoint, but stop events for that breakpoint were
already queued and arrive later. To suppress those spurious
SIGTRAPs, we keep a list of such breakpoint locations for a bit,
- and retire them after a number of stop events are reported. */
+ and retire them after a number of stop events are reported. Note
+ this is an heuristic and can thus get confused. The real fix is
+ to get the "stopped by SW BP and needs adjustment" info out of
+ the target/kernel (and thus never reach here; see above). */
if (software_breakpoint_inserted_here_p (aspace, breakpoint_pc)
|| (non_stop && moribund_breakpoint_here_p (aspace, breakpoint_pc)))
{
The SIGTRAP can be due to a completed hardware single-step only if
- we didn't insert software single-step breakpoints
- - the thread to be examined is still the current thread
- this thread is currently being stepped
If any of these events did not occur, we must have stopped due
software breakpoint. In this case (prev_pc == breakpoint_pc),
we also need to back up to the breakpoint address. */
- if (singlestep_breakpoints_inserted_p
- || !ptid_equal (ecs->ptid, inferior_ptid)
+ if (thread_has_single_step_breakpoints_set (ecs->event_thread)
|| !currently_stepping (ecs->event_thread)
- || ecs->event_thread->prev_pc == breakpoint_pc)
+ || (ecs->event_thread->stepped_breakpoint
+ && ecs->event_thread->prev_pc == breakpoint_pc))
regcache_write_pc (regcache, breakpoint_pc);
do_cleanups (old_cleanups);
}
}
-static void
-init_infwait_state (void)
-{
- waiton_ptid = pid_to_ptid (-1);
- infwait_state = infwait_normal_state;
-}
-
static int
stepped_in_from (struct frame_info *frame, struct frame_id step_frame_id)
{
static enum stop_kind
get_inferior_stop_soon (ptid_t ptid)
{
- struct inferior *inf = find_inferior_pid (ptid_get_pid (ptid));
+ struct inferior *inf = find_inferior_ptid (ptid);
gdb_assert (inf != NULL);
return inf->control.stop_soon;
once). */
static void
-handle_inferior_event (struct execution_control_state *ecs)
+handle_inferior_event_1 (struct execution_control_state *ecs)
{
enum stop_kind stop_soon;
&& ecs->ws.kind != TARGET_WAITKIND_EXITED)
set_executing (ecs->ptid, 0);
- switch (infwait_state)
- {
- case infwait_normal_state:
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "infrun: infwait_normal_state\n");
- break;
-
- case infwait_step_watch_state:
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog,
- "infrun: infwait_step_watch_state\n");
-
- ecs->stepped_after_stopped_by_watchpoint = 1;
- break;
-
- case infwait_nonstep_watch_state:
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog,
- "infrun: infwait_nonstep_watch_state\n");
- insert_breakpoints ();
-
- /* FIXME-maybe: is this cleaner than setting a flag? Does it
- handle things like signals arriving and other things happening
- in combination correctly? */
- ecs->stepped_after_stopped_by_watchpoint = 1;
- break;
-
- default:
- internal_error (__FILE__, __LINE__, _("bad switch"));
- }
-
- infwait_state = infwait_normal_state;
- waiton_ptid = pid_to_ptid (-1);
-
switch (ecs->ws.kind)
{
case TARGET_WAITKIND_LOADED:
{
/* Loading of shared libraries might have changed breakpoint
addresses. Make sure new breakpoints are inserted. */
- if (stop_soon == NO_STOP_QUIETLY
- && !breakpoints_always_inserted_mode ())
+ if (stop_soon == NO_STOP_QUIETLY)
insert_breakpoints ();
- resume (0, GDB_SIGNAL_0);
+ resume (GDB_SIGNAL_0);
prepare_to_wait (ecs);
return;
}
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SPURIOUS\n");
if (!ptid_equal (ecs->ptid, inferior_ptid))
context_switch (ecs->ptid);
- resume (0, GDB_SIGNAL_0);
+ resume (GDB_SIGNAL_0);
prepare_to_wait (ecs);
return;
}
inferior_ptid = ecs->ptid;
- set_current_inferior (find_inferior_pid (ptid_get_pid (ecs->ptid)));
+ set_current_inferior (find_inferior_ptid (ecs->ptid));
set_current_program_space (current_inferior ()->pspace);
handle_vfork_child_exec_or_exit (0);
target_terminal_ours (); /* Must do this before mourn anyway. */
gdb_flush (gdb_stdout);
target_mourn_inferior ();
- singlestep_breakpoints_inserted_p = 0;
- cancel_single_step_breakpoints ();
stop_print_frame = 0;
stop_waiting (ecs);
return;
if (displaced && ptid_equal (displaced->step_ptid, ecs->ptid))
{
struct inferior *parent_inf
- = find_inferior_pid (ptid_get_pid (ecs->ptid));
+ = find_inferior_ptid (ecs->ptid);
struct regcache *child_regcache;
CORE_ADDR parent_pc;
detach_breakpoints (ecs->ws.value.related_pid);
}
- if (singlestep_breakpoints_inserted_p)
- {
- /* Pull the single step breakpoints out of the target. */
- remove_single_step_breakpoints ();
- singlestep_breakpoints_inserted_p = 0;
- }
+ delete_just_stopped_threads_single_step_breakpoints ();
/* In case the event is caught by a catchpoint, remember that
the event is to be followed at the next resume of the thread,
if (!ptid_equal (ecs->ptid, inferior_ptid))
context_switch (ecs->ptid);
- singlestep_breakpoints_inserted_p = 0;
- cancel_single_step_breakpoints ();
-
stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));
/* Do whatever is necessary to the parent branch of the vfork. */
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_NO_HISTORY\n");
/* Reverse execution: target ran out of history info. */
- /* Pull the single step breakpoints out of the target. */
- if (singlestep_breakpoints_inserted_p)
- {
- if (!ptid_equal (ecs->ptid, inferior_ptid))
- context_switch (ecs->ptid);
- remove_single_step_breakpoints ();
- singlestep_breakpoints_inserted_p = 0;
- }
+ delete_just_stopped_threads_single_step_breakpoints ();
stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));
observer_notify_no_history ();
stop_waiting (ecs);
}
}
+/* A wrapper around handle_inferior_event_1, which also makes sure
+ that all temporary struct value objects that were created during
+ the handling of the event get deleted at the end. */
+
+static void
+handle_inferior_event (struct execution_control_state *ecs)
+{
+ struct value *mark = value_mark ();
+
+ handle_inferior_event_1 (ecs);
+ /* Purge all temporary values created during the event handling,
+ as it could be a long time before we return to the command level
+ where such values would otherwise be purged. */
+ value_free_to_mark (mark);
+}
+
/* Come here when the program has stopped with a signal. */
static void
gdbarch = get_frame_arch (frame);
/* Pull the single step breakpoints out of the target. */
- if (singlestep_breakpoints_inserted_p)
+ if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP)
{
+ struct regcache *regcache;
+ struct address_space *aspace;
+ CORE_ADDR pc;
+
+ regcache = get_thread_regcache (ecs->ptid);
+ aspace = get_regcache_aspace (regcache);
+ pc = regcache_read_pc (regcache);
+
/* However, before doing so, if this single-step breakpoint was
actually for another thread, set this thread up for moving
past it. */
- if (!ptid_equal (ecs->ptid, singlestep_ptid)
- && ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP)
+ if (!thread_has_single_step_breakpoint_here (ecs->event_thread,
+ aspace, pc))
{
- struct regcache *regcache;
- struct address_space *aspace;
- CORE_ADDR pc;
-
- regcache = get_thread_regcache (ecs->ptid);
- aspace = get_regcache_aspace (regcache);
- pc = regcache_read_pc (regcache);
if (single_step_breakpoint_inserted_here_p (aspace, pc))
{
if (debug_infrun)
{
fprintf_unfiltered (gdb_stdlog,
- "infrun: [%s] hit step over single-step"
- " breakpoint of [%s]\n",
- target_pid_to_str (ecs->ptid),
- target_pid_to_str (singlestep_ptid));
+ "infrun: [%s] hit another thread's "
+ "single-step breakpoint\n",
+ target_pid_to_str (ecs->ptid));
}
ecs->hit_singlestep_breakpoint = 1;
}
}
-
- remove_single_step_breakpoints ();
- singlestep_breakpoints_inserted_p = 0;
+ else
+ {
+ if (debug_infrun)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: [%s] hit its "
+ "single-step breakpoint\n",
+ target_pid_to_str (ecs->ptid));
+ }
+ }
}
+ delete_just_stopped_threads_single_step_breakpoints ();
- if (ecs->stepped_after_stopped_by_watchpoint)
+ if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP
+ && ecs->event_thread->control.trap_expected
+ && ecs->event_thread->stepping_over_watchpoint)
stopped_by_watchpoint = 0;
else
stopped_by_watchpoint = watchpoints_triggered (&ecs->ws);
watchpoint expression. We do this by single-stepping the
target.
- It may not be necessary to disable the watchpoint to stop over
+ It may not be necessary to disable the watchpoint to step over
it. For example, the PA can (with some kernel cooperation)
single step over a watchpoint without disabling the watchpoint.
It is far more common to need to disable a watchpoint to step
the inferior over it. If we have non-steppable watchpoints,
we must disable the current watchpoint; it's simplest to
- disable all watchpoints and breakpoints. */
- int hw_step = 1;
-
- if (!target_have_steppable_watchpoint)
- {
- remove_breakpoints ();
- /* See comment in resume why we need to stop bypassing signals
- while breakpoints have been removed. */
- target_pass_signals (0, NULL);
- }
- /* Single step */
- hw_step = maybe_software_singlestep (gdbarch, stop_pc);
- target_resume (ecs->ptid, hw_step, GDB_SIGNAL_0);
- waiton_ptid = ecs->ptid;
- if (target_have_steppable_watchpoint)
- infwait_state = infwait_step_watch_state;
- else
- infwait_state = infwait_nonstep_watch_state;
- prepare_to_wait (ecs);
+ disable all watchpoints.
+
+ Any breakpoint at PC must also be stepped over -- if there's
+ one, it will have already triggered before the watchpoint
+ triggered, and we either already reported it to the user, or
+ it didn't cause a stop and we called keep_going. In either
+ case, if there was a breakpoint at PC, we must be trying to
+ step past it. */
+ ecs->event_thread->stepping_over_watchpoint = 1;
+ keep_going (ecs);
return;
}
ecs->event_thread->stepping_over_breakpoint = 0;
+ ecs->event_thread->stepping_over_watchpoint = 0;
bpstat_clear (&ecs->event_thread->control.stop_bpstat);
ecs->event_thread->control.stop_step = 0;
stop_print_frame = 1;
= !bpstat_explains_signal (ecs->event_thread->control.stop_bpstat,
ecs->event_thread->suspend.stop_signal);
+ /* Maybe this was a trap for a software breakpoint that has since
+ been removed. */
+ if (random_signal && target_stopped_by_sw_breakpoint ())
+ {
+ if (program_breakpoint_here_p (gdbarch, stop_pc))
+ {
+ struct regcache *regcache;
+ int decr_pc;
+
+ /* Re-adjust PC to what the program would see if GDB was not
+ debugging it. */
+ regcache = get_thread_regcache (ecs->event_thread->ptid);
+ decr_pc = gdbarch_decr_pc_after_break (gdbarch);
+ if (decr_pc != 0)
+ {
+ struct cleanup *old_cleanups = make_cleanup (null_cleanup, NULL);
+
+ if (record_full_is_used ())
+ record_full_gdb_operation_disable_set ();
+
+ regcache_write_pc (regcache, stop_pc + decr_pc);
+
+ do_cleanups (old_cleanups);
+ }
+ }
+ else
+ {
+ /* A delayed software breakpoint event. Ignore the trap. */
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: delayed software breakpoint "
+ "trap, ignoring\n");
+ random_signal = 0;
+ }
+ }
+
+ /* Maybe this was a trap for a hardware breakpoint/watchpoint that
+ has since been removed. */
+ if (random_signal && target_stopped_by_hw_breakpoint ())
+ {
+ /* A delayed hardware breakpoint event. Ignore the trap. */
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: delayed hardware breakpoint/watchpoint "
+ "trap, ignoring\n");
+ random_signal = 0;
+ }
+
/* If not, perhaps stepping/nexting can. */
if (random_signal)
random_signal = !(ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP
if (random_signal)
{
/* Signal not for debugging purposes. */
- int printed = 0;
- struct inferior *inf = find_inferior_pid (ptid_get_pid (ecs->ptid));
+ struct inferior *inf = find_inferior_ptid (ecs->ptid);
enum gdb_signal stop_signal = ecs->event_thread->suspend.stop_signal;
if (debug_infrun)
stopped_by_random_signal = 1;
- if (signal_print[ecs->event_thread->suspend.stop_signal])
- {
- /* The signal table tells us to print about this signal. */
- printed = 1;
- target_terminal_ours_for_output ();
- observer_notify_signal_received (ecs->event_thread->suspend.stop_signal);
- }
/* Always stop on signals if we're either just gaining control
of the program, or the user explicitly requested this thread
to remain stopped. */
stop_waiting (ecs);
return;
}
- /* If not going to stop, give terminal back
- if we took it away. */
- else if (printed)
- target_terminal_inferior ();
+
+ /* Notify observers the signal has "handle print" set. Note we
+ returned early above if stopping; normal_stop handles the
+ printing in that case. */
+ if (signal_print[ecs->event_thread->suspend.stop_signal])
+ {
+ /* The signal table tells us to print about this signal. */
+ target_terminal_ours_for_output ();
+ observer_notify_signal_received (ecs->event_thread->suspend.stop_signal);
+ target_terminal_inferior ();
+ }
/* Clear the signal if it should not be passed. */
if (signal_program[ecs->event_thread->suspend.stop_signal] == 0)
return;
}
- if (ecs->event_thread->control.step_range_end != 0
- && ecs->event_thread->suspend.stop_signal != GDB_SIGNAL_0
- && pc_in_thread_step_range (stop_pc, ecs->event_thread)
+ if (ecs->event_thread->suspend.stop_signal != GDB_SIGNAL_0
+ && (pc_in_thread_step_range (stop_pc, ecs->event_thread)
+ || ecs->event_thread->control.step_range_end == 1)
&& frame_id_eq (get_stack_frame_id (frame),
ecs->event_thread->control.step_stack_frame_id)
&& ecs->event_thread->control.step_resume_breakpoint == NULL)
"single-step range\n");
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;
keep_going (ecs);
is the third argument to the probe. */
arg_value = probe_safe_evaluate_at_pc (frame, 2);
if (arg_value)
- jmp_buf_pc = value_as_address (arg_value);
+ {
+ jmp_buf_pc = value_as_address (arg_value);
+ jmp_buf_pc = gdbarch_addr_bits_remove (gdbarch, jmp_buf_pc);
+ }
else if (!gdbarch_get_longjmp_target_p (gdbarch)
|| !gdbarch_get_longjmp_target (gdbarch,
frame, &jmp_buf_pc))
break;
}
+ /* If we stepped a permanent breakpoint and we had a high priority
+ step-resume breakpoint for the address we stepped, but we didn't
+ hit it, then we must have stepped into the signal handler. The
+ step-resume was only necessary to catch the case of _not_
+ stepping into the handler, so delete it, and fall through to
+ checking whether the step finished. */
+ if (ecs->event_thread->stepped_breakpoint)
+ {
+ struct breakpoint *sr_bp
+ = ecs->event_thread->control.step_resume_breakpoint;
+
+ if (sr_bp != NULL
+ && sr_bp->loc->permanent
+ && sr_bp->type == bp_hp_step_resume
+ && sr_bp->loc->address == ecs->event_thread->prev_pc)
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: stepped permanent breakpoint, stopped in "
+ "handler\n");
+ delete_step_resume_breakpoint (ecs->event_thread);
+ ecs->event_thread->step_after_step_resume_breakpoint = 0;
+ }
+ }
+
/* We come here if we hit a breakpoint but should not stop for it.
Possibly we also were stepping and should stop for that. So fall
through and test for stepping. But, if not stepping, do not
ecs->event_thread->control.step_stack_frame_id)
&& (!frame_id_eq (ecs->event_thread->control.step_stack_frame_id,
outer_frame_id)
- || step_start_function != find_pc_function (stop_pc))))
+ || (ecs->event_thread->control.step_start_function
+ != find_pc_function (stop_pc)))))
{
CORE_ADDR real_stop_pc;
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: stepped into subroutine\n");
- if ((ecs->event_thread->control.step_over_calls == STEP_OVER_NONE)
- || ((ecs->event_thread->control.step_range_end == 1)
- && in_prologue (gdbarch, ecs->event_thread->prev_pc,
- ecs->stop_func_start)))
+ if (ecs->event_thread->control.step_over_calls == STEP_OVER_NONE)
{
/* I presume that step_over_calls is only 0 when we're
supposed to be stepping at the assembly language level
("stepi"). Just stop. */
- /* Also, maybe we just did a "nexti" inside a prolog, so we
- thought it was a subroutine call but it was not. Stop as
- well. FENN */
/* And this works the same backward as frontward. MVS */
end_stepping_range (ecs);
return;
current thread is stepping. If some other thread not the
event thread is stepping, then it must be that scheduler
locking is not in effect. */
- if (schedlock_applies (0))
+ if (schedlock_applies (ecs->event_thread))
return 0;
/* Look for the stepping/nexting thread, and check if any other
stepping, then scheduler locking can't be in effect,
otherwise we wouldn't have resumed the current event
thread in the first place. */
- gdb_assert (!schedlock_applies (1));
+ gdb_assert (!schedlock_applies (tp));
stepping_thread = tp;
}
breakpoint forward, one instruction at a time,
overstepping. */
- if (gdbarch_software_single_step_p (gdbarch)
- && stop_pc != tp->prev_pc)
+ if (stop_pc != tp->prev_pc)
{
+ ptid_t resume_ptid;
+
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog,
"infrun: expected thread advanced also\n");
+ /* Clear the info of the previous step-over, as it's no
+ longer valid. It's what keep_going would do too, if
+ we called it. Must do this before trying to insert
+ the sss breakpoint, otherwise if we were previously
+ trying to step over this exact address in another
+ thread, the breakpoint ends up not installed. */
+ clear_step_over_info ();
+
insert_single_step_breakpoint (get_frame_arch (frame),
get_frame_address_space (frame),
stop_pc);
- singlestep_breakpoints_inserted_p = 1;
- ecs->event_thread->control.trap_expected = 1;
- singlestep_ptid = inferior_ptid;
- singlestep_pc = stop_pc;
- resume (0, GDB_SIGNAL_0);
+ resume_ptid = user_visible_resume_ptid (tp->control.stepping_command);
+ do_target_resume (resume_ptid,
+ currently_stepping (tp), GDB_SIGNAL_0);
prepare_to_wait (ecs);
}
else
return ((tp->control.step_range_end
&& tp->control.step_resume_breakpoint == NULL)
|| tp->control.trap_expected
+ || tp->stepped_breakpoint
|| bpstat_should_step ());
}
handle_step_into_function (struct gdbarch *gdbarch,
struct execution_control_state *ecs)
{
- struct symtab *s;
+ struct compunit_symtab *cust;
struct symtab_and_line stop_func_sal, sr_sal;
fill_in_stop_func (gdbarch, ecs);
- s = find_pc_symtab (stop_pc);
- if (s && s->language != language_asm)
+ cust = find_pc_compunit_symtab (stop_pc);
+ if (cust != NULL && compunit_language (cust) != language_asm)
ecs->stop_func_start = gdbarch_skip_prologue (gdbarch,
ecs->stop_func_start);
handle_step_into_function_backward (struct gdbarch *gdbarch,
struct execution_control_state *ecs)
{
- struct symtab *s;
+ struct compunit_symtab *cust;
struct symtab_and_line stop_func_sal;
fill_in_stop_func (gdbarch, ecs);
- s = find_pc_symtab (stop_pc);
- if (s && s->language != language_asm)
+ cust = find_pc_compunit_symtab (stop_pc);
+ if (cust != NULL && compunit_language (cust) != language_asm)
ecs->stop_func_start = gdbarch_skip_prologue (gdbarch,
ecs->stop_func_start);
struct frame_info *frame,
struct symbol *sym)
{
- volatile struct gdb_exception e;
-
- /* We want to ignore errors here. */
- TRY_CATCH (e, RETURN_MASK_ERROR)
+ TRY
{
struct symbol *vsym;
struct value *value;
inferior_thread ()->control.exception_resume_breakpoint = bp;
}
}
+ CATCH (e, RETURN_MASK_ERROR)
+ {
+ /* We want to ignore errors here. */
+ }
+ END_CATCH
}
/* A helper for check_exception_resume that sets an
check_exception_resume (struct execution_control_state *ecs,
struct frame_info *frame)
{
- volatile struct gdb_exception e;
struct bound_probe probe;
struct symbol *func;
if (!func)
return;
- TRY_CATCH (e, RETURN_MASK_ERROR)
+ TRY
{
const struct block *b;
struct block_iterator iter;
}
}
}
+ CATCH (e, RETURN_MASK_ERROR)
+ {
+ }
+ END_CATCH
}
static void
are supposed to pass through to the inferior. Simply
continue. */
discard_cleanups (old_cleanups);
- resume (currently_stepping (ecs->event_thread),
- ecs->event_thread->suspend.stop_signal);
+ resume (ecs->event_thread->suspend.stop_signal);
}
else
{
- volatile struct gdb_exception e;
struct regcache *regcache = get_current_regcache ();
+ int remove_bp;
+ int remove_wps;
/* Either the trap was not expected, but we are continuing
anyway (if we got a signal, the user asked it be passed to
(watchpoints, etc.) but the one we're stepping over, step one
instruction, and then re-insert the breakpoint when that step
is finished. */
- if ((ecs->hit_singlestep_breakpoint
- || thread_still_needs_step_over (ecs->event_thread))
- && !use_displaced_stepping (get_regcache_arch (regcache)))
+
+ remove_bp = (ecs->hit_singlestep_breakpoint
+ || thread_still_needs_step_over (ecs->event_thread));
+ remove_wps = (ecs->event_thread->stepping_over_watchpoint
+ && !target_have_steppable_watchpoint);
+
+ /* We can't use displaced stepping if we need to step past a
+ watchpoint. The instruction copied to the scratch pad would
+ still trigger the watchpoint. */
+ if (remove_bp
+ && (remove_wps
+ || !use_displaced_stepping (get_regcache_arch (regcache))))
{
set_step_over_info (get_regcache_aspace (regcache),
- regcache_read_pc (regcache));
+ regcache_read_pc (regcache), remove_wps);
}
+ else if (remove_wps)
+ set_step_over_info (NULL, 0, remove_wps);
else
clear_step_over_info ();
/* Stop stepping if inserting breakpoints fails. */
- TRY_CATCH (e, RETURN_MASK_ERROR)
+ TRY
{
insert_breakpoints ();
}
- if (e.reason < 0)
+ CATCH (e, RETURN_MASK_ERROR)
{
exception_print (gdb_stderr, e);
stop_waiting (ecs);
+ discard_cleanups (old_cleanups);
return;
}
+ END_CATCH
- ecs->event_thread->control.trap_expected
- = (ecs->event_thread->stepping_over_breakpoint
- || ecs->hit_singlestep_breakpoint);
+ ecs->event_thread->control.trap_expected = (remove_bp || remove_wps);
/* Do not deliver GDB_SIGNAL_TRAP (except when the user
explicitly specifies that such a signal should be delivered
ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0;
discard_cleanups (old_cleanups);
- resume (currently_stepping (ecs->event_thread),
- ecs->event_thread->suspend.stop_signal);
+ resume (ecs->event_thread->suspend.stop_signal);
}
prepare_to_wait (ecs);
}
/* We are done with the step range of a step/next/si/ni command.
- Called once for each n of a "step n" operation. Notify observers
- if not in the middle of doing a "step N" operation for N > 1. */
+ Called once for each n of a "step n" operation. */
static void
end_stepping_range (struct execution_control_state *ecs)
{
ecs->event_thread->control.stop_step = 1;
- if (!ecs->event_thread->step_multi)
- observer_notify_end_stepping_range ();
stop_waiting (ecs);
}
if (tp->control.stop_step
&& frame_id_eq (tp->control.step_frame_id,
get_frame_id (get_current_frame ()))
- && step_start_function == find_pc_function (stop_pc))
+ && tp->control.step_start_function == find_pc_function (stop_pc))
{
/* Finished step, just print source line. */
source_flag = SRC_LINE;
&& last.kind != TARGET_WAITKIND_NO_RESUMED)
make_cleanup (finish_thread_state_cleanup, &inferior_ptid);
+ /* As we're presenting a stop, and potentially removing breakpoints,
+ update the thread list so we can tell whether there are threads
+ running on the target. With target remote, for example, we can
+ only learn about new threads when we explicitly update the thread
+ list. Do this before notifying the interpreters about signal
+ stops, end of stepping ranges, etc., so that the "new thread"
+ output is emitted before e.g., "Program received signal FOO",
+ instead of after. */
+ update_thread_list ();
+
+ if (last.kind == TARGET_WAITKIND_STOPPED && stopped_by_random_signal)
+ observer_notify_signal_received (inferior_thread ()->suspend.stop_signal);
+
/* As with the notification of thread events, we want to delay
notifying the user that we've switched thread context until
the inferior actually stops.
printf_filtered (_("No unwaited-for children left.\n"));
}
- if (!breakpoints_always_inserted_mode () && target_has_execution)
+ /* Note: this depends on the update_thread_list call above. */
+ if (!breakpoints_should_be_inserted_now () && target_has_execution)
{
if (remove_breakpoints ())
{
if (stopped_by_random_signal)
disable_current_display ();
- /* Don't print a message if in the middle of doing a "step n"
- operation for n > 1 */
+ /* Notify observers if we finished a "step"-like command, etc. */
if (target_has_execution
&& last.kind != TARGET_WAITKIND_SIGNALLED
&& last.kind != TARGET_WAITKIND_EXITED
- && inferior_thread ()->step_multi
&& inferior_thread ()->control.stop_step)
- goto done;
+ {
+ /* But not if in the middle of doing a "step n" operation for
+ n > 1 */
+ if (inferior_thread ()->step_multi)
+ goto done;
+
+ observer_notify_end_stepping_range ();
+ }
target_terminal_ours ();
async_enable_stdin ();
if (has_stack_frames () && !stop_stack_dummy)
set_current_sal_from_frame (get_current_frame ());
- /* Let the user/frontend see the threads as stopped, but do nothing
- if the thread was running an infcall. We may be e.g., evaluating
- a breakpoint condition. In that case, the thread had state
- THREAD_RUNNING before the infcall, and shall remain set to
- running, all without informing the user/frontend about state
- transition changes. If this is actually a call command, then the
- thread was originally already stopped, so there's no state to
- finish either. */
- if (target_has_execution && inferior_thread ()->control.in_infcall)
+ /* Let the user/frontend see the threads as stopped, but defer to
+ call_function_by_hand if the thread finished an infcall
+ successfully. We may be e.g., evaluating a breakpoint condition.
+ In that case, the thread had state THREAD_RUNNING before the
+ infcall, and shall remain marked running, all without informing
+ the user/frontend about state transition changes. */
+ if (target_has_execution
+ && inferior_thread ()->control.in_infcall
+ && stop_stack_dummy == STOP_STACK_DUMMY)
discard_cleanups (old_chain);
else
do_cleanups (old_chain);
print_stop_event (&last);
}
- /* Save the function value return registers, if we care.
- We might be about to restore their previous contents. */
- if (inferior_thread ()->control.proceed_to_finish
- && execution_direction != EXEC_REVERSE)
- {
- /* This should not be necessary. */
- if (stop_registers)
- regcache_xfree (stop_registers);
-
- /* NB: The copy goes through to the target picking up the value of
- all the registers. */
- stop_registers = regcache_dup (get_current_regcache ());
- }
-
if (stop_stack_dummy == STOP_STACK_DUMMY)
{
/* Pop the empty frame that contains the stack dummy.
return return_val;
}
-static void
-xdb_handle_command (char *args, int from_tty)
-{
- char **argv;
- struct cleanup *old_chain;
-
- if (args == NULL)
- error_no_arg (_("xdb command"));
-
- /* Break the command line up into args. */
-
- argv = gdb_buildargv (args);
- old_chain = make_cleanup_freeargv (argv);
- if (argv[1] != (char *) NULL)
- {
- char *argBuf;
- int bufLen;
-
- bufLen = strlen (argv[0]) + 20;
- argBuf = (char *) xmalloc (bufLen);
- if (argBuf)
- {
- int validFlag = 1;
- enum gdb_signal oursig;
-
- oursig = gdb_signal_from_name (argv[0]);
- memset (argBuf, 0, bufLen);
- if (strcmp (argv[1], "Q") == 0)
- sprintf (argBuf, "%s %s", argv[0], "noprint");
- else
- {
- if (strcmp (argv[1], "s") == 0)
- {
- if (!signal_stop[oursig])
- sprintf (argBuf, "%s %s", argv[0], "stop");
- else
- sprintf (argBuf, "%s %s", argv[0], "nostop");
- }
- else if (strcmp (argv[1], "i") == 0)
- {
- if (!signal_program[oursig])
- sprintf (argBuf, "%s %s", argv[0], "pass");
- else
- sprintf (argBuf, "%s %s", argv[0], "nopass");
- }
- else if (strcmp (argv[1], "r") == 0)
- {
- if (!signal_print[oursig])
- sprintf (argBuf, "%s %s", argv[0], "print");
- else
- sprintf (argBuf, "%s %s", argv[0], "noprint");
- }
- else
- validFlag = 0;
- }
- if (validFlag)
- handle_command (argBuf, from_tty);
- else
- printf_filtered (_("Invalid signal handling flag.\n"));
- if (argBuf)
- xfree (argBuf);
- }
- }
- do_cleanups (old_chain);
-}
-
enum gdb_signal
gdb_signal_from_command (int num)
{
struct infcall_suspend_state
{
struct thread_suspend_state thread_suspend;
-#if 0 /* Currently unused and empty structures are not valid C. */
- struct inferior_suspend_state inferior_suspend;
-#endif
/* Other fields: */
CORE_ADDR stop_pc;
{
struct infcall_suspend_state *inf_state;
struct thread_info *tp = inferior_thread ();
-#if 0
- struct inferior *inf = current_inferior ();
-#endif
struct regcache *regcache = get_current_regcache ();
struct gdbarch *gdbarch = get_regcache_arch (regcache);
gdb_byte *siginfo_data = NULL;
}
inf_state->thread_suspend = tp->suspend;
-#if 0 /* Currently unused and empty structures are not valid C. */
- inf_state->inferior_suspend = inf->suspend;
-#endif
/* run_inferior_call will not use the signal due to its `proceed' call with
GDB_SIGNAL_0 anyway. */
restore_infcall_suspend_state (struct infcall_suspend_state *inf_state)
{
struct thread_info *tp = inferior_thread ();
-#if 0
- struct inferior *inf = current_inferior ();
-#endif
struct regcache *regcache = get_current_regcache ();
struct gdbarch *gdbarch = get_regcache_arch (regcache);
tp->suspend = inf_state->thread_suspend;
-#if 0 /* Currently unused and empty structures are not valid C. */
- inf->suspend = inf_state->inferior_suspend;
-#endif
stop_pc = inf_state->stop_pc;
return make_cleanup (restore_inferior_ptid, saved_ptid_ptr);
}
-/* See inferior.h. */
+/* See infrun.h. */
void
clear_exit_convenience_vars (void)
all signals cumulatively specified."));
set_cmd_completer (c, handle_completer);
- if (xdb_commands)
- {
- add_com ("lz", class_info, signals_info, _("\
-What debugger does when program gets various signals.\n\
-Specify a signal as argument to print info on that signal only."));
- add_com ("z", class_run, xdb_handle_command, _("\
-Specify how to handle a signal.\n\
-Args are signals and actions to apply to those signals.\n\
-Symbolic signals (e.g. SIGSEGV) are recommended but numeric signals\n\
-from 1-15 are allowed for compatibility with old versions of GDB.\n\
-Numeric ranges may be specified with the form LOW-HIGH (e.g. 1-5).\n\
-The special arg \"all\" is recognized to mean all signals except those\n\
-used by the debugger, typically SIGTRAP and SIGINT.\n\
-Recognized actions include \"s\" (toggles between stop and nostop),\n\
-\"r\" (toggles between print and noprint), \"i\" (toggles between pass and \
-nopass), \"Q\" (noprint)\n\
-Stop means reenter debugger if this signal happens (implies print).\n\
-Print means print a message if this signal happens.\n\
-Pass means let program see this signal; otherwise program doesn't know.\n\
-Ignore is a synonym for nopass and noignore is a synonym for pass.\n\
-Pass and Stop may be combined."));
- }
-
if (!dbx_commands)
stop_command = add_cmd ("stop", class_obscure,
not_just_help_class_command, _("\
Show mode for locking scheduler during execution."), _("\
off == no locking (threads may preempt at any time)\n\
on == full locking (no thread except the current thread may run)\n\
-step == scheduler locked during every single-step operation.\n\
- In this mode, no other thread may run during a step command.\n\
- Other threads may run while stepping over a function call ('next')."),
+step == scheduler locked during stepping commands (step, next, stepi, nexti).\n\
+ In this mode, other threads may run during other commands."),
set_schedlock_func, /* traps on target vector */
show_scheduler_mode,
&setlist, &showlist);