process.
Copyright 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994,
- 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 Free Software
- Foundation, Inc.
+ 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Free
+ Software Foundation, Inc.
This file is part of GDB.
#include "inf-loop.h"
#include "regcache.h"
#include "value.h"
+#include "observer.h"
+#include "language.h"
+#include "gdb_assert.h"
/* Prototypes for local functions */
static void delete_breakpoint_current_contents (void *);
-static void set_follow_fork_mode_command (char *arg, int from_tty,
- struct cmd_list_element *c);
-
static int restore_selected_frame (void *);
static void build_infrun (void);
-static void follow_inferior_fork (int parent_pid, int child_pid,
- int has_forked, int has_vforked);
-
-static void follow_fork (int parent_pid, int child_pid);
-
-static void follow_vfork (int parent_pid, int child_pid);
+static int follow_fork (void);
static void set_schedlock_func (char *args, int from_tty,
struct cmd_list_element *c);
static void xdb_handle_command (char *args, int from_tty);
+static int prepare_to_proceed (void);
+
void _initialize_infrun (void);
int inferior_ignoring_startup_exec_events = 0;
static int may_follow_exec = MAY_FOLLOW_EXEC;
-/* Dynamic function trampolines are similar to solib trampolines in that they
- are between the caller and the callee. The difference is that when you
- enter a dynamic trampoline, you can't determine the callee's address. Some
- (usually complex) code needs to run in the dynamic trampoline to figure out
- the callee's address. This macro is usually called twice. First, when we
- enter the trampoline (looks like a normal function call at that point). It
- should return the PC of a point within the trampoline where the callee's
- address is known. Second, when we hit the breakpoint, this routine returns
- the callee's address. At that point, things proceed as per a step resume
- breakpoint. */
-
-#ifndef DYNAMIC_TRAMPOLINE_NEXTPC
-#define DYNAMIC_TRAMPOLINE_NEXTPC(pc) 0
-#endif
-
/* If the program uses ELF-style shared libraries, then calls to
functions in shared libraries go through stubs, which live in a
table called the PLT (Procedure Linkage Table). The first time the
#define IN_SOLIB_DYNSYM_RESOLVE_CODE(pc) 0
#endif
-#ifndef SKIP_SOLIB_RESOLVER
-#define SKIP_SOLIB_RESOLVER(pc) 0
-#endif
-
/* This function returns TRUE if pc is the address of an instruction
that lies within the dynamic linker (such as the event hook, or the
dld itself).
#define HAVE_STEPPABLE_WATCHPOINT 1
#endif
-#ifndef HAVE_CONTINUABLE_WATCHPOINT
-#define HAVE_CONTINUABLE_WATCHPOINT 0
-#else
-#undef HAVE_CONTINUABLE_WATCHPOINT
-#define HAVE_CONTINUABLE_WATCHPOINT 1
-#endif
-
#ifndef CANNOT_STEP_HW_WATCHPOINTS
#define CANNOT_STEP_HW_WATCHPOINTS 0
#else
when running in the shell before the child program has been exec'd;
and when running some kinds of remote stuff (FIXME?). */
-int stop_soon_quietly;
+enum stop_kind stop_soon;
/* Nonzero if proceed is being used for a "finish" command or a similar
situation when stop_registers should be saved. */
struct
{
int parent_pid;
- int saw_parent_fork;
int child_pid;
- int saw_child_fork;
- int saw_child_exec;
}
fork_event;
char *execd_pathname;
}
pending_follow;
-static const char follow_fork_mode_ask[] = "ask";
static const char follow_fork_mode_child[] = "child";
static const char follow_fork_mode_parent[] = "parent";
static const char *follow_fork_mode_kind_names[] = {
- follow_fork_mode_ask,
follow_fork_mode_child,
follow_fork_mode_parent,
NULL
static const char *follow_fork_mode_string = follow_fork_mode_parent;
\f
-static void
-follow_inferior_fork (int parent_pid, int child_pid, int has_forked,
- int has_vforked)
+static int
+follow_fork (void)
{
- int followed_parent = 0;
- int followed_child = 0;
-
- /* Which process did the user want us to follow? */
- const char *follow_mode = follow_fork_mode_string;
-
- /* Or, did the user not know, and want us to ask? */
- if (follow_fork_mode_string == follow_fork_mode_ask)
- {
- internal_error (__FILE__, __LINE__,
- "follow_inferior_fork: \"ask\" mode not implemented");
- /* follow_mode = follow_fork_mode_...; */
- }
-
- /* If we're to be following the parent, then detach from child_pid.
- We're already following the parent, so need do nothing explicit
- for it. */
- if (follow_mode == follow_fork_mode_parent)
- {
- followed_parent = 1;
-
- /* We're already attached to the parent, by default. */
-
- /* Before detaching from the child, remove all breakpoints from
- it. (This won't actually modify the breakpoint list, but will
- physically remove the breakpoints from the child.) */
- detach_breakpoints (child_pid);
-#ifdef SOLIB_REMOVE_INFERIOR_HOOK
- SOLIB_REMOVE_INFERIOR_HOOK (child_pid);
-#endif
-
- /* Detach from the child. */
- dont_repeat ();
+ int follow_child = (follow_fork_mode_string == follow_fork_mode_child);
- target_require_detach (child_pid, "", 1);
- }
-
- /* If we're to be following the child, then attach to it, detach
- from inferior_ptid, and set inferior_ptid to child_pid. */
- else if (follow_mode == follow_fork_mode_child)
- {
- char child_pid_spelling[100]; /* Arbitrary length. */
-
- followed_child = 1;
-
- /* Before detaching from the parent, detach all breakpoints from
- the child. Note that this only works if we're following vforks
- right away; if we've exec'd then the breakpoints are already detached
- and the shadow contents are out of date. */
- detach_breakpoints (child_pid);
-
- /* Before detaching from the parent, remove all breakpoints from it. */
- remove_breakpoints ();
-
- /* Also reset the solib inferior hook from the parent. */
-#ifdef SOLIB_REMOVE_INFERIOR_HOOK
- SOLIB_REMOVE_INFERIOR_HOOK (PIDGET (inferior_ptid));
-#endif
-
- /* Detach from the parent. */
- dont_repeat ();
- target_detach (NULL, 1);
-
- /* Attach to the child. */
- inferior_ptid = pid_to_ptid (child_pid);
- sprintf (child_pid_spelling, "%d", child_pid);
- dont_repeat ();
-
- target_require_attach (child_pid_spelling, 1);
-
- /* Was there a step_resume breakpoint? (There was if the user
- did a "next" at the fork() call.) If so, explicitly reset its
- thread number.
-
- step_resumes are a form of bp that are made to be per-thread.
- Since we created the step_resume bp when the parent process
- was being debugged, and now are switching to the child process,
- from the breakpoint package's viewpoint, that's a switch of
- "threads". We must update the bp's notion of which thread
- it is for, or it'll be ignored when it triggers... */
- /* As above, if we're following vforks at exec time then resetting the
- step resume breakpoint is probably wrong. */
- if (step_resume_breakpoint)
- breakpoint_re_set_thread (step_resume_breakpoint);
-
- /* Reinsert all breakpoints in the child. (The user may've set
- breakpoints after catching the fork, in which case those
- actually didn't get set in the child, but only in the parent.) */
- breakpoint_re_set ();
- insert_breakpoints ();
- }
-
- /* The parent and child of a vfork share the same address space.
- Also, on some targets the order in which vfork and exec events
- are received for parent in child requires some delicate handling
- of the events.
-
- For instance, on ptrace-based HPUX we receive the child's vfork
- event first, at which time the parent has been suspended by the
- OS and is essentially untouchable until the child's exit or second
- exec event arrives. At that time, the parent's vfork event is
- delivered to us, and that's when we see and decide how to follow
- the vfork. But to get to that point, we must continue the child
- until it execs or exits. To do that smoothly, all breakpoints
- must be removed from the child, in case there are any set between
- the vfork() and exec() calls. But removing them from the child
- also removes them from the parent, due to the shared-address-space
- nature of a vfork'd parent and child. On HPUX, therefore, we must
- take care to restore the bp's to the parent before we continue it.
- Else, it's likely that we may not stop in the expected place. (The
- worst scenario is when the user tries to step over a vfork() call;
- the step-resume bp must be restored for the step to properly stop
- in the parent after the call completes!)
-
- Sequence of events, as reported to gdb from HPUX:
-
- Parent Child Action for gdb to take
- -------------------------------------------------------
- 1 VFORK Continue child
- 2 EXEC
- 3 EXEC or EXIT
- 4 VFORK */
- if (has_vforked)
- {
- target_post_follow_vfork (parent_pid,
- followed_parent, child_pid, followed_child);
- }
-
- pending_follow.fork_event.saw_parent_fork = 0;
- pending_follow.fork_event.saw_child_fork = 0;
+ return target_follow_fork (follow_child);
}
-static void
-follow_fork (int parent_pid, int child_pid)
+void
+follow_inferior_reset_breakpoints (void)
{
- follow_inferior_fork (parent_pid, child_pid, 1, 0);
-}
+ /* Was there a step_resume breakpoint? (There was if the user
+ did a "next" at the fork() call.) If so, explicitly reset its
+ thread number.
+ step_resumes are a form of bp that are made to be per-thread.
+ Since we created the step_resume bp when the parent process
+ was being debugged, and now are switching to the child process,
+ from the breakpoint package's viewpoint, that's a switch of
+ "threads". We must update the bp's notion of which thread
+ it is for, or it'll be ignored when it triggers. */
-/* Forward declaration. */
-static void follow_exec (int, char *);
+ if (step_resume_breakpoint)
+ breakpoint_re_set_thread (step_resume_breakpoint);
-static void
-follow_vfork (int parent_pid, int child_pid)
-{
- follow_inferior_fork (parent_pid, child_pid, 0, 1);
+ /* Reinsert all breakpoints in the child. The user may have set
+ breakpoints after catching the fork, in which case those
+ were never set in the child, but only in the parent. This makes
+ sure the inserted breakpoints match the breakpoint list. */
- /* Did we follow the child? Had it exec'd before we saw the parent vfork? */
- if (pending_follow.fork_event.saw_child_exec
- && (PIDGET (inferior_ptid) == child_pid))
- {
- pending_follow.fork_event.saw_child_exec = 0;
- pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
- follow_exec (PIDGET (inferior_ptid), pending_follow.execd_pathname);
- xfree (pending_follow.execd_pathname);
- }
+ breakpoint_re_set ();
+ insert_breakpoints ();
}
/* EXECD_PATHNAME is assumed to be non-NULL. */
because we cannot remove the breakpoints in the inferior process
until after the `wait' in `wait_for_inferior'. */
static int singlestep_breakpoints_inserted_p = 0;
+
+/* The thread we inserted single-step breakpoints for. */
+static ptid_t singlestep_ptid;
+
+/* If another thread hit the singlestep breakpoint, we save the original
+ thread here so that we can resume single-stepping it later. */
+static ptid_t saved_singlestep_ptid;
+static int stepping_past_singlestep_breakpoint;
\f
/* Things to clean up if we QUIT out of resume (). */
-/* ARGSUSED */
static void
resume_cleanups (void *ignore)
{
the set command passed as a parameter. The clone operation will
include (BUG?) any ``set'' command callback, if present.
Commands like ``info set'' call all the ``show'' command
- callbacks. Unfortunatly, for ``show'' commands cloned from
+ callbacks. Unfortunately, for ``show'' commands cloned from
``set'', this includes callbacks belonging to ``set'' commands.
Making this worse, this only occures if add_show_from_set() is
called after add_cmd_sfunc() (BUG?). */
/* and do not pull these breakpoints until after a `wait' in
`wait_for_inferior' */
singlestep_breakpoints_inserted_p = 1;
+ singlestep_ptid = inferior_ptid;
}
/* Handle any optimized stores to the inferior NOW... */
#endif
/* If there were any forks/vforks/execs that were caught and are
- now to be followed, then do so. */
+ now to be followed, then do so. */
switch (pending_follow.kind)
{
- case (TARGET_WAITKIND_FORKED):
+ case TARGET_WAITKIND_FORKED:
+ case TARGET_WAITKIND_VFORKED:
pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
- follow_fork (PIDGET (inferior_ptid),
- pending_follow.fork_event.child_pid);
- break;
-
- case (TARGET_WAITKIND_VFORKED):
- {
- int saw_child_exec = pending_follow.fork_event.saw_child_exec;
-
- pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
- follow_vfork (PIDGET (inferior_ptid),
- pending_follow.fork_event.child_pid);
-
- /* Did we follow the child, but not yet see the child's exec event?
- If so, then it actually ought to be waiting for us; we respond to
- parent vfork events. We don't actually want to resume the child
- in this situation; we want to just get its exec event. */
- if (!saw_child_exec &&
- (PIDGET (inferior_ptid) == pending_follow.fork_event.child_pid))
- should_resume = 0;
- }
+ if (follow_fork ())
+ should_resume = 0;
break;
- case (TARGET_WAITKIND_EXECD):
- /* If we saw a vfork event but couldn't follow it until we saw
- an exec, then now might be the time! */
- pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
+ case TARGET_WAITKIND_EXECD:
/* follow_exec is called as soon as the exec event is seen. */
+ pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
break;
default:
resume_ptid = RESUME_ALL; /* Default */
if ((step || singlestep_breakpoints_inserted_p) &&
- !breakpoints_inserted && breakpoint_here_p (read_pc ()))
+ (stepping_past_singlestep_breakpoint
+ || (!breakpoints_inserted && breakpoint_here_p (read_pc ()))))
{
/* Stepping past a breakpoint without inserting breakpoints.
Make sure only the current thread gets to step, so that
trap_expected = 0;
step_range_start = 0;
step_range_end = 0;
- step_frame_address = 0;
+ step_frame_id = null_frame_id;
step_over_calls = STEP_OVER_UNDEBUGGABLE;
stop_after_trap = 0;
- stop_soon_quietly = 0;
+ stop_soon = NO_STOP_QUIETLY;
proceed_to_finish = 0;
breakpoint_proceeded = 1; /* We're about to proceed... */
bpstat_clear (&stop_bpstat);
}
+/* This should be suitable for any targets that support threads. */
+
+static int
+prepare_to_proceed (void)
+{
+ 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);
+
+ /* Make sure we were stopped either at a breakpoint, or because
+ of a Ctrl-C. */
+ if (wait_status.kind != TARGET_WAITKIND_STOPPED
+ || (wait_status.value.sig != TARGET_SIGNAL_TRAP &&
+ wait_status.value.sig != TARGET_SIGNAL_INT))
+ {
+ return 0;
+ }
+
+ if (!ptid_equal (wait_ptid, minus_one_ptid)
+ && !ptid_equal (inferior_ptid, wait_ptid))
+ {
+ /* Switched over from WAIT_PID. */
+ CORE_ADDR wait_pc = read_pc_pid (wait_ptid);
+
+ if (wait_pc != read_pc ())
+ {
+ /* Switch back to WAIT_PID thread. */
+ inferior_ptid = wait_ptid;
+
+ /* FIXME: This stuff came from switch_to_thread() in
+ thread.c (which should probably be a public function). */
+ flush_cached_frames ();
+ registers_changed ();
+ stop_pc = wait_pc;
+ select_frame (get_current_frame ());
+ }
+
+ /* We return 1 to indicate that there is a breakpoint here,
+ so we need to step over it before continuing to avoid
+ hitting it straight away. */
+ if (breakpoint_here_p (wait_pc))
+ return 1;
+ }
+
+ return 0;
+
+}
+
+/* Record the pc of the program the last time it stopped. This is
+ just used internally by wait_for_inferior, but need to be preserved
+ over calls to it and cleared when the inferior is started. */
+static CORE_ADDR prev_pc;
+
/* Basic routine for continuing the program in various fashions.
ADDR is the address to resume at, or -1 for resume where stopped.
write_pc (addr);
}
-#ifdef PREPARE_TO_PROCEED
/* In a multi-threaded task we may select another thread
and then continue or step.
any execution (i.e. it will report a breakpoint hit
incorrectly). So we must step over it first.
- PREPARE_TO_PROCEED checks the current thread against the thread
+ prepare_to_proceed checks the current thread against the thread
that reported the most recent event. If a step-over is required
it returns TRUE and sets the current thread to the old thread. */
- if (PREPARE_TO_PROCEED (1) && breakpoint_here_p (read_pc ()))
- {
- oneproc = 1;
- }
-
-#endif /* PREPARE_TO_PROCEED */
+ if (prepare_to_proceed () && breakpoint_here_p (read_pc ()))
+ oneproc = 1;
#ifdef HP_OS_BUG
if (trap_expected_after_continue)
inferior. */
gdb_flush (gdb_stdout);
+ /* Refresh prev_pc value just prior to resuming. This used to be
+ done in stop_stepping, however, setting prev_pc there did not handle
+ scenarios such as inferior function calls or returning from
+ a function via the return command. In those cases, the prev_pc
+ value was not set properly for subsequent commands. The prev_pc value
+ is used to initialize the starting line number in the ecs. With an
+ invalid value, the gdb next command ends up stopping at the position
+ represented by the next line table entry past our start position.
+ On platforms that generate one line table entry per line, this
+ is not a problem. However, on the ia64, the compiler generates
+ extraneous line table entries that do not increase the line number.
+ When we issue the gdb next command on the ia64 after an inferior call
+ 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 read_pc ()
+ call can fail. Setting the prev_pc value here ensures the value is
+ updated correctly when the inferior is stopped. */
+ prev_pc = read_pc ();
+
/* Resume inferior. */
resume (oneproc || step || bpstat_should_step (), stop_signal);
normal_stop ();
}
}
-
-/* Record the pc and sp of the program the last time it stopped.
- These are just used internally by wait_for_inferior, but need
- to be preserved over calls to it and cleared when the inferior
- is started. */
-static CORE_ADDR prev_pc;
-static CORE_ADDR prev_func_start;
-static char *prev_func_name;
\f
/* Start remote-debugging of a machine over a serial link. */
{
init_thread_list ();
init_wait_for_inferior ();
- stop_soon_quietly = 1;
+ stop_soon = STOP_QUIETLY;
trap_expected = 0;
/* Always go on waiting for the target, regardless of the mode. */
{
/* These are meaningless until the first time through wait_for_inferior. */
prev_pc = 0;
- prev_func_start = 0;
- prev_func_name = NULL;
#ifdef HP_OS_BUG
trap_expected_after_continue = 0;
/* The first resume is not following a fork/vfork/exec. */
pending_follow.kind = TARGET_WAITKIND_SPURIOUS; /* I.e., none. */
- pending_follow.fork_event.saw_parent_fork = 0;
- pending_follow.fork_event.saw_child_fork = 0;
- pending_follow.fork_event.saw_child_exec = 0;
/* See wait_for_inferior's handling of SYSCALL_ENTRY/RETURN events. */
number_of_threads_in_syscalls = 0;
clear_proceed_status ();
+
+ stepping_past_singlestep_breakpoint = 0;
}
static void
void init_execution_control_state (struct execution_control_state *ecs);
+static void handle_step_into_function (struct execution_control_state *ecs);
void handle_inferior_event (struct execution_control_state *ecs);
static void check_sigtramp2 (struct execution_control_state *ecs);
{ /* Perform infrun state context switch: */
/* Save infrun state for the old thread. */
save_infrun_state (inferior_ptid, prev_pc,
- prev_func_start, prev_func_name,
trap_expected, step_resume_breakpoint,
through_sigtramp_breakpoint, step_range_start,
- step_range_end, step_frame_address,
+ step_range_end, &step_frame_id,
ecs->handling_longjmp, ecs->another_trap,
ecs->stepping_through_solib_after_catch,
ecs->stepping_through_solib_catchpoints,
/* Load infrun state for the new thread. */
load_infrun_state (ecs->ptid, &prev_pc,
- &prev_func_start, &prev_func_name,
&trap_expected, &step_resume_breakpoint,
&through_sigtramp_breakpoint, &step_range_start,
- &step_range_end, &step_frame_address,
+ &step_range_end, &step_frame_id,
&ecs->handling_longjmp, &ecs->another_trap,
&ecs->stepping_through_solib_after_catch,
&ecs->stepping_through_solib_catchpoints,
inferior_ptid = ecs->ptid;
}
+/* Wrapper for DEPRECATED_PC_IN_SIGTRAMP that takes care of the need
+ to find the function's name.
+
+ In a classic example of "left hand VS right hand", "infrun.c" was
+ trying to improve GDB's performance by caching the result of calls
+ to calls to find_pc_partial_funtion, while at the same time
+ find_pc_partial_function was also trying to ramp up performance by
+ caching its most recent return value. The below makes the the
+ function find_pc_partial_function solely responsibile for
+ performance issues (the local cache that relied on a global
+ variable - arrrggg - deleted).
+
+ Using the testsuite and gcov, it was found that dropping the local
+ "infrun.c" cache and instead relying on find_pc_partial_function
+ increased the number of calls to 12000 (from 10000), but the number
+ of times find_pc_partial_function's cache missed (this is what
+ matters) was only increased by only 4 (to 3569). (A quick back of
+ envelope caculation suggests that the extra 2000 function calls
+ @1000 extra instructions per call make the 1 MIP VAX testsuite run
+ take two extra seconds, oops :-)
+
+ Long term, this function can be eliminated, replaced by the code:
+ get_frame_type(current_frame()) == SIGTRAMP_FRAME (for new
+ architectures this is very cheap). */
+
+static int
+pc_in_sigtramp (CORE_ADDR pc)
+{
+ char *name;
+ find_pc_partial_function (pc, &name, NULL, NULL);
+ return DEPRECATED_PC_IN_SIGTRAMP (pc, name);
+}
+
+/* Handle the inferior event in the cases when we just stepped
+ into a function. */
+
+static void
+handle_step_into_function (struct execution_control_state *ecs)
+{
+ CORE_ADDR real_stop_pc;
+
+ if ((step_over_calls == STEP_OVER_NONE)
+ || ((step_range_end == 1)
+ && in_prologue (prev_pc, ecs->stop_func_start)))
+ {
+ /* I presume that step_over_calls is only 0 when we're
+ supposed to be stepping at the assembly language level
+ ("stepi"). Just stop. */
+ /* Also, maybe we just did a "nexti" inside a prolog,
+ so we thought it was a subroutine call but it was not.
+ Stop as well. FENN */
+ stop_step = 1;
+ print_stop_reason (END_STEPPING_RANGE, 0);
+ stop_stepping (ecs);
+ return;
+ }
+
+ if (step_over_calls == STEP_OVER_ALL || IGNORE_HELPER_CALL (stop_pc))
+ {
+ /* We're doing a "next". */
+
+ if (legacy_frame_p (current_gdbarch)
+ && pc_in_sigtramp (stop_pc)
+ && frame_id_inner (step_frame_id,
+ frame_id_build (read_sp (), 0)))
+ /* NOTE: cagney/2004-03-15: This is only needed for legacy
+ systems. On non-legacy systems step_over_function doesn't
+ use STEP_FRAME_ID and hence the below update "hack" isn't
+ needed. */
+ /* We stepped out of a signal handler, and into its calling
+ trampoline. This is misdetected as a subroutine call, but
+ stepping over the signal trampoline isn't such a bad idea.
+ In order to do that, we have to ignore the value in
+ step_frame_id, since that doesn't represent the frame
+ that'll reach when we return from the signal trampoline.
+ Otherwise we'll probably continue to the end of the
+ program. */
+ step_frame_id = null_frame_id;
+
+ step_over_function (ecs);
+ keep_going (ecs);
+ return;
+ }
+
+ /* If we are in a function call trampoline (a stub between
+ the calling routine and the real function), locate the real
+ 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 (stop_pc);
+ if (real_stop_pc == 0)
+ real_stop_pc = SKIP_TRAMPOLINE_CODE (stop_pc);
+ if (real_stop_pc != 0)
+ ecs->stop_func_start = real_stop_pc;
+
+ /* If we have line number information for the function we
+ are thinking of stepping into, step into it.
+
+ If there are several symtabs at that PC (e.g. with include
+ files), just want to know whether *any* of them have line
+ numbers. find_pc_line handles this. */
+ {
+ struct symtab_and_line tmp_sal;
+
+ tmp_sal = find_pc_line (ecs->stop_func_start, 0);
+ if (tmp_sal.line != 0)
+ {
+ step_into_function (ecs);
+ return;
+ }
+ }
+
+ /* 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
+ switch in assembly mode. */
+ if (step_over_calls == STEP_OVER_UNDEBUGGABLE && step_stop_if_no_debug)
+ {
+ stop_step = 1;
+ print_stop_reason (END_STEPPING_RANGE, 0);
+ stop_stepping (ecs);
+ return;
+ }
+
+ step_over_function (ecs);
+ keep_going (ecs);
+ return;
+}
+
+static void
+adjust_pc_after_break (struct execution_control_state *ecs)
+{
+ CORE_ADDR stop_pc;
+
+ /* If this target does not decrement the PC after breakpoints, then
+ we have nothing to do. */
+ if (DECR_PC_AFTER_BREAK == 0)
+ return;
+
+ /* If we've hit a breakpoint, we'll normally be stopped with SIGTRAP. If
+ we aren't, just return.
+
+ We assume that waitkinds other than TARGET_WAITKIND_STOPPED are not
+ affected by DECR_PC_AFTER_BREAK. Other waitkinds which are implemented
+ by software breakpoints should be handled through the normal breakpoint
+ layer.
+
+ NOTE drow/2004-01-31: On some targets, breakpoints may generate
+ different signals (SIGILL or SIGEMT for instance), but it is less
+ clear where the PC is pointing afterwards. It may not match
+ DECR_PC_AFTER_BREAK. I don't know any specific target that generates
+ these signals at breakpoints (the code has been in GDB since at least
+ 1992) so I can not guess how to handle them here.
+
+ In earlier versions of GDB, a target with HAVE_NONSTEPPABLE_WATCHPOINTS
+ would have the PC after hitting a watchpoint affected by
+ DECR_PC_AFTER_BREAK. I haven't found any target with both of these set
+ in GDB history, and it seems unlikely to be correct, so
+ HAVE_NONSTEPPABLE_WATCHPOINTS is not checked here. */
+
+ if (ecs->ws.kind != TARGET_WAITKIND_STOPPED)
+ return;
+
+ if (ecs->ws.value.sig != TARGET_SIGNAL_TRAP)
+ return;
+
+ /* Find the location where (if we've hit a breakpoint) the breakpoint would
+ be. */
+ stop_pc = read_pc_pid (ecs->ptid) - DECR_PC_AFTER_BREAK;
+
+ /* If we're software-single-stepping, then assume this is a breakpoint.
+ NOTE drow/2004-01-17: This doesn't check that the PC matches, or that
+ we're even in the right thread. The software-single-step code needs
+ some modernization.
+
+ If we're not software-single-stepping, then we first check that there
+ is an enabled software breakpoint at this address. If there is, and
+ we weren't using hardware-single-step, then we've hit the breakpoint.
+
+ If we were using hardware-single-step, we check prev_pc; if we just
+ stepped over an inserted software breakpoint, then we should decrement
+ the PC and eventually report hitting the breakpoint. The prev_pc check
+ prevents us from decrementing the PC if we just stepped over a jump
+ instruction and landed on the instruction after a breakpoint.
+
+ The last bit checks that we didn't hit a breakpoint in a signal handler
+ without an intervening stop in sigtramp, which is detected by a new
+ stack pointer value below any usual function calling stack adjustments.
+
+ NOTE drow/2004-01-17: I'm not sure that this is necessary. The check
+ predates checking for software single step at the same time. Also,
+ if we've moved into a signal handler we should have seen the
+ signal. */
+
+ if ((SOFTWARE_SINGLE_STEP_P () && singlestep_breakpoints_inserted_p)
+ || (software_breakpoint_inserted_here_p (stop_pc)
+ && !(currently_stepping (ecs)
+ && prev_pc != stop_pc
+ && !(step_range_end && INNER_THAN (read_sp (), (step_sp - 16))))))
+ write_pc_pid (stop_pc, ecs->ptid);
+}
/* Given an execution control state that has been freshly filled in
by an event from the inferior, figure out what it means and take
void
handle_inferior_event (struct execution_control_state *ecs)
{
- CORE_ADDR tmp;
+ /* NOTE: cagney/2003-03-28: If you're looking at this code and
+ thinking that the variable stepped_after_stopped_by_watchpoint
+ isn't used, then you're wrong! The macro STOPPED_BY_WATCHPOINT,
+ defined in the file "config/pa/nm-hppah.h", accesses the variable
+ indirectly. Mutter something rude about the HP merge. */
int stepped_after_stopped_by_watchpoint;
int sw_single_step_trap_p = 0;
target_last_wait_ptid = ecs->ptid;
target_last_waitstatus = *ecs->wp;
+ adjust_pc_after_break (ecs);
+
switch (ecs->infwait_state)
{
case infwait_thread_hop_state:
/* Cancel the waiton_ptid. */
ecs->waiton_ptid = pid_to_ptid (-1);
- /* Fall thru to the normal_state case. */
+ /* See comments where a TARGET_WAITKIND_SYSCALL_RETURN event
+ is serviced in this loop, below. */
+ if (ecs->enable_hw_watchpoints_after_wait)
+ {
+ TARGET_ENABLE_HW_WATCHPOINTS (PIDGET (inferior_ptid));
+ ecs->enable_hw_watchpoints_after_wait = 0;
+ }
+ stepped_after_stopped_by_watchpoint = 0;
+ break;
case infwait_normal_state:
/* See comments where a TARGET_WAITKIND_SYSCALL_RETURN event
break;
case infwait_nullified_state:
+ stepped_after_stopped_by_watchpoint = 0;
break;
case infwait_nonstep_watch_state:
in combination correctly? */
stepped_after_stopped_by_watchpoint = 1;
break;
+
+ default:
+ internal_error (__FILE__, __LINE__, "bad switch");
}
ecs->infwait_state = infwait_normal_state;
might be the shell which has just loaded some objects,
otherwise add the symbols for the newly loaded objects. */
#ifdef SOLIB_ADD
- if (!stop_soon_quietly)
+ if (stop_soon == NO_STOP_QUIETLY)
{
/* Remove breakpoints, SOLIB_ADD might adjust
breakpoint addresses via breakpoint_re_set. */
terminal for any messages produced by
breakpoint_re_set. */
target_terminal_ours_for_output ();
- SOLIB_ADD (NULL, 0, NULL, auto_solib_add);
+ /* NOTE: cagney/2003-11-25: Make certain that the target
+ stack's section table is kept up-to-date. Architectures,
+ (e.g., PPC64), use the section table to perform
+ operations such as address => section name and hence
+ require the table to contain all sections (including
+ those found in shared libraries). */
+ /* 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. */
+ SOLIB_ADD (NULL, 0, ¤t_target, auto_solib_add);
target_terminal_inferior ();
/* Reinsert breakpoints and continue. */
/* The following are the only cases in which we keep going;
the above cases end in a continue or goto. */
case TARGET_WAITKIND_FORKED:
+ case TARGET_WAITKIND_VFORKED:
stop_signal = TARGET_SIGNAL_TRAP;
pending_follow.kind = ecs->ws.kind;
- /* Ignore fork events reported for the parent; we're only
- interested in reacting to forks of the child. Note that
- we expect the child's fork event to be available if we
- waited for it now. */
- if (ptid_equal (inferior_ptid, ecs->ptid))
- {
- pending_follow.fork_event.saw_parent_fork = 1;
- pending_follow.fork_event.parent_pid = PIDGET (ecs->ptid);
- pending_follow.fork_event.child_pid = ecs->ws.value.related_pid;
- prepare_to_wait (ecs);
- return;
- }
- else
- {
- pending_follow.fork_event.saw_child_fork = 1;
- pending_follow.fork_event.child_pid = PIDGET (ecs->ptid);
- pending_follow.fork_event.parent_pid = ecs->ws.value.related_pid;
- }
+ pending_follow.fork_event.parent_pid = PIDGET (ecs->ptid);
+ pending_follow.fork_event.child_pid = ecs->ws.value.related_pid;
- stop_pc = read_pc_pid (ecs->ptid);
- ecs->saved_inferior_ptid = inferior_ptid;
- inferior_ptid = ecs->ptid;
- /* The second argument of bpstat_stop_status is meant to help
- distinguish between a breakpoint trap and a singlestep trap.
- This is only important on targets where DECR_PC_AFTER_BREAK
- is non-zero. The prev_pc test is meant to distinguish between
- singlestepping a trap instruction, and singlestepping thru a
- jump to the instruction following a trap instruction. */
-
- stop_bpstat = bpstat_stop_status (&stop_pc,
- currently_stepping (ecs) &&
- prev_pc !=
- stop_pc - DECR_PC_AFTER_BREAK);
- ecs->random_signal = !bpstat_explains_signal (stop_bpstat);
- inferior_ptid = ecs->saved_inferior_ptid;
- goto process_event_stop_test;
+ stop_pc = read_pc ();
- /* If this a platform which doesn't allow a debugger to touch a
- vfork'd inferior until after it exec's, then we'd best keep
- our fingers entirely off the inferior, other than continuing
- it. This has the unfortunate side-effect that catchpoints
- of vforks will be ignored. But since the platform doesn't
- allow the inferior be touched at vfork time, there's really
- little choice. */
- case TARGET_WAITKIND_VFORKED:
- stop_signal = TARGET_SIGNAL_TRAP;
- pending_follow.kind = ecs->ws.kind;
+ stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
- /* Is this a vfork of the parent? If so, then give any
- vfork catchpoints a chance to trigger now. (It's
- dangerous to do so if the child canot be touched until
- it execs, and the child has not yet exec'd. We probably
- should warn the user to that effect when the catchpoint
- triggers...) */
- if (ptid_equal (ecs->ptid, inferior_ptid))
- {
- pending_follow.fork_event.saw_parent_fork = 1;
- pending_follow.fork_event.parent_pid = PIDGET (ecs->ptid);
- pending_follow.fork_event.child_pid = ecs->ws.value.related_pid;
- }
+ ecs->random_signal = !bpstat_explains_signal (stop_bpstat);
- /* If we've seen the child's vfork event but cannot really touch
- the child until it execs, then we must continue the child now.
- Else, give any vfork catchpoints a chance to trigger now. */
- else
+ /* If no catchpoint triggered for this, then keep going. */
+ if (ecs->random_signal)
{
- pending_follow.fork_event.saw_child_fork = 1;
- pending_follow.fork_event.child_pid = PIDGET (ecs->ptid);
- pending_follow.fork_event.parent_pid = ecs->ws.value.related_pid;
- target_post_startup_inferior (pid_to_ptid
- (pending_follow.fork_event.
- child_pid));
+ stop_signal = TARGET_SIGNAL_0;
+ keep_going (ecs);
+ return;
}
-
- stop_pc = read_pc ();
- /* The second argument of bpstat_stop_status is meant to help
- distinguish between a breakpoint trap and a singlestep trap.
- This is only important on targets where DECR_PC_AFTER_BREAK
- is non-zero. The prev_pc test is meant to distinguish between
- singlestepping a trap instruction, and singlestepping thru a
- jump to the instruction following a trap instruction. */
-
- stop_bpstat = bpstat_stop_status (&stop_pc,
- currently_stepping (ecs) &&
- prev_pc !=
- stop_pc - DECR_PC_AFTER_BREAK);
- ecs->random_signal = !bpstat_explains_signal (stop_bpstat);
goto process_event_stop_test;
case TARGET_WAITKIND_EXECD:
stop_signal = TARGET_SIGNAL_TRAP;
+ /* NOTE drow/2002-12-05: This code should be pushed down into the
+ target_wait function. Until then following vfork on HP/UX 10.20
+ is probably broken by this. Of course, it's broken anyway. */
/* Is this a target which reports multiple exec events per actual
call to exec()? (HP-UX using ptrace does, for example.) If so,
ignore all but the last one. Just resume the exec'r, and wait
savestring (ecs->ws.value.execd_pathname,
strlen (ecs->ws.value.execd_pathname));
- /* Did inferior_ptid exec, or did a (possibly not-yet-followed)
- child of a vfork exec?
-
- ??rehrauer: This is unabashedly an HP-UX specific thing. On
- HP-UX, events associated with a vforking inferior come in
- threes: a vfork event for the child (always first), followed
- a vfork event for the parent and an exec event for the child.
- The latter two can come in either order.
-
- If we get the parent vfork event first, life's good: We follow
- either the parent or child, and then the child's exec event is
- a "don't care".
-
- But if we get the child's exec event first, then we delay
- responding to it until we handle the parent's vfork. Because,
- otherwise we can't satisfy a "catch vfork". */
- if (pending_follow.kind == TARGET_WAITKIND_VFORKED)
- {
- pending_follow.fork_event.saw_child_exec = 1;
-
- /* On some targets, the child must be resumed before
- the parent vfork event is delivered. A single-step
- suffices. */
- if (RESUME_EXECD_VFORKING_CHILD_TO_GET_PARENT_VFORK ())
- target_resume (ecs->ptid, 1, TARGET_SIGNAL_0);
- /* We expect the parent vfork event to be available now. */
- prepare_to_wait (ecs);
- return;
- }
-
/* This causes the eventpoints and symbol table to be reset. Must
do this now, before trying to determine whether to stop. */
follow_exec (PIDGET (inferior_ptid), pending_follow.execd_pathname);
stop_pc = read_pc_pid (ecs->ptid);
ecs->saved_inferior_ptid = inferior_ptid;
inferior_ptid = ecs->ptid;
- /* The second argument of bpstat_stop_status is meant to help
- distinguish between a breakpoint trap and a singlestep trap.
- This is only important on targets where DECR_PC_AFTER_BREAK
- is non-zero. The prev_pc test is meant to distinguish between
- singlestepping a trap instruction, and singlestepping thru a
- jump to the instruction following a trap instruction. */
-
- stop_bpstat = bpstat_stop_status (&stop_pc,
- currently_stepping (ecs) &&
- prev_pc !=
- stop_pc - DECR_PC_AFTER_BREAK);
+
+ stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
+
ecs->random_signal = !bpstat_explains_signal (stop_bpstat);
inferior_ptid = ecs->saved_inferior_ptid;
+
+ /* If no catchpoint triggered for this, then keep going. */
+ if (ecs->random_signal)
+ {
+ stop_signal = TARGET_SIGNAL_0;
+ keep_going (ecs);
+ return;
+ }
goto process_event_stop_test;
/* These syscall events are returned on HP-UX, as part of its
/* 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. This case can
- occur only when the target is async or extended-async. One
- of the circumstamces for this to happen is when the
- inferior produces output for the console. The inferior has
- not stopped, and we are ignoring the event. */
+ 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:
- ecs->wait_some_more = 1;
+ prepare_to_wait (ecs);
return;
}
stop_pc = read_pc_pid (ecs->ptid);
+ if (stepping_past_singlestep_breakpoint)
+ {
+ gdb_assert (SOFTWARE_SINGLE_STEP_P () && singlestep_breakpoints_inserted_p);
+ gdb_assert (ptid_equal (singlestep_ptid, ecs->ptid));
+ gdb_assert (!ptid_equal (singlestep_ptid, saved_singlestep_ptid));
+
+ stepping_past_singlestep_breakpoint = 0;
+
+ /* We've either finished single-stepping past the single-step
+ breakpoint, or stopped for some other reason. It would be nice if
+ we could tell, but we can't reliably. */
+ if (stop_signal == TARGET_SIGNAL_TRAP)
+ {
+ /* Pull the single step breakpoints out of the target. */
+ SOFTWARE_SINGLE_STEP (0, 0);
+ singlestep_breakpoints_inserted_p = 0;
+
+ ecs->random_signal = 0;
+
+ ecs->ptid = saved_singlestep_ptid;
+ context_switch (ecs);
+ if (context_hook)
+ context_hook (pid_to_thread_id (ecs->ptid));
+
+ resume (1, TARGET_SIGNAL_0);
+ prepare_to_wait (ecs);
+ return;
+ }
+ }
+
+ stepping_past_singlestep_breakpoint = 0;
+
/* See if a thread hit a thread-specific breakpoint that was meant for
another thread. If so, then step that thread past the breakpoint,
and continue it. */
if (stop_signal == TARGET_SIGNAL_TRAP)
{
+ int thread_hop_needed = 0;
+
/* 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 (breakpoints_inserted
- && breakpoint_here_p (stop_pc - DECR_PC_AFTER_BREAK))
+ if (breakpoints_inserted && breakpoint_here_p (stop_pc))
+ {
+ ecs->random_signal = 0;
+ if (!breakpoint_thread_match (stop_pc, ecs->ptid))
+ thread_hop_needed = 1;
+ }
+ else if (SOFTWARE_SINGLE_STEP_P () && singlestep_breakpoints_inserted_p)
{
ecs->random_signal = 0;
- if (!breakpoint_thread_match (stop_pc - DECR_PC_AFTER_BREAK,
- ecs->ptid))
+ /* The call to in_thread_list is necessary because PTIDs sometimes
+ change when we go from single-threaded to multi-threaded. If
+ the singlestep_ptid is still in the list, assume that it is
+ really different from ecs->ptid. */
+ if (!ptid_equal (singlestep_ptid, ecs->ptid)
+ && in_thread_list (singlestep_ptid))
+ {
+ thread_hop_needed = 1;
+ stepping_past_singlestep_breakpoint = 1;
+ saved_singlestep_ptid = singlestep_ptid;
+ }
+ }
+
+ if (thread_hop_needed)
{
int remove_status;
/* Saw a breakpoint, but it was hit by the wrong thread.
Just continue. */
- if (DECR_PC_AFTER_BREAK)
- write_pc_pid (stop_pc - DECR_PC_AFTER_BREAK, ecs->ptid);
+
+ if (SOFTWARE_SINGLE_STEP_P () && singlestep_breakpoints_inserted_p)
+ {
+ /* Pull the single step breakpoints out of the target. */
+ SOFTWARE_SINGLE_STEP (0, 0);
+ singlestep_breakpoints_inserted_p = 0;
+ }
remove_status = remove_breakpoints ();
/* Did we fail to remove breakpoints? If so, try
if (remove_status != 0)
{
/* FIXME! This is obviously non-portable! */
- write_pc_pid (stop_pc - DECR_PC_AFTER_BREAK + 4, ecs->ptid);
+ write_pc_pid (stop_pc + 4, ecs->ptid);
/* We need to restart all the threads now,
* unles we're running in scheduler-locked mode.
* Use currently_stepping to determine whether to
registers_changed ();
return;
}
- }
}
else if (SOFTWARE_SINGLE_STEP_P () && singlestep_breakpoints_inserted_p)
{
- /* Readjust the stop_pc as it is off by DECR_PC_AFTER_BREAK
- compared to the value it would have if the system stepping
- capability was used. This allows the rest of the code in
- this function to use this address without having to worry
- whether software single step is in use or not. */
- if (DECR_PC_AFTER_BREAK)
- {
- stop_pc -= DECR_PC_AFTER_BREAK;
- write_pc_pid (stop_pc, ecs->ptid);
- }
-
sw_single_step_trap_p = 1;
ecs->random_signal = 0;
}
ecs->random_signal = 1;
/* See if something interesting happened to the non-current thread. If
- so, then switch to that thread, and eventually give control back to
- the user.
-
- Note that if there's any kind of pending follow (i.e., of a fork,
- vfork or exec), we don't want to do this now. Rather, we'll let
- the next resume handle it. */
- if (!ptid_equal (ecs->ptid, inferior_ptid) &&
- (pending_follow.kind == TARGET_WAITKIND_SPURIOUS))
+ so, then switch to that thread. */
+ if (!ptid_equal (ecs->ptid, inferior_ptid))
{
- int printed = 0;
-
- /* If it's a random signal for a non-current thread, notify user
- if he's expressed an interest. */
- if (ecs->random_signal && signal_print[stop_signal])
- {
-/* ??rehrauer: I don't understand the rationale for this code. If the
- inferior will stop as a result of this signal, then the act of handling
- the stop ought to print a message that's couches the stoppage in user
- terms, e.g., "Stopped for breakpoint/watchpoint". If the inferior
- won't stop as a result of the signal -- i.e., if the signal is merely
- a side-effect of something GDB's doing "under the covers" for the
- user, such as stepping threads over a breakpoint they shouldn't stop
- for -- then the message seems to be a serious annoyance at best.
-
- For now, remove the message altogether. */
-#if 0
- printed = 1;
- target_terminal_ours_for_output ();
- printf_filtered ("\nProgram received signal %s, %s.\n",
- target_signal_to_name (stop_signal),
- target_signal_to_string (stop_signal));
- gdb_flush (gdb_stdout);
-#endif
- }
-
- /* If it's not SIGTRAP and not a signal we want to stop for, then
- continue the thread. */
-
- if (stop_signal != TARGET_SIGNAL_TRAP && !signal_stop[stop_signal])
- {
- if (printed)
- target_terminal_inferior ();
-
- /* Clear the signal if it should not be passed. */
- if (signal_program[stop_signal] == 0)
- stop_signal = TARGET_SIGNAL_0;
-
- target_resume (ecs->ptid, 0, stop_signal);
- prepare_to_wait (ecs);
- return;
- }
-
- /* It's a SIGTRAP or a signal we're interested in. Switch threads,
- and fall into the rest of wait_for_inferior(). */
-
context_switch (ecs);
if (context_hook)
includes evaluating watchpoints, things will come to a
stop in the correct manner. */
- if (DECR_PC_AFTER_BREAK)
- write_pc (stop_pc - DECR_PC_AFTER_BREAK);
-
remove_breakpoints ();
registers_changed ();
target_resume (ecs->ptid, 1, TARGET_SIGNAL_0); /* Single step */
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. */
+ 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 (stop_signal == TARGET_SIGNAL_TRAP
|| (breakpoints_inserted &&
(stop_signal == TARGET_SIGNAL_ILL
- || stop_signal == TARGET_SIGNAL_EMT)) || stop_soon_quietly)
+ || stop_signal == TARGET_SIGNAL_SEGV
+ || stop_signal == TARGET_SIGNAL_EMT))
+ || stop_soon == STOP_QUIETLY
+ || stop_soon == STOP_QUIETLY_NO_SIGSTOP)
{
if (stop_signal == TARGET_SIGNAL_TRAP && stop_after_trap)
{
stop_stepping (ecs);
return;
}
- if (stop_soon_quietly)
+
+ /* This is originated from start_remote(), start_inferior() and
+ shared libraries hook functions. */
+ if (stop_soon == STOP_QUIETLY)
+ {
+ stop_stepping (ecs);
+ return;
+ }
+
+ /* This originates from attach_command(). We need to overwrite
+ the stop_signal here, because some kernels don't ignore a
+ SIGSTOP in a subsequent ptrace(PTRACE_SONT,SOGSTOP) call.
+ See more comments in inferior.h. */
+ if (stop_soon == STOP_QUIETLY_NO_SIGSTOP)
{
stop_stepping (ecs);
+ if (stop_signal == TARGET_SIGNAL_STOP)
+ stop_signal = TARGET_SIGNAL_0;
return;
}
else
{
/* See if there is a breakpoint at the current PC. */
+ stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
- /* The second argument of bpstat_stop_status is meant to help
- distinguish between a breakpoint trap and a singlestep trap.
- This is only important on targets where DECR_PC_AFTER_BREAK
- is non-zero. The prev_pc test is meant to distinguish between
- singlestepping a trap instruction, and singlestepping thru a
- jump to the instruction following a trap instruction.
-
- Therefore, pass TRUE if our reason for stopping is
- something other than hitting a breakpoint. We do this by
- checking that either: we detected earlier a software single
- step trap or, 1) stepping is going on and 2) we didn't hit
- a breakpoint in a signal handler without an intervening stop
- in sigtramp, which is detected by a new stack pointer value
- below any usual function calling stack adjustments. */
- stop_bpstat =
- bpstat_stop_status
- (&stop_pc,
- sw_single_step_trap_p
- || (currently_stepping (ecs)
- && prev_pc != stop_pc - DECR_PC_AFTER_BREAK
- && !(step_range_end
- && INNER_THAN (read_sp (), (step_sp - 16)))));
/* Following in case break condition called a
function. */
stop_print_frame = 1;
}
+ /* 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
+ comment, that went with the test, read:
+
+ ``End of a stack dummy. Some systems (e.g. Sony news) give
+ another signal besides SIGTRAP, so check here as well as
+ above.''
+
+ If someone ever tries to get get call dummys on a
+ non-executable stack to work (where the target would stop
+ with something like a SIGSEGV), then those tests might need
+ to be re-instated. Given, however, that the tests were only
+ enabled when momentary breakpoints were not being used, I
+ suspect that it won't be the case.
+
+ NOTE: kettenis/2004-02-05: Indeed such checks don't seem to
+ be necessary for call dummies on a non-executable stack on
+ SPARC. */
+
if (stop_signal == TARGET_SIGNAL_TRAP)
ecs->random_signal
= !(bpstat_explains_signal (stop_bpstat)
|| trap_expected
- || (!CALL_DUMMY_BREAKPOINT_OFFSET_P
- && DEPRECATED_PC_IN_CALL_DUMMY (stop_pc, read_sp (),
- get_frame_base (get_current_frame ())))
|| (step_range_end && step_resume_breakpoint == NULL));
-
else
{
- ecs->random_signal = !(bpstat_explains_signal (stop_bpstat)
- /* End of a stack dummy. Some systems (e.g. Sony
- news) give another signal besides SIGTRAP, so
- check here as well as above. */
- || (!CALL_DUMMY_BREAKPOINT_OFFSET_P
- && DEPRECATED_PC_IN_CALL_DUMMY (stop_pc, read_sp (),
- get_frame_base
- (get_current_frame
- ()))));
+ ecs->random_signal = !bpstat_explains_signal (stop_bpstat);
if (!ecs->random_signal)
stop_signal = TARGET_SIGNAL_TRAP;
}
else
ecs->random_signal = 1;
- /* If a fork, vfork or exec event was seen, then there are two
- possible responses we can make:
-
- 1. If a catchpoint triggers for the event (ecs->random_signal == 0),
- then we must stop now and issue a prompt. We will resume
- the inferior when the user tells us to.
- 2. If no catchpoint triggers for the event (ecs->random_signal == 1),
- then we must resume the inferior now and keep checking.
-
- In either case, we must take appropriate steps to "follow" the
- the fork/vfork/exec when the inferior is resumed. For example,
- if follow-fork-mode is "child", then we must detach from the
- parent inferior and follow the new child inferior.
-
- In either case, setting pending_follow causes the next resume()
- to take the appropriate following action. */
-process_event_stop_test:
- if (ecs->ws.kind == TARGET_WAITKIND_FORKED)
- {
- if (ecs->random_signal) /* I.e., no catchpoint triggered for this. */
- {
- trap_expected = 1;
- stop_signal = TARGET_SIGNAL_0;
- keep_going (ecs);
- return;
- }
- }
- else if (ecs->ws.kind == TARGET_WAITKIND_VFORKED)
- {
- if (ecs->random_signal) /* I.e., no catchpoint triggered for this. */
- {
- stop_signal = TARGET_SIGNAL_0;
- keep_going (ecs);
- return;
- }
- }
- else if (ecs->ws.kind == TARGET_WAITKIND_EXECD)
- {
- pending_follow.kind = ecs->ws.kind;
- if (ecs->random_signal) /* I.e., no catchpoint triggered for this. */
- {
- trap_expected = 1;
- stop_signal = TARGET_SIGNAL_0;
- keep_going (ecs);
- return;
- }
- }
+process_event_stop_test:
/* For the program's own signals, act according to
the signal handling tables. */
set_longjmp_resume_breakpoint (jmp_buf_pc, get_current_frame ());
else
#endif /* 0 */
- set_longjmp_resume_breakpoint (jmp_buf_pc, NULL);
+ set_longjmp_resume_breakpoint (jmp_buf_pc, null_frame_id);
ecs->handling_longjmp = 1; /* FIXME */
keep_going (ecs);
return;
#if 0
/* FIXME - Need to implement nested temporary breakpoints */
if (step_over_calls
- && (INNER_THAN (get_frame_base (get_current_frame ()),
- step_frame_address)))
+ && (frame_id_inner (get_frame_id (get_current_frame ()),
+ step_frame_id)))
{
ecs->another_trap = 1;
keep_going (ecs);
terminal for any messages produced by
breakpoint_re_set. */
target_terminal_ours_for_output ();
- SOLIB_ADD (NULL, 0, NULL, auto_solib_add);
+ /* NOTE: cagney/2003-11-25: Make certain that the target
+ stack's section table is kept up-to-date. Architectures,
+ (e.g., PPC64), use the section table to perform
+ operations such as address => section name and hence
+ require the table to contain all sections (including
+ those found in shared libraries). */
+ /* 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. */
+ SOLIB_ADD (NULL, 0, ¤t_target, auto_solib_add);
target_terminal_inferior ();
/* Try to reenable shared library breakpoints, additional
gdb of events. This allows the user to get control
and place breakpoints in initializer routines for
dynamically loaded objects (among other things). */
- if (stop_on_solib_events)
+ if (stop_on_solib_events || stop_stack_dummy)
{
stop_stepping (ecs);
return;
return;
}
- if (!CALL_DUMMY_BREAKPOINT_OFFSET_P)
- {
- /* This is the old way of detecting the end of the stack dummy.
- An architecture which defines CALL_DUMMY_BREAKPOINT_OFFSET gets
- handled above. As soon as we can test it on all of them, all
- architectures should define it. */
-
- /* If this is the breakpoint at the end of a stack dummy,
- just stop silently, unless the user was doing an si/ni, in which
- case she'd better know what she's doing. */
-
- if (CALL_DUMMY_HAS_COMPLETED (stop_pc, read_sp (),
- get_frame_base (get_current_frame ()))
- && !step_range_end)
- {
- stop_print_frame = 0;
- stop_stack_dummy = 1;
-#ifdef HP_OS_BUG
- trap_expected_after_continue = 1;
-#endif
- stop_stepping (ecs);
- return;
- }
- }
-
if (step_resume_breakpoint)
{
/* Having a step-resume breakpoint overrides anything
if (step_over_calls == STEP_OVER_UNDEBUGGABLE
&& IN_SOLIB_DYNSYM_RESOLVE_CODE (stop_pc))
{
- CORE_ADDR pc_after_resolver = SKIP_SOLIB_RESOLVER (stop_pc);
+ CORE_ADDR pc_after_resolver =
+ gdbarch_skip_solib_resolver (current_gdbarch, stop_pc);
if (pc_after_resolver)
{
check_for_old_step_resume_breakpoint ();
step_resume_breakpoint =
- set_momentary_breakpoint (sr_sal, NULL, bp_step_resume);
+ set_momentary_breakpoint (sr_sal, null_frame_id, bp_step_resume);
if (breakpoints_inserted)
insert_breakpoints ();
}
But we can update it every time we leave the step range. */
ecs->update_step_sp = 1;
- /* Did we just take a signal? */
- if (PC_IN_SIGTRAMP (stop_pc, ecs->stop_func_name)
- && !PC_IN_SIGTRAMP (prev_pc, prev_func_name)
- && INNER_THAN (read_sp (), step_sp))
+ /* Did we just step into a singal trampoline (either by stepping out
+ of a handler, or by taking a signal)? */
+ /* NOTE: cagney/2004-03-16: Replaced (except for legacy) a check for
+ "pc_in_sigtramp(stop_pc) != pc_in_sigtramp(step_pc)" with
+ frame_type == SIGTRAMP && !frame_id_eq. The latter is far more
+ robust as it will correctly handle nested signal trampolines. */
+ if (legacy_frame_p (current_gdbarch)
+ ? (pc_in_sigtramp (stop_pc)
+ && !pc_in_sigtramp (prev_pc)
+ && INNER_THAN (read_sp (), step_sp))
+ : (get_frame_type (get_current_frame ()) == SIGTRAMP_FRAME
+ && !frame_id_eq (get_frame_id (get_current_frame ()), step_frame_id)))
{
- /* We've just taken a signal; go until we are back to
- the point where we took it and one more. */
-
- /* Note: The test above succeeds not only when we stepped
- into a signal handler, but also when we step past the last
- statement of a signal handler and end up in the return stub
- of the signal handler trampoline. To distinguish between
- these two cases, check that the frame is INNER_THAN the
- previous one below. pai/1997-09-11 */
-
-
{
- CORE_ADDR current_frame = get_frame_base (get_current_frame ());
+ struct frame_id current_frame = get_frame_id (get_current_frame ());
- if (INNER_THAN (current_frame, step_frame_address))
+ if (frame_id_inner (current_frame, step_frame_id))
{
/* We have just taken a signal; go until we are back to
the point where we took it and one more. */
sr_sal.line = 0;
sr_sal.pc = prev_pc;
/* We could probably be setting the frame to
- step_frame_address; I don't think anyone thought to
- try it. */
+ step_frame_id; I don't think anyone thought to try it. */
check_for_old_step_resume_breakpoint ();
step_resume_breakpoint =
- set_momentary_breakpoint (sr_sal, NULL, bp_step_resume);
+ set_momentary_breakpoint (sr_sal, null_frame_id, bp_step_resume);
if (breakpoints_inserted)
insert_breakpoints ();
}
return;
}
- if (stop_pc == ecs->stop_func_start /* Quick test */
- || (in_prologue (stop_pc, ecs->stop_func_start) &&
- !IN_SOLIB_RETURN_TRAMPOLINE (stop_pc, ecs->stop_func_name))
+ if (((stop_pc == ecs->stop_func_start /* Quick test */
+ || in_prologue (stop_pc, ecs->stop_func_start))
+ && !IN_SOLIB_RETURN_TRAMPOLINE (stop_pc, ecs->stop_func_name))
|| IN_SOLIB_CALL_TRAMPOLINE (stop_pc, ecs->stop_func_name)
|| ecs->stop_func_name == 0)
{
/* It's a subroutine call. */
-
- if ((step_over_calls == STEP_OVER_NONE)
- || ((step_range_end == 1)
- && in_prologue (prev_pc, ecs->stop_func_start)))
- {
- /* I presume that step_over_calls is only 0 when we're
- supposed to be stepping at the assembly language level
- ("stepi"). Just stop. */
- /* Also, maybe we just did a "nexti" inside a prolog,
- so we thought it was a subroutine call but it was not.
- Stop as well. FENN */
- stop_step = 1;
- print_stop_reason (END_STEPPING_RANGE, 0);
- stop_stepping (ecs);
- return;
- }
-
- if (step_over_calls == STEP_OVER_ALL || IGNORE_HELPER_CALL (stop_pc))
- {
- /* We're doing a "next". */
-
- if (PC_IN_SIGTRAMP (stop_pc, ecs->stop_func_name)
- && INNER_THAN (step_frame_address, read_sp ()))
- /* We stepped out of a signal handler, and into its
- calling trampoline. This is misdetected as a
- subroutine call, but stepping over the signal
- trampoline isn't such a bad idea. In order to do
- that, we have to ignore the value in
- step_frame_address, since that doesn't represent the
- frame that'll reach when we return from the signal
- trampoline. Otherwise we'll probably continue to the
- end of the program. */
- step_frame_address = 0;
-
- step_over_function (ecs);
- keep_going (ecs);
- return;
- }
-
- /* If we are in a function call trampoline (a stub between
- the calling routine and the real function), locate the real
- 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. */
- tmp = SKIP_TRAMPOLINE_CODE (stop_pc);
- if (tmp != 0)
- ecs->stop_func_start = tmp;
- else
- {
- tmp = DYNAMIC_TRAMPOLINE_NEXTPC (stop_pc);
- if (tmp)
- {
- struct symtab_and_line xxx;
- /* Why isn't this s_a_l called "sr_sal", like all of the
- other s_a_l's where this code is duplicated? */
- init_sal (&xxx); /* initialize to zeroes */
- xxx.pc = tmp;
- xxx.section = find_pc_overlay (xxx.pc);
- check_for_old_step_resume_breakpoint ();
- step_resume_breakpoint =
- set_momentary_breakpoint (xxx, NULL, bp_step_resume);
- insert_breakpoints ();
- keep_going (ecs);
- return;
- }
- }
-
- /* If we have line number information for the function we
- are thinking of stepping into, step into it.
-
- If there are several symtabs at that PC (e.g. with include
- files), just want to know whether *any* of them have line
- numbers. find_pc_line handles this. */
- {
- struct symtab_and_line tmp_sal;
-
- tmp_sal = find_pc_line (ecs->stop_func_start, 0);
- if (tmp_sal.line != 0)
- {
- step_into_function (ecs);
- return;
- }
- }
-
- /* 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
- switch in assembly mode. */
- if (step_over_calls == STEP_OVER_UNDEBUGGABLE && step_stop_if_no_debug)
- {
- stop_step = 1;
- print_stop_reason (END_STEPPING_RANGE, 0);
- stop_stepping (ecs);
- return;
- }
-
- step_over_function (ecs);
- keep_going (ecs);
+ handle_step_into_function (ecs);
return;
-
}
/* We've wandered out of the step range. */
we want to proceed through the trampoline when stepping. */
if (IN_SOLIB_RETURN_TRAMPOLINE (stop_pc, ecs->stop_func_name))
{
- CORE_ADDR tmp;
-
/* Determine where this trampoline returns. */
- tmp = SKIP_TRAMPOLINE_CODE (stop_pc);
+ CORE_ADDR real_stop_pc = SKIP_TRAMPOLINE_CODE (stop_pc);
/* Only proceed through if we know where it's going. */
- if (tmp)
+ if (real_stop_pc)
{
/* And put the step-breakpoint there and go until there. */
struct symtab_and_line sr_sal;
init_sal (&sr_sal); /* initialize to zeroes */
- sr_sal.pc = tmp;
+ sr_sal.pc = real_stop_pc;
sr_sal.section = find_pc_overlay (sr_sal.pc);
/* 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. */
check_for_old_step_resume_breakpoint ();
step_resume_breakpoint =
- set_momentary_breakpoint (sr_sal, NULL, bp_step_resume);
+ set_momentary_breakpoint (sr_sal, null_frame_id, bp_step_resume);
if (breakpoints_inserted)
insert_breakpoints ();
}
step_range_start = ecs->sal.pc;
step_range_end = ecs->sal.end;
- step_frame_address = get_frame_base (get_current_frame ());
+ step_frame_id = get_frame_id (get_current_frame ());
ecs->current_line = ecs->sal.line;
ecs->current_symtab = ecs->sal.symtab;
- /* In the case where we just stepped out of a function into the middle
- of a line of the caller, continue stepping, but step_frame_address
- must be modified to current frame */
+ /* In the case where we just stepped out of a function into the
+ middle of a line of the caller, continue stepping, but
+ step_frame_id must be modified to current frame */
+#if 0
+ /* NOTE: cagney/2003-10-16: I think this frame ID inner test is too
+ generous. It will trigger on things like a step into a frameless
+ stackless leaf function. I think the logic should instead look
+ at the unwound frame ID has that should give a more robust
+ indication of what happened. */
+ if (step-ID == current-ID)
+ still stepping in same function;
+ else if (step-ID == unwind (current-ID))
+ stepped into a function;
+ else
+ stepped out of a function;
+ /* Of course this assumes that the frame ID unwind code is robust
+ and we're willing to introduce frame unwind logic into this
+ function. Fortunately, those days are nearly upon us. */
+#endif
{
- CORE_ADDR current_frame = get_frame_base (get_current_frame ());
- if (!(INNER_THAN (current_frame, step_frame_address)))
- step_frame_address = current_frame;
+ struct frame_id current_frame = get_frame_id (get_current_frame ());
+ if (!(frame_id_inner (current_frame, step_frame_id)))
+ step_frame_id = current_frame;
}
keep_going (ecs);
check_sigtramp2 (struct execution_control_state *ecs)
{
if (trap_expected
- && PC_IN_SIGTRAMP (stop_pc, ecs->stop_func_name)
- && !PC_IN_SIGTRAMP (prev_pc, prev_func_name)
+ && pc_in_sigtramp (stop_pc)
+ && !pc_in_sigtramp (prev_pc)
&& INNER_THAN (read_sp (), step_sp))
{
/* What has happened here is that we have just stepped the
/* We perhaps could set the frame if we kept track of what the
frame corresponding to prev_pc was. But we don't, so don't. */
through_sigtramp_breakpoint =
- set_momentary_breakpoint (sr_sal, NULL, bp_through_sigtramp);
+ set_momentary_breakpoint (sr_sal, null_frame_id, bp_through_sigtramp);
if (breakpoints_inserted)
insert_breakpoints ();
/* If the prologue ends in the middle of a source line, continue to
the end of that source line (if it is still within the function).
Otherwise, just go to end of prologue. */
-#ifdef PROLOGUE_FIRSTLINE_OVERLAP
- /* no, don't either. It skips any code that's legitimately on the
- first line. */
-#else
if (ecs->sal.end
&& ecs->sal.pc != ecs->stop_func_start
&& ecs->sal.end < ecs->stop_func_end)
ecs->stop_func_start = ecs->sal.end;
-#endif
+
+ /* Architectures which require breakpoint adjustment might not be able
+ to place a breakpoint at the computed address. If so, the test
+ ``ecs->stop_func_start == stop_pc'' will never succeed. Adjust
+ ecs->stop_func_start to an address at which a breakpoint may be
+ legitimately placed.
+
+ Note: kevinb/2004-01-19: On FR-V, if this adjustment is not
+ made, GDB will enter an infinite loop when stepping through
+ optimized code consisting of VLIW instructions which contain
+ subinstructions corresponding to different source lines. On
+ FR-V, it's not permitted to place a breakpoint on any but the
+ first subinstruction of a VLIW instruction. When a breakpoint is
+ set, GDB will adjust the breakpoint address to the beginning of
+ 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))
+ {
+ ecs->stop_func_start
+ = gdbarch_adjust_breakpoint_address (current_gdbarch,
+ ecs->stop_func_start);
+ }
if (ecs->stop_func_start == stop_pc)
{
established. */
check_for_old_step_resume_breakpoint ();
step_resume_breakpoint =
- set_momentary_breakpoint (sr_sal, NULL, bp_step_resume);
+ set_momentary_breakpoint (sr_sal, null_frame_id, bp_step_resume);
if (breakpoints_inserted)
insert_breakpoints ();
However, if the callee is recursing, we want to be careful not to
catch returns of those recursive calls, but only of THIS instance
- of the call.
+ of the caller.
To do this, we set the step_resume bp's frame to our current
- caller's frame (step_frame_address, which is set by the "next" or
- "until" command, before execution begins). */
+ caller's frame (obtained by doing a frame ID unwind). */
static void
step_over_function (struct execution_control_state *ecs)
{
struct symtab_and_line sr_sal;
+ struct frame_id sr_id;
init_sal (&sr_sal); /* initialize to zeros */
- sr_sal.pc = ADDR_BITS_REMOVE (SAVED_PC_AFTER_CALL (get_current_frame ()));
+
+ /* NOTE: cagney/2003-04-06:
+
+ At this point the equality get_frame_pc() == get_frame_func()
+ should hold. This may make it possible for this code to tell the
+ frame where it's function is, instead of the reverse. This would
+ avoid the need to search for the frame's function, which can get
+ very messy when there is no debug info available (look at the
+ heuristic find pc start code found in targets like the MIPS). */
+
+ /* NOTE: cagney/2003-04-06:
+
+ The intent of DEPRECATED_SAVED_PC_AFTER_CALL was to:
+
+ - provide a very light weight equivalent to frame_unwind_pc()
+ (nee FRAME_SAVED_PC) that avoids the prologue analyzer
+
+ - avoid handling the case where the PC hasn't been saved in the
+ prologue analyzer
+
+ Unfortunately, not five lines further down, is a call to
+ get_frame_id() and that is guarenteed to trigger the prologue
+ analyzer.
+
+ The `correct fix' is for the prologe analyzer to handle the case
+ where the prologue is incomplete (PC in prologue) and,
+ consequently, the return pc has not yet been saved. It should be
+ noted that the prologue analyzer needs to handle this case
+ anyway: frameless leaf functions that don't save the return PC;
+ single stepping through a prologue.
+
+ The d10v handles all this by bailing out of the prologue analsis
+ when it reaches the current instruction. */
+
+ if (DEPRECATED_SAVED_PC_AFTER_CALL_P ())
+ sr_sal.pc = ADDR_BITS_REMOVE (DEPRECATED_SAVED_PC_AFTER_CALL (get_current_frame ()));
+ else
+ sr_sal.pc = ADDR_BITS_REMOVE (frame_pc_unwind (get_current_frame ()));
sr_sal.section = find_pc_overlay (sr_sal.pc);
check_for_old_step_resume_breakpoint ();
- step_resume_breakpoint =
- set_momentary_breakpoint (sr_sal, get_current_frame (), bp_step_resume);
- if (step_frame_address && !IN_SOLIB_DYNSYM_RESOLVE_CODE (sr_sal.pc))
- step_resume_breakpoint->frame = step_frame_address;
+ /* NOTE: cagney/2004-03-15: Code using the current value of
+ "step_frame_id", instead of unwinding that frame ID, removed (at
+ least for non-legacy platforms). On s390 GNU/Linux, after taking
+ a signal, the program is directly resumed at the signal handler
+ and, consequently, the PC would point at at the first instruction
+ of that signal handler but STEP_FRAME_ID would [incorrectly] at
+ the interrupted code when it should point at the signal
+ trampoline. By always and locally doing a frame ID unwind, it's
+ possible to assert that the code is always using the correct
+ ID. */
+ if (legacy_frame_p (current_gdbarch))
+ {
+ if (frame_id_p (step_frame_id)
+ && !IN_SOLIB_DYNSYM_RESOLVE_CODE (sr_sal.pc))
+ /* NOTE: cagney/2004-02-27: Use the global state's idea of the
+ stepping frame ID. I suspect this is done as it is lighter
+ weight than a call to get_prev_frame. */
+ /* NOTE: cagney/2004-03-15: See comment above about how this
+ is also broken. */
+ sr_id = step_frame_id;
+ else
+ /* NOTE: cagney/2004-03-15: This is the way it was 'cos this
+ is the way it always was. It should be using the unwound
+ (or caller's) ID, and not this (or the callee's) ID. It
+ appeared to work because: legacy architectures used the
+ wrong end of the frame for the ID.stack (inner-most rather
+ than outer-most) so that the callee's id.stack (un
+ adjusted) matched the caller's id.stack giving the
+ "correct" id; more often than not
+ !IN_SOLIB_DYNSYM_RESOLVE_CODE and hence the code above (it
+ was originally later in the function) fixed the ID by using
+ global state. */
+ sr_id = get_frame_id (get_current_frame ());
+ }
+ else
+ sr_id = get_frame_id (get_prev_frame (get_current_frame ()));
+
+ step_resume_breakpoint = set_momentary_breakpoint (sr_sal, sr_id, bp_step_resume);
if (breakpoints_inserted)
insert_breakpoints ();
static void
stop_stepping (struct execution_control_state *ecs)
{
- if (target_has_execution)
- {
- /* Are we stopping for a vfork event? We only stop when we see
- the child's event. However, we may not yet have seen the
- parent's event. And, inferior_ptid is still set to the
- parent's pid, until we resume again and follow either the
- parent or child.
-
- To ensure that we can really touch inferior_ptid (aka, the
- parent process) -- which calls to functions like read_pc
- implicitly do -- wait on the parent if necessary. */
- if ((pending_follow.kind == TARGET_WAITKIND_VFORKED)
- && !pending_follow.fork_event.saw_parent_fork)
- {
- ptid_t parent_ptid;
-
- do
- {
- if (target_wait_hook)
- parent_ptid = target_wait_hook (pid_to_ptid (-1), &(ecs->ws));
- else
- parent_ptid = target_wait (pid_to_ptid (-1), &(ecs->ws));
- }
- while (!ptid_equal (parent_ptid, inferior_ptid));
- }
-
- /* Assuming the inferior still exists, set these up for next
- time, just like we did above if we didn't break out of the
- loop. */
- prev_pc = read_pc ();
- prev_func_start = ecs->stop_func_start;
- prev_func_name = ecs->stop_func_name;
- }
-
/* Let callers know we don't want to wait for the inferior anymore. */
ecs->wait_some_more = 0;
}
{
/* Save the pc before execution, to compare with pc after stop. */
prev_pc = read_pc (); /* Might have been DECR_AFTER_BREAK */
- prev_func_start = ecs->stop_func_start; /* Ok, since if DECR_PC_AFTER
- BREAK is defined, the
- original pc would not have
- been at the start of a
- function. */
- prev_func_name = ecs->stop_func_name;
if (ecs->update_step_sp)
step_sp = read_sp ();
if (stop_signal == TARGET_SIGNAL_TRAP && !signal_program[stop_signal])
stop_signal = TARGET_SIGNAL_0;
-#ifdef SHIFT_INST_REGS
- /* I'm not sure when this following segment applies. I do know,
- now, that we shouldn't rewrite the regs when we were stopped
- by a random signal from the inferior process. */
- /* FIXME: Shouldn't this be based on the valid bit of the SXIP?
- (this is only used on the 88k). */
-
- if (!bpstat_explains_signal (stop_bpstat)
- && (stop_signal != TARGET_SIGNAL_CHLD) && !stopped_by_random_signal)
- SHIFT_INST_REGS ();
-#endif /* SHIFT_INST_REGS */
resume (currently_stepping (ecs), stop_signal);
}
void
normal_stop (void)
{
+ struct target_waitstatus last;
+ ptid_t last_ptid;
+
+ get_last_target_status (&last_ptid, &last);
+
/* As with the notification of thread events, we want to delay
notifying the user that we've switched thread context until
the inferior actually stops.
- (Note that there's no point in saying anything if the inferior
- has exited!) */
+ There's no point in saying anything if the inferior has exited.
+ Note that SIGNALLED here means "exited with a signal", not
+ "received a signal". */
if (!ptid_equal (previous_inferior_ptid, inferior_ptid)
- && target_has_execution)
+ && target_has_execution
+ && last.kind != TARGET_WAITKIND_SIGNALLED
+ && last.kind != TARGET_WAITKIND_EXITED)
{
target_terminal_ours_for_output ();
printf_filtered ("[Switching to %s]\n",
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
DECR_PC_AFTER_BREAK */
- if (target_has_execution && get_current_frame ())
- (get_current_frame ())->pc = read_pc ();
+ if (target_has_execution)
+ /* FIXME: cagney/2002-12-06: Has the PC changed? Thanks to
+ DECR_PC_AFTER_BREAK, the program counter can change. Ask the
+ frame code to check for this and sort out any resultant mess.
+ DECR_PC_AFTER_BREAK needs to just go away. */
+ deprecated_update_frame_pc_hack (get_current_frame (), read_pc ());
if (target_has_execution && breakpoints_inserted)
{
switch (bpstat_ret)
{
case PRINT_UNKNOWN:
+ /* FIXME: cagney/2002-12-01: Given that a frame ID does
+ (or should) carry around the function and does (or
+ should) use that when doing a frame comparison. */
if (stop_step
- && step_frame_address == get_frame_base (get_current_frame ())
+ && frame_id_eq (step_frame_id,
+ get_frame_id (get_current_frame ()))
&& step_start_function == find_pc_function (stop_pc))
source_flag = SRC_LINE; /* finished step, just print source line */
else
LOCATION: Print only location
SRC_AND_LOC: Print location and source line */
if (do_frame_printing)
- show_and_print_stack_frame (deprecated_selected_frame, -1, source_flag);
+ print_stack_frame (deprecated_selected_frame, -1, source_flag);
/* Display the auto-display expressions. */
do_displays ();
if (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. */
- POP_FRAME;
+ /* 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. */
done:
annotate_stopped ();
+ observer_notify_normal_stop ();
}
static int
int trap_expected;
CORE_ADDR step_range_start;
CORE_ADDR step_range_end;
- CORE_ADDR step_frame_address;
+ struct frame_id step_frame_id;
enum step_over_calls_kind step_over_calls;
CORE_ADDR step_resume_break_address;
int stop_after_trap;
- int stop_soon_quietly;
+ int stop_soon;
struct regcache *stop_registers;
/* These are here because if call_function_by_hand has written some
write_inferior_status_register (struct inferior_status *inf_status, int regno,
LONGEST val)
{
- int size = REGISTER_RAW_SIZE (regno);
+ int size = DEPRECATED_REGISTER_RAW_SIZE (regno);
void *buf = alloca (size);
store_signed_integer (buf, size, val);
regcache_raw_write (inf_status->registers, regno, buf);
inf_status->trap_expected = trap_expected;
inf_status->step_range_start = step_range_start;
inf_status->step_range_end = step_range_end;
- inf_status->step_frame_address = step_frame_address;
+ inf_status->step_frame_id = step_frame_id;
inf_status->step_over_calls = step_over_calls;
inf_status->stop_after_trap = stop_after_trap;
- inf_status->stop_soon_quietly = stop_soon_quietly;
+ inf_status->stop_soon = stop_soon;
/* Save original bpstat chain here; replace it with copy of chain.
If caller's caller is walking the chain, they'll be happier if we
hand them back the original chain when restore_inferior_status is
frame = frame_find_by_id (*fid);
- /* If inf_status->selected_frame_address is NULL, there was no
- previously selected frame. */
+ /* If inf_status->selected_frame_id is NULL, there was no previously
+ selected frame. */
if (frame == NULL)
{
warning ("Unable to restore previously selected frame.\n");
trap_expected = inf_status->trap_expected;
step_range_start = inf_status->step_range_start;
step_range_end = inf_status->step_range_end;
- step_frame_address = inf_status->step_frame_address;
+ step_frame_id = inf_status->step_frame_id;
step_over_calls = inf_status->step_over_calls;
stop_after_trap = inf_status->stop_after_trap;
- stop_soon_quietly = inf_status->stop_soon_quietly;
+ stop_soon = inf_status->stop_soon;
bpstat_clear (&stop_bpstat);
stop_bpstat = inf_status->stop_bpstat;
breakpoint_proceeded = inf_status->breakpoint_proceeded;
void
_initialize_infrun (void)
{
- register int i;
- register int numsigs;
+ int i;
+ int numsigs;
struct cmd_list_element *c;
- register_gdbarch_swap (&stop_registers, sizeof (stop_registers), NULL);
- register_gdbarch_swap (NULL, 0, build_infrun);
+ DEPRECATED_REGISTER_GDBARCH_SWAP (stop_registers);
+ deprecated_register_gdbarch_swap (NULL, 0, build_infrun);
add_info ("signals", signals_info,
"What debugger does when program gets various signals.\n\
c = add_set_enum_cmd ("follow-fork-mode",
class_run,
follow_fork_mode_kind_names, &follow_fork_mode_string,
-/* ??rehrauer: The "both" option is broken, by what may be a 10.20
- kernel problem. It's also not terribly useful without a GUI to
- help the user drive two debuggers. So for now, I'm disabling
- the "both" option. */
-/* "Set debugger response to a program call of fork \
- or vfork.\n\
- A fork or vfork creates a new process. follow-fork-mode can be:\n\
- parent - the original process is debugged after a fork\n\
- child - the new process is debugged after a fork\n\
- both - both the parent and child are debugged after a fork\n\
- ask - the debugger will ask for one of the above choices\n\
- For \"both\", another copy of the debugger will be started to follow\n\
- the new child process. The original debugger will continue to follow\n\
- the original parent process. To distinguish their prompts, the\n\
- debugger copy's prompt will be changed.\n\
- For \"parent\" or \"child\", the unfollowed process will run free.\n\
- By default, the debugger will follow the parent process.",
- */
"Set debugger response to a program call of fork \
or vfork.\n\
A fork or vfork creates a new process. follow-fork-mode can be:\n\
parent - the original process is debugged after a fork\n\
child - the new process is debugged after a fork\n\
- ask - the debugger will ask for one of the above choices\n\
-For \"parent\" or \"child\", the unfollowed process will run free.\n\
+The unfollowed process will continue to run.\n\
By default, the debugger will follow the parent process.", &setlist);
add_show_from_set (c, &showlist);