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 Free Software Foundation, Inc.
+ 2008, 2009, 2010 Free Software Foundation, Inc.
This file is part of GDB.
#include "language.h"
#include "solib.h"
#include "main.h"
-
#include "gdb_assert.h"
#include "mi/mi-common.h"
#include "event-top.h"
+#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,
static int currently_stepping (struct thread_info *tp);
-static int currently_stepping_callback (struct thread_info *tp, void *data);
+static int currently_stepping_or_nexting_callback (struct thread_info *tp,
+ void *data);
static void xdb_handle_command (char *args, int from_tty);
void _initialize_infrun (void);
+void nullify_last_target_wait_ptid (void);
+
/* 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 ptid_t previous_inferior_ptid;
+/* Default behavior is to detach newly forked processes (legacy). */
+int detach_fork = 1;
+
int debug_displaced = 0;
static void
show_debug_displaced (struct ui_file *file, int from_tty,
#endif
-/* Convert the #defines into values. This is temporary until wfi control
- flow is completely sorted out. */
-
-#ifndef CANNOT_STEP_HW_WATCHPOINTS
-#define CANNOT_STEP_HW_WATCHPOINTS 0
-#else
-#undef CANNOT_STEP_HW_WATCHPOINTS
-#define CANNOT_STEP_HW_WATCHPOINTS 1
-#endif
-
/* Tables of how to react to signals; the user sets them. */
static unsigned char *signal_stop;
/* Value to pass to target_resume() to cause all threads to resume */
-#define RESUME_ALL (pid_to_ptid (-1))
+#define RESUME_ALL minus_one_ptid
/* Command list pointer for the "stop" placeholder. */
void init_infwait_state (void);
-/* This is used to remember when a fork, vfork or exec event
- was caught by a catchpoint, and thus the event is to be
- followed at the next resume of the inferior, and not
- immediately. */
-static struct
-{
- enum target_waitkind kind;
- struct
- {
- ptid_t parent_pid;
- ptid_t child_pid;
- }
- fork_event;
- char *execd_pathname;
-}
-pending_follow;
-
static const char follow_fork_mode_child[] = "child";
static const char follow_fork_mode_parent[] = "parent";
}
\f
+/* 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. */
+
static int
follow_fork (void)
{
int follow_child = (follow_fork_mode_string == follow_fork_mode_child);
+ int should_resume = 1;
+ struct thread_info *tp;
+
+ /* Copy user stepping state to the new inferior thread. FIXME: the
+ followed fork child thread should have a copy of most of the
+ parent thread structure's run control related fields, not just these.
+ Initialized to avoid "may be used uninitialized" warnings from gcc. */
+ struct breakpoint *step_resume_breakpoint = NULL;
+ CORE_ADDR step_range_start = 0;
+ CORE_ADDR step_range_end = 0;
+ struct frame_id step_frame_id = { 0 };
+
+ if (!non_stop)
+ {
+ ptid_t wait_ptid;
+ struct target_waitstatus wait_status;
+
+ /* Get the last target status returned by target_wait(). */
+ get_last_target_status (&wait_ptid, &wait_status);
- return target_follow_fork (follow_child);
+ /* If not stopped at a fork event, then there's nothing else to
+ do. */
+ if (wait_status.kind != TARGET_WAITKIND_FORKED
+ && wait_status.kind != TARGET_WAITKIND_VFORKED)
+ return 1;
+
+ /* Check if we switched over from WAIT_PTID, since the event was
+ reported. */
+ if (!ptid_equal (wait_ptid, minus_one_ptid)
+ && !ptid_equal (inferior_ptid, wait_ptid))
+ {
+ /* We did. Switch back to WAIT_PTID thread, to tell the
+ target to follow it (in either direction). We'll
+ afterwards refuse to resume, and inform the user what
+ happened. */
+ switch_to_thread (wait_ptid);
+ should_resume = 0;
+ }
+ }
+
+ tp = inferior_thread ();
+
+ /* If there were any forks/vforks that were caught and are now to be
+ followed, then do so now. */
+ switch (tp->pending_follow.kind)
+ {
+ case TARGET_WAITKIND_FORKED:
+ case TARGET_WAITKIND_VFORKED:
+ {
+ ptid_t parent, child;
+
+ /* If the user did a next/step, etc, over a fork call,
+ preserve the stepping state in the fork child. */
+ if (follow_child && should_resume)
+ {
+ step_resume_breakpoint
+ = clone_momentary_breakpoint (tp->step_resume_breakpoint);
+ step_range_start = tp->step_range_start;
+ step_range_end = tp->step_range_end;
+ step_frame_id = tp->step_frame_id;
+
+ /* For now, delete the parent's sr breakpoint, otherwise,
+ parent/child sr breakpoints are considered duplicates,
+ and the child version will not be installed. Remove
+ this when the breakpoints module becomes aware of
+ inferiors and address spaces. */
+ delete_step_resume_breakpoint (tp);
+ tp->step_range_start = 0;
+ tp->step_range_end = 0;
+ tp->step_frame_id = null_frame_id;
+ }
+
+ 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))
+ {
+ /* Target refused to follow, or there's some other reason
+ we shouldn't resume. */
+ should_resume = 0;
+ }
+ else
+ {
+ /* This pending follow fork event is now handled, one way
+ or another. The previous selected thread may be gone
+ from the lists by now, but if it is still around, need
+ to clear the pending follow request. */
+ tp = find_thread_ptid (parent);
+ if (tp)
+ tp->pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
+
+ /* This makes sure we don't try to apply the "Switched
+ over from WAIT_PID" logic above. */
+ nullify_last_target_wait_ptid ();
+
+ /* If we followed the child, switch to it... */
+ if (follow_child)
+ {
+ switch_to_thread (child);
+
+ /* ... and preserve the stepping state, in case the
+ user was stepping over the fork call. */
+ if (should_resume)
+ {
+ tp = inferior_thread ();
+ tp->step_resume_breakpoint = step_resume_breakpoint;
+ tp->step_range_start = step_range_start;
+ tp->step_range_end = step_range_end;
+ tp->step_frame_id = step_frame_id;
+ }
+ else
+ {
+ /* If we get here, it was because we're trying to
+ resume from a fork catchpoint, but, the user
+ has switched threads away from the thread that
+ forked. In that case, the resume command
+ issued is most likely not applicable to the
+ child, so just warn, and refuse to resume. */
+ warning (_("\
+Not resuming: switched threads before following fork child.\n"));
+ }
+
+ /* Reset breakpoints in the child as appropriate. */
+ follow_inferior_reset_breakpoints ();
+ }
+ else
+ switch_to_thread (parent);
+ }
+ }
+ break;
+ case TARGET_WAITKIND_SPURIOUS:
+ /* Nothing to follow. */
+ break;
+ default:
+ internal_error (__FILE__, __LINE__,
+ "Unexpected pending_follow.kind %d\n",
+ tp->pending_follow.kind);
+ break;
+ }
+
+ return should_resume;
}
void
insert_breakpoints ();
}
+/* The child has exited or execed: resume threads of the parent the
+ user wanted to be executing. */
+
+static int
+proceed_after_vfork_done (struct thread_info *thread,
+ void *arg)
+{
+ int pid = * (int *) arg;
+
+ if (ptid_get_pid (thread->ptid) == pid
+ && is_running (thread->ptid)
+ && !is_executing (thread->ptid)
+ && !thread->stop_requested
+ && thread->stop_signal == TARGET_SIGNAL_0)
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: resuming vfork parent thread %s\n",
+ target_pid_to_str (thread->ptid));
+
+ switch_to_thread (thread->ptid);
+ clear_proceed_status ();
+ proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
+ }
+
+ return 0;
+}
+
+/* Called whenever we notice an exec or exit event, to handle
+ detaching or resuming a vfork parent. */
+
+static void
+handle_vfork_child_exec_or_exit (int exec)
+{
+ struct inferior *inf = current_inferior ();
+
+ if (inf->vfork_parent)
+ {
+ int resume_parent = -1;
+
+ /* This exec or exit marks the end of the shared memory region
+ between the parent and the child. If the user wanted to
+ detach from the parent, now is the time. */
+
+ if (inf->vfork_parent->pending_detach)
+ {
+ struct thread_info *tp;
+ struct cleanup *old_chain;
+ struct program_space *pspace;
+ struct address_space *aspace;
+
+ /* follow-fork child, detach-on-fork on */
+
+ old_chain = make_cleanup_restore_current_thread ();
+
+ /* We're letting loose of the parent. */
+ tp = any_live_thread_of_process (inf->vfork_parent->pid);
+ switch_to_thread (tp->ptid);
+
+ /* We're about to detach from the parent, which implicitly
+ removes breakpoints from its address space. There's a
+ catch here: we want to reuse the spaces for the child,
+ but, parent/child are still sharing the pspace at this
+ point, although the exec in reality makes the kernel give
+ the child a fresh set of new pages. The problem here is
+ that the breakpoints module being unaware of this, would
+ likely chose the child process to write to the parent
+ address space. Swapping the child temporarily away from
+ the spaces has the desired effect. Yes, this is "sort
+ of" a hack. */
+
+ pspace = inf->pspace;
+ aspace = inf->aspace;
+ inf->aspace = NULL;
+ inf->pspace = NULL;
+
+ if (debug_infrun || info_verbose)
+ {
+ target_terminal_ours ();
+
+ if (exec)
+ 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);
+ }
+
+ target_detach (NULL, 0);
+
+ /* Put it back. */
+ inf->pspace = pspace;
+ inf->aspace = aspace;
+
+ do_cleanups (old_chain);
+ }
+ else if (exec)
+ {
+ /* We're staying attached to the parent, so, really give the
+ child a new address space. */
+ inf->pspace = add_program_space (maybe_new_address_space ());
+ inf->aspace = inf->pspace->aspace;
+ inf->removable = 1;
+ set_current_program_space (inf->pspace);
+
+ resume_parent = inf->vfork_parent->pid;
+
+ /* Break the bonds. */
+ inf->vfork_parent->vfork_child = NULL;
+ }
+ else
+ {
+ struct cleanup *old_chain;
+ struct program_space *pspace;
+
+ /* If this is a vfork child exiting, then the pspace and
+ aspaces were shared with the parent. Since we're
+ reporting the process exit, we'll be mourning all that is
+ found in the address space, and switching to null_ptid,
+ preparing to start a new inferior. But, since we don't
+ want to clobber the parent's address/program spaces, we
+ go ahead and create a new one for this exiting
+ inferior. */
+
+ /* Switch to null_ptid, so that clone_program_space doesn't want
+ to read the selected frame of a dead process. */
+ old_chain = save_inferior_ptid ();
+ inferior_ptid = null_ptid;
+
+ /* This inferior is dead, so avoid giving the breakpoints
+ module the option to write through to it (cloning a
+ program space resets breakpoints). */
+ inf->aspace = NULL;
+ inf->pspace = NULL;
+ pspace = add_program_space (maybe_new_address_space ());
+ set_current_program_space (pspace);
+ inf->removable = 1;
+ clone_program_space (pspace, inf->vfork_parent->pspace);
+ inf->pspace = pspace;
+ inf->aspace = pspace->aspace;
+
+ /* Put back inferior_ptid. We'll continue mourning this
+ inferior. */
+ do_cleanups (old_chain);
+
+ resume_parent = inf->vfork_parent->pid;
+ /* Break the bonds. */
+ inf->vfork_parent->vfork_child = NULL;
+ }
+
+ inf->vfork_parent = NULL;
+
+ gdb_assert (current_program_space == inf->pspace);
+
+ if (non_stop && resume_parent != -1)
+ {
+ /* If the user wanted the parent to be running, let it go
+ free now. */
+ struct cleanup *old_chain = make_cleanup_restore_current_thread ();
+
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "infrun: resuming vfork parent process %d\n",
+ resume_parent);
+
+ iterate_over_threads (proceed_after_vfork_done, &resume_parent);
+
+ do_cleanups (old_chain);
+ }
+ }
+}
+
+/* Enum strings for "set|show displaced-stepping". */
+
+static const char follow_exec_mode_new[] = "new";
+static const char follow_exec_mode_same[] = "same";
+static const char *follow_exec_mode_names[] =
+{
+ follow_exec_mode_new,
+ follow_exec_mode_same,
+ NULL,
+};
+
+static const char *follow_exec_mode_string = follow_exec_mode_same;
+static void
+show_follow_exec_mode_string (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ fprintf_filtered (file, _("Follow exec mode is \"%s\".\n"), value);
+}
+
/* EXECD_PATHNAME is assumed to be non-NULL. */
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 ();
/* 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
that may write the bp's "shadow contents" (the instruction
value that was overwritten witha TRAP instruction). Since
we now have a new a.out, those shadow contents aren't valid. */
+
+ mark_breakpoints_out ();
+
update_breakpoints_after_exec ();
/* If there was one, it's gone now. We cannot truly step-to-next
th->step_range_start = 0;
th->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. */
+ th->stop_requested = 0;
+
/* What is this a.out's name? */
- printf_unfiltered (_("Executing new program: %s\n"), execd_pathname);
+ printf_unfiltered (_("%s is executing new program: %s\n"),
+ target_pid_to_str (inferior_ptid),
+ execd_pathname);
/* We've followed the inferior through an exec. Therefore, the
inferior has essentially been killed & reborn. */
char *name = alloca (strlen (gdb_sysroot)
+ strlen (execd_pathname)
+ 1);
+
strcpy (name, gdb_sysroot);
strcat (name, execd_pathname);
execd_pathname = name;
}
- /* That a.out is now the one to use. */
- exec_file_attach (execd_pathname, 0);
-
/* Reset the shared library package. This ensures that we get a
shlib event when the child reaches "_start", at which point the
dld will have had a chance to initialize the child. */
previous incarnation of this process. */
no_shared_libraries (NULL, 0);
+ if (follow_exec_mode_string == follow_exec_mode_new)
+ {
+ struct program_space *pspace;
+
+ /* The user wants to keep the old inferior and program spaces
+ around. Create a new fresh one, and switch to it. */
+
+ inf = add_inferior (current_inferior ()->pid);
+ pspace = add_program_space (maybe_new_address_space ());
+ inf->pspace = pspace;
+ inf->aspace = pspace->aspace;
+
+ exit_inferior_num_silent (current_inferior ()->num);
+
+ set_current_inferior (inf);
+ set_current_program_space (pspace);
+ }
+
+ gdb_assert (current_program_space == inf->pspace);
+
+ /* 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);
#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 ();
+
/* 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 architecture the thread had when we stepped it. */
-static struct gdbarch *displaced_step_gdbarch;
+ /* The process this displaced step state refers to. */
+ int pid;
-/* The closure provided gdbarch_displaced_step_copy_insn, to be used
- for post-step cleanup. */
-static struct displaced_step_closure *displaced_step_closure;
+ /* 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. */
+ struct gdbarch *step_gdbarch;
+
+ /* 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 list of states of processes involved in displaced stepping
+ presently. */
+static struct displaced_step_inferior_state *displaced_step_inferior_states;
+
+/* 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;
+ }
-/* The address of the original instruction, and the copy we made. */
-static CORE_ADDR displaced_step_original, displaced_step_copy;
+ prev_next_p = &it->next;
+ it = *prev_next_p;
+ }
+}
-/* Saved contents of copy area. */
-static gdb_byte *displaced_step_saved_copy;
+static void
+infrun_inferior_exit (struct inferior *inf)
+{
+ remove_displaced_stepping_state (inf->pid);
+}
/* Enum strings for "set|show displaced-stepping". */
return (((can_use_displaced_stepping == can_use_displaced_stepping_auto
&& non_stop)
|| can_use_displaced_stepping == can_use_displaced_stepping_on)
- && gdbarch_displaced_step_copy_insn_p (gdbarch));
+ && gdbarch_displaced_step_copy_insn_p (gdbarch)
+ && !RECORD_IS_USED);
}
/* 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
-cleanup_displaced_step_closure (void *ptr)
+displaced_step_clear_cleanup (void *arg)
{
- struct displaced_step_closure *closure = ptr;
+ struct displaced_step_inferior_state *state = arg;
- gdbarch_displaced_step_free_closure (current_gdbarch, closure);
+ 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. */
- if (!ptid_equal (displaced_step_ptid, null_ptid))
+ displaced = add_displaced_stepping_state (ptid_get_pid (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 0x%s: ",
- paddr_nz (copy));
- displaced_step_dump_bytes (gdb_stdlog, displaced_step_saved_copy, len);
+ fprintf_unfiltered (gdb_stdlog, "displaced: saved %s: ",
+ paddress (gdbarch, copy));
+ displaced_step_dump_bytes (gdb_stdlog,
+ displaced->step_saved_copy,
+ len);
};
closure = gdbarch_displaced_step_copy_insn (gdbarch,
/* We don't support the fully-simulated case at present. */
gdb_assert (closure);
- make_cleanup (cleanup_displaced_step_closure, closure);
+ /* 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;
+
+ make_cleanup (displaced_step_clear_cleanup, displaced);
/* Resume execution at the copy. */
regcache_write_pc (regcache, copy);
do_cleanups (old_cleanups);
if (debug_displaced)
- fprintf_unfiltered (gdb_stdlog, "displaced: displaced pc to 0x%s\n",
- paddr_nz (copy));
+ fprintf_unfiltered (gdb_stdlog, "displaced: displaced pc to %s\n",
+ paddress (gdbarch, copy));
- /* 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;
return 1;
}
-static void
-displaced_step_clear_cleanup (void *ignore)
-{
- displaced_step_clear ();
-}
-
static void
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 0x%s\n",
- paddr_nz (displaced_step_copy));
+ fprintf_unfiltered (gdb_stdlog, "displaced: restored %s\n",
+ 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;
+ struct regcache *regcache;
+ struct gdbarch *gdbarch;
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);
- actual_pc = read_pc ();
+ regcache = get_thread_regcache (ptid);
+ actual_pc = regcache_read_pc (regcache);
+ aspace = get_regcache_aspace (regcache);
- if (breakpoint_here_p (actual_pc))
+ if (breakpoint_here_p (aspace, actual_pc))
{
if (debug_displaced)
fprintf_unfiltered (gdb_stdlog,
displaced_step_prepare (ptid);
+ gdbarch = get_regcache_arch (regcache);
+
if (debug_displaced)
{
+ CORE_ADDR actual_pc = regcache_read_pc (regcache);
gdb_byte buf[4];
- fprintf_unfiltered (gdb_stdlog, "displaced: run 0x%s: ",
- paddr_nz (actual_pc));
+ fprintf_unfiltered (gdb_stdlog, "displaced: run %s: ",
+ paddress (gdbarch, actual_pc));
read_memory (actual_pc, buf, sizeof (buf));
displaced_step_dump_bytes (gdb_stdlog, buf, sizeof (buf));
}
- target_resume (ptid, 1, TARGET_SIGNAL_0);
+ 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);
/* Done, we're stepping a thread. */
break;
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
}
}
+/* True if execution commands resume all threads of all processes by
+ default; otherwise, resume only threads of the current inferior
+ process. */
+int sched_multi = 0;
+
+/* Try to setup for software single stepping over the specified location.
+ Return 1 if target_resume() should use hardware single step.
+
+ GDBARCH the current gdbarch.
+ PC the location to step over. */
+
+static int
+maybe_software_singlestep (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+ int hw_step = 1;
+
+ if (gdbarch_software_single_step_p (gdbarch)
+ && 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;
+}
/* Resume the inferior, but allow a QUIT. This is useful if the user
wants to interrupt some lengthy single-stepping operation
{
int should_resume = 1;
struct cleanup *old_cleanups = make_cleanup (resume_cleanups, 0);
-
- /* Note that these must be reset if we follow a fork below. */
struct regcache *regcache = get_current_regcache ();
struct gdbarch *gdbarch = get_regcache_arch (regcache);
struct thread_info *tp = inferior_thread ();
CORE_ADDR pc = regcache_read_pc (regcache);
+ struct address_space *aspace = get_regcache_aspace (regcache);
QUIT;
"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
breakpoints can't be removed. So we have to test for it here. */
- if (breakpoint_here_p (pc) == permanent_breakpoint_here)
+ if (breakpoint_here_p (aspace, pc) == permanent_breakpoint_here)
{
if (gdbarch_skip_permanent_breakpoint_p (gdbarch))
gdbarch_skip_permanent_breakpoint (gdbarch, regcache);
comments in the handle_inferior event for dealing with 'random
signals' explain what we do instead. */
if (use_displaced_stepping (gdbarch)
- && tp->trap_expected
+ && (tp->trap_expected
+ || (step && gdbarch_software_single_step_p (gdbarch)))
&& sig == TARGET_SIGNAL_0)
{
+ struct displaced_step_inferior_state *displaced;
+
if (!displaced_step_prepare (inferior_ptid))
{
/* Got placed in displaced stepping queue. Will be resumed
discard_cleanups (old_cleanups);
return;
}
- }
- if (step && gdbarch_software_single_step_p (gdbarch))
- {
- /* Do it the hard way, w/temp breakpoints */
- if (gdbarch_software_single_step (gdbarch, get_current_frame ()))
- {
- /* ...and don't ask hardware to do it. */
- step = 0;
- /* and 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;
- }
+ displaced = get_displaced_stepping_state (ptid_get_pid (inferior_ptid));
+ step = gdbarch_displaced_step_hw_singlestep (gdbarch,
+ displaced->step_closure);
}
- /* If there were any forks/vforks/execs that were caught and are
- now to be followed, then do so. */
- switch (pending_follow.kind)
- {
- case TARGET_WAITKIND_FORKED:
- case TARGET_WAITKIND_VFORKED:
- pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
- if (follow_fork ())
- should_resume = 0;
-
- /* Following a child fork will change our notion of current
- thread. */
- tp = inferior_thread ();
- regcache = get_current_regcache ();
- gdbarch = get_regcache_arch (regcache);
- pc = regcache_read_pc (regcache);
- break;
-
- case TARGET_WAITKIND_EXECD:
- /* follow_exec is called as soon as the exec event is seen. */
- pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
- break;
-
- default:
- break;
- }
-
- /* Install inferior's terminal modes. */
- target_terminal_inferior ();
+ /* Do we need to do it the hard way, w/temp breakpoints? */
+ else if (step)
+ step = maybe_software_singlestep (gdbarch, pc);
if (should_resume)
{
ptid_t resume_ptid;
- resume_ptid = RESUME_ALL; /* Default */
-
/* 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));
+ /* 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. */
+
+ /* By default, resume all threads of all processes. */
+ 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));
+ }
+
+ /* Maybe resume a single thread after all. */
if (singlestep_breakpoints_inserted_p
&& stepping_past_singlestep_breakpoint)
{
to support, and has no value. */
resume_ptid = inferior_ptid;
}
-
- if ((step || singlestep_breakpoints_inserted_p)
- && tp->trap_expected)
+ else if ((step || singlestep_breakpoints_inserted_p)
+ && tp->trap_expected)
{
/* We're allowing a thread to run past a breakpoint it has
hit, by single-stepping the thread with the breakpoint
breakpoint, not just the one at PC. */
resume_ptid = inferior_ptid;
}
-
- if (non_stop)
+ else if (non_stop)
{
/* With non-stop mode on, threads are always handled
individually. */
/* 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 (pc))
+ if (step && breakpoint_inserted_here_p (aspace, pc))
step = 0;
}
&& tp->trap_expected)
{
struct regcache *resume_regcache = get_thread_regcache (resume_ptid);
+ struct gdbarch *resume_gdbarch = get_regcache_arch (resume_regcache);
CORE_ADDR actual_pc = regcache_read_pc (resume_regcache);
gdb_byte buf[4];
- fprintf_unfiltered (gdb_stdlog, "displaced: run 0x%s: ",
- paddr_nz (actual_pc));
+ fprintf_unfiltered (gdb_stdlog, "displaced: run %s: ",
+ paddress (resume_gdbarch, actual_pc));
read_memory (actual_pc, buf, sizeof (buf));
displaced_step_dump_bytes (gdb_stdlog, buf, sizeof (buf));
}
+ /* 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->stop_signal = TARGET_SIGNAL_0;
tp->step_range_start = 0;
tp->step_range_end = 0;
tp->step_frame_id = null_frame_id;
+ tp->step_stack_frame_id = null_frame_id;
tp->step_over_calls = STEP_OVER_UNDEBUGGABLE;
tp->stop_requested = 0;
void
clear_proceed_status (void)
{
+ if (!non_stop)
+ {
+ /* In all-stop mode, delete the per-thread status of all
+ threads, even if inferior_ptid is null_ptid, there may be
+ threads on the list. E.g., we may be launching a new
+ process, while selecting the executable. */
+ iterate_over_threads (clear_proceed_status_callback, NULL);
+ }
+
if (!ptid_equal (inferior_ptid, null_ptid))
{
struct inferior *inferior;
if (non_stop)
{
- /* If in non-stop mode, only delete the per-thread status
- of the current thread. */
+ /* If in non-stop mode, only delete the per-thread status of
+ the current thread. */
clear_proceed_status_thread (inferior_thread ());
}
- else
- {
- /* In all-stop mode, delete the per-thread status of
- *all* threads. */
- iterate_over_threads (clear_proceed_status_callback, NULL);
- }
-
+
inferior = current_inferior ();
inferior->stop_soon = NO_STOP_QUIETLY;
}
stop_after_trap = 0;
- breakpoint_proceeded = 1; /* We're about to proceed... */
+
+ observer_notify_about_to_proceed ();
if (stop_registers)
{
}
}
-/* This should be suitable for any targets that support threads. */
+/* Check the current thread against the thread that reported the most recent
+ event. If a step-over is required return TRUE and set the current thread
+ to the old thread. Otherwise return FALSE.
+
+ This should be suitable for any targets that support threads. */
static int
prepare_to_proceed (int step)
{
ptid_t wait_ptid;
struct target_waitstatus wait_status;
+ int schedlock_enabled;
+
+ /* With non-stop mode on, threads are always handled individually. */
+ gdb_assert (! non_stop);
/* Get the last target status returned by target_wait(). */
get_last_target_status (&wait_ptid, &wait_status);
/* 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;
}
+ schedlock_enabled = (scheduler_mode == schedlock_on
+ || (scheduler_mode == schedlock_step
+ && step));
+
+ /* Don't switch over to WAIT_PTID if scheduler locking is on. */
+ if (schedlock_enabled)
+ return 0;
+
+ /* Don't switch over if we're about to resume some other process
+ other than WAIT_PTID's, and schedule-multiple is off. */
+ if (!sched_multi
+ && ptid_get_pid (wait_ptid) != ptid_get_pid (inferior_ptid))
+ return 0;
+
/* Switched over from WAIT_PID. */
if (!ptid_equal (wait_ptid, minus_one_ptid)
&& !ptid_equal (inferior_ptid, wait_ptid))
{
struct regcache *regcache = get_thread_regcache (wait_ptid);
- if (breakpoint_here_p (regcache_read_pc (regcache)))
+ if (breakpoint_here_p (get_regcache_aspace (regcache),
+ regcache_read_pc (regcache)))
{
/* If stepping, remember current thread to switch back to. */
if (step)
void
proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
{
- struct regcache *regcache = get_current_regcache ();
- struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ struct regcache *regcache;
+ struct gdbarch *gdbarch;
struct thread_info *tp;
- CORE_ADDR pc = regcache_read_pc (regcache);
+ CORE_ADDR pc;
+ struct address_space *aspace;
int oneproc = 0;
+ /* If we're stopped at a fork/vfork, follow the branch set by the
+ "set follow-fork-mode" command; otherwise, we'll just proceed
+ resuming the current thread. */
+ if (!follow_fork ())
+ {
+ /* The target for some reason decided not to resume. */
+ normal_stop ();
+ return;
+ }
+
+ regcache = get_current_regcache ();
+ gdbarch = get_regcache_arch (regcache);
+ aspace = get_regcache_aspace (regcache);
+ pc = regcache_read_pc (regcache);
+
if (step > 0)
step_start_function = find_pc_function (pc);
if (step < 0)
if (addr == (CORE_ADDR) -1)
{
- if (pc == stop_pc && breakpoint_here_p (pc)
+ if (pc == stop_pc && breakpoint_here_p (aspace, pc)
&& 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=0x%s, signal=%d, step=%d)\n",
- paddr_nz (addr), siggnal, step);
+ "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
&& !ptid_equal (last_ptid, null_ptid)
&& !ptid_equal (last_ptid, minus_one_ptid))
{
- last_thread = find_thread_pid (last_ptid);
+ last_thread = find_thread_ptid (last_ptid);
if (last_thread)
{
tp->stop_signal = last_thread->stop_signal;
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;
breakpoint_init_inferior (inf_starting);
- /* The first resume is not following a fork/vfork/exec. */
- pending_follow.kind = TARGET_WAITKIND_SPURIOUS; /* I.e., none. */
-
clear_proceed_status ();
stepping_past_singlestep_breakpoint = 0;
previous_inferior_ptid = null_ptid;
init_infwait_state ();
- displaced_step_clear ();
+ /* Discard any skipped inlined frames. */
+ clear_inline_frame_state (minus_one_ptid);
}
\f
int wait_some_more;
};
-void init_execution_control_state (struct execution_control_state *ecs);
-
-void handle_inferior_event (struct execution_control_state *ecs);
+static void handle_inferior_event (struct execution_control_state *ecs);
-static void handle_step_into_function (struct execution_control_state *ecs);
-static void handle_step_into_function_backward (struct execution_control_state *ecs);
+static void handle_step_into_function (struct gdbarch *gdbarch,
+ struct execution_control_state *ecs);
+static void handle_step_into_function_backward (struct gdbarch *gdbarch,
+ struct execution_control_state *ecs);
static void insert_step_resume_breakpoint_at_frame (struct frame_info *step_frame);
static void insert_step_resume_breakpoint_at_caller (struct frame_info *);
-static void insert_step_resume_breakpoint_at_sal (struct symtab_and_line sr_sal,
+static void insert_step_resume_breakpoint_at_sal (struct gdbarch *gdbarch,
+ struct symtab_and_line sr_sal,
struct frame_id sr_id);
-static void insert_longjmp_resume_breakpoint (CORE_ADDR);
+static void insert_longjmp_resume_breakpoint (struct gdbarch *, CORE_ADDR);
static void stop_stepping (struct execution_control_state *ecs);
static void prepare_to_wait (struct execution_control_state *ecs);
have consistent output as if the stop event had been
reported. */
ecs->ptid = info->ptid;
- ecs->event_thread = find_thread_pid (info->ptid);
+ ecs->event_thread = find_thread_ptid (info->ptid);
ecs->ws.kind = TARGET_WAITKIND_STOPPED;
ecs->ws.value.sig = TARGET_SIGNAL_0;
Cleanup local state that assumed the PTID was to be resumed, and
report the stop to the frontend. */
-void
+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);
}
+static void
+infrun_thread_thread_exit (struct thread_info *tp, int silent)
+{
+ if (ptid_equal (target_last_wait_ptid, tp->ptid))
+ nullify_last_target_wait_ptid ();
+}
+
/* Callback for iterate_over_threads. */
static int
longjmp-resume breakpoint of the thread that just stopped
stepping. */
struct thread_info *tp = inferior_thread ();
+
delete_step_resume_breakpoint (tp);
}
else
iterate_over_threads (delete_step_resume_breakpoint_callback, NULL);
}
-/* A cleanup wrapper. */
+/* A cleanup wrapper. */
+
+static void
+delete_step_thread_step_resume_breakpoint_cleanup (void *arg)
+{
+ delete_step_thread_step_resume_breakpoint ();
+}
+
+/* Pretty print the results of target_wait, for debugging purposes. */
+
+static void
+print_target_wait_results (ptid_t waiton_ptid, ptid_t result_ptid,
+ const struct target_waitstatus *ws)
+{
+ char *status_string = target_waitstatus_to_string (ws);
+ struct ui_file *tmp_stream = mem_fileopen ();
+ char *text;
+
+ /* The text is split over several lines because it was getting too long.
+ Call fprintf_unfiltered (gdb_stdlog) once so that the text is still
+ output as a unit; we want only one timestamp printed if debug_timestamp
+ is set. */
+
+ fprintf_unfiltered (tmp_stream,
+ "infrun: target_wait (%d", PIDGET (waiton_ptid));
+ if (PIDGET (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",
+ PIDGET (result_ptid), target_pid_to_str (result_ptid));
+ fprintf_unfiltered (tmp_stream,
+ "infrun: %s\n",
+ status_string);
+
+ text = ui_file_xstrdup (tmp_stream, NULL);
+
+ /* This uses %s in part to handle %'s in the text, but also to avoid
+ a gcc error: the format attribute requires a string literal. */
+ fprintf_unfiltered (gdb_stdlog, "%s", text);
+
+ xfree (status_string);
+ xfree (text);
+ 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);
-static void
-delete_step_thread_step_resume_breakpoint_cleanup (void *arg)
-{
- delete_step_thread_step_resume_breakpoint ();
+ /* 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.
ecs = &ecss;
memset (ecs, 0, sizeof (*ecs));
- overlay_cache_invalid = 1;
-
/* We'll update this if & when we switch to a new thread. */
previous_inferior_ptid = inferior_ptid;
- /* 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 ();
-
while (1)
{
+ struct cleanup *old_chain;
+
+ /* 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. */
+
+ overlay_cache_invalid = 1;
+ registers_changed ();
+
if (deprecated_target_wait_hook)
- ecs->ptid = deprecated_target_wait_hook (waiton_ptid, &ecs->ws);
+ ecs->ptid = deprecated_target_wait_hook (waiton_ptid, &ecs->ws, 0);
else
- ecs->ptid = target_wait (waiton_ptid, &ecs->ws);
+ ecs->ptid = target_wait (waiton_ptid, &ecs->ws, 0);
+
+ if (debug_infrun)
+ print_target_wait_results (waiton_ptid, ecs->ptid, &ecs->ws);
if (treat_exec_as_sigtrap && ecs->ws.kind == TARGET_WAITKIND_EXECD)
{
ecs->ws.value.sig = TARGET_SIGNAL_TRAP;
}
+ /* 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);
+
+ if (ecs->ws.kind == TARGET_WAITKIND_SYSCALL_ENTRY
+ || ecs->ws.kind == TARGET_WAITKIND_SYSCALL_RETURN)
+ ecs->ws.value.syscall_number = UNKNOWN_SYSCALL;
+
/* 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;
}
struct execution_control_state ecss;
struct execution_control_state *ecs = &ecss;
struct cleanup *old_chain = make_cleanup (null_cleanup, NULL);
+ struct cleanup *ts_old_chain;
int was_sync = sync_execution;
memset (ecs, 0, sizeof (*ecs));
- overlay_cache_invalid = 1;
-
- /* We can only rely on wait_for_more being correct before handling
- the event in all-stop, but previous_inferior_ptid isn't used in
- non-stop. */
- if (!ecs->wait_some_more)
- /* We'll update this if & when we switch to a new thread. */
- previous_inferior_ptid = inferior_ptid;
+ /* We'll update this if & when we switch to a new thread. */
+ previous_inferior_ptid = inferior_ptid;
if (non_stop)
/* In non-stop mode, the user/frontend should not notice a thread
targets that provide critical registers as part of their normal
status mechanism. */
+ overlay_cache_invalid = 1;
registers_changed ();
if (deprecated_target_wait_hook)
ecs->ptid =
- deprecated_target_wait_hook (waiton_ptid, &ecs->ws);
+ deprecated_target_wait_hook (waiton_ptid, &ecs->ws, TARGET_WNOHANG);
else
- ecs->ptid = target_wait (waiton_ptid, &ecs->ws);
+ ecs->ptid = target_wait (waiton_ptid, &ecs->ws, TARGET_WNOHANG);
+
+ if (debug_infrun)
+ print_target_wait_results (waiton_ptid, ecs->ptid, &ecs->ws);
if (non_stop
&& ecs->ws.kind != TARGET_WAITKIND_IGNORE
thread. */
context_switch (ecs->ptid);
+ /* If an error happens while handling the event, propagate GDB's
+ knowledge of the executing state to the frontend/user running
+ state. */
+ if (!non_stop)
+ ts_old_chain = make_cleanup (finish_thread_state_cleanup, &minus_one_ptid);
+ else
+ ts_old_chain = make_cleanup (finish_thread_state_cleanup, &ecs->ptid);
+
/* Now figure out what to do with the result of the result. */
handle_inferior_event (ecs);
inferior_event_handler (INF_EXEC_COMPLETE, NULL);
}
+ /* No error, don't finish the thread states yet. */
+ discard_cleanups (ts_old_chain);
+
/* Revert thread and frame. */
do_cleanups (old_chain);
display_gdb_prompt (0);
}
-/* Prepare an execution control state for looping through a
- wait_for_inferior-type loop. */
-
+/* Record the frame and location we're currently stepping through. */
void
-init_execution_control_state (struct execution_control_state *ecs)
+set_step_info (struct frame_info *frame, struct symtab_and_line sal)
{
- ecs->random_signal = 0;
+ struct thread_info *tp = inferior_thread ();
+
+ tp->step_frame_id = get_frame_id (frame);
+ tp->step_stack_frame_id = get_stack_frame_id (frame);
+
+ tp->current_symtab = sal.symtab;
+ tp->current_line = sal.line;
}
/* Clear context switchable stepping state. */
void
init_thread_stepping_state (struct thread_info *tss)
{
- struct symtab_and_line sal;
-
tss->stepping_over_breakpoint = 0;
tss->step_after_step_resume_breakpoint = 0;
tss->stepping_through_solib_after_catch = 0;
tss->stepping_through_solib_catchpoints = NULL;
-
- sal = find_pc_line (tss->prev_pc, 0);
- tss->current_line = sal.line;
- tss->current_symtab = sal.symtab;
}
/* Return the cached copy of the last pid/waitstatus returned by
{
struct regcache *regcache;
struct gdbarch *gdbarch;
+ struct address_space *aspace;
CORE_ADDR breakpoint_pc;
/* If we've hit a breakpoint, we'll normally be stopped with SIGTRAP. If
if (gdbarch_decr_pc_after_break (gdbarch) == 0)
return;
+ aspace = get_regcache_aspace (regcache);
+
/* Find the location where (if we've hit a breakpoint) the
breakpoint would be. */
breakpoint_pc = regcache_read_pc (regcache)
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. */
- if (software_breakpoint_inserted_here_p (breakpoint_pc)
- || (non_stop && moribund_breakpoint_here_p (breakpoint_pc)))
+ if (software_breakpoint_inserted_here_p (aspace, breakpoint_pc)
+ || (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 ();
+
/* When using hardware single-step, a SIGTRAP is reported for both
a completed single-step and a software breakpoint. Need to
differentiate between the two, as the latter needs adjusting
|| !currently_stepping (ecs->event_thread)
|| ecs->event_thread->prev_pc == breakpoint_pc)
regcache_write_pc (regcache, breakpoint_pc);
+
+ if (RECORD_IS_USED)
+ do_cleanups (old_cleanups);
}
}
error_is_running ();
}
+static int
+stepped_in_from (struct frame_info *frame, struct frame_id step_frame_id)
+{
+ for (frame = get_prev_frame (frame);
+ frame != NULL;
+ frame = get_prev_frame (frame))
+ {
+ if (frame_id_eq (get_frame_id (frame), step_frame_id))
+ return 1;
+ if (get_frame_type (frame) != INLINE_FRAME)
+ break;
+ }
+
+ return 0;
+}
+
+/* Auxiliary function that handles syscall entry/return events.
+ It returns 1 if the inferior should keep going (and GDB
+ should ignore the event), or 0 if the event deserves to be
+ processed. */
+
+static int
+handle_syscall_event (struct execution_control_state *ecs)
+{
+ struct regcache *regcache;
+ struct gdbarch *gdbarch;
+ int syscall_number;
+
+ if (!ptid_equal (ecs->ptid, inferior_ptid))
+ context_switch (ecs->ptid);
+
+ regcache = get_thread_regcache (ecs->ptid);
+ gdbarch = get_regcache_arch (regcache);
+ syscall_number = gdbarch_get_syscall_number (gdbarch, ecs->ptid);
+ stop_pc = regcache_read_pc (regcache);
+
+ target_last_waitstatus.value.syscall_number = syscall_number;
+
+ if (catch_syscall_enabled () > 0
+ && catching_syscall_number (syscall_number) > 0)
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "infrun: syscall number = '%d'\n",
+ syscall_number);
+
+ ecs->event_thread->stop_bpstat
+ = bpstat_stop_status (get_regcache_aspace (regcache),
+ stop_pc, ecs->ptid);
+ ecs->random_signal = !bpstat_explains_signal (ecs->event_thread->stop_bpstat);
+
+ if (!ecs->random_signal)
+ {
+ /* Catchpoint hit. */
+ ecs->event_thread->stop_signal = TARGET_SIGNAL_TRAP;
+ return 0;
+ }
+ }
+
+ /* If no catchpoint triggered for this, then keep going. */
+ ecs->event_thread->stop_signal = TARGET_SIGNAL_0;
+ keep_going (ecs);
+ return 1;
+}
+
/* Given an execution control state that has been freshly filled in
by an event from the inferior, figure out what it means and take
appropriate action. */
-void
+static void
handle_inferior_event (struct execution_control_state *ecs)
{
+ struct frame_info *frame;
+ struct gdbarch *gdbarch;
int sw_single_step_trap_p = 0;
int stopped_by_watchpoint;
int stepped_after_stopped_by_watchpoint = 0;
struct symtab_and_line stop_pc_sal;
enum stop_kind stop_soon;
+ if (ecs->ws.kind == TARGET_WAITKIND_IGNORE)
+ {
+ /* We had an event in the inferior, but we are not interested in
+ handling it at this level. The lower layers have already
+ done what needs to be done, if anything.
+
+ One of the possible circumstances for this is when the
+ inferior produces output for the console. The inferior has
+ not stopped, and we are ignoring the event. Another possible
+ circumstance is any event which the lower level knows will be
+ reported multiple times without an intervening resume. */
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_IGNORE\n");
+ prepare_to_wait (ecs);
+ return;
+ }
+
if (ecs->ws.kind != TARGET_WAITKIND_EXITED
- && ecs->ws.kind != TARGET_WAITKIND_SIGNALLED
- && ecs->ws.kind != TARGET_WAITKIND_IGNORE)
+ && 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 */
&& ecs->ws.kind != TARGET_WAITKIND_SIGNALLED && ecs->new_thread_event)
add_thread (ecs->ptid);
- ecs->event_thread = find_thread_pid (ecs->ptid);
+ ecs->event_thread = find_thread_ptid (ecs->ptid);
/* Dependent on valid ECS->EVENT_THREAD. */
adjust_pc_after_break (ecs);
/* Dependent on the current PC value modified by adjust_pc_after_break. */
reinit_frame_cache ();
- if (ecs->ws.kind != TARGET_WAITKIND_IGNORE)
+ 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))
{
- breakpoint_retire_moribund ();
+ struct regcache *regcache = get_thread_regcache (ecs->ptid);
- /* Mark the non-executing threads accordingly. */
- if (!non_stop
- || ecs->ws.kind == TARGET_WAITKIND_EXITED
- || ecs->ws.kind == TARGET_WAITKIND_SIGNALLED)
- set_executing (pid_to_ptid (-1), 0);
- else
- set_executing (ecs->ptid, 0);
+ 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
+ we're handling a process exit in non-stop mode, there's nothing
+ to do, as threads of the dead process are gone, and threads of
+ any other process were left running. */
+ if (!non_stop)
+ set_executing (minus_one_ptid, 0);
+ else if (ecs->ws.kind != TARGET_WAITKIND_SIGNALLED
+ && ecs->ws.kind != TARGET_WAITKIND_EXITED)
+ set_executing (inferior_ptid, 0);
+
switch (infwait_state)
{
case infwait_thread_hop_state:
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: infwait_thread_hop_state\n");
- /* Cancel the waiton_ptid. */
- waiton_ptid = pid_to_ptid (-1);
break;
case infwait_normal_state:
default:
internal_error (__FILE__, __LINE__, _("bad switch"));
}
+
infwait_state = infwait_normal_state;
+ waiton_ptid = pid_to_ptid (-1);
switch (ecs->ws.kind)
{
operations such as address => section name and hence
require the table to contain all sections (including
those found in shared libraries). */
- /* NOTE: cagney/2003-11-25: Pass current_target and not
- exec_ops to SOLIB_ADD. This is because current GDB is
- only tooled to propagate section_table changes out from
- the "current_target" (see target_resize_to_sections), and
- not up from the exec stratum. This, of course, isn't
- right. "infrun.c" should only interact with the
- exec/process stratum, instead relying on the target stack
- to propagate relevant changes (stop, section table
- changed, ...) up to other layers. */
#ifdef SOLIB_ADD
SOLIB_ADD (NULL, 0, ¤t_target, auto_solib_add);
#else
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;
}
case TARGET_WAITKIND_EXITED:
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_EXITED\n");
+ inferior_ptid = ecs->ptid;
+ set_current_inferior (find_inferior_pid (ptid_get_pid (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 */
print_stop_reason (EXITED, ecs->ws.value.integer);
/* Record the exit code in the convenience variable $_exitcode, so
that the user can inspect this again later. */
- set_internalvar (lookup_internalvar ("_exitcode"),
- value_from_longest (builtin_type_int32,
- (LONGEST) ecs->ws.value.integer));
+ set_internalvar_integer (lookup_internalvar ("_exitcode"),
+ (LONGEST) ecs->ws.value.integer);
gdb_flush (gdb_stdout);
target_mourn_inferior ();
singlestep_breakpoints_inserted_p = 0;
case TARGET_WAITKIND_SIGNALLED:
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SIGNALLED\n");
+ inferior_ptid = ecs->ptid;
+ set_current_inferior (find_inferior_pid (ptid_get_pid (ecs->ptid)));
+ set_current_program_space (current_inferior ()->pspace);
+ handle_vfork_child_exec_or_exit (0);
stop_print_frame = 0;
target_terminal_ours (); /* Must do this before mourn anyway */
case TARGET_WAITKIND_VFORKED:
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_FORKED\n");
- pending_follow.kind = ecs->ws.kind;
-
- pending_follow.fork_event.parent_pid = ecs->ptid;
- pending_follow.fork_event.child_pid = ecs->ws.value.related_pid;
if (!ptid_equal (ecs->ptid, inferior_ptid))
{
reinit_frame_cache ();
}
- stop_pc = read_pc ();
+ /* Immediately detach breakpoints from the child before there's
+ any chance of letting the user delete breakpoints from the
+ breakpoint lists. If we don't do this early, it's easy to
+ leave left over traps in the child, vis: "break foo; catch
+ fork; c; <fork>; del; c; <child calls foo>". We only follow
+ the fork on the last `continue', and by that time the
+ breakpoint at "foo" is long gone from the breakpoint table.
+ If we vforked, then we don't need to unpatch here, since both
+ parent and child are sharing the same memory pages; we'll
+ need to unpatch at follow/detach time instead to be certain
+ that new breakpoints added between catchpoint hit time and
+ vfork follow are detached. */
+ if (ecs->ws.kind != TARGET_WAITKIND_VFORKED)
+ {
+ int child_pid = ptid_get_pid (ecs->ws.value.related_pid);
+
+ /* This won't actually modify the breakpoint list, but will
+ physically remove the breakpoints from the child. */
+ detach_breakpoints (child_pid);
+ }
+
+ /* 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. */
+ ecs->event_thread->pending_follow = ecs->ws;
- ecs->event_thread->stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
+ stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));
- ecs->random_signal = !bpstat_explains_signal (ecs->event_thread->stop_bpstat);
+ ecs->event_thread->stop_bpstat
+ = bpstat_stop_status (get_regcache_aspace (get_current_regcache ()),
+ stop_pc, ecs->ptid);
+
+ /* 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)
{
+ ptid_t parent;
+ ptid_t child;
+ int should_resume;
+ int follow_child = (follow_fork_mode_string == follow_fork_mode_child);
+
ecs->event_thread->stop_signal = TARGET_SIGNAL_0;
- keep_going (ecs);
+
+ should_resume = follow_fork ();
+
+ parent = ecs->ptid;
+ child = ecs->ws.value.related_pid;
+
+ /* In non-stop mode, also resume the other branch. */
+ if (non_stop && !detach_fork)
+ {
+ if (follow_child)
+ switch_to_thread (parent);
+ else
+ switch_to_thread (child);
+
+ ecs->event_thread = inferior_thread ();
+ ecs->ptid = inferior_ptid;
+ keep_going (ecs);
+ }
+
+ if (follow_child)
+ switch_to_thread (child);
+ else
+ switch_to_thread (parent);
+
+ ecs->event_thread = inferior_thread ();
+ ecs->ptid = inferior_ptid;
+
+ if (should_resume)
+ keep_going (ecs);
+ else
+ stop_stepping (ecs);
return;
}
ecs->event_thread->stop_signal = TARGET_SIGNAL_TRAP;
goto process_event_stop_test;
+ case TARGET_WAITKIND_VFORK_DONE:
+ /* Done with the shared memory region. Re-insert breakpoints in
+ the parent, and keep going. */
+
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_VFORK_DONE\n");
+
+ if (!ptid_equal (ecs->ptid, inferior_ptid))
+ 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);
+ return;
+
case TARGET_WAITKIND_EXECD:
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_EXECD\n");
- pending_follow.execd_pathname =
- savestring (ecs->ws.value.execd_pathname,
- strlen (ecs->ws.value.execd_pathname));
if (!ptid_equal (ecs->ptid, inferior_ptid))
{
reinit_frame_cache ();
}
- stop_pc = read_pc ();
+ stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));
+
+ /* Do whatever is necessary to the parent branch of the vfork. */
+ handle_vfork_child_exec_or_exit (1);
/* This causes the eventpoints and symbol table to be reset.
Must do this now, before trying to determine whether to
stop. */
- follow_exec (inferior_ptid, pending_follow.execd_pathname);
- xfree (pending_follow.execd_pathname);
+ follow_exec (inferior_ptid, ecs->ws.value.execd_pathname);
- ecs->event_thread->stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
+ ecs->event_thread->stop_bpstat
+ = 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 this may be referenced from inside
+ bpstat_stop_status above, through inferior_has_execd. */
+ xfree (ecs->ws.value.execd_pathname);
+ ecs->ws.value.execd_pathname = NULL;
+
/* If no catchpoint triggered for this, then keep going. */
if (ecs->random_signal)
{
case TARGET_WAITKIND_SYSCALL_ENTRY:
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SYSCALL_ENTRY\n");
- resume (0, TARGET_SIGNAL_0);
- prepare_to_wait (ecs);
- return;
+ /* Getting the current syscall number */
+ if (handle_syscall_event (ecs) != 0)
+ return;
+ goto process_event_stop_test;
/* Before examining the threads further, step this thread to
get it entirely out of the syscall. (We get notice of the
case TARGET_WAITKIND_SYSCALL_RETURN:
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SYSCALL_RETURN\n");
- target_resume (ecs->ptid, 1, TARGET_SIGNAL_0);
- prepare_to_wait (ecs);
- return;
+ if (handle_syscall_event (ecs) != 0)
+ return;
+ goto process_event_stop_test;
case TARGET_WAITKIND_STOPPED:
if (debug_infrun)
case TARGET_WAITKIND_NO_HISTORY:
/* Reverse execution: target ran out of history info. */
- stop_pc = read_pc ();
+ stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));
print_stop_reason (NO_HISTORY, 0);
stop_stepping (ecs);
return;
-
- /* We had an event in the inferior, but we are not interested
- in handling it at this level. The lower layers have already
- done what needs to be done, if anything.
-
- One of the possible circumstances for this is when the
- inferior produces output for the console. The inferior has
- not stopped, and we are ignoring the event. Another possible
- circumstance is any event which the lower level knows will be
- reported multiple times without an intervening resume. */
- case TARGET_WAITKIND_IGNORE:
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_IGNORE\n");
- prepare_to_wait (ecs);
- return;
}
if (ecs->new_thread_event)
in either the OS or the native code). Therefore we need to
continue all threads in order to make progress. */
+ if (!ptid_equal (ecs->ptid, inferior_ptid))
+ context_switch (ecs->ptid);
target_resume (RESUME_ALL, 0, TARGET_SIGNAL_0);
prepare_to_wait (ecs);
return;
if (debug_infrun)
{
- fprintf_unfiltered (gdb_stdlog, "infrun: stop_pc = 0x%s\n",
- paddr_nz (stop_pc));
- if (STOPPED_BY_WATCHPOINT (&ecs->ws))
+ 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: stopped data address = 0x%s\n",
- paddr_nz (addr));
+ "infrun: stopped data address = %s\n",
+ paddress (gdbarch, addr));
else
fprintf_unfiltered (gdb_stdlog,
"infrun: (no data address available)\n");
}
+
+ do_cleanups (old_chain);
}
if (stepping_past_singlestep_breakpoint)
singlestep_breakpoints_inserted_p = 0;
ecs->random_signal = 0;
+ ecs->event_thread->trap_expected = 0;
context_switch (saved_singlestep_ptid);
if (deprecated_context_hook)
}
}
- stepping_past_singlestep_breakpoint = 0;
-
if (!ptid_equal (deferred_step_ptid, null_ptid))
{
/* In non-stop mode, there's never a deferred_step_ptid set. */
if (ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP)
{
int thread_hop_needed = 0;
+ 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
not see this breakpoint hit when stepping onto breakpoints. */
- if (regular_breakpoint_inserted_here_p (stop_pc))
+ if (regular_breakpoint_inserted_here_p (aspace, stop_pc))
{
ecs->random_signal = 0;
- if (!breakpoint_thread_match (stop_pc, ecs->ptid))
+ if (!breakpoint_thread_match (aspace, stop_pc, ecs->ptid))
thread_hop_needed = 1;
}
else if (singlestep_breakpoints_inserted_p)
stop_signal = ecs->event_thread->stop_signal;
ecs->event_thread->stop_signal = TARGET_SIGNAL_0;
ecs->ptid = singlestep_ptid;
- ecs->event_thread = find_thread_pid (ecs->ptid);
+ ecs->event_thread = find_thread_ptid (ecs->ptid);
ecs->event_thread->stop_signal = stop_signal;
stop_pc = new_singlestep_pc;
}
if (thread_hop_needed)
{
+ struct regcache *thread_regcache;
int remove_status = 0;
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: thread_hop_needed\n");
+ /* Switch context before touching inferior memory, the
+ previous thread may have exited. */
+ if (!ptid_equal (inferior_ptid, ecs->ptid))
+ context_switch (ecs->ptid);
+
/* Saw a breakpoint, but it was hit by the wrong thread.
Just continue. */
/* If the arch can displace step, don't remove the
breakpoints. */
- if (!use_displaced_stepping (current_gdbarch))
+ thread_regcache = get_thread_regcache (ecs->ptid);
+ if (!use_displaced_stepping (get_regcache_arch (thread_regcache)))
remove_status = remove_breakpoints ();
/* Did we fail to remove breakpoints? If so, try
error (_("Cannot step over breakpoint hit in wrong thread"));
else
{ /* Single step */
- if (!ptid_equal (inferior_ptid, ecs->ptid))
- context_switch (ecs->ptid);
-
if (!non_stop)
{
/* Only need to require the next event from this
ecs->event_thread->stepping_over_breakpoint = 1;
keep_going (ecs);
- registers_changed ();
return;
}
}
deprecated_context_hook (pid_to_thread_id (ecs->ptid));
}
+ /* At this point, get hold of the now-current thread's frame. */
+ frame = get_current_frame ();
+ gdbarch = get_frame_arch (frame);
+
if (singlestep_breakpoints_inserted_p)
{
/* Pull the single step breakpoints out of the target. */
/* If necessary, step over this watchpoint. We'll be back to display
it in a moment. */
if (stopped_by_watchpoint
- && (HAVE_STEPPABLE_WATCHPOINT
- || gdbarch_have_nonsteppable_watchpoint (current_gdbarch)))
+ && (target_have_steppable_watchpoint
+ || gdbarch_have_nonsteppable_watchpoint (gdbarch)))
{
/* At this point, we are stopped at an instruction which has
attempted to write to a piece of memory under control of
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. */
-
- if (!HAVE_STEPPABLE_WATCHPOINT)
+ int hw_step = 1;
+
+ if (!target_have_steppable_watchpoint)
remove_breakpoints ();
- registers_changed ();
- target_resume (ecs->ptid, 1, TARGET_SIGNAL_0); /* Single step */
+ /* Single step */
+ hw_step = maybe_software_singlestep (gdbarch, stop_pc);
+ target_resume (ecs->ptid, hw_step, TARGET_SIGNAL_0);
waiton_ptid = ecs->ptid;
- if (HAVE_STEPPABLE_WATCHPOINT)
+ if (target_have_steppable_watchpoint)
infwait_state = infwait_step_watch_state;
else
infwait_state = infwait_nonstep_watch_state;
find_pc_partial_function (stop_pc, &ecs->stop_func_name,
&ecs->stop_func_start, &ecs->stop_func_end);
ecs->stop_func_start
- += gdbarch_deprecated_function_start_offset (current_gdbarch);
+ += gdbarch_deprecated_function_start_offset (gdbarch);
ecs->event_thread->stepping_over_breakpoint = 0;
bpstat_clear (&ecs->event_thread->stop_bpstat);
ecs->event_thread->stop_step = 0;
ecs->random_signal = 0;
stopped_by_random_signal = 0;
+ /* Hide inlined functions starting here, unless we just performed stepi or
+ nexti. After stepi and nexti, always show the innermost frame (not any
+ inline function call sites). */
+ if (ecs->event_thread->step_range_end != 1)
+ skip_inline_frames (ecs->ptid);
+
if (ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP
&& ecs->event_thread->trap_expected
- && gdbarch_single_step_through_delay_p (current_gdbarch)
+ && gdbarch_single_step_through_delay_p (gdbarch)
&& currently_stepping (ecs->event_thread))
{
/* We're trying to step off a breakpoint. Turns out that we're
with a delay slot. It needs to be stepped twice, once for
the instruction and once for the delay slot. */
int step_through_delay
- = gdbarch_single_step_through_delay (current_gdbarch,
- get_current_frame ());
+ = 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 (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)
{
}
/* See if there is a breakpoint at the current PC. */
- ecs->event_thread->stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
-
+ ecs->event_thread->stop_bpstat
+ = bpstat_stop_status (get_regcache_aspace (get_current_regcache ()),
+ stop_pc, ecs->ptid);
+
/* Following in case break condition called a
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));
ecs->random_signal = 1;
process_event_stop_test:
+
+ /* Re-fetch current thread's frame in case we did a
+ "goto process_event_stop_test" above. */
+ frame = get_current_frame ();
+ gdbarch = get_frame_arch (frame);
+
/* For the program's own signals, act according to
the signal handling tables. */
{
/* 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",
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 (signal_program[ecs->event_thread->stop_signal] == 0)
ecs->event_thread->stop_signal = TARGET_SIGNAL_0;
- if (ecs->event_thread->prev_pc == read_pc ()
+ if (ecs->event_thread->prev_pc == stop_pc
&& ecs->event_thread->trap_expected
&& ecs->event_thread->step_resume_breakpoint == NULL)
{
"infrun: signal arrived while stepping over "
"breakpoint\n");
- insert_step_resume_breakpoint_at_frame (get_current_frame ());
+ insert_step_resume_breakpoint_at_frame (frame);
ecs->event_thread->step_after_step_resume_breakpoint = 1;
keep_going (ecs);
return;
&& ecs->event_thread->stop_signal != TARGET_SIGNAL_0
&& (ecs->event_thread->step_range_start <= stop_pc
&& stop_pc < ecs->event_thread->step_range_end)
- && frame_id_eq (get_frame_id (get_current_frame ()),
- ecs->event_thread->step_frame_id)
+ && frame_id_eq (get_stack_frame_id (frame),
+ ecs->event_thread->step_stack_frame_id)
&& ecs->event_thread->step_resume_breakpoint == NULL)
{
/* The inferior is about to take a signal that will take it
"infrun: signal may take us out of "
"single-step range\n");
- insert_step_resume_breakpoint_at_frame (get_current_frame ());
+ insert_step_resume_breakpoint_at_frame (frame);
keep_going (ecs);
return;
}
if (what.call_dummy)
{
- stop_stack_dummy = 1;
+ stop_stack_dummy = what.call_dummy;
}
switch (what.main_action)
ecs->event_thread->stepping_over_breakpoint = 1;
- if (!gdbarch_get_longjmp_target_p (current_gdbarch)
- || !gdbarch_get_longjmp_target (current_gdbarch,
- get_current_frame (), &jmp_buf_pc))
+ if (!gdbarch_get_longjmp_target_p (gdbarch)
+ || !gdbarch_get_longjmp_target (gdbarch, frame, &jmp_buf_pc))
{
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "\
delete_step_resume_breakpoint (ecs->event_thread);
/* Insert a breakpoint at resume address. */
- insert_longjmp_resume_breakpoint (jmp_buf_pc);
+ insert_longjmp_resume_breakpoint (gdbarch, jmp_buf_pc);
keep_going (ecs);
return;
operations such as address => section name and hence
require the table to contain all sections (including
those found in shared libraries). */
- /* NOTE: cagney/2003-11-25: Pass current_target and not
- exec_ops to SOLIB_ADD. This is because current GDB is
- only tooled to propagate section_table changes out from
- the "current_target" (see target_resize_to_sections), and
- not up from the exec stratum. This, of course, isn't
- right. "infrun.c" should only interact with the
- exec/process stratum, instead relying on the target stack
- to propagate relevant changes (stop, section table
- changed, ...) up to other layers. */
#ifdef SOLIB_ADD
SOLIB_ADD (NULL, 0, ¤t_target, auto_solib_add);
#else
}
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. */
if (!non_stop)
{
struct thread_info *tp;
- tp = iterate_over_threads (currently_stepping_callback,
+
+ tp = iterate_over_threads (currently_stepping_or_nexting_callback,
ecs->event_thread);
if (tp)
{
return;
}
+ /* If the stepping thread exited, then don't try to switch
+ back and resume it, which could fail in several different
+ ways depending on the target. Instead, just keep going.
+
+ We can find a stepping dead thread in the thread list in
+ two cases:
+
+ - The target supports thread exit events, and when the
+ target tries to delete the thread from the thread list,
+ inferior_ptid pointed at the exiting thread. In such
+ case, calling delete_thread does not really remove the
+ thread from the list; instead, the thread is left listed,
+ with 'exited' state.
+
+ - The target's debug interface does not support thread
+ exit events, and so we have no idea whatsoever if the
+ previously stepping thread is still alive. For that
+ reason, we need to synchronously query the target
+ now. */
+ if (is_exited (tp->ptid)
+ || !target_thread_alive (tp->ptid))
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "\
+infrun: not switching back to stepped thread, it has vanished\n");
+
+ delete_thread (tp->ptid);
+ keep_going (ecs);
+ return;
+ }
+
/* Otherwise, we no longer expect a trap in the current thread.
Clear the trap_expected flag before switching back -- this is
what keep_going would do as well, if we called it. */
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 ();
+
/* If stepping through a line, keep going if still within it.
Note that step_range_end is the address of the first instruction
beyond the step range, and NOT the address of the last instruction
- within it! */
+ within it!
+
+ Note also that during reverse execution, we may be stepping
+ through a function epilogue and therefore must detect when
+ the current-frame changes in the middle of a line. */
+
if (stop_pc >= ecs->event_thread->step_range_start
- && stop_pc < ecs->event_thread->step_range_end)
+ && stop_pc < ecs->event_thread->step_range_end
+ && (execution_direction != EXEC_REVERSE
+ || frame_id_eq (get_frame_id (frame),
+ ecs->event_thread->step_frame_id)))
{
if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "infrun: stepping inside range [0x%s-0x%s]\n",
- paddr_nz (ecs->event_thread->step_range_start),
- paddr_nz (ecs->event_thread->step_range_end));
+ fprintf_unfiltered
+ (gdb_stdlog, "infrun: stepping inside range [%s-%s]\n",
+ paddress (gdbarch, ecs->event_thread->step_range_start),
+ paddress (gdbarch, ecs->event_thread->step_range_end));
/* When stepping backward, stop at beginning of line range
(unless it's the function entry point, in which case
/* We stepped out of the stepping range. */
/* If we are stepping at the source level and entered the runtime
- loader dynamic symbol resolution code, we keep on single stepping
- until we exit the run time loader code and reach the callee's
- address. */
- if (ecs->event_thread->step_over_calls == STEP_OVER_UNDEBUGGABLE
+ loader dynamic symbol resolution code...
+
+ EXEC_FORWARD: we keep on single stepping until we exit the run
+ time loader code and reach the callee's address.
+
+ EXEC_REVERSE: we've already executed the callee (backward), and
+ the runtime loader code is handled just like any other
+ undebuggable function call. Now we need only keep stepping
+ backward through the trampoline code, and that's handled further
+ down, so there is nothing for us to do here. */
+
+ if (execution_direction != EXEC_REVERSE
+ && ecs->event_thread->step_over_calls == STEP_OVER_UNDEBUGGABLE
&& in_solib_dynsym_resolve_code (stop_pc))
{
CORE_ADDR pc_after_resolver =
- gdbarch_skip_solib_resolver (current_gdbarch, stop_pc);
+ gdbarch_skip_solib_resolver (gdbarch, stop_pc);
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: stepped into dynsym resolve code\n");
/* 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);
- insert_step_resume_breakpoint_at_sal (sr_sal, null_frame_id);
+ insert_step_resume_breakpoint_at_sal (gdbarch,
+ sr_sal, null_frame_id);
}
keep_going (ecs);
if (ecs->event_thread->step_range_end != 1
&& (ecs->event_thread->step_over_calls == STEP_OVER_UNDEBUGGABLE
|| ecs->event_thread->step_over_calls == STEP_OVER_ALL)
- && get_frame_type (get_current_frame ()) == SIGTRAMP_FRAME)
+ && get_frame_type (frame) == SIGTRAMP_FRAME)
{
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: stepped into signal trampoline\n");
NOTE: frame_id_eq will never report two invalid frame IDs as
being equal, so to get into this block, both the current and
previous frame must have valid frame IDs. */
- if (!frame_id_eq (get_frame_id (get_current_frame ()),
- ecs->event_thread->step_frame_id)
- && (frame_id_eq (frame_unwind_id (get_current_frame ()),
- ecs->event_thread->step_frame_id)
- || execution_direction == EXEC_REVERSE))
+ /* The outer_frame_id check is a heuristic to detect stepping
+ through startup code. If we step over an instruction which
+ sets the stack pointer from an invalid value to a valid value,
+ we may detect that as a subroutine call from the mythical
+ "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 comment in frame_id_eq
+ for more. */
+ if (!frame_id_eq (get_stack_frame_id (frame),
+ ecs->event_thread->step_stack_frame_id)
+ && (frame_id_eq (frame_unwind_caller_id (get_current_frame ()),
+ ecs->event_thread->step_stack_frame_id)
+ && (!frame_id_eq (ecs->event_thread->step_stack_frame_id,
+ outer_frame_id)
+ || step_start_function != find_pc_function (stop_pc))))
{
CORE_ADDR real_stop_pc;
if ((ecs->event_thread->step_over_calls == STEP_OVER_NONE)
|| ((ecs->event_thread->step_range_end == 1)
- && in_prologue (ecs->event_thread->prev_pc,
+ && in_prologue (gdbarch, ecs->event_thread->prev_pc,
ecs->stop_func_start)))
{
/* I presume that step_over_calls is only 0 when we're
/* 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 */
ecs->event_thread->stop_step = 1;
print_stop_reason (END_STEPPING_RANGE, 0);
stop_stepping (ecs);
return;
}
+ /* Reverse stepping through solib trampolines. */
+
+ if (execution_direction == EXEC_REVERSE
+ && ecs->event_thread->step_over_calls != STEP_OVER_NONE
+ && (gdbarch_skip_trampoline_code (gdbarch, frame, stop_pc)
+ || (ecs->stop_func_start == 0
+ && in_solib_dynsym_resolve_code (stop_pc))))
+ {
+ /* Any solib trampoline code can be handled in reverse
+ by simply continuing to single-step. We have already
+ executed the solib function (backwards), and a few
+ steps will take us back through the trampoline to the
+ caller. */
+ keep_going (ecs);
+ return;
+ }
+
if (ecs->event_thread->step_over_calls == STEP_OVER_ALL)
{
/* We're doing a "next".
{
struct symtab_and_line sr_sal;
- if (ecs->stop_func_start == 0
- && in_solib_dynsym_resolve_code (stop_pc))
- {
- /* Stepped into runtime loader dynamic symbol
- resolution code. Since we're in reverse,
- we have already backed up through the runtime
- loader and the dynamic function. This is just
- the trampoline (jump table).
-
- Just keep stepping, we'll soon be home.
- */
- keep_going (ecs);
- return;
- }
- /* Normal (staticly linked) function call return. */
+ /* Normal function call return (static or dynamic). */
init_sal (&sr_sal);
sr_sal.pc = ecs->stop_func_start;
- insert_step_resume_breakpoint_at_sal (sr_sal, null_frame_id);
+ sr_sal.pspace = get_frame_program_space (frame);
+ insert_step_resume_breakpoint_at_sal (gdbarch,
+ sr_sal, null_frame_id);
}
else
- insert_step_resume_breakpoint_at_caller (get_current_frame ());
+ insert_step_resume_breakpoint_at_caller (frame);
keep_going (ecs);
return;
function. That's what tells us (a) whether we want to step
into it at all, and (b) what prologue we want to run to the
end of, if we do step into it. */
- real_stop_pc = skip_language_trampoline (get_current_frame (), stop_pc);
+ real_stop_pc = skip_language_trampoline (frame, stop_pc);
if (real_stop_pc == 0)
- real_stop_pc = gdbarch_skip_trampoline_code
- (current_gdbarch, get_current_frame (), stop_pc);
+ real_stop_pc = gdbarch_skip_trampoline_code (gdbarch, frame, stop_pc);
if (real_stop_pc != 0)
ecs->stop_func_start = real_stop_pc;
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);
- insert_step_resume_breakpoint_at_sal (sr_sal, null_frame_id);
+ insert_step_resume_breakpoint_at_sal (gdbarch,
+ sr_sal, null_frame_id);
keep_going (ecs);
return;
}
struct symtab_and_line tmp_sal;
tmp_sal = find_pc_line (ecs->stop_func_start, 0);
+ tmp_sal.pspace = get_frame_program_space (frame);
if (tmp_sal.line != 0)
{
if (execution_direction == EXEC_REVERSE)
- handle_step_into_function_backward (ecs);
+ handle_step_into_function_backward (gdbarch, ecs);
else
- handle_step_into_function (ecs);
+ handle_step_into_function (gdbarch, ecs);
return;
}
}
stop_stepping (ecs);
return;
}
-
- if (execution_direction == EXEC_REVERSE)
+
+ if (execution_direction == EXEC_REVERSE)
+ {
+ /* 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);
+ insert_step_resume_breakpoint_at_sal (gdbarch,
+ sr_sal, null_frame_id);
+ }
+ else
+ /* Set a breakpoint at callee's return address (the address
+ at which the caller will resume). */
+ insert_step_resume_breakpoint_at_caller (frame);
+
+ keep_going (ecs);
+ return;
+ }
+
+ /* Reverse stepping through solib trampolines. */
+
+ if (execution_direction == EXEC_REVERSE
+ && ecs->event_thread->step_over_calls != STEP_OVER_NONE)
+ {
+ if (gdbarch_skip_trampoline_code (gdbarch, frame, stop_pc)
+ || (ecs->stop_func_start == 0
+ && in_solib_dynsym_resolve_code (stop_pc)))
+ {
+ /* Any solib trampoline code can be handled in reverse
+ by simply continuing to single-step. We have already
+ executed the solib function (backwards), and a few
+ steps will take us back through the trampoline to the
+ caller. */
+ keep_going (ecs);
+ return;
+ }
+ else if (in_solib_dynsym_resolve_code (stop_pc))
{
- /* Set a breakpoint at callee's start address.
- From there we can step once and be back in the caller. */
+ /* Stepped backward into the solib dynsym resolver.
+ 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;
- insert_step_resume_breakpoint_at_sal (sr_sal, null_frame_id);
+ sr_sal.pspace = get_frame_program_space (frame);
+ insert_step_resume_breakpoint_at_sal (gdbarch,
+ sr_sal, null_frame_id);
+ keep_going (ecs);
+ return;
}
- else
- /* Set a breakpoint at callee's return address (the address
- at which the caller will resume). */
- insert_step_resume_breakpoint_at_caller (get_current_frame ());
-
- keep_going (ecs);
- return;
}
/* If we're in the return path from a shared library trampoline,
we want to proceed through the trampoline when stepping. */
- if (gdbarch_in_solib_return_trampoline (current_gdbarch,
+ if (gdbarch_in_solib_return_trampoline (gdbarch,
stop_pc, ecs->stop_func_name))
{
/* Determine where this trampoline returns. */
CORE_ADDR real_stop_pc;
- real_stop_pc = gdbarch_skip_trampoline_code
- (current_gdbarch, get_current_frame (), stop_pc);
+
+ real_stop_pc = gdbarch_skip_trampoline_code (gdbarch, frame, stop_pc);
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: stepped into solib return tramp\n");
init_sal (&sr_sal); /* initialize to zeroes */
sr_sal.pc = real_stop_pc;
sr_sal.section = find_pc_overlay (sr_sal.pc);
+ sr_sal.pspace = get_frame_program_space (frame);
/* Do not specify what the fp should be when we stop since
on some machines the prologue is where the new fp value
is established. */
- insert_step_resume_breakpoint_at_sal (sr_sal, null_frame_id);
+ insert_step_resume_breakpoint_at_sal (gdbarch,
+ sr_sal, null_frame_id);
/* Restart without fiddling with the step ranges or
other state. */
set step-mode) or we no longer know how to get back
to the call site. */
if (step_stop_if_no_debug
- || !frame_id_p (frame_unwind_id (get_current_frame ())))
+ || !frame_id_p (frame_unwind_caller_id (frame)))
{
/* If we have no line number and the step-stop-if-no-debug
is set, we stop the step so that the user has a chance to
{
/* Set a breakpoint at callee's return address (the address
at which the caller will resume). */
- insert_step_resume_breakpoint_at_caller (get_current_frame ());
+ insert_step_resume_breakpoint_at_caller (frame);
keep_going (ecs);
return;
}
return;
}
+ /* Look for "calls" to inlined functions, part one. If the inline
+ frame machinery detected some skipped call sites, we have entered
+ a new inline function. */
+
+ if (frame_id_eq (get_frame_id (get_current_frame ()),
+ ecs->event_thread->step_frame_id)
+ && inline_skipped_frames (ecs->ptid))
+ {
+ struct symtab_and_line call_sal;
+
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: stepped into inlined function\n");
+
+ find_frame_sal (get_current_frame (), &call_sal);
+
+ if (ecs->event_thread->step_over_calls != STEP_OVER_ALL)
+ {
+ /* For "step", we're going to stop. But if the call site
+ for this inlined function is on the same source line as
+ we were previously stepping, go down into the function
+ first. Otherwise stop at the call site. */
+
+ if (call_sal.line == ecs->event_thread->current_line
+ && call_sal.symtab == ecs->event_thread->current_symtab)
+ step_into_inline_frame (ecs->ptid);
+
+ ecs->event_thread->stop_step = 1;
+ print_stop_reason (END_STEPPING_RANGE, 0);
+ stop_stepping (ecs);
+ return;
+ }
+ else
+ {
+ /* For "next", we should stop at the call site if it is on a
+ different source line. Otherwise continue through the
+ inlined function. */
+ if (call_sal.line == ecs->event_thread->current_line
+ && call_sal.symtab == ecs->event_thread->current_symtab)
+ keep_going (ecs);
+ else
+ {
+ ecs->event_thread->stop_step = 1;
+ print_stop_reason (END_STEPPING_RANGE, 0);
+ stop_stepping (ecs);
+ }
+ return;
+ }
+ }
+
+ /* Look for "calls" to inlined functions, part two. If we are still
+ in the same real function we were stepping through, but we have
+ to go further up to find the exact frame ID, we are stepping
+ through a more inlined call beyond its call site. */
+
+ if (get_frame_type (get_current_frame ()) == INLINE_FRAME
+ && !frame_id_eq (get_frame_id (get_current_frame ()),
+ ecs->event_thread->step_frame_id)
+ && stepped_in_from (get_current_frame (),
+ ecs->event_thread->step_frame_id))
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: stepping through inlined function\n");
+
+ if (ecs->event_thread->step_over_calls == STEP_OVER_ALL)
+ keep_going (ecs);
+ else
+ {
+ ecs->event_thread->stop_step = 1;
+ print_stop_reason (END_STEPPING_RANGE, 0);
+ stop_stepping (ecs);
+ }
+ return;
+ }
+
if ((stop_pc == stop_pc_sal.pc)
&& (ecs->event_thread->current_line != stop_pc_sal.line
|| ecs->event_thread->current_symtab != stop_pc_sal.symtab))
ecs->event_thread->step_range_start = stop_pc_sal.pc;
ecs->event_thread->step_range_end = stop_pc_sal.end;
- ecs->event_thread->step_frame_id = get_frame_id (get_current_frame ());
- ecs->event_thread->current_line = stop_pc_sal.line;
- ecs->event_thread->current_symtab = stop_pc_sal.symtab;
+ set_step_info (frame, stop_pc_sal);
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: keep going\n");
keep_going (ecs);
}
-/* Are we in the middle of stepping? */
+/* Is thread TP in the middle of single-stepping? */
static int
-currently_stepping_thread (struct thread_info *tp)
+currently_stepping (struct thread_info *tp)
{
- return (tp->step_range_end && tp->step_resume_breakpoint == NULL)
- || tp->trap_expected
- || tp->stepping_through_solib_after_catch;
+ return ((tp->step_range_end && tp->step_resume_breakpoint == NULL)
+ || tp->trap_expected
+ || tp->stepping_through_solib_after_catch
+ || bpstat_should_step ());
}
-static int
-currently_stepping_callback (struct thread_info *tp, void *data)
-{
- /* Return true if any thread *but* the one passed in "data" is
- in the middle of stepping. */
- return tp != data && currently_stepping_thread (tp);
-}
+/* Returns true if any thread *but* the one passed in "data" is in the
+ middle of stepping or of handling a "next". */
static int
-currently_stepping (struct thread_info *tp)
+currently_stepping_or_nexting_callback (struct thread_info *tp, void *data)
{
- return currently_stepping_thread (tp) || bpstat_should_step ();
+ if (tp == data)
+ return 0;
+
+ return (tp->step_range_end
+ || tp->trap_expected
+ || tp->stepping_through_solib_after_catch);
}
/* Inferior has stepped into a subroutine call with source code that
it. */
static void
-handle_step_into_function (struct execution_control_state *ecs)
+handle_step_into_function (struct gdbarch *gdbarch,
+ struct execution_control_state *ecs)
{
struct symtab *s;
struct symtab_and_line stop_func_sal, sr_sal;
s = find_pc_symtab (stop_pc);
if (s && s->language != language_asm)
- ecs->stop_func_start = gdbarch_skip_prologue (current_gdbarch,
+ ecs->stop_func_start = gdbarch_skip_prologue (gdbarch,
ecs->stop_func_start);
stop_func_sal = find_pc_line (ecs->stop_func_start, 0);
the VLIW instruction. Thus, we need to make the corresponding
adjustment here when computing the stop address. */
- if (gdbarch_adjust_breakpoint_address_p (current_gdbarch))
+ if (gdbarch_adjust_breakpoint_address_p (gdbarch))
{
ecs->stop_func_start
- = gdbarch_adjust_breakpoint_address (current_gdbarch,
+ = gdbarch_adjust_breakpoint_address (gdbarch,
ecs->stop_func_start);
}
init_sal (&sr_sal); /* initialize to zeroes */
sr_sal.pc = ecs->stop_func_start;
sr_sal.section = find_pc_overlay (ecs->stop_func_start);
+ sr_sal.pspace = get_frame_program_space (get_current_frame ());
/* Do not specify what the fp should be when we stop since on
some machines the prologue is where the new fp value is
established. */
- insert_step_resume_breakpoint_at_sal (sr_sal, null_frame_id);
+ insert_step_resume_breakpoint_at_sal (gdbarch, sr_sal, null_frame_id);
/* And make sure stepping stops right away then. */
ecs->event_thread->step_range_end = ecs->event_thread->step_range_start;
last line of code in it. */
static void
-handle_step_into_function_backward (struct execution_control_state *ecs)
+handle_step_into_function_backward (struct gdbarch *gdbarch,
+ 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)
- ecs->stop_func_start = gdbarch_skip_prologue (current_gdbarch,
+ ecs->stop_func_start = gdbarch_skip_prologue (gdbarch,
ecs->stop_func_start);
stop_func_sal = find_pc_line (stop_pc, 0);
This is used to both functions and to skip over code. */
static void
-insert_step_resume_breakpoint_at_sal (struct symtab_and_line sr_sal,
+insert_step_resume_breakpoint_at_sal (struct gdbarch *gdbarch,
+ struct symtab_and_line sr_sal,
struct frame_id sr_id)
{
/* There should never be more than one step-resume or longjmp-resume
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog,
- "infrun: inserting step-resume breakpoint at 0x%s\n",
- paddr_nz (sr_sal.pc));
+ "infrun: inserting step-resume breakpoint at %s\n",
+ paddress (gdbarch, sr_sal.pc));
inferior_thread ()->step_resume_breakpoint
- = set_momentary_breakpoint (sr_sal, sr_id, bp_step_resume);
+ = set_momentary_breakpoint (gdbarch, sr_sal, sr_id, bp_step_resume);
}
/* Insert a "step-resume breakpoint" at RETURN_FRAME.pc. This is used
insert_step_resume_breakpoint_at_frame (struct frame_info *return_frame)
{
struct symtab_and_line sr_sal;
+ struct gdbarch *gdbarch;
gdb_assert (return_frame != NULL);
init_sal (&sr_sal); /* initialize to zeros */
- sr_sal.pc = gdbarch_addr_bits_remove
- (current_gdbarch, get_frame_pc (return_frame));
+ gdbarch = get_frame_arch (return_frame);
+ sr_sal.pc = gdbarch_addr_bits_remove (gdbarch, get_frame_pc (return_frame));
sr_sal.section = find_pc_overlay (sr_sal.pc);
+ sr_sal.pspace = get_frame_program_space (return_frame);
- insert_step_resume_breakpoint_at_sal (sr_sal, get_frame_id (return_frame));
+ insert_step_resume_breakpoint_at_sal (gdbarch, sr_sal,
+ get_stack_frame_id (return_frame));
}
/* Similar to insert_step_resume_breakpoint_at_frame, except
This is a separate function rather than reusing
insert_step_resume_breakpoint_at_frame in order to avoid
get_prev_frame, which may stop prematurely (see the implementation
- of frame_unwind_id for an example). */
+ of frame_unwind_caller_id for an example). */
static void
insert_step_resume_breakpoint_at_caller (struct frame_info *next_frame)
{
struct symtab_and_line sr_sal;
+ struct gdbarch *gdbarch;
/* We shouldn't have gotten here if we don't know where the call site
is. */
- gdb_assert (frame_id_p (frame_unwind_id (next_frame)));
+ gdb_assert (frame_id_p (frame_unwind_caller_id (next_frame)));
init_sal (&sr_sal); /* initialize to zeros */
- sr_sal.pc = gdbarch_addr_bits_remove
- (current_gdbarch, frame_pc_unwind (next_frame));
+ gdbarch = frame_unwind_caller_arch (next_frame);
+ sr_sal.pc = gdbarch_addr_bits_remove (gdbarch,
+ frame_unwind_caller_pc (next_frame));
sr_sal.section = find_pc_overlay (sr_sal.pc);
+ sr_sal.pspace = frame_unwind_program_space (next_frame);
- insert_step_resume_breakpoint_at_sal (sr_sal, frame_unwind_id (next_frame));
+ insert_step_resume_breakpoint_at_sal (gdbarch, sr_sal,
+ frame_unwind_caller_id (next_frame));
}
/* Insert a "longjmp-resume" breakpoint at PC. This is used to set a
"step-resume" breakpoints. */
static void
-insert_longjmp_resume_breakpoint (CORE_ADDR pc)
+insert_longjmp_resume_breakpoint (struct gdbarch *gdbarch, CORE_ADDR pc)
{
/* There should never be more than one step-resume or longjmp-resume
breakpoint per thread, so we should never be setting a new
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog,
- "infrun: inserting longjmp-resume breakpoint at 0x%s\n",
- paddr_nz (pc));
+ "infrun: inserting longjmp-resume breakpoint at %s\n",
+ paddress (gdbarch, pc));
inferior_thread ()->step_resume_breakpoint =
- set_momentary_breakpoint_at_pc (pc, bp_longjmp_resume);
+ set_momentary_breakpoint_at_pc (gdbarch, pc, bp_longjmp_resume);
}
static void
static void
keep_going (struct execution_control_state *ecs)
{
+ /* Make sure normal_stop is called if we get a QUIT handled before
+ reaching resume. */
+ struct cleanup *old_cleanups = make_cleanup (resume_cleanups, 0);
+
/* Save the pc before execution, to compare with pc after stop. */
- ecs->event_thread->prev_pc = read_pc (); /* Might have been DECR_AFTER_BREAK */
+ ecs->event_thread->prev_pc
+ = regcache_read_pc (get_thread_regcache (ecs->ptid));
/* If we did not do break;, it means we should keep running the
inferior and not return to debugger. */
/* We took a signal (which we are supposed to pass through to
the inferior, else we'd not get here) and we haven't yet
gotten our trap. Simply continue. */
+
+ discard_cleanups (old_cleanups);
resume (currently_stepping (ecs->event_thread),
ecs->event_thread->stop_signal);
}
if (ecs->event_thread->stepping_over_breakpoint)
{
- if (! use_displaced_stepping (current_gdbarch))
+ 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
simple, we remove them all. */
else
{
struct gdb_exception e;
+
/* Stop stepping when inserting breakpoints
has failed. */
TRY_CATCH (e, RETURN_MASK_ERROR)
}
if (e.reason < 0)
{
+ exception_print (gdb_stderr, e);
stop_stepping (ecs);
return;
}
&& !signal_program[ecs->event_thread->stop_signal])
ecs->event_thread->stop_signal = TARGET_SIGNAL_0;
+ discard_cleanups (old_cleanups);
resume (currently_stepping (ecs->event_thread),
ecs->event_thread->stop_signal);
}
{
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: prepare_to_wait\n");
- if (infwait_state == infwait_normal_state)
- {
- 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 ();
- waiton_ptid = pid_to_ptid (-1);
- }
/* This is the old end of the while loop. Let everybody know we
want to wait for the inferior some more and get called again
soon. */
{
struct target_waitstatus last;
ptid_t last_ptid;
+ struct cleanup *old_chain = make_cleanup (null_cleanup, NULL);
get_last_target_status (&last_ptid, &last);
+ /* If an exception is thrown from this point on, make sure to
+ propagate GDB's knowledge of the executing state to the
+ frontend/user running state. A QUIT is an easy exception to see
+ here, so do this before any filtered output. */
+ if (!non_stop)
+ make_cleanup (finish_thread_state_cleanup, &minus_one_ptid);
+ else if (last.kind != TARGET_WAITKIND_SIGNALLED
+ && last.kind != TARGET_WAITKIND_EXITED)
+ make_cleanup (finish_thread_state_cleanup, &inferior_ptid);
+
/* In non-stop mode, we don't want GDB to switch threads behind the
user's back, to avoid races where the user is typing a command to
apply to thread x, but GDB switches to thread y before the user
previous_inferior_ptid = inferior_ptid;
}
- /* NOTE drow/2004-01-17: Is this still necessary? */
- /* Make sure that the current_frame's pc is correct. This
- is a correction for setting up the frame info before doing
- gdbarch_decr_pc_after_break */
- if (target_has_execution)
- /* FIXME: cagney/2002-12-06: Has the PC changed? Thanks to
- gdbarch_decr_pc_after_break, the program counter can change. Ask the
- frame code to check for this and sort out any resultant mess.
- gdbarch_decr_pc_after_break needs to just go away. */
- deprecated_update_frame_pc_hack (get_current_frame (), read_pc ());
-
if (!breakpoints_always_inserted_mode () && target_has_execution)
{
if (remove_breakpoints ())
target_terminal_ours_for_output ();
printf_filtered (_("\
Cannot remove breakpoints because program is no longer writable.\n\
-It might be running in another process.\n\
Further execution is probably impossible.\n"));
}
}
/* Set the current source location. This will also happen if we
display the frame below, but the current SAL will be incorrect
during a user hook-stop function. */
- if (target_has_stack && !stop_stack_dummy)
+ if (has_stack_frames () && !stop_stack_dummy)
set_current_sal_from_frame (get_current_frame (), 1);
- if (!target_has_stack)
+ /* Let the user/frontend see the threads as stopped. */
+ do_cleanups (old_chain);
+
+ /* Look up the hook_stop and run it (CLI internally handles problem
+ of stop_command's pre-hook not existing). */
+ if (stop_command)
+ catch_errors (hook_stop_stub, stop_command,
+ "Error while running hook_stop:\n", RETURN_MASK_ALL);
+
+ if (!has_stack_frames ())
goto done;
if (last.kind == TARGET_WAITKIND_SIGNALLED
internal_error (__FILE__, __LINE__, _("Unknown value."));
}
- if (ui_out_is_mi_like_p (uiout))
- {
-
- ui_out_field_int (uiout, "thread-id",
- pid_to_thread_id (inferior_ptid));
- if (non_stop)
- {
- struct cleanup *back_to = make_cleanup_ui_out_list_begin_end
- (uiout, "stopped-threads");
- ui_out_field_int (uiout, NULL,
- pid_to_thread_id (inferior_ptid));
- do_cleanups (back_to);
- }
- else
- ui_out_field_string (uiout, "stopped-threads", "all");
- }
/* The behavior of this routine with respect to the source
flag is:
SRC_LINE: Print only source line
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. POP_FRAME
- ends with a setting of the current frame, so we can use that
- next. */
- frame_pop (get_current_frame ());
- /* Set stop_pc to what it was before we called the function.
- Can't rely on restore_inferior_status because that only gets
- called if we don't stop in the called function. */
- stop_pc = read_pc ();
+ /* 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
+ which means there's currently no selected frame. We don't need
+ to re-establish a selected frame if the dummy call returns normally,
+ that will be done by restore_inferior_status. However, we do have
+ to handle the case where the dummy call is returning after being
+ stopped (e.g. the dummy call previously hit a breakpoint). We
+ can't know which case we have so just always re-establish a
+ selected frame here. */
select_frame (get_current_frame ());
}
done:
annotate_stopped ();
- if (!suppress_stop_observer
- && !(target_has_execution
- && last.kind != TARGET_WAITKIND_SIGNALLED
- && last.kind != TARGET_WAITKIND_EXITED
- && inferior_thread ()->step_multi))
+
+ /* Suppress the stop observer if we're in the middle of:
+
+ - a step n (n > 1), as there still more steps to be done.
+
+ - a "finish" command, as the observer will be called in
+ finish_command_continuation, so it can include the inferior
+ function's return value.
+
+ - calling an inferior function, as we pretend we inferior didn't
+ run at all. The return value of the call is handled by the
+ expression evaluator, through call_function_by_hand. */
+
+ if (!target_has_execution
+ || last.kind == TARGET_WAITKIND_SIGNALLED
+ || last.kind == TARGET_WAITKIND_EXITED
+ || (!inferior_thread ()->step_multi
+ && !(inferior_thread ()->stop_bpstat
+ && inferior_thread ()->proceed_to_finish)
+ && !inferior_thread ()->in_infcall))
{
if (!ptid_equal (inferior_ptid, null_ptid))
- observer_notify_normal_stop (inferior_thread ()->stop_bpstat);
+ observer_notify_normal_stop (inferior_thread ()->stop_bpstat,
+ stop_print_frame);
else
- observer_notify_normal_stop (NULL);
+ observer_notify_normal_stop (NULL, stop_print_frame);
}
- if (target_has_execution
- && last.kind != TARGET_WAITKIND_SIGNALLED
- && last.kind != TARGET_WAITKIND_EXITED)
- {
- /* Delete the breakpoint we stopped at, if it wants to be deleted.
- Delete any breakpoint that is to be deleted at the next stop. */
- breakpoint_auto_delete (inferior_thread ()->stop_bpstat);
- if (!non_stop)
- set_running (pid_to_ptid (-1), 0);
- else
- set_running (inferior_ptid, 0);
+ if (target_has_execution)
+ {
+ if (last.kind != TARGET_WAITKIND_SIGNALLED
+ && last.kind != TARGET_WAITKIND_EXITED)
+ /* Delete the breakpoint we stopped at, if it wants to be deleted.
+ Delete any breakpoint that is to be deleted at the next stop. */
+ breakpoint_auto_delete (inferior_thread ()->stop_bpstat);
}
- /* Look up the hook_stop and run it (CLI internally handles problem
- of stop_command's pre-hook not existing). */
- if (stop_command)
- catch_errors (hook_stop_stub, stop_command,
- "Error while running hook_stop:\n", RETURN_MASK_ALL);
-
+ /* Try to get rid of automatically added inferiors that are no
+ longer needed. Keeping those around slows down things linearly.
+ Note that this never removes the current inferior. */
+ prune_inferiors ();
}
static int
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;
}
static void
sig_print_info (enum target_signal oursig)
{
- char *name = target_signal_to_name (oursig);
+ const char *name = target_signal_to_name (oursig);
int name_padding = 13 - strlen (name);
if (name_padding <= 0)
case TARGET_SIGNAL_INT:
if (!allsigs && !sigs[signum])
{
- if (query ("%s is used by the debugger.\n\
-Are you sure you want to change it? ", target_signal_to_name ((enum target_signal) signum)))
+ if (query (_("%s is used by the debugger.\n\
+Are you sure you want to change it? "), target_signal_to_name ((enum target_signal) signum)))
{
sigs[signum] = 1;
}
argv++;
}
- target_notice_signals (inferior_ptid);
+ for (signum = 0; signum < nsigs; signum++)
+ if (sigs[signum])
+ {
+ target_notice_signals (inferior_ptid);
- if (from_tty)
- {
- /* Show the results. */
- sig_print_header ();
- for (signum = 0; signum < nsigs; signum++)
- {
- if (sigs[signum])
- {
- sig_print_info (signum);
- }
- }
- }
+ if (from_tty)
+ {
+ /* Show the results. */
+ sig_print_header ();
+ for (; signum < nsigs; signum++)
+ if (sigs[signum])
+ sig_print_info (signum);
+ }
+
+ break;
+ }
do_cleanups (old_chain);
}
signals_info (char *signum_exp, int from_tty)
{
enum target_signal oursig;
+
sig_print_header ();
if (signum_exp)
printf_filtered (_("\nUse the \"handle\" command to change these tables.\n"));
}
+
+/* The $_siginfo convenience variable is a bit special. We don't know
+ for sure the type of the value until we actually have a chance to
+ fetch the data. The type can change depending on gdbarch, so it it
+ also dependent on which thread you have selected.
+
+ 1. making $_siginfo be an internalvar that creates a new value on
+ access.
+
+ 2. making the value of $_siginfo be an lval_computed value. */
+
+/* This function implements the lval_computed support for reading a
+ $_siginfo value. */
+
+static void
+siginfo_value_read (struct value *v)
+{
+ LONGEST transferred;
+
+ transferred =
+ target_read (¤t_target, TARGET_OBJECT_SIGNAL_INFO,
+ NULL,
+ value_contents_all_raw (v),
+ value_offset (v),
+ TYPE_LENGTH (value_type (v)));
+
+ if (transferred != TYPE_LENGTH (value_type (v)))
+ error (_("Unable to read siginfo"));
+}
+
+/* This function implements the lval_computed support for writing a
+ $_siginfo value. */
+
+static void
+siginfo_value_write (struct value *v, struct value *fromval)
+{
+ LONGEST transferred;
+
+ transferred = target_write (¤t_target,
+ TARGET_OBJECT_SIGNAL_INFO,
+ NULL,
+ value_contents_all_raw (fromval),
+ value_offset (v),
+ TYPE_LENGTH (value_type (fromval)));
+
+ if (transferred != TYPE_LENGTH (value_type (fromval)))
+ error (_("Unable to write siginfo"));
+}
+
+static struct lval_funcs siginfo_value_funcs =
+ {
+ siginfo_value_read,
+ siginfo_value_write
+ };
+
+/* Return a new value with the correct type for the siginfo object of
+ the current thread using architecture GDBARCH. Return a void value
+ if there's no object available. */
+
+static struct value *
+siginfo_make_value (struct gdbarch *gdbarch, struct internalvar *var)
+{
+ if (target_has_stack
+ && !ptid_equal (inferior_ptid, null_ptid)
+ && gdbarch_get_siginfo_type_p (gdbarch))
+ {
+ struct type *type = gdbarch_get_siginfo_type (gdbarch);
+
+ return allocate_computed_value (type, &siginfo_value_funcs, NULL);
+ }
+
+ return allocate_value (builtin_type (gdbarch)->builtin_void);
+}
+
\f
-struct inferior_status
+/* Inferior thread state.
+ These are details related to the inferior itself, and don't include
+ things like what frame the user had selected or what gdb was doing
+ with the target at the time.
+ For inferior function calls these are things we want to restore
+ regardless of whether the function call successfully completes
+ or the dummy frame has to be manually popped. */
+
+struct inferior_thread_state
{
enum target_signal stop_signal;
CORE_ADDR stop_pc;
+ struct regcache *registers;
+};
+
+struct inferior_thread_state *
+save_inferior_thread_state (void)
+{
+ struct inferior_thread_state *inf_state = XMALLOC (struct inferior_thread_state);
+ struct thread_info *tp = inferior_thread ();
+
+ inf_state->stop_signal = tp->stop_signal;
+ inf_state->stop_pc = stop_pc;
+
+ inf_state->registers = regcache_dup (get_current_regcache ());
+
+ return inf_state;
+}
+
+/* Restore inferior session state to INF_STATE. */
+
+void
+restore_inferior_thread_state (struct inferior_thread_state *inf_state)
+{
+ struct thread_info *tp = inferior_thread ();
+
+ tp->stop_signal = inf_state->stop_signal;
+ stop_pc = inf_state->stop_pc;
+
+ /* 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_xfree (inf_state->registers);
+ xfree (inf_state);
+}
+
+static void
+do_restore_inferior_thread_state_cleanup (void *state)
+{
+ restore_inferior_thread_state (state);
+}
+
+struct cleanup *
+make_cleanup_restore_inferior_thread_state (struct inferior_thread_state *inf_state)
+{
+ return make_cleanup (do_restore_inferior_thread_state_cleanup, inf_state);
+}
+
+void
+discard_inferior_thread_state (struct inferior_thread_state *inf_state)
+{
+ regcache_xfree (inf_state->registers);
+ xfree (inf_state);
+}
+
+struct regcache *
+get_inferior_thread_state_regcache (struct inferior_thread_state *inf_state)
+{
+ return inf_state->registers;
+}
+
+/* Session related state for inferior function calls.
+ These are the additional bits of state that need to be restored
+ when an inferior function call successfully completes. */
+
+struct inferior_status
+{
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;
CORE_ADDR step_range_end;
struct frame_id step_frame_id;
+ struct frame_id step_stack_frame_id;
enum step_over_calls_kind step_over_calls;
CORE_ADDR step_resume_break_address;
int stop_after_trap;
int stop_soon;
- /* These are here because if call_function_by_hand has written some
- registers and then decides to call error(), we better not have changed
- any registers. */
- struct regcache *registers;
-
- /* A frame unique identifier. */
+ /* ID if the selected frame when the inferior function call was made. */
struct frame_id selected_frame_id;
- int breakpoint_proceeded;
- int restore_stack_info;
int proceed_to_finish;
+ int in_infcall;
};
/* Save all of the information associated with the inferior<==>gdb
- connection. INF_STATUS is a pointer to a "struct inferior_status"
- (defined in inferior.h). */
+ connection. */
struct inferior_status *
-save_inferior_status (int restore_stack_info)
+save_inferior_status (void)
{
struct inferior_status *inf_status = XMALLOC (struct inferior_status);
struct thread_info *tp = inferior_thread ();
struct inferior *inf = current_inferior ();
- inf_status->stop_signal = tp->stop_signal;
- inf_status->stop_pc = stop_pc;
inf_status->stop_step = tp->stop_step;
inf_status->stop_stack_dummy = stop_stack_dummy;
inf_status->stopped_by_random_signal = stopped_by_random_signal;
inf_status->step_range_start = tp->step_range_start;
inf_status->step_range_end = tp->step_range_end;
inf_status->step_frame_id = tp->step_frame_id;
+ inf_status->step_stack_frame_id = tp->step_stack_frame_id;
inf_status->step_over_calls = tp->step_over_calls;
inf_status->stop_after_trap = stop_after_trap;
inf_status->stop_soon = inf->stop_soon;
called. */
inf_status->stop_bpstat = tp->stop_bpstat;
tp->stop_bpstat = bpstat_copy (tp->stop_bpstat);
- inf_status->breakpoint_proceeded = breakpoint_proceeded;
- inf_status->restore_stack_info = restore_stack_info;
inf_status->proceed_to_finish = tp->proceed_to_finish;
-
- inf_status->registers = regcache_dup (get_current_regcache ());
+ inf_status->in_infcall = tp->in_infcall;
inf_status->selected_frame_id = get_frame_id (get_selected_frame (NULL));
+
return inf_status;
}
return (1);
}
+/* Restore inferior session state to INF_STATUS. */
+
void
restore_inferior_status (struct inferior_status *inf_status)
{
struct thread_info *tp = inferior_thread ();
struct inferior *inf = current_inferior ();
- tp->stop_signal = inf_status->stop_signal;
- stop_pc = inf_status->stop_pc;
tp->stop_step = inf_status->stop_step;
stop_stack_dummy = inf_status->stop_stack_dummy;
stopped_by_random_signal = inf_status->stopped_by_random_signal;
tp->step_range_start = inf_status->step_range_start;
tp->step_range_end = inf_status->step_range_end;
tp->step_frame_id = inf_status->step_frame_id;
+ tp->step_stack_frame_id = inf_status->step_stack_frame_id;
tp->step_over_calls = inf_status->step_over_calls;
stop_after_trap = inf_status->stop_after_trap;
inf->stop_soon = inf_status->stop_soon;
bpstat_clear (&tp->stop_bpstat);
tp->stop_bpstat = inf_status->stop_bpstat;
- breakpoint_proceeded = inf_status->breakpoint_proceeded;
+ inf_status->stop_bpstat = NULL;
tp->proceed_to_finish = inf_status->proceed_to_finish;
+ tp->in_infcall = inf_status->in_infcall;
- /* 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_status->registers);
- regcache_xfree (inf_status->registers);
-
- /* FIXME: If we are being called after stopping in a function which
- is called from gdb, we should not be trying to restore the
- selected frame; it just prints a spurious error message (The
- message is useful, however, in detecting bugs in gdb (like if gdb
- clobbers the stack)). In fact, should we be restoring the
- inferior status at all in that case? . */
-
- if (target_has_stack && inf_status->restore_stack_info)
+ if (target_has_stack)
{
/* The point of catch_errors is that if the stack is clobbered,
walking the stack might encounter a garbage pointer and
/* Error in restoring the selected frame. Select the innermost
frame. */
select_frame (get_current_frame ());
-
}
xfree (inf_status);
{
/* See save_inferior_status for info on stop_bpstat. */
bpstat_clear (&inf_status->stop_bpstat);
- regcache_xfree (inf_status->registers);
xfree (inf_status);
}
-
+\f
int
inferior_has_forked (ptid_t pid, ptid_t *child_pid)
{
return 1;
}
+int
+inferior_has_called_syscall (ptid_t pid, int *syscall_number)
+{
+ struct target_waitstatus last;
+ ptid_t last_ptid;
+
+ get_last_target_status (&last_ptid, &last);
+
+ if (last.kind != TARGET_WAITKIND_SYSCALL_ENTRY &&
+ last.kind != TARGET_WAITKIND_SYSCALL_RETURN)
+ return 0;
+
+ if (!ptid_equal (last_ptid, pid))
+ return 0;
+
+ *syscall_number = last.value.syscall_number;
+ return 1;
+}
+
/* Oft used ptids */
ptid_t null_ptid;
ptid_t minus_one_ptid;
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);
}
value);
}
+static void
+show_schedule_multiple (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ fprintf_filtered (file, _("\
+Resuming the execution of threads of all processes is %s.\n"), value);
+}
void
_initialize_infrun (void)
{
int i;
int numsigs;
- struct cmd_list_element *c;
add_info ("signals", signals_info, _("\
What debugger does when program gets various signals.\n\
show_follow_fork_mode_string,
&setlist, &showlist);
+ add_setshow_enum_cmd ("follow-exec-mode", class_run,
+ follow_exec_mode_names,
+ &follow_exec_mode_string, _("\
+Set debugger response to a program call of exec."), _("\
+Show debugger response to a program call of exec."), _("\
+An exec call replaces the program image of a process.\n\
+\n\
+follow-exec-mode can be:\n\
+\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\
+\n\
+ same - the debugger keeps the process bound to the same inferior.\n\
+The new executable image replaces the previous executable loaded in\n\
+the inferior. Restarting the inferior after the exec call restarts\n\
+the executable the process was running after the exec call.\n\
+\n\
+By default, the debugger will use the same inferior."),
+ NULL,
+ show_follow_exec_mode_string,
+ &setlist, &showlist);
+
add_setshow_enum_cmd ("scheduler-locking", class_run,
scheduler_enums, &scheduler_mode, _("\
Set mode for locking scheduler during execution."), _("\
show_scheduler_mode,
&setlist, &showlist);
+ add_setshow_boolean_cmd ("schedule-multiple", class_run, &sched_multi, _("\
+Set mode for resuming threads of all processes."), _("\
+Show mode for resuming threads of all processes."), _("\
+When on, execution commands (such as 'continue' or 'next') resume all\n\
+threads of all processes. When off (which is the default), execution\n\
+commands only resume the threads of the current process. The set of\n\
+threads that are resumed is further refined by the scheduler-locking\n\
+mode (see help set scheduler-locking)."),
+ NULL,
+ show_schedule_multiple,
+ &setlist, &showlist);
+
add_setshow_boolean_cmd ("step-mode", class_run, &step_stop_if_no_debug, _("\
Set mode of the step operation."), _("\
Show mode of the step operation."), _("\
set_exec_direction_func, show_exec_direction_func,
&setlist, &showlist);
+ /* Set/show detach-on-fork: user-settable mode. */
+
+ add_setshow_boolean_cmd ("detach-on-fork", class_run, &detach_fork, _("\
+Set whether gdb will detach the child of a fork."), _("\
+Show whether gdb will detach the child of a fork."), _("\
+Tells gdb whether to detach the child of a fork."),
+ NULL, NULL, &setlist, &showlist);
+
/* ptid initializations */
null_ptid = ptid_build (0, 0, 0);
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);
}