Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995,
1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
- 2008, 2009 Free Software Foundation, Inc.
+ 2008, 2009, 2010 Free Software Foundation, Inc.
This file is part of GDB.
#include "record.h"
#include "inline-frame.h"
#include "jit.h"
+#include "tracepoint.h"
/* Prototypes for local functions */
static int restore_selected_frame (void *);
-static void build_infrun (void);
-
static int follow_fork (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 int currently_stepping_or_nexting_callback (struct thread_info *tp,
void *data);
static int prepare_to_proceed (int);
+static void print_exited_reason (int exitstatus);
+
+static void print_signal_exited_reason (enum target_signal siggnal);
+
+static void print_no_history_reason (void);
+
+static void print_signal_received_reason (enum target_signal siggnal);
+
+static void print_end_stepping_range_reason (void);
+
void _initialize_infrun (void);
void nullify_last_target_wait_ptid (void);
fprintf_filtered (file, _("Displace stepping debugging is %s.\n"), value);
}
-static int debug_infrun = 0;
+int debug_infrun = 0;
static void
show_debug_infrun (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
#define SOLIB_IN_DYNAMIC_LINKER(pid,pc) 0
#endif
+/* "Observer mode" is somewhat like a more extreme version of
+ non-stop, in which all GDB operations that might affect the
+ target's execution have been disabled. */
-/* Convert the #defines into values. This is temporary until wfi control
- flow is completely sorted out. */
+static int non_stop_1 = 0;
-#ifndef CANNOT_STEP_HW_WATCHPOINTS
-#define CANNOT_STEP_HW_WATCHPOINTS 0
-#else
-#undef CANNOT_STEP_HW_WATCHPOINTS
-#define CANNOT_STEP_HW_WATCHPOINTS 1
-#endif
+int observer_mode = 0;
+static int observer_mode_1 = 0;
+
+static void
+set_observer_mode (char *args, int from_tty,
+ struct cmd_list_element *c)
+{
+ extern int pagination_enabled;
+
+ if (target_has_execution)
+ {
+ observer_mode_1 = observer_mode;
+ error (_("Cannot change this setting while the inferior is running."));
+ }
+
+ observer_mode = observer_mode_1;
+
+ may_write_registers = !observer_mode;
+ may_write_memory = !observer_mode;
+ may_insert_breakpoints = !observer_mode;
+ may_insert_tracepoints = !observer_mode;
+ /* We can insert fast tracepoints in or out of observer mode,
+ but enable them if we're going into this mode. */
+ if (observer_mode)
+ may_insert_fast_tracepoints = 1;
+ may_stop = !observer_mode;
+ update_target_permissions ();
+
+ /* Going *into* observer mode we must force non-stop, then
+ going out we leave it that way. */
+ if (observer_mode)
+ {
+ target_async_permitted = 1;
+ pagination_enabled = 0;
+ non_stop = non_stop_1 = 1;
+ }
+
+ if (from_tty)
+ printf_filtered (_("Observer mode is now %s.\n"),
+ (observer_mode ? "on" : "off"));
+}
+
+static void
+show_observer_mode (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ fprintf_filtered (file, _("Observer mode is %s.\n"), value);
+}
+
+/* This updates the value of observer mode based on changes in
+ permissions. Note that we are deliberately ignoring the values of
+ may-write-registers and may-write-memory, since the user may have
+ reason to enable these during a session, for instance to turn on a
+ debugging-related global. */
+
+void
+update_observer_mode (void)
+{
+ int newval;
+
+ newval = (!may_insert_breakpoints
+ && !may_insert_tracepoints
+ && may_insert_fast_tracepoints
+ && !may_stop
+ && non_stop);
+
+ /* Let the user know if things change. */
+ if (newval != observer_mode)
+ printf_filtered (_("Observer mode is now %s.\n"),
+ (newval ? "on" : "off"));
+
+ observer_mode = observer_mode_1 = newval;
+}
/* Tables of how to react to signals; the user sets them. */
/* Nonzero if we want to give control to the user when we're notified
of shared library events by the dynamic linker. */
-static int stop_on_solib_events;
+int stop_on_solib_events;
static void
show_stop_on_solib_events (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
static void
follow_exec (ptid_t pid, char *execd_pathname)
{
- struct target_ops *tgt;
struct thread_info *th = inferior_thread ();
struct inferior *inf = current_inferior ();
char *name = alloca (strlen (gdb_sysroot)
+ strlen (execd_pathname)
+ 1);
+
strcpy (name, gdb_sysroot);
strcat (name, execd_pathname);
execd_pathname = name;
if (follow_exec_mode_string == follow_exec_mode_new)
{
struct program_space *pspace;
- struct inferior *new_inf;
/* The user wants to keep the old inferior and program spaces
around. Create a new fresh one, and switch to it. */
/* That a.out is now the one to use. */
exec_file_attach (execd_pathname, 0);
- /* Load the main file's symbols. */
- symbol_file_add_main (execd_pathname, 0);
+ /* SYMFILE_DEFER_BP_RESET is used as the proper displacement for PIE
+ (Position Independent Executable) main symbol file will get applied by
+ solib_create_inferior_hook below. breakpoint_re_set would fail to insert
+ the breakpoints with the zero displacement. */
+
+ symbol_file_add (execd_pathname, SYMFILE_MAINLINE | SYMFILE_DEFER_BP_RESET,
+ NULL, 0);
+
+ set_initial_language ();
#ifdef SOLIB_CREATE_INFERIOR_HOOK
SOLIB_CREATE_INFERIOR_HOOK (PIDGET (inferior_ptid));
#else
- solib_create_inferior_hook ();
+ solib_create_inferior_hook (0);
#endif
jit_inferior_created_hook ();
+ breakpoint_re_set ();
+
/* Reinsert all breakpoints. (Those which were symbolic have
been reset to the proper address in the new a.out, thanks
to symbol_file_command...) */
displaced step operation on it. See displaced_step_prepare and
displaced_step_fixup for details. */
-/* If this is not null_ptid, this is the thread carrying out a
- displaced single-step. This thread's state will require fixing up
- once it has completed its step. */
-static ptid_t displaced_step_ptid;
-
struct displaced_step_request
{
ptid_t ptid;
struct displaced_step_request *next;
};
-/* A queue of pending displaced stepping requests. */
-struct displaced_step_request *displaced_step_request_queue;
+/* Per-inferior displaced stepping state. */
+struct displaced_step_inferior_state
+{
+ /* Pointer to next in linked list. */
+ struct displaced_step_inferior_state *next;
+
+ /* The process this displaced step state refers to. */
+ int pid;
+
+ /* A queue of pending displaced stepping requests. One entry per
+ thread that needs to do a displaced step. */
+ struct displaced_step_request *step_request_queue;
+
+ /* If this is not null_ptid, this is the thread carrying out a
+ displaced single-step in process PID. This thread's state will
+ require fixing up once it has completed its step. */
+ ptid_t step_ptid;
-/* The architecture the thread had when we stepped it. */
-static struct gdbarch *displaced_step_gdbarch;
+ /* The architecture the thread had when we stepped it. */
+ struct gdbarch *step_gdbarch;
-/* The closure provided gdbarch_displaced_step_copy_insn, to be used
- for post-step cleanup. */
-static struct displaced_step_closure *displaced_step_closure;
+ /* The closure provided gdbarch_displaced_step_copy_insn, to be used
+ for post-step cleanup. */
+ struct displaced_step_closure *step_closure;
+
+ /* The address of the original instruction, and the copy we
+ made. */
+ CORE_ADDR step_original, step_copy;
+
+ /* Saved contents of copy area. */
+ gdb_byte *step_saved_copy;
+};
-/* The address of the original instruction, and the copy we made. */
-static CORE_ADDR displaced_step_original, displaced_step_copy;
+/* The list of states of processes involved in displaced stepping
+ presently. */
+static struct displaced_step_inferior_state *displaced_step_inferior_states;
-/* Saved contents of copy area. */
-static gdb_byte *displaced_step_saved_copy;
+/* Get the displaced stepping state of process PID. */
+
+static struct displaced_step_inferior_state *
+get_displaced_stepping_state (int pid)
+{
+ struct displaced_step_inferior_state *state;
+
+ for (state = displaced_step_inferior_states;
+ state != NULL;
+ state = state->next)
+ if (state->pid == pid)
+ return state;
+
+ return NULL;
+}
+
+/* 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. */
+
+static struct displaced_step_inferior_state *
+add_displaced_stepping_state (int pid)
+{
+ struct displaced_step_inferior_state *state;
+
+ for (state = displaced_step_inferior_states;
+ state != NULL;
+ state = state->next)
+ if (state->pid == pid)
+ return state;
+
+ state = xcalloc (1, sizeof (*state));
+ state->pid = pid;
+ state->next = displaced_step_inferior_states;
+ displaced_step_inferior_states = state;
+
+ return state;
+}
+
+/* Remove the displaced stepping state of process PID. */
+
+static void
+remove_displaced_stepping_state (int pid)
+{
+ struct displaced_step_inferior_state *it, **prev_next_p;
+
+ gdb_assert (pid != 0);
+
+ it = displaced_step_inferior_states;
+ prev_next_p = &displaced_step_inferior_states;
+ while (it)
+ {
+ if (it->pid == pid)
+ {
+ *prev_next_p = it->next;
+ xfree (it);
+ return;
+ }
+
+ prev_next_p = &it->next;
+ it = *prev_next_p;
+ }
+}
+
+static void
+infrun_inferior_exit (struct inferior *inf)
+{
+ remove_displaced_stepping_state (inf->pid);
+}
/* Enum strings for "set|show displaced-stepping". */
/* Clean out any stray displaced stepping state. */
static void
-displaced_step_clear (void)
+displaced_step_clear (struct displaced_step_inferior_state *displaced)
{
/* Indicate that there is no cleanup pending. */
- displaced_step_ptid = null_ptid;
+ displaced->step_ptid = null_ptid;
- if (displaced_step_closure)
+ if (displaced->step_closure)
{
- gdbarch_displaced_step_free_closure (displaced_step_gdbarch,
- displaced_step_closure);
- displaced_step_closure = NULL;
+ gdbarch_displaced_step_free_closure (displaced->step_gdbarch,
+ displaced->step_closure);
+ displaced->step_closure = NULL;
}
}
static void
-displaced_step_clear_cleanup (void *ignore)
+displaced_step_clear_cleanup (void *arg)
{
- displaced_step_clear ();
+ struct displaced_step_inferior_state *state = arg;
+
+ displaced_step_clear (state);
}
/* Dump LEN bytes at BUF in hex to FILE, followed by a newline. */
CORE_ADDR original, copy;
ULONGEST len;
struct displaced_step_closure *closure;
+ struct displaced_step_inferior_state *displaced;
/* We should never reach this function if the architecture does not
support displaced stepping. */
gdb_assert (gdbarch_displaced_step_copy_insn_p (gdbarch));
- /* For the first cut, we're displaced stepping one thread at a
- time. */
+ /* We have to displaced step one thread at a time, as we only have
+ access to a single scratch space per inferior. */
+
+ displaced = add_displaced_stepping_state (ptid_get_pid (ptid));
- if (!ptid_equal (displaced_step_ptid, null_ptid))
+ if (!ptid_equal (displaced->step_ptid, null_ptid))
{
/* Already waiting for a displaced step to finish. Defer this
request and place in queue. */
new_req->ptid = ptid;
new_req->next = NULL;
- if (displaced_step_request_queue)
+ if (displaced->step_request_queue)
{
- for (req = displaced_step_request_queue;
+ for (req = displaced->step_request_queue;
req && req->next;
req = req->next)
;
req->next = new_req;
}
else
- displaced_step_request_queue = new_req;
+ displaced->step_request_queue = new_req;
return 0;
}
target_pid_to_str (ptid));
}
- displaced_step_clear ();
+ displaced_step_clear (displaced);
old_cleanups = save_inferior_ptid ();
inferior_ptid = ptid;
len = gdbarch_max_insn_length (gdbarch);
/* Save the original contents of the copy area. */
- displaced_step_saved_copy = xmalloc (len);
+ displaced->step_saved_copy = xmalloc (len);
ignore_cleanups = make_cleanup (free_current_contents,
- &displaced_step_saved_copy);
- read_memory (copy, displaced_step_saved_copy, len);
+ &displaced->step_saved_copy);
+ read_memory (copy, displaced->step_saved_copy, len);
if (debug_displaced)
{
fprintf_unfiltered (gdb_stdlog, "displaced: saved %s: ",
paddress (gdbarch, copy));
- displaced_step_dump_bytes (gdb_stdlog, displaced_step_saved_copy, len);
+ displaced_step_dump_bytes (gdb_stdlog,
+ displaced->step_saved_copy,
+ len);
};
closure = gdbarch_displaced_step_copy_insn (gdbarch,
/* Save the information we need to fix things up if the step
succeeds. */
- displaced_step_ptid = ptid;
- displaced_step_gdbarch = gdbarch;
- displaced_step_closure = closure;
- displaced_step_original = original;
- displaced_step_copy = copy;
+ displaced->step_ptid = ptid;
+ displaced->step_gdbarch = gdbarch;
+ displaced->step_closure = closure;
+ displaced->step_original = original;
+ displaced->step_copy = copy;
- make_cleanup (displaced_step_clear_cleanup, 0);
+ make_cleanup (displaced_step_clear_cleanup, displaced);
/* Resume execution at the copy. */
regcache_write_pc (regcache, copy);
write_memory_ptid (ptid_t ptid, CORE_ADDR memaddr, const gdb_byte *myaddr, int len)
{
struct cleanup *ptid_cleanup = save_inferior_ptid ();
+
inferior_ptid = ptid;
write_memory (memaddr, myaddr, len);
do_cleanups (ptid_cleanup);
displaced_step_fixup (ptid_t event_ptid, enum target_signal signal)
{
struct cleanup *old_cleanups;
+ struct displaced_step_inferior_state *displaced
+ = get_displaced_stepping_state (ptid_get_pid (event_ptid));
+
+ /* Was any thread of this process doing a displaced step? */
+ if (displaced == NULL)
+ return;
/* Was this event for the pid we displaced? */
- if (ptid_equal (displaced_step_ptid, null_ptid)
- || ! ptid_equal (displaced_step_ptid, event_ptid))
+ if (ptid_equal (displaced->step_ptid, null_ptid)
+ || ! ptid_equal (displaced->step_ptid, event_ptid))
return;
- old_cleanups = make_cleanup (displaced_step_clear_cleanup, 0);
+ old_cleanups = make_cleanup (displaced_step_clear_cleanup, displaced);
/* Restore the contents of the copy area. */
{
- ULONGEST len = gdbarch_max_insn_length (displaced_step_gdbarch);
- write_memory_ptid (displaced_step_ptid, displaced_step_copy,
- displaced_step_saved_copy, len);
+ ULONGEST len = gdbarch_max_insn_length (displaced->step_gdbarch);
+
+ write_memory_ptid (displaced->step_ptid, displaced->step_copy,
+ displaced->step_saved_copy, len);
if (debug_displaced)
fprintf_unfiltered (gdb_stdlog, "displaced: restored %s\n",
- paddress (displaced_step_gdbarch,
- displaced_step_copy));
+ paddress (displaced->step_gdbarch,
+ displaced->step_copy));
}
/* Did the instruction complete successfully? */
if (signal == TARGET_SIGNAL_TRAP)
{
/* Fix up the resulting state. */
- gdbarch_displaced_step_fixup (displaced_step_gdbarch,
- displaced_step_closure,
- displaced_step_original,
- displaced_step_copy,
- get_thread_regcache (displaced_step_ptid));
+ gdbarch_displaced_step_fixup (displaced->step_gdbarch,
+ displaced->step_closure,
+ displaced->step_original,
+ displaced->step_copy,
+ get_thread_regcache (displaced->step_ptid));
}
else
{
relocate the PC. */
struct regcache *regcache = get_thread_regcache (event_ptid);
CORE_ADDR pc = regcache_read_pc (regcache);
- pc = displaced_step_original + (pc - displaced_step_copy);
+
+ pc = displaced->step_original + (pc - displaced->step_copy);
regcache_write_pc (regcache, pc);
}
do_cleanups (old_cleanups);
- displaced_step_ptid = null_ptid;
+ displaced->step_ptid = null_ptid;
/* Are there any pending displaced stepping requests? If so, run
- one now. */
- while (displaced_step_request_queue)
+ one now. Leave the state object around, since we're likely to
+ need it again soon. */
+ while (displaced->step_request_queue)
{
struct displaced_step_request *head;
ptid_t ptid;
CORE_ADDR actual_pc;
struct address_space *aspace;
- head = displaced_step_request_queue;
+ head = displaced->step_request_queue;
ptid = head->ptid;
- displaced_step_request_queue = head->next;
+ displaced->step_request_queue = head->next;
xfree (head);
context_switch (ptid);
displaced_step_dump_bytes (gdb_stdlog, buf, sizeof (buf));
}
- if (gdbarch_displaced_step_hw_singlestep
- (gdbarch, displaced_step_closure))
+ if (gdbarch_displaced_step_hw_singlestep (gdbarch,
+ displaced->step_closure))
target_resume (ptid, 1, TARGET_SIGNAL_0);
else
target_resume (ptid, 0, TARGET_SIGNAL_0);
infrun_thread_ptid_changed (ptid_t old_ptid, ptid_t new_ptid)
{
struct displaced_step_request *it;
+ struct displaced_step_inferior_state *displaced;
if (ptid_equal (inferior_ptid, old_ptid))
inferior_ptid = new_ptid;
if (ptid_equal (singlestep_ptid, old_ptid))
singlestep_ptid = new_ptid;
- if (ptid_equal (displaced_step_ptid, old_ptid))
- displaced_step_ptid = new_ptid;
-
if (ptid_equal (deferred_step_ptid, old_ptid))
deferred_step_ptid = new_ptid;
- for (it = displaced_step_request_queue; it; it = it->next)
- if (ptid_equal (it->ptid, old_ptid))
- it->ptid = new_ptid;
+ for (displaced = displaced_step_inferior_states;
+ displaced;
+ displaced = displaced->next)
+ {
+ if (ptid_equal (displaced->step_ptid, old_ptid))
+ displaced->step_ptid = new_ptid;
+
+ for (it = displaced->step_request_queue; it; it = it->next)
+ if (ptid_equal (it->ptid, old_ptid))
+ it->ptid = new_ptid;
+ }
}
\f
{
int hw_step = 1;
- if (gdbarch_software_single_step_p (gdbarch)
+ if (execution_direction == EXEC_FORWARD
+ && gdbarch_software_single_step_p (gdbarch)
&& gdbarch_software_single_step (gdbarch, get_current_frame ()))
{
hw_step = 0;
QUIT;
+ if (current_inferior ()->waiting_for_vfork_done)
+ {
+ /* Don't try to single-step a vfork parent that is waiting for
+ the child to get out of the shared memory region (by exec'ing
+ or exiting). This is particularly important on software
+ single-step archs, as the child process would trip on the
+ software single step breakpoint inserted for the parent
+ process. Since the parent will not actually execute any
+ instruction until the child is out of the shared region (such
+ are vfork's semantics), it is safe to simply continue it.
+ Eventually, we'll see a TARGET_WAITKIND_VFORK_DONE event for
+ the parent, and tell it to `keep_going', which automatically
+ re-sets it stepping. */
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: resume : clear step\n");
+ step = 0;
+ }
+
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog,
"infrun: resume (step=%d, signal=%d), "
"trap_expected=%d\n",
step, sig, tp->trap_expected);
- /* Some targets (e.g. Solaris x86) have a kernel bug when stepping
- over an instruction that causes a page fault without triggering
- a hardware watchpoint. The kernel properly notices that it shouldn't
- stop, because the hardware watchpoint is not triggered, but it forgets
- the step request and continues the program normally.
- Work around the problem by removing hardware watchpoints if a step is
- requested, GDB will check for a hardware watchpoint trigger after the
- step anyway. */
- if (CANNOT_STEP_HW_WATCHPOINTS && step)
- remove_hw_watchpoints ();
-
-
/* Normally, by the time we reach `resume', the breakpoints are either
removed or inserted, as appropriate. The exception is if we're sitting
at a permanent breakpoint; we need to step over it, but permanent
We can't use displaced stepping when we have a signal to deliver;
the comments for displaced_step_prepare explain why. The
comments in the handle_inferior event for dealing with 'random
- signals' explain what we do instead. */
+ signals' explain what we do instead.
+
+ We can't use displaced stepping when we are waiting for vfork_done
+ event, displaced stepping breaks the vfork child similarly as single
+ step software breakpoint. */
if (use_displaced_stepping (gdbarch)
&& (tp->trap_expected
|| (step && gdbarch_software_single_step_p (gdbarch)))
- && sig == TARGET_SIGNAL_0)
+ && sig == TARGET_SIGNAL_0
+ && !current_inferior ()->waiting_for_vfork_done)
{
+ struct displaced_step_inferior_state *displaced;
+
if (!displaced_step_prepare (inferior_ptid))
{
/* Got placed in displaced stepping queue. Will be resumed
return;
}
- step = gdbarch_displaced_step_hw_singlestep
- (gdbarch, displaced_step_closure);
+ displaced = get_displaced_stepping_state (ptid_get_pid (inferior_ptid));
+ step = gdbarch_displaced_step_hw_singlestep (gdbarch,
+ displaced->step_closure);
}
/* Do we need to do it the hard way, w/temp breakpoints? */
/* Make sure we were stopped at a breakpoint. */
if (wait_status.kind != TARGET_WAITKIND_STOPPED
- || wait_status.value.sig != TARGET_SIGNAL_TRAP)
+ || (wait_status.value.sig != TARGET_SIGNAL_TRAP
+ && wait_status.value.sig != TARGET_SIGNAL_ILL
+ && wait_status.value.sig != TARGET_SIGNAL_SEGV
+ && wait_status.value.sig != TARGET_SIGNAL_EMT))
{
return 0;
}
"infrun: proceed (addr=%s, signal=%d, step=%d)\n",
paddress (gdbarch, addr), siggnal, step);
+ /* 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
+ handling the event. */
+ if (non_stop)
+ {
+ make_cleanup_restore_current_traceframe ();
+ set_traceframe_number (-1);
+ }
+
if (non_stop)
/* In non-stop, each thread is handled individually. The context
must already be set to the right thread here. */
or a return command, we often end up a few instructions forward, still
within the original line we started.
- An attempt was made to have init_execution_control_state () refresh
- the prev_pc value before calculating the line number. This approach
- did not work because on platforms that use ptrace, the pc register
- cannot be read unless the inferior is stopped. At that point, we
- are not guaranteed the inferior is stopped and so the regcache_read_pc ()
- call can fail. Setting the prev_pc value here ensures the value is
- updated correctly when the inferior is stopped. */
+ An attempt was made to refresh the prev_pc at the same time the
+ execution_control_state is initialized (for instance, just before
+ waiting for an inferior event). But this approach did not work
+ because of platforms that use ptrace, where the pc register cannot
+ be read unless the inferior is stopped. At that point, we are not
+ guaranteed the inferior is stopped and so the regcache_read_pc() call
+ can fail. Setting the prev_pc value here ensures the value is updated
+ correctly when the inferior is stopped. */
tp->prev_pc = regcache_read_pc (get_current_regcache ());
/* Fill in with reasonable starting values. */
start_remote (int from_tty)
{
struct inferior *inferior;
- init_wait_for_inferior ();
+ init_wait_for_inferior ();
inferior = current_inferior ();
inferior->stop_soon = STOP_QUIETLY_REMOTE;
previous_inferior_ptid = null_ptid;
init_infwait_state ();
- displaced_step_clear ();
-
/* Discard any skipped inlined frames. */
clear_inline_frame_state (minus_one_ptid);
}
infwait_nonstep_watch_state
};
-/* Why did the inferior stop? Used to print the appropriate messages
- to the interface from within handle_inferior_event(). */
-enum inferior_stop_reason
-{
- /* Step, next, nexti, stepi finished. */
- END_STEPPING_RANGE,
- /* Inferior terminated by signal. */
- SIGNAL_EXITED,
- /* Inferior exited. */
- EXITED,
- /* Inferior received signal, and user asked to be notified. */
- SIGNAL_RECEIVED,
- /* Reverse execution -- target ran out of history info. */
- NO_HISTORY
-};
-
/* The PTID we'll do a target_wait on.*/
ptid_t waiton_ptid;
int wait_some_more;
};
-static void init_execution_control_state (struct execution_control_state *ecs);
-
static void handle_inferior_event (struct execution_control_state *ecs);
static void handle_step_into_function (struct gdbarch *gdbarch,
static void stop_stepping (struct execution_control_state *ecs);
static void prepare_to_wait (struct execution_control_state *ecs);
static void keep_going (struct execution_control_state *ecs);
-static void print_stop_reason (enum inferior_stop_reason stop_reason,
- int stop_info);
/* Callback for iterate over threads. If the thread is stopped, but
the user/frontend doesn't know about that yet, go through
static void
infrun_thread_stop_requested (ptid_t ptid)
{
- struct displaced_step_request *it, *next, *prev = NULL;
+ struct displaced_step_inferior_state *displaced;
/* PTID was requested to stop. Remove it from the displaced
stepping queue, so we don't try to resume it automatically. */
- for (it = displaced_step_request_queue; it; it = next)
+
+ for (displaced = displaced_step_inferior_states;
+ displaced;
+ displaced = displaced->next)
{
- next = it->next;
+ struct displaced_step_request *it, **prev_next_p;
- if (ptid_equal (it->ptid, ptid)
- || ptid_equal (minus_one_ptid, ptid)
- || (ptid_is_pid (ptid)
- && ptid_get_pid (ptid) == ptid_get_pid (it->ptid)))
+ it = displaced->step_request_queue;
+ prev_next_p = &displaced->step_request_queue;
+ while (it)
{
- if (displaced_step_request_queue == it)
- displaced_step_request_queue = it->next;
+ if (ptid_match (it->ptid, ptid))
+ {
+ *prev_next_p = it->next;
+ it->next = NULL;
+ xfree (it);
+ }
else
- prev->next = it->next;
+ {
+ prev_next_p = &it->next;
+ }
- xfree (it);
+ it = *prev_next_p;
}
- else
- prev = it;
}
iterate_over_threads (infrun_thread_stop_requested_callback, &ptid);
longjmp-resume breakpoint of the thread that just stopped
stepping. */
struct thread_info *tp = inferior_thread ();
+
delete_step_resume_breakpoint (tp);
}
else
ui_file_delete (tmp_stream);
}
+/* Prepare and stabilize the inferior for detaching it. E.g.,
+ detaching while a thread is displaced stepping is a recipe for
+ crashing it, as nothing would readjust the PC out of the scratch
+ pad. */
+
+void
+prepare_for_detach (void)
+{
+ struct inferior *inf = current_inferior ();
+ ptid_t pid_ptid = pid_to_ptid (inf->pid);
+ struct cleanup *old_chain_1;
+ struct displaced_step_inferior_state *displaced;
+
+ displaced = get_displaced_stepping_state (inf->pid);
+
+ /* Is any thread of this process displaced stepping? If not,
+ there's nothing else to do. */
+ if (displaced == NULL || ptid_equal (displaced->step_ptid, null_ptid))
+ return;
+
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "displaced-stepping in-process while detaching");
+
+ old_chain_1 = make_cleanup_restore_integer (&inf->detaching);
+ inf->detaching = 1;
+
+ while (!ptid_equal (displaced->step_ptid, null_ptid))
+ {
+ struct cleanup *old_chain_2;
+ struct execution_control_state ecss;
+ struct execution_control_state *ecs;
+
+ ecs = &ecss;
+ memset (ecs, 0, sizeof (*ecs));
+
+ overlay_cache_invalid = 1;
+
+ /* We have to invalidate the registers BEFORE calling
+ target_wait because they can be loaded from the target while
+ in target_wait. This makes remote debugging a bit more
+ efficient for those targets that provide critical registers
+ as part of their normal status mechanism. */
+
+ registers_changed ();
+
+ if (deprecated_target_wait_hook)
+ ecs->ptid = deprecated_target_wait_hook (pid_ptid, &ecs->ws, 0);
+ else
+ ecs->ptid = target_wait (pid_ptid, &ecs->ws, 0);
+
+ if (debug_infrun)
+ print_target_wait_results (pid_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_2 = make_cleanup (finish_thread_state_cleanup, &minus_one_ptid);
+
+ /* In non-stop mode, each thread is handled individually.
+ Switch early, so the global state is set correctly for this
+ thread. */
+ if (non_stop
+ && ecs->ws.kind != TARGET_WAITKIND_EXITED
+ && ecs->ws.kind != TARGET_WAITKIND_SIGNALLED)
+ context_switch (ecs->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_2);
+
+ /* Breakpoints and watchpoints are not installed on the target
+ at this point, and signals are passed directly to the
+ inferior, so this must mean the process is gone. */
+ if (!ecs->wait_some_more)
+ {
+ discard_cleanups (old_chain_1);
+ error (_("Program exited while detaching"));
+ }
+ }
+
+ discard_cleanups (old_chain_1);
+}
+
/* Wait for control to return from inferior to debugger.
If TREAT_EXEC_AS_SIGTRAP is non-zero, then handle EXEC signals
tp->current_line = sal.line;
}
-/* Prepare an execution control state for looping through a
- wait_for_inferior-type loop. */
-
-static void
-init_execution_control_state (struct execution_control_state *ecs)
-{
- ecs->random_signal = 0;
-}
-
/* Clear context switchable stepping state. */
void
|| (non_stop && moribund_breakpoint_here_p (aspace, breakpoint_pc)))
{
struct cleanup *old_cleanups = NULL;
+
if (RECORD_IS_USED)
old_cleanups = record_gdb_operation_disable_set ();
&& ecs->ws.kind != TARGET_WAITKIND_SIGNALLED)
{
struct inferior *inf = find_inferior_pid (ptid_get_pid (ecs->ptid));
+
gdb_assert (inf);
stop_soon = inf->stop_soon;
}
target_last_waitstatus = ecs->ws;
/* Always clear state belonging to the previous time we stopped. */
- stop_stack_dummy = 0;
+ stop_stack_dummy = STOP_NONE;
/* If it's a new process, add it to the thread database */
breakpoint_retire_moribund ();
+ /* First, distinguish signals caused by the debugger from signals
+ that have to do with the program's own actions. Note that
+ breakpoint insns may cause SIGTRAP or SIGILL or SIGEMT, depending
+ on the operating system version. Here we detect when a SIGILL or
+ SIGEMT is really a breakpoint and change it to SIGTRAP. We do
+ something similar for SIGSEGV, since a SIGSEGV will be generated
+ when we're trying to execute a breakpoint instruction on a
+ non-executable stack. This happens for call dummy breakpoints
+ for architectures like SPARC that place call dummies on the
+ stack. */
+ if (ecs->ws.kind == TARGET_WAITKIND_STOPPED
+ && (ecs->ws.value.sig == TARGET_SIGNAL_ILL
+ || ecs->ws.value.sig == TARGET_SIGNAL_SEGV
+ || ecs->ws.value.sig == TARGET_SIGNAL_EMT))
+ {
+ struct regcache *regcache = get_thread_regcache (ecs->ptid);
+
+ if (breakpoint_inserted_here_p (get_regcache_aspace (regcache),
+ regcache_read_pc (regcache)))
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: Treating signal as SIGTRAP\n");
+ ecs->ws.value.sig = TARGET_SIGNAL_TRAP;
+ }
+ }
+
/* 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. If
dynamically loaded objects (among other things). */
if (stop_on_solib_events)
{
+ /* Make sure we print "Stopped due to solib-event" in
+ normal_stop. */
+ stop_print_frame = 1;
+
stop_stepping (ecs);
return;
}
set_current_program_space (current_inferior ()->pspace);
handle_vfork_child_exec_or_exit (0);
target_terminal_ours (); /* Must do this before mourn anyway */
- print_stop_reason (EXITED, ecs->ws.value.integer);
+ print_exited_reason (ecs->ws.value.integer);
/* Record the exit code in the convenience variable $_exitcode, so
that the user can inspect this again later. */
gdb_flush (gdb_stdout);
target_mourn_inferior ();
singlestep_breakpoints_inserted_p = 0;
+ cancel_single_step_breakpoints ();
stop_print_frame = 0;
stop_stepping (ecs);
return;
may be needed. */
target_mourn_inferior ();
- print_stop_reason (SIGNAL_EXITED, ecs->ws.value.sig);
+ print_signal_exited_reason (ecs->ws.value.sig);
singlestep_breakpoints_inserted_p = 0;
+ cancel_single_step_breakpoints ();
stop_stepping (ecs);
return;
detach_breakpoints (child_pid);
}
+ if (singlestep_breakpoints_inserted_p)
+ {
+ /* Pull the single step breakpoints out of the target. */
+ remove_single_step_breakpoints ();
+ singlestep_breakpoints_inserted_p = 0;
+ }
+
/* In case the event is caught by a catchpoint, remember that
the event is to be followed at the next resume of the thread,
and not immediately. */
= bpstat_stop_status (get_regcache_aspace (get_current_regcache ()),
stop_pc, ecs->ptid);
- ecs->random_signal = !bpstat_explains_signal (ecs->event_thread->stop_bpstat);
+ /* Note that we're interested in knowing the bpstat actually
+ causes a stop, not just if it may explain the signal.
+ Software watchpoints, for example, always appear in the
+ bpstat. */
+ ecs->random_signal = !bpstat_causes_stop (ecs->event_thread->stop_bpstat);
/* If no catchpoint triggered for this, then keep going. */
if (ecs->random_signal)
context_switch (ecs->ptid);
current_inferior ()->waiting_for_vfork_done = 0;
+ current_inferior ()->pspace->breakpoints_not_allowed = 0;
/* This also takes care of reinserting breakpoints in the
previously locked inferior. */
keep_going (ecs);
reinit_frame_cache ();
}
+ 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. */
case TARGET_WAITKIND_NO_HISTORY:
/* Reverse execution: target ran out of history info. */
stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));
- print_stop_reason (NO_HISTORY, 0);
+ print_no_history_reason ();
stop_stepping (ecs);
return;
}
{
struct regcache *regcache = get_thread_regcache (ecs->ptid);
struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ struct cleanup *old_chain = save_inferior_ptid ();
+
+ inferior_ptid = ecs->ptid;
fprintf_unfiltered (gdb_stdlog, "infrun: stop_pc = %s\n",
paddress (gdbarch, stop_pc));
if (target_stopped_by_watchpoint ())
{
CORE_ADDR addr;
+
fprintf_unfiltered (gdb_stdlog, "infrun: stopped by watchpoint\n");
if (target_stopped_data_address (¤t_target, &addr))
fprintf_unfiltered (gdb_stdlog,
"infrun: (no data address available)\n");
}
+
+ do_cleanups (old_chain);
}
if (stepping_past_singlestep_breakpoint)
if (ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP)
{
int thread_hop_needed = 0;
- struct address_space *aspace = get_regcache_aspace (get_current_regcache ());
+ struct address_space *aspace =
+ get_regcache_aspace (get_thread_regcache (ecs->ptid));
/* Check if a regular breakpoint has been hit before checking
for a potential single step breakpoint. Otherwise, GDB will
the instruction and once for the delay slot. */
int step_through_delay
= gdbarch_single_step_through_delay (gdbarch, frame);
+
if (debug_infrun && step_through_delay)
fprintf_unfiltered (gdb_stdlog, "infrun: step through delay\n");
if (ecs->event_thread->step_range_end == 0 && step_through_delay)
3) set ecs->random_signal to 1, and the decision between 1 and 2
will be made according to the signal handling tables. */
- /* First, distinguish signals caused by the debugger from signals
- that have to do with the program's own actions. Note that
- breakpoint insns may cause SIGTRAP or SIGILL or SIGEMT, depending
- on the operating system version. Here we detect when a SIGILL or
- SIGEMT is really a breakpoint and change it to SIGTRAP. We do
- something similar for SIGSEGV, since a SIGSEGV will be generated
- when we're trying to execute a breakpoint instruction on a
- non-executable stack. This happens for call dummy breakpoints
- for architectures like SPARC that place call dummies on the
- stack.
-
- If we're doing a displaced step past a breakpoint, then the
- breakpoint is always inserted at the original instruction;
- non-standard signals can't be explained by the breakpoint. */
if (ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP
- || (! ecs->event_thread->trap_expected
- && breakpoint_inserted_here_p (get_regcache_aspace (get_current_regcache ()),
- stop_pc)
- && (ecs->event_thread->stop_signal == TARGET_SIGNAL_ILL
- || ecs->event_thread->stop_signal == TARGET_SIGNAL_SEGV
- || ecs->event_thread->stop_signal == TARGET_SIGNAL_EMT))
|| stop_soon == STOP_QUIETLY || stop_soon == STOP_QUIETLY_NO_SIGSTOP
|| stop_soon == STOP_QUIETLY_REMOTE)
{
function. */
stop_print_frame = 1;
+ /* This is where we handle "moribund" watchpoints. Unlike
+ software breakpoints traps, hardware watchpoint traps are
+ always distinguishable from random traps. If no high-level
+ watchpoint is associated with the reported stop data address
+ anymore, then the bpstat does not explain the signal ---
+ simply make sure to ignore it if `stopped_by_watchpoint' is
+ set. */
+
+ if (debug_infrun
+ && ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP
+ && !bpstat_explains_signal (ecs->event_thread->stop_bpstat)
+ && stopped_by_watchpoint)
+ fprintf_unfiltered (gdb_stdlog, "\
+infrun: no user watchpoint explains watchpoint SIGTRAP, ignoring\n");
+
/* NOTE: cagney/2003-03-29: These two checks for a random signal
at one stage in the past included checks for an inferior
function call's call dummy's return breakpoint. The original
if (ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP)
ecs->random_signal
= !(bpstat_explains_signal (ecs->event_thread->stop_bpstat)
+ || stopped_by_watchpoint
|| ecs->event_thread->trap_expected
|| (ecs->event_thread->step_range_end
&& ecs->event_thread->step_resume_breakpoint == NULL));
{
/* Signal not for debugging purposes. */
int printed = 0;
+ struct inferior *inf = find_inferior_pid (ptid_get_pid (ecs->ptid));
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: random signal %d\n",
{
printed = 1;
target_terminal_ours_for_output ();
- print_stop_reason (SIGNAL_RECEIVED, ecs->event_thread->stop_signal);
+ print_signal_received_reason (ecs->event_thread->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. */
if (stop_soon != NO_STOP_QUIETLY
|| ecs->event_thread->stop_requested
- || signal_stop_state (ecs->event_thread->stop_signal))
+ || (!inf->detaching
+ && signal_stop_state (ecs->event_thread->stop_signal)))
{
stop_stepping (ecs);
return;
if (what.call_dummy)
{
- stop_stack_dummy = 1;
+ stop_stack_dummy = what.call_dummy;
}
+ /* If we hit an internal event that triggers symbol changes, the
+ current frame will be invalidated within bpstat_what (e.g., if
+ we hit an internal solib event). Re-fetch it. */
+ frame = get_current_frame ();
+ gdbarch = get_frame_arch (frame);
+
switch (what.main_action)
{
case BPSTAT_WHAT_SET_LONGJMP_RESUME:
delete_step_resume_breakpoint (ecs->event_thread);
ecs->event_thread->stop_step = 1;
- print_stop_reason (END_STEPPING_RANGE, 0);
+ print_end_stepping_range_reason ();
stop_stepping (ecs);
return;
}
break;
- case BPSTAT_WHAT_CHECK_SHLIBS:
- {
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_CHECK_SHLIBS\n");
-
- /* Check for any newly added shared libraries if we're
- supposed to be adding them automatically. Switch
- terminal for any messages produced by
- breakpoint_re_set. */
- target_terminal_ours_for_output ();
- /* NOTE: cagney/2003-11-25: Make certain that the target
- stack's section table is kept up-to-date. Architectures,
- (e.g., PPC64), use the section table to perform
- operations such as address => section name and hence
- require the table to contain all sections (including
- those found in shared libraries). */
-#ifdef SOLIB_ADD
- SOLIB_ADD (NULL, 0, ¤t_target, auto_solib_add);
-#else
- solib_add (NULL, 0, ¤t_target, auto_solib_add);
-#endif
- target_terminal_inferior ();
-
- /* If requested, stop when the dynamic linker notifies
- gdb of events. This allows the user to get control
- and place breakpoints in initializer routines for
- dynamically loaded objects (among other things). */
- if (stop_on_solib_events || stop_stack_dummy)
- {
- stop_stepping (ecs);
- return;
- }
- else
- {
- /* We want to step over this breakpoint, then keep going. */
- ecs->event_thread->stepping_over_breakpoint = 1;
- break;
- }
- }
- break;
-
- case BPSTAT_WHAT_CHECK_JIT:
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_CHECK_JIT\n");
-
- /* Switch terminal for any messages produced by breakpoint_re_set. */
- target_terminal_ours_for_output ();
-
- jit_event_handler (gdbarch);
-
- target_terminal_inferior ();
-
- /* We want to step over this breakpoint, then keep going. */
- ecs->event_thread->stepping_over_breakpoint = 1;
-
- break;
-
- case BPSTAT_WHAT_LAST:
- /* Not a real code, but listed here to shut up gcc -Wall. */
-
case BPSTAT_WHAT_KEEP_CHECKING:
break;
}
if (!non_stop)
{
struct thread_info *tp;
+
tp = iterate_over_threads (currently_stepping_or_nexting_callback,
ecs->event_thread);
if (tp)
return;
}
+ /* Re-fetch current thread's frame in case the code above caused
+ the frame cache to be re-initialized, making our FRAME variable
+ a dangling pointer. */
+ frame = get_current_frame ();
+ gdbarch = get_frame_arch (frame);
+
/* If stepping through a line, keep going if still within it.
Note that step_range_end is the address of the first instruction
&& execution_direction == EXEC_REVERSE)
{
ecs->event_thread->stop_step = 1;
- print_stop_reason (END_STEPPING_RANGE, 0);
+ print_end_stepping_range_reason ();
stop_stepping (ecs);
}
else
/* Set up a step-resume breakpoint at the address
indicated by SKIP_SOLIB_RESOLVER. */
struct symtab_and_line sr_sal;
+
init_sal (&sr_sal);
sr_sal.pc = pc_after_resolver;
sr_sal.pspace = get_frame_program_space (frame);
"outermost" function. This could be fixed by marking
outermost frames as !stack_p,code_p,special_p. Then the
initial outermost frame, before sp was valid, would
- have code_addr == &_start. See the commend in frame_id_eq
+ have code_addr == &_start. See the comment in frame_id_eq
for more. */
if (!frame_id_eq (get_stack_frame_id (frame),
ecs->event_thread->step_stack_frame_id)
well. FENN */
/* And this works the same backward as frontward. MVS */
ecs->event_thread->stop_step = 1;
- print_stop_reason (END_STEPPING_RANGE, 0);
+ print_end_stepping_range_reason ();
stop_stepping (ecs);
return;
}
if (real_stop_pc != 0 && in_solib_dynsym_resolve_code (real_stop_pc))
{
struct symtab_and_line sr_sal;
+
init_sal (&sr_sal);
sr_sal.pc = ecs->stop_func_start;
sr_sal.pspace = get_frame_program_space (frame);
&& step_stop_if_no_debug)
{
ecs->event_thread->stop_step = 1;
- print_stop_reason (END_STEPPING_RANGE, 0);
+ print_end_stepping_range_reason ();
stop_stepping (ecs);
return;
}
/* Set a breakpoint at callee's start address.
From there we can step once and be back in the caller. */
struct symtab_and_line sr_sal;
+
init_sal (&sr_sal);
sr_sal.pc = ecs->stop_func_start;
sr_sal.pspace = get_frame_program_space (frame);
Set a breakpoint at its start and continue, then
one more step will take us out. */
struct symtab_and_line sr_sal;
+
init_sal (&sr_sal);
sr_sal.pc = ecs->stop_func_start;
sr_sal.pspace = get_frame_program_space (frame);
{
/* Determine where this trampoline returns. */
CORE_ADDR real_stop_pc;
+
real_stop_pc = gdbarch_skip_trampoline_code (gdbarch, frame, stop_pc);
if (debug_infrun)
is set, we stop the step so that the user has a chance to
switch in assembly mode. */
ecs->event_thread->stop_step = 1;
- print_stop_reason (END_STEPPING_RANGE, 0);
+ print_end_stepping_range_reason ();
stop_stepping (ecs);
return;
}
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: stepi/nexti\n");
ecs->event_thread->stop_step = 1;
- print_stop_reason (END_STEPPING_RANGE, 0);
+ print_end_stepping_range_reason ();
stop_stepping (ecs);
return;
}
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: no line number info\n");
ecs->event_thread->stop_step = 1;
- print_stop_reason (END_STEPPING_RANGE, 0);
+ print_end_stepping_range_reason ();
stop_stepping (ecs);
return;
}
step_into_inline_frame (ecs->ptid);
ecs->event_thread->stop_step = 1;
- print_stop_reason (END_STEPPING_RANGE, 0);
+ print_end_stepping_range_reason ();
stop_stepping (ecs);
return;
}
else
{
ecs->event_thread->stop_step = 1;
- print_stop_reason (END_STEPPING_RANGE, 0);
+ print_end_stepping_range_reason ();
stop_stepping (ecs);
}
return;
else
{
ecs->event_thread->stop_step = 1;
- print_stop_reason (END_STEPPING_RANGE, 0);
+ print_end_stepping_range_reason ();
stop_stepping (ecs);
}
return;
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: stepped to a different line\n");
ecs->event_thread->stop_step = 1;
- print_stop_reason (END_STEPPING_RANGE, 0);
+ print_end_stepping_range_reason ();
stop_stepping (ecs);
return;
}
/* Is thread TP in the middle of single-stepping? */
-static int
+int
currently_stepping (struct thread_info *tp)
{
return ((tp->step_range_end && tp->step_resume_breakpoint == NULL)
{
/* We are already there: stop now. */
ecs->event_thread->stop_step = 1;
- print_stop_reason (END_STEPPING_RANGE, 0);
+ print_end_stepping_range_reason ();
stop_stepping (ecs);
return;
}
struct execution_control_state *ecs)
{
struct symtab *s;
- struct symtab_and_line stop_func_sal, sr_sal;
+ struct symtab_and_line stop_func_sal;
s = find_pc_symtab (stop_pc);
if (s && s->language != language_asm)
{
/* We're there already. Just stop stepping now. */
ecs->event_thread->stop_step = 1;
- print_stop_reason (END_STEPPING_RANGE, 0);
+ print_end_stepping_range_reason ();
stop_stepping (ecs);
}
else
if (ecs->event_thread->stepping_over_breakpoint)
{
struct regcache *thread_regcache = get_thread_regcache (ecs->ptid);
+
if (!use_displaced_stepping (get_regcache_arch (thread_regcache)))
/* Since we can't do a displaced step, we have to remove
the breakpoint while we step it. To keep things
else
{
struct gdb_exception e;
+
/* Stop stepping when inserting breakpoints
has failed. */
TRY_CATCH (e, RETURN_MASK_ERROR)
ecs->wait_some_more = 1;
}
-/* Print why the inferior has stopped. We always print something when
- the inferior exits, or receives a signal. The rest of the cases are
- dealt with later on in normal_stop() and print_it_typical(). Ideally
- there should be a call to this function from handle_inferior_event()
- each time stop_stepping() is called.*/
+/* Several print_*_reason functions to print why the inferior has stopped.
+ We always print something when the inferior exits, or receives a signal.
+ The rest of the cases are dealt with later on in normal_stop and
+ print_it_typical. Ideally there should be a call to one of these
+ print_*_reason functions functions from handle_inferior_event each time
+ stop_stepping is called. */
+
+/* Print why the inferior has stopped.
+ We are done with a step/next/si/ni command, print why the inferior has
+ stopped. For now print nothing. Print a message only if not in the middle
+ of doing a "step n" operation for n > 1. */
+
+static void
+print_end_stepping_range_reason (void)
+{
+ if ((!inferior_thread ()->step_multi || !inferior_thread ()->stop_step)
+ && ui_out_is_mi_like_p (uiout))
+ ui_out_field_string (uiout, "reason",
+ async_reason_lookup (EXEC_ASYNC_END_STEPPING_RANGE));
+}
+
+/* The inferior was terminated by a signal, print why it stopped. */
+
+static void
+print_signal_exited_reason (enum target_signal siggnal)
+{
+ annotate_signalled ();
+ if (ui_out_is_mi_like_p (uiout))
+ ui_out_field_string
+ (uiout, "reason", async_reason_lookup (EXEC_ASYNC_EXITED_SIGNALLED));
+ ui_out_text (uiout, "\nProgram terminated with signal ");
+ annotate_signal_name ();
+ ui_out_field_string (uiout, "signal-name",
+ target_signal_to_name (siggnal));
+ annotate_signal_name_end ();
+ ui_out_text (uiout, ", ");
+ annotate_signal_string ();
+ ui_out_field_string (uiout, "signal-meaning",
+ target_signal_to_string (siggnal));
+ annotate_signal_string_end ();
+ ui_out_text (uiout, ".\n");
+ ui_out_text (uiout, "The program no longer exists.\n");
+}
+
+/* The inferior program is finished, print why it stopped. */
+
static void
-print_stop_reason (enum inferior_stop_reason stop_reason, int stop_info)
+print_exited_reason (int exitstatus)
{
- switch (stop_reason)
+ annotate_exited (exitstatus);
+ if (exitstatus)
+ {
+ if (ui_out_is_mi_like_p (uiout))
+ ui_out_field_string (uiout, "reason",
+ async_reason_lookup (EXEC_ASYNC_EXITED));
+ ui_out_text (uiout, "\nProgram exited with code ");
+ ui_out_field_fmt (uiout, "exit-code", "0%o", (unsigned int) exitstatus);
+ ui_out_text (uiout, ".\n");
+ }
+ else
{
- case END_STEPPING_RANGE:
- /* We are done with a step/next/si/ni command. */
- /* For now print nothing. */
- /* Print a message only if not in the middle of doing a "step n"
- operation for n > 1 */
- if (!inferior_thread ()->step_multi
- || !inferior_thread ()->stop_step)
- if (ui_out_is_mi_like_p (uiout))
- ui_out_field_string
- (uiout, "reason",
- async_reason_lookup (EXEC_ASYNC_END_STEPPING_RANGE));
- break;
- case SIGNAL_EXITED:
- /* The inferior was terminated by a signal. */
- annotate_signalled ();
if (ui_out_is_mi_like_p (uiout))
ui_out_field_string
- (uiout, "reason",
- async_reason_lookup (EXEC_ASYNC_EXITED_SIGNALLED));
- ui_out_text (uiout, "\nProgram terminated with signal ");
+ (uiout, "reason", async_reason_lookup (EXEC_ASYNC_EXITED_NORMALLY));
+ ui_out_text (uiout, "\nProgram exited normally.\n");
+ }
+ /* Support the --return-child-result option. */
+ return_child_result_value = exitstatus;
+}
+
+/* Signal received, print why the inferior has stopped. The signal table
+ tells us to print about it. */
+
+static void
+print_signal_received_reason (enum target_signal siggnal)
+{
+ annotate_signal ();
+
+ if (siggnal == TARGET_SIGNAL_0 && !ui_out_is_mi_like_p (uiout))
+ {
+ struct thread_info *t = inferior_thread ();
+
+ ui_out_text (uiout, "\n[");
+ ui_out_field_string (uiout, "thread-name",
+ target_pid_to_str (t->ptid));
+ ui_out_field_fmt (uiout, "thread-id", "] #%d", t->num);
+ ui_out_text (uiout, " stopped");
+ }
+ else
+ {
+ ui_out_text (uiout, "\nProgram received signal ");
annotate_signal_name ();
+ if (ui_out_is_mi_like_p (uiout))
+ ui_out_field_string
+ (uiout, "reason", async_reason_lookup (EXEC_ASYNC_SIGNAL_RECEIVED));
ui_out_field_string (uiout, "signal-name",
- target_signal_to_name (stop_info));
+ target_signal_to_name (siggnal));
annotate_signal_name_end ();
ui_out_text (uiout, ", ");
annotate_signal_string ();
ui_out_field_string (uiout, "signal-meaning",
- target_signal_to_string (stop_info));
+ target_signal_to_string (siggnal));
annotate_signal_string_end ();
- ui_out_text (uiout, ".\n");
- ui_out_text (uiout, "The program no longer exists.\n");
- break;
- case EXITED:
- /* The inferior program is finished. */
- annotate_exited (stop_info);
- if (stop_info)
- {
- if (ui_out_is_mi_like_p (uiout))
- ui_out_field_string (uiout, "reason",
- async_reason_lookup (EXEC_ASYNC_EXITED));
- ui_out_text (uiout, "\nProgram exited with code ");
- ui_out_field_fmt (uiout, "exit-code", "0%o",
- (unsigned int) stop_info);
- ui_out_text (uiout, ".\n");
- }
- else
- {
- if (ui_out_is_mi_like_p (uiout))
- ui_out_field_string
- (uiout, "reason",
- async_reason_lookup (EXEC_ASYNC_EXITED_NORMALLY));
- ui_out_text (uiout, "\nProgram exited normally.\n");
- }
- /* Support the --return-child-result option. */
- return_child_result_value = stop_info;
- break;
- case SIGNAL_RECEIVED:
- /* Signal received. The signal table tells us to print about
- it. */
- annotate_signal ();
+ }
+ ui_out_text (uiout, ".\n");
+}
- if (stop_info == TARGET_SIGNAL_0 && !ui_out_is_mi_like_p (uiout))
- {
- struct thread_info *t = inferior_thread ();
+/* Reverse execution: target ran out of history info, print why the inferior
+ has stopped. */
- ui_out_text (uiout, "\n[");
- ui_out_field_string (uiout, "thread-name",
- target_pid_to_str (t->ptid));
- ui_out_field_fmt (uiout, "thread-id", "] #%d", t->num);
- ui_out_text (uiout, " stopped");
- }
- else
- {
- ui_out_text (uiout, "\nProgram received signal ");
- annotate_signal_name ();
- if (ui_out_is_mi_like_p (uiout))
- ui_out_field_string
- (uiout, "reason", async_reason_lookup (EXEC_ASYNC_SIGNAL_RECEIVED));
- ui_out_field_string (uiout, "signal-name",
- target_signal_to_name (stop_info));
- annotate_signal_name_end ();
- ui_out_text (uiout, ", ");
- annotate_signal_string ();
- ui_out_field_string (uiout, "signal-meaning",
- target_signal_to_string (stop_info));
- annotate_signal_string_end ();
- }
- ui_out_text (uiout, ".\n");
- break;
- case NO_HISTORY:
- /* Reverse execution: target ran out of history info. */
- ui_out_text (uiout, "\nNo more reverse-execution history.\n");
- break;
- default:
- internal_error (__FILE__, __LINE__,
- _("print_stop_reason: unrecognized enum value"));
- break;
- }
+static void
+print_no_history_reason (void)
+{
+ ui_out_text (uiout, "\nNo more reverse-execution history.\n");
}
-\f
/* Here to return control to GDB when the inferior stops for real.
Print appropriate messages, remove breakpoints, give terminal our modes.
stop_registers = regcache_dup (get_current_regcache ());
}
- if (stop_stack_dummy)
+ if (stop_stack_dummy == STOP_STACK_DUMMY)
{
/* Pop the empty frame that contains the stack dummy.
This also restores inferior state prior to the call
(struct inferior_thread_state). */
struct frame_info *frame = get_current_frame ();
+
gdb_assert (get_frame_type (frame) == DUMMY_FRAME);
frame_pop (frame);
/* frame_pop() calls reinit_frame_cache as the last thing it does
signal_stop_update (int signo, int state)
{
int ret = signal_stop[signo];
+
signal_stop[signo] = state;
return ret;
}
signal_print_update (int signo, int state)
{
int ret = signal_print[signo];
+
signal_print[signo] = state;
return ret;
}
signal_pass_update (int signo, int state)
{
int ret = signal_program[signo];
+
signal_program[signo] = state;
return ret;
}
signals_info (char *signum_exp, int from_tty)
{
enum target_signal oursig;
+
sig_print_header ();
if (signum_exp)
&& gdbarch_get_siginfo_type_p (gdbarch))
{
struct type *type = gdbarch_get_siginfo_type (gdbarch);
+
return allocate_computed_value (type, &siginfo_value_funcs, NULL);
}
enum target_signal stop_signal;
CORE_ADDR stop_pc;
struct regcache *registers;
+
+ /* Format of SIGINFO or NULL if it is not present. */
+ struct gdbarch *siginfo_gdbarch;
+
+ /* The inferior format depends on SIGINFO_GDBARCH and it has a length of
+ TYPE_LENGTH (gdbarch_get_siginfo_type ()). For different gdbarch the
+ content would be invalid. */
+ gdb_byte *siginfo_data;
};
struct inferior_thread_state *
save_inferior_thread_state (void)
{
- struct inferior_thread_state *inf_state = XMALLOC (struct inferior_thread_state);
+ struct inferior_thread_state *inf_state;
struct thread_info *tp = inferior_thread ();
+ struct regcache *regcache = get_current_regcache ();
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ gdb_byte *siginfo_data = NULL;
+
+ if (gdbarch_get_siginfo_type_p (gdbarch))
+ {
+ struct type *type = gdbarch_get_siginfo_type (gdbarch);
+ size_t len = TYPE_LENGTH (type);
+ struct cleanup *back_to;
+
+ siginfo_data = xmalloc (len);
+ back_to = make_cleanup (xfree, siginfo_data);
+
+ if (target_read (¤t_target, TARGET_OBJECT_SIGNAL_INFO, NULL,
+ siginfo_data, 0, len) == len)
+ discard_cleanups (back_to);
+ else
+ {
+ /* Errors ignored. */
+ do_cleanups (back_to);
+ siginfo_data = NULL;
+ }
+ }
+
+ inf_state = XZALLOC (struct inferior_thread_state);
+
+ if (siginfo_data)
+ {
+ inf_state->siginfo_gdbarch = gdbarch;
+ inf_state->siginfo_data = siginfo_data;
+ }
inf_state->stop_signal = tp->stop_signal;
inf_state->stop_pc = stop_pc;
- inf_state->registers = regcache_dup (get_current_regcache ());
+ inf_state->registers = regcache_dup (regcache);
return inf_state;
}
restore_inferior_thread_state (struct inferior_thread_state *inf_state)
{
struct thread_info *tp = inferior_thread ();
+ struct regcache *regcache = get_current_regcache ();
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
tp->stop_signal = inf_state->stop_signal;
stop_pc = inf_state->stop_pc;
+ if (inf_state->siginfo_gdbarch == gdbarch)
+ {
+ struct type *type = gdbarch_get_siginfo_type (gdbarch);
+ size_t len = TYPE_LENGTH (type);
+
+ /* Errors ignored. */
+ target_write (¤t_target, TARGET_OBJECT_SIGNAL_INFO, NULL,
+ inf_state->siginfo_data, 0, len);
+ }
+
/* The inferior can be gone if the user types "print exit(0)"
(and perhaps other times). */
if (target_has_execution)
/* NB: The register write goes through to the target. */
- regcache_cpy (get_current_regcache (), inf_state->registers);
+ regcache_cpy (regcache, inf_state->registers);
regcache_xfree (inf_state->registers);
+ xfree (inf_state->siginfo_data);
xfree (inf_state);
}
{
bpstat stop_bpstat;
int stop_step;
- int stop_stack_dummy;
+ enum stop_stack_kind stop_stack_dummy;
int stopped_by_random_signal;
int stepping_over_breakpoint;
CORE_ADDR step_range_start;
return (ptid_get_lwp (ptid) == 0 && ptid_get_tid (ptid) == 0);
}
+int
+ptid_match (ptid_t ptid, ptid_t filter)
+{
+ /* Since both parameters have the same type, prevent easy mistakes
+ from happening. */
+ gdb_assert (!ptid_equal (ptid, minus_one_ptid)
+ && !ptid_equal (ptid, null_ptid));
+
+ if (ptid_equal (filter, minus_one_ptid))
+ return 1;
+ if (ptid_is_pid (filter)
+ && ptid_get_pid (ptid) == ptid_get_pid (filter))
+ return 1;
+ else if (ptid_equal (ptid, filter))
+ return 1;
+
+ return 0;
+}
+
/* restore_inferior_ptid() will be used by the cleanup machinery
to restore the inferior_ptid value saved in a call to
save_inferior_ptid(). */
restore_inferior_ptid (void *arg)
{
ptid_t *saved_ptid_ptr = arg;
+
inferior_ptid = *saved_ptid_ptr;
xfree (arg);
}
else if (!strcmp (exec_direction, exec_reverse))
execution_direction = EXEC_REVERSE;
}
+ else
+ {
+ exec_direction = exec_forward;
+ error (_("Target does not support this operation."));
+ }
}
static void
/* User interface for non-stop mode. */
int non_stop = 0;
-static int non_stop_1 = 0;
static void
set_non_stop (char *args, int from_tty,
{
int i;
int numsigs;
- struct cmd_list_element *c;
add_info ("signals", signals_info, _("\
What debugger does when program gets various signals.\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\
+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\
\n\
follow-exec-mode can be:\n\
\n\
- new - the debugger creates a new inferior and rebinds the process \n\
+ new - the debugger creates a new inferior and rebinds the process\n\
to this new inferior. The program the process was running before\n\
the exec call can be restarted afterwards by restarting the original\n\
inferior.\n\
minus_one_ptid = ptid_build (-1, 0, 0);
inferior_ptid = null_ptid;
target_last_wait_ptid = minus_one_ptid;
- displaced_step_ptid = null_ptid;
observer_attach_thread_ptid_changed (infrun_thread_ptid_changed);
observer_attach_thread_stop_requested (infrun_thread_stop_requested);
observer_attach_thread_exit (infrun_thread_thread_exit);
+ observer_attach_inferior_exit (infrun_inferior_exit);
/* Explicitly create without lookup, since that tries to create a
value with a void typed value, and when we get here, gdbarch
isn't initialized yet. At this point, we're quite sure there
isn't another convenience variable of the same name. */
create_internalvar_type_lazy ("_siginfo", siginfo_make_value);
+
+ add_setshow_boolean_cmd ("observer", no_class,
+ &observer_mode_1, _("\
+Set whether gdb controls the inferior in observer mode."), _("\
+Show whether gdb controls the inferior in observer mode."), _("\
+In observer mode, GDB can get data from the inferior, but not\n\
+affect its execution. Registers and memory may not be changed,\n\
+breakpoints may not be set, and the program cannot be interrupted\n\
+or signalled."),
+ set_observer_mode,
+ show_observer_mode,
+ &setlist,
+ &showlist);
}