/* Target-struct-independent code to start (run) and stop an inferior process.
- Copyright 1986, 1987, 1988, 1989, 1991, 1992, 1993, 1994
+ Copyright 1986, 1987, 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1996
Free Software Foundation, Inc.
This file is part of GDB.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include "defs.h"
-#include <string.h>
+#include "gdb_string.h"
#include <ctype.h>
#include "symtab.h"
#include "frame.h"
#include "gdbcore.h"
#include "gdbcmd.h"
#include "target.h"
-#include "thread.h"
+#include "gdbthread.h"
#include "annotate.h"
#include <signal.h>
/* Prototypes for local functions */
-static void
-signals_info PARAMS ((char *, int));
+static void signals_info PARAMS ((char *, int));
-static void
-handle_command PARAMS ((char *, int));
+static void handle_command PARAMS ((char *, int));
static void sig_print_info PARAMS ((enum target_signal));
-static void
-sig_print_header PARAMS ((void));
+static void sig_print_header PARAMS ((void));
-static void
-resume_cleanups PARAMS ((int));
+static void resume_cleanups PARAMS ((int));
-static int
-hook_stop_stub PARAMS ((char *));
+static int hook_stop_stub PARAMS ((char *));
+
+static void delete_breakpoint_current_contents PARAMS ((PTR));
/* GET_LONGJMP_TARGET returns the PC at which longjmp() will resume the
program. It needs to examine the jmp_buf argument and extract the PC
from it. The return value is non-zero on success, zero otherwise. */
+
#ifndef GET_LONGJMP_TARGET
#define GET_LONGJMP_TARGET(PC_ADDR) 0
#endif
/* Some machines have trampoline code that sits between function callers
and the actual functions themselves. If this machine doesn't have
such things, disable their processing. */
+
#ifndef SKIP_TRAMPOLINE_CODE
#define SKIP_TRAMPOLINE_CODE(pc) 0
#endif
+/* 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
+
/* For SVR4 shared libraries, each call goes through a small piece of
- trampoline code in the ".plt" section. IN_SOLIB_TRAMPOLINE evaluates
+ trampoline code in the ".plt" section. IN_SOLIB_CALL_TRAMPOLINE evaluates
to nonzero if we are current stopped in one of these. */
-#ifndef IN_SOLIB_TRAMPOLINE
-#define IN_SOLIB_TRAMPOLINE(pc,name) 0
+
+#ifndef IN_SOLIB_CALL_TRAMPOLINE
+#define IN_SOLIB_CALL_TRAMPOLINE(pc,name) 0
+#endif
+
+/* In some shared library schemes, the return path from a shared library
+ call may need to go through a trampoline too. */
+
+#ifndef IN_SOLIB_RETURN_TRAMPOLINE
+#define IN_SOLIB_RETURN_TRAMPOLINE(pc,name) 0
#endif
/* On some systems, the PC may be left pointing at an instruction that won't
static int trap_expected;
+#ifdef SOLIB_ADD
+/* Nonzero if we want to give control to the user when we're notified
+ of shared library events by the dynamic linker. */
+static int stop_on_solib_events;
+#endif
+
+#ifdef HP_OS_BUG
/* Nonzero if the next time we try to continue the inferior, it will
step one instruction and generate a spurious trace trap.
This is used to compensate for a bug in HP-UX. */
static int trap_expected_after_continue;
+#endif
/* Nonzero means expecting a trace trap
and should stop the inferior and return silently when it happens. */
static int stop_print_frame;
-#ifdef NO_SINGLE_STEP
-extern int one_stepped; /* From machine dependent code */
-extern void single_step (); /* Same. */
-#endif /* NO_SINGLE_STEP */
-
\f
/* Things to clean up if we QUIT out of resume (). */
/* ARGSUSED */
else
write_pc (addr);
+#ifdef PREPARE_TO_PROCEED
+ /* In a multi-threaded task we may select another thread and then continue.
+
+ In this case the thread that stopped at a breakpoint will immediately
+ cause another stop, if it is not stepped over first. On the other hand,
+ if (ADDR != -1) we only want to single step over the breakpoint if we did
+ switch to another thread.
+
+ If we are single stepping, don't do any of the above.
+ (Note that in the current implementation single stepping another
+ thread after a breakpoint and then continuing will cause the original
+ breakpoint to be hit again, but you can always continue, so it's not
+ a big deal.) */
+
+ if (! step && PREPARE_TO_PROCEED (1) && breakpoint_here_p (read_pc ()))
+ oneproc = 1;
+#endif /* PREPARE_TO_PROCEED */
+
+#ifdef HP_OS_BUG
if (trap_expected_after_continue)
{
/* If (step == 0), a trap will be automatically generated after
oneproc = 1;
trap_expected_after_continue = 0;
}
+#endif /* HP_OS_BUG */
if (oneproc)
/* We will get a trace trap after one instruction.
annotate_starting ();
+ /* Make sure that output from GDB appears before output from the
+ inferior. */
+ gdb_flush (gdb_stdout);
+
/* Resume inferior. */
resume (oneproc || step || bpstat_should_step (), stop_signal);
to be preserved over calls to it and cleared when the inferior
is started. */
static CORE_ADDR prev_pc;
-static CORE_ADDR prev_sp;
static CORE_ADDR prev_func_start;
static char *prev_func_name;
-static CORE_ADDR prev_frame_address;
\f
/* Start remote-debugging of a machine over a serial link. */
void
start_remote ()
{
+ init_thread_list ();
init_wait_for_inferior ();
clear_proceed_status ();
stop_soon_quietly = 1;
{
/* These are meaningless until the first time through wait_for_inferior. */
prev_pc = 0;
- prev_sp = 0;
prev_func_start = 0;
prev_func_name = NULL;
- prev_frame_address = 0;
+#ifdef HP_OS_BUG
trap_expected_after_continue = 0;
+#endif
breakpoints_inserted = 0;
breakpoint_init_inferior ();
struct cleanup *old_cleanups;
struct target_waitstatus w;
int another_trap;
- int random_signal;
- CORE_ADDR stop_sp = 0;
+ int random_signal = 0;
CORE_ADDR stop_func_start;
CORE_ADDR stop_func_end;
char *stop_func_name;
- CORE_ADDR prologue_pc = 0, tmp;
+#if 0
+ CORE_ADDR prologue_pc = 0;
+#endif
+ CORE_ADDR tmp;
struct symtab_and_line sal;
int remove_breakpoints_on_following_step = 0;
int current_line;
struct breakpoint *step_resume_breakpoint = NULL;
struct breakpoint *through_sigtramp_breakpoint = NULL;
int pid;
+ int update_step_sp = 0;
old_cleanups = make_cleanup (delete_breakpoint_current_contents,
&step_resume_breakpoint);
while (1)
{
- pid = target_wait (-1, &w);
+ /* 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. */
- /* Clean up saved state that will become invalid. */
- flush_cached_frames ();
registers_changed ();
+ if (target_wait_hook)
+ pid = target_wait_hook (-1, &w);
+ else
+ pid = target_wait (-1, &w);
+
+ /* Gross.
+
+ We goto this label from elsewhere in wait_for_inferior when we want
+ to continue the main loop without calling "wait" and trashing the
+ waitstatus contained in W. */
+ have_waited:
+
+ flush_cached_frames ();
+
+ /* If it's a new process, add it to the thread database */
+
+ if (pid != inferior_pid
+ && !in_thread_list (pid))
+ {
+ fprintf_unfiltered (gdb_stderr, "[New %s]\n", target_pid_to_str (pid));
+ add_thread (pid);
+
+ /* We may want to consider not doing a resume here in order to give
+ the user a chance to play with the new thread. It might be good
+ to make that a user-settable option. */
+
+ /* At this point, all threads are stopped (happens automatically in
+ either the OS or the native code). Therefore we need to continue
+ all threads in order to make progress. */
+
+ target_resume (-1, 0, TARGET_SIGNAL_0);
+ continue;
+ }
+
switch (w.kind)
{
case TARGET_WAITKIND_LOADED:
printf_filtered ("\nProgram exited with code 0%o.\n",
(unsigned int)w.value.integer);
else
- if (!batch_mode())
- printf_filtered ("\nProgram exited normally.\n");
+ printf_filtered ("\nProgram exited normally.\n");
+
+ /* 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_int,
+ (LONGEST) w.value.integer));
gdb_flush (gdb_stdout);
target_mourn_inferior ();
#ifdef NO_SINGLE_STEP
stop_signal = w.value.sig;
target_terminal_ours (); /* Must do this before mourn anyway */
annotate_signalled ();
+
+ /* This looks pretty bogus to me. Doesn't TARGET_WAITKIND_SIGNALLED
+ mean it is already dead? This has been here since GDB 2.8, so
+ perhaps it means rms didn't understand unix waitstatuses?
+ For the moment I'm just kludging around this in remote.c
+ rather than trying to change it here --kingdon, 5 Dec 1994. */
target_kill (); /* kill mourns as well */
+
printf_filtered ("\nProgram terminated with signal ");
annotate_signal_name ();
printf_filtered ("%s", target_signal_to_name (stop_signal));
stop_signal = w.value.sig;
- if (pid != inferior_pid)
- {
- int save_pid = inferior_pid;
+ stop_pc = read_pc_pid (pid);
- inferior_pid = pid; /* Setup for target memory/regs */
- registers_changed ();
- stop_pc = read_pc ();
- inferior_pid = save_pid;
- registers_changed ();
- }
- else
- stop_pc = read_pc ();
+ /* 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
- && breakpoint_here_p (stop_pc - DECR_PC_AFTER_BREAK))
+ if (stop_signal == TARGET_SIGNAL_TRAP)
{
- if (!breakpoint_thread_match (stop_pc - DECR_PC_AFTER_BREAK, pid))
- {
- /* Saw a breakpoint, but it was hit by the wrong thread. Just continue. */
- if (breakpoints_inserted)
- {
- if (pid != inferior_pid)
- {
- int save_pid = inferior_pid;
-
- inferior_pid = pid;
- registers_changed ();
- write_pc (stop_pc - DECR_PC_AFTER_BREAK);
- inferior_pid = save_pid;
- registers_changed ();
- }
- else
- write_pc (stop_pc - DECR_PC_AFTER_BREAK);
-
- remove_breakpoints ();
- target_resume (pid, 1, TARGET_SIGNAL_0); /* Single step */
- /* FIXME: What if a signal arrives instead of the single-step
- happening? */
- target_wait (pid, &w);
- insert_breakpoints ();
- }
- target_resume (-1, 0, TARGET_SIGNAL_0);
- continue;
- }
+#ifdef NO_SINGLE_STEP
+ if (one_stepped)
+ random_signal = 0;
else
- if (pid != inferior_pid)
- goto switch_thread;
+#endif
+ if (breakpoints_inserted
+ && breakpoint_here_p (stop_pc - DECR_PC_AFTER_BREAK))
+ {
+ random_signal = 0;
+ if (!breakpoint_thread_match (stop_pc - DECR_PC_AFTER_BREAK, pid))
+ {
+ /* Saw a breakpoint, but it was hit by the wrong thread. Just continue. */
+ write_pc_pid (stop_pc - DECR_PC_AFTER_BREAK, pid);
+
+ remove_breakpoints ();
+ target_resume (pid, 1, TARGET_SIGNAL_0); /* Single step */
+ /* FIXME: What if a signal arrives instead of the single-step
+ happening? */
+
+ if (target_wait_hook)
+ target_wait_hook (pid, &w);
+ else
+ target_wait (pid, &w);
+ insert_breakpoints ();
+
+ /* We need to restart all the threads now. */
+ target_resume (-1, 0, TARGET_SIGNAL_0);
+ continue;
+ }
+ }
}
+ else
+ 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. */
if (pid != inferior_pid)
{
int printed = 0;
- if (!in_thread_list (pid))
- {
- fprintf_unfiltered (gdb_stderr, "[New %s]\n", target_pid_to_str (pid));
- add_thread (pid);
+ /* If it's a random signal for a non-current thread, notify user
+ if he's expressed an interest. */
- target_resume (-1, 0, TARGET_SIGNAL_0);
- continue;
- }
- else
+ if (random_signal
+ && signal_print[stop_signal])
{
- if (signal_print[stop_signal])
- {
- 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);
- }
+ 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);
+ }
- if (stop_signal == TARGET_SIGNAL_TRAP
- || signal_stop[stop_signal])
- {
-switch_thread:
- inferior_pid = pid;
- printf_filtered ("[Switching to %s]\n", target_pid_to_str (pid));
-
- flush_cached_frames ();
- registers_changed ();
- trap_expected = 0;
- if (step_resume_breakpoint)
- {
- delete_breakpoint (step_resume_breakpoint);
- step_resume_breakpoint = NULL;
- }
-
- /* Not sure whether we need to blow this away too,
- but probably it is like the step-resume
- breakpoint. */
- if (through_sigtramp_breakpoint)
- {
- delete_breakpoint (through_sigtramp_breakpoint);
- through_sigtramp_breakpoint = NULL;
- }
- prev_pc = 0;
- prev_sp = 0;
- prev_func_name = NULL;
- step_range_start = 0;
- step_range_end = 0;
- step_frame_address = 0;
- handling_longjmp = 0;
- another_trap = 0;
- }
- else
- {
- if (printed)
- target_terminal_inferior ();
+ /* If it's not SIGTRAP and not a signal we want to stop for, then
+ continue the thread. */
- /* Clear the signal if it should not be passed. */
- if (signal_program[stop_signal] == 0)
- stop_signal = TARGET_SIGNAL_0;
+ if (stop_signal != TARGET_SIGNAL_TRAP
+ && !signal_stop[stop_signal])
+ {
+ if (printed)
+ target_terminal_inferior ();
- target_resume (pid, 0, stop_signal);
- continue;
- }
+ /* Clear the signal if it should not be passed. */
+ if (signal_program[stop_signal] == 0)
+ stop_signal = TARGET_SIGNAL_0;
+
+ target_resume (pid, 0, stop_signal);
+ continue;
}
+
+ /* It's a SIGTRAP or a signal we're interested in. Switch threads,
+ and fall into the rest of wait_for_inferior(). */
+
+ /* Save infrun state for the old thread. */
+ save_infrun_state (inferior_pid, 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, handling_longjmp,
+ another_trap);
+
+ inferior_pid = pid;
+
+ /* Load infrun state for the new thread. */
+ load_infrun_state (inferior_pid, &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, &handling_longjmp,
+ &another_trap);
+ printf_filtered ("[Switching to %s]\n", target_pid_to_str (pid));
+
+ flush_cached_frames ();
}
#ifdef NO_SINGLE_STEP
if (INSTRUCTION_NULLIFIED)
{
- resume (1, 0);
- continue;
- }
+ struct target_waitstatus tmpstatus;
- set_current_frame (create_new_frame (read_fp (), stop_pc));
- select_frame (get_current_frame (), 0);
+ registers_changed ();
+ target_resume (pid, 1, TARGET_SIGNAL_0);
+
+ /* We may have received a signal that we want to pass to
+ the inferior; therefore, we must not clobber the waitstatus
+ in W. So we call wait ourselves, then continue the loop
+ at the "have_waited" label. */
+ if (target_wait_hook)
+ target_wait_hook (pid, &tmpstatus);
+ else
+ target_wait (pid, &tmpstatus);
+
+
+ goto have_waited;
+ }
#ifdef HAVE_STEPPABLE_WATCHPOINT
/* It may not be necessary to disable the watchpoint to stop over
here? */
if (STOPPED_BY_WATCHPOINT (w))
{
+/* At this point, we are stopped at an instruction which has attempted to write
+ to a piece of memory under control of a watchpoint. The instruction hasn't
+ actually executed yet. If we were to evaluate the watchpoint expression
+ now, we would get the old value, and therefore no change would seem to have
+ occurred.
+
+ In order to make watchpoints work `right', we really need to complete the
+ memory write, and then evaluate the watchpoint expression. The following
+ code does that by removing the watchpoint (actually, all watchpoints and
+ breakpoints), single-stepping the target, re-inserting watchpoints, and then
+ falling through to let normal single-step processing handle proceed. Since
+ this includes evaluating watchpoints, things will come to a stop in the
+ correct manner. */
+
+ write_pc (stop_pc - DECR_PC_AFTER_BREAK);
+
remove_breakpoints ();
- resume (1, 0);
+ target_resume (pid, 1, TARGET_SIGNAL_0); /* Single step */
- /* FIXME: This is bogus. You can't interact with the
- inferior except when it is stopped. It apparently
- happens to work on Irix4, but it depends on /proc
- allowing us to muck with the memory of a running process,
- and the kernel deciding to run one instruction of the
- inferior before it executes our insert_breakpoints code,
- which seems like an awfully dubious assumption. */
+ if (target_wait_hook)
+ target_wait_hook (pid, &w);
+ else
+ target_wait (pid, &w);
insert_breakpoints ();
-
- continue;
+ /* FIXME-maybe: is this cleaner than setting a flag? Does it
+ handle things like signals arriving and other things happening
+ in combination correctly? */
+ goto have_waited;
}
#endif
- stop_frame_address = FRAME_FP (get_current_frame ());
- stop_sp = read_sp ();
+#ifdef HAVE_CONTINUABLE_WATCHPOINT
+ /* It may be possible to simply continue after a watchpoint. */
+ STOPPED_BY_WATCHPOINT (w);
+#endif
+
stop_func_start = 0;
stop_func_name = 0;
/* Don't care about return value; stop_func_start and stop_func_name
{
/* See if there is a breakpoint at the current PC. */
stop_bpstat = bpstat_stop_status
- (&stop_pc, stop_frame_address,
+ (&stop_pc,
#if DECR_PC_AFTER_BREAK
/* Notice the case of stepping through a jump
that lands just after a breakpoint.
= !(bpstat_explains_signal (stop_bpstat)
|| trap_expected
#ifndef CALL_DUMMY_BREAKPOINT_OFFSET
- || PC_IN_CALL_DUMMY (stop_pc, stop_sp, stop_frame_address)
+ || PC_IN_CALL_DUMMY (stop_pc, read_sp (),
+ FRAME_FP (get_current_frame ()))
#endif /* No CALL_DUMMY_BREAKPOINT_OFFSET. */
|| (step_range_end && step_resume_breakpoint == NULL));
else
news) give another signal besides SIGTRAP,
so check here as well as above. */
#ifndef CALL_DUMMY_BREAKPOINT_OFFSET
- || PC_IN_CALL_DUMMY (stop_pc, stop_sp, stop_frame_address)
+ || PC_IN_CALL_DUMMY (stop_pc, read_sp (),
+ FRAME_FP (get_current_frame ()))
#endif /* No CALL_DUMMY_BREAKPOINT_OFFSET. */
);
if (!random_signal)
#if 0
/* FIXME - Need to implement nested temporary breakpoints */
if (step_over_calls
- && (stop_frame_address
+ && (FRAME_FP (get_current_frame ())
INNER_THAN step_frame_address))
{
another_trap = 1;
break;
case BPSTAT_WHAT_THROUGH_SIGTRAMP:
- delete_breakpoint (through_sigtramp_breakpoint);
+ if (through_sigtramp_breakpoint)
+ delete_breakpoint (through_sigtramp_breakpoint);
through_sigtramp_breakpoint = NULL;
/* If were waiting for a trap, hitting the step_resume_break
another_trap = 1;
break;
+ case BPSTAT_WHAT_CHECK_SHLIBS:
+#ifdef SOLIB_ADD
+ {
+ extern int auto_solib_add;
+
+ /* Remove breakpoints, we eventually want to step over the
+ shlib event breakpoint, and SOLIB_ADD might adjust
+ breakpoint addresses via breakpoint_re_set. */
+ if (breakpoints_inserted)
+ remove_breakpoints ();
+ breakpoints_inserted = 0;
+
+ /* Check for any newly added shared libraries if we're
+ supposed to be adding them automatically. */
+ if (auto_solib_add)
+ {
+ /* Switch terminal for any messages produced by
+ breakpoint_re_set. */
+ target_terminal_ours_for_output ();
+ SOLIB_ADD (NULL, 0, NULL);
+ re_enable_breakpoints_in_shlibs ();
+ target_terminal_inferior ();
+ }
+
+ /* If requested, stop when the dynamic linker notifies
+ gdb of events. This allows the user to get control
+ and place breakpoints in initializer routines for
+ dynamically loaded objects (among other things). */
+ if (stop_on_solib_events)
+ {
+ stop_print_frame = 0;
+ goto stop_stepping;
+ }
+ else
+ {
+ /* We want to step over this breakpoint, then keep going. */
+ another_trap = 1;
+ break;
+ }
+ }
+#endif
+ break;
+
case BPSTAT_WHAT_LAST:
/* Not a real code, but listed here to shut up gcc -Wall. */
just stop silently, unless the user was doing an si/ni, in which
case she'd better know what she's doing. */
- if (PC_IN_CALL_DUMMY (stop_pc, stop_sp, stop_frame_address)
+ if (PC_IN_CALL_DUMMY (stop_pc, read_sp (), FRAME_FP (get_current_frame ()))
&& !step_range_end)
{
stop_print_frame = 0;
/* If stepping through a line, keep going if still within it. */
if (stop_pc >= step_range_start
&& stop_pc < step_range_end
+#if 0
+/* I haven't a clue what might trigger this clause, and it seems wrong anyway,
+ so I've disabled it until someone complains. -Stu 10/24/95 */
+
/* The step range might include the start of the
function, so if we are at the start of the
step range and either the stack or frame pointers
just changed, we've stepped outside */
&& !(stop_pc == step_range_start
- && stop_frame_address
- && (stop_sp INNER_THAN prev_sp
- || stop_frame_address != step_frame_address)))
+ && FRAME_FP (get_current_frame ())
+ && (read_sp () INNER_THAN step_sp
+ || FRAME_FP (get_current_frame ()) != step_frame_address))
+#endif
+)
{
/* We might be doing a BPSTAT_WHAT_SINGLE and getting a signal.
So definately need to check for sigtramp here. */
goto check_sigtramp2;
}
- /* We stepped out of the stepping range. See if that was due
- to a subroutine call that we should proceed to the end of. */
+ /* We stepped out of the stepping range. */
+
+ /* We can't update step_sp every time through the loop, because
+ reading the stack pointer would slow down stepping too much.
+ But we can update it every time we leave the step range. */
+ update_step_sp = 1;
/* Did we just take a signal? */
if (IN_SIGTRAMP (stop_pc, stop_func_name)
- && !IN_SIGTRAMP (prev_pc, prev_func_name))
+ && !IN_SIGTRAMP (prev_pc, prev_func_name)
+ && read_sp () INNER_THAN step_sp)
{
/* We've just taken a signal; go until we are back to
the point where we took it and one more. */
sr_sal.symtab = NULL;
sr_sal.line = 0;
/* We could probably be setting the frame to
- prev_frame_address; the reason we don't is that it didn't used
- to exist. */
+ step_frame_address; I don't think anyone thought to try it. */
step_resume_breakpoint =
set_momentary_breakpoint (sr_sal, NULL, bp_step_resume);
if (breakpoints_inserted)
goto keep_going;
}
+#if 0
+ /* I disabled this test because it was too complicated and slow. The
+ SKIP_PROLOGUE was especially slow, because it caused unnecessary
+ prologue examination on various architectures. The code in the #else
+ clause has been tested on the Sparc, Mips, PA, and Power
+ architectures, so it's pretty likely to be correct. -Stu 10/24/95 */
+
+ /* See if we left the step range due to a subroutine call that
+ we should proceed to the end of. */
+
if (stop_func_start)
{
+ struct symtab *s;
+
/* Do this after the IN_SIGTRAMP check; it might give
an error. */
prologue_pc = stop_func_start;
- SKIP_PROLOGUE (prologue_pc);
- }
- if ((/* Might be a non-recursive call. If the symbols are missing
- enough that stop_func_start == prev_func_start even though
- they are really two functions, we will treat some calls as
- jumps. */
- stop_func_start != prev_func_start
+ /* Don't skip the prologue if this is assembly source */
+ s = find_pc_symtab (stop_pc);
+ if (s && s->language != language_asm)
+ SKIP_PROLOGUE (prologue_pc);
+ }
- /* Might be a recursive call if either we have a prologue
- or the call instruction itself saves the PC on the stack. */
- || prologue_pc != stop_func_start
- || stop_sp != prev_sp)
+ if (!(step_sp INNER_THAN read_sp ()) /* don't mistake (sig)return as a call */
+ && (/* Might be a non-recursive call. If the symbols are missing
+ enough that stop_func_start == prev_func_start even though
+ they are really two functions, we will treat some calls as
+ jumps. */
+ stop_func_start != prev_func_start
+
+ /* Might be a recursive call if either we have a prologue
+ or the call instruction itself saves the PC on the stack. */
+ || prologue_pc != stop_func_start
+ || read_sp () != step_sp)
&& (/* PC is completely out of bounds of any known objfiles. Treat
like a subroutine call. */
! stop_func_start
|| stop_pc < prologue_pc
+ /* ...and if it is a leaf function, the prologue might
+ consist of gp loading only, so the call transfers to
+ the first instruction after the prologue. */
+ || (stop_pc == prologue_pc
+
+ /* Distinguish this from the case where we jump back
+ to the first instruction after the prologue,
+ within a function. */
+ && stop_func_start != prev_func_start)
+
/* If we end up in certain places, it means we did a subroutine
call. I'm not completely sure this is necessary now that we
have the above checks with stop_func_start (and now that
find_pc_partial_function is pickier). */
- || IN_SOLIB_TRAMPOLINE (stop_pc, stop_func_name)
+ || IN_SOLIB_CALL_TRAMPOLINE (stop_pc, stop_func_name)
/* If none of the above apply, it is a jump within a function,
or a return from a subroutine. The other case is longjmp,
which can no longer happen here as long as the
handling_longjmp stuff is working. */
))
+#else
+ /* This test is a much more streamlined, (but hopefully correct)
+ replacement for the code above. It's been tested on the Sparc,
+ Mips, PA, and Power architectures with good results. */
+
+ if (stop_pc == stop_func_start /* Quick test */
+ || in_prologue (stop_pc, stop_func_start)
+ || IN_SOLIB_CALL_TRAMPOLINE (stop_pc, stop_func_name)
+ || stop_func_start == 0)
+#endif
+
{
/* It's a subroutine call. */
tmp = SKIP_TRAMPOLINE_CODE (stop_pc);
if (tmp != 0)
stop_func_start = tmp;
+ else
+ {
+ tmp = DYNAMIC_TRAMPOLINE_NEXTPC (stop_pc);
+ if (tmp)
+ {
+ struct symtab_and_line xxx;
+
+ xxx.pc = tmp;
+ xxx.symtab = NULL;
+ xxx.line = 0;
+ step_resume_breakpoint =
+ set_momentary_breakpoint (xxx, NULL, bp_step_resume);
+ insert_breakpoints ();
+ goto keep_going;
+ }
+ }
/* If we have line number information for the function we
are thinking of stepping into, step into it.
step_resume_breakpoint =
set_momentary_breakpoint (sr_sal, get_current_frame (),
bp_step_resume);
- step_resume_breakpoint->frame = prev_frame_address;
+ step_resume_breakpoint->frame = step_frame_address;
if (breakpoints_inserted)
insert_breakpoints ();
}
step_into_function:
/* Subroutine call with source code we should not step over.
Do step to the first line of code in it. */
- SKIP_PROLOGUE (stop_func_start);
+ {
+ struct symtab *s;
+
+ s = find_pc_symtab (stop_pc);
+ if (s && s->language != language_asm)
+ SKIP_PROLOGUE (stop_func_start);
+ }
sal = find_pc_line (stop_func_start, 0);
/* Use the step_resume_break to step until
the end of the prologue, even if that involves jumps
break;
}
+ /* If we're in the return path from a shared library trampoline,
+ we want to proceed through the trampoline when stepping. */
+ if (IN_SOLIB_RETURN_TRAMPOLINE(stop_pc, stop_func_name))
+ {
+ CORE_ADDR tmp;
+
+ /* Determine where this trampoline returns. */
+ tmp = SKIP_TRAMPOLINE_CODE (stop_pc);
+
+ /* Only proceed through if we know where it's going. */
+ if (tmp)
+ {
+ /* And put the step-breakpoint there and go until there. */
+ struct symtab_and_line sr_sal;
+
+ sr_sal.pc = tmp;
+ sr_sal.symtab = NULL;
+ sr_sal.line = 0;
+ /* 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. */
+ step_resume_breakpoint =
+ set_momentary_breakpoint (sr_sal, NULL, bp_step_resume);
+ if (breakpoints_inserted)
+ insert_breakpoints ();
+
+ /* Restart without fiddling with the step ranges or
+ other state. */
+ goto keep_going;
+ }
+ }
+
if (sal.line == 0)
{
/* We have no line number information. That means to stop
}
step_range_start = sal.pc;
step_range_end = sal.end;
+ step_frame_address = FRAME_FP (get_current_frame ());
goto keep_going;
check_sigtramp2:
if (trap_expected
&& IN_SIGTRAMP (stop_pc, stop_func_name)
- && !IN_SIGTRAMP (prev_pc, prev_func_name))
+ && !IN_SIGTRAMP (prev_pc, prev_func_name)
+ && read_sp () INNER_THAN step_sp)
{
/* What has happened here is that we have just stepped the inferior
with a signal (because it is a signal which shouldn't make
been at the start of a
function. */
prev_func_name = stop_func_name;
- prev_sp = stop_sp;
- prev_frame_address = stop_frame_address;
+
+ if (update_step_sp)
+ step_sp = read_sp ();
+ update_step_sp = 0;
/* If we did not do break;, it means we should keep
running the inferior and not return to debugger. */
prev_pc = read_pc ();
prev_func_start = stop_func_start;
prev_func_name = stop_func_name;
- prev_sp = stop_sp;
- prev_frame_address = stop_frame_address;
}
do_cleanups (old_cleanups);
}
target_terminal_ours ();
+ if (stop_bpstat
+ && stop_bpstat->breakpoint_at
+ && stop_bpstat->breakpoint_at->type == bp_shlib_event)
+ printf_filtered ("Stopped due to shared library event\n");
+
/* Look up the hook_stop and run it if it exists. */
if (stop_command->hook)
if we have one. */
if (!stop_stack_dummy)
{
+ select_frame (get_current_frame (), 0);
+
if (stop_print_frame)
{
int source_only;
source_only = bpstat_print (stop_bpstat);
source_only = source_only ||
( stop_step
- && step_frame_address == stop_frame_address
+ && step_frame_address == FRAME_FP (get_current_frame ())
&& step_start_function == find_pc_function (stop_pc));
print_stack_frame (selected_frame, -1, source_only? -1: 1);
anyway, and the common ones like SIGHUP, SIGINT, SIGALRM, etc.
will work right anyway. */
- sigfirst = siglast = atoi (*argv);
+ sigfirst = siglast = (int) target_signal_from_command (atoi (*argv));
if ((*argv)[digits] == '-')
{
- siglast = atoi ((*argv) + digits + 1);
+ siglast =
+ (int) target_signal_from_command (atoi ((*argv) + digits + 1));
}
if (sigfirst > siglast)
{
sigfirst = siglast;
siglast = signum;
}
- if (sigfirst < 0 || sigfirst >= nsigs)
- {
- error ("Signal %d not in range 0-%d", sigfirst, nsigs - 1);
- }
- if (siglast < 0 || siglast >= nsigs)
- {
- error ("Signal %d not in range 0-%d", siglast, nsigs - 1);
- }
}
else
{
}
}
break;
+ case TARGET_SIGNAL_0:
+ case TARGET_SIGNAL_DEFAULT:
+ case TARGET_SIGNAL_UNKNOWN:
+ /* Make sure that "all" doesn't print these. */
+ break;
default:
sigs[signum] = 1;
break;
oursig = target_signal_from_name (signum_exp);
if (oursig == TARGET_SIGNAL_UNKNOWN)
{
- /* Nope, maybe it's an address which evaluates to a signal
- number. */
- /* The numeric signal refers to our own internal
- signal numbering from target.h, not to host/target signal number.
- This is a feature; users really should be using symbolic names
- anyway, and the common ones like SIGHUP, SIGINT, SIGALRM, etc.
- will work right anyway. */
- int i = parse_and_eval_address (signum_exp);
- if (i >= (int)TARGET_SIGNAL_LAST
- || i < 0
- || i == (int)TARGET_SIGNAL_UNKNOWN
- || i == (int)TARGET_SIGNAL_DEFAULT)
- error ("Signal number out of bounds.");
- oursig = (enum target_signal)i;
+ /* No, try numeric. */
+ oursig =
+ target_signal_from_command (parse_and_eval_address (signum_exp));
}
sig_print_info (oursig);
return;
{
inf_status->stop_signal = stop_signal;
inf_status->stop_pc = stop_pc;
- inf_status->stop_frame_address = stop_frame_address;
inf_status->stop_step = stop_step;
inf_status->stop_stack_dummy = stop_stack_dummy;
inf_status->stopped_by_random_signal = stopped_by_random_signal;
}
struct restore_selected_frame_args {
- FRAME_ADDR frame_address;
+ CORE_ADDR frame_address;
int level;
};
restore_selected_frame_args * (declared as char * for catch_errors)
telling us what frame to restore. Returns 1 for success, or 0 for
failure. An error message will have been printed on error. */
+
static int
restore_selected_frame (args)
char *args;
{
struct restore_selected_frame_args *fr =
(struct restore_selected_frame_args *) args;
- FRAME fid;
+ struct frame_info *frame;
int level = fr->level;
- fid = find_relative_frame (get_current_frame (), &level);
+ frame = find_relative_frame (get_current_frame (), &level);
/* If inf_status->selected_frame_address is NULL, there was no
previously selected frame. */
- if (fid == 0 ||
- FRAME_FP (fid) != fr->frame_address ||
+ if (frame == NULL ||
+ FRAME_FP (frame) != fr->frame_address ||
level != 0)
{
warning ("Unable to restore previously selected frame.\n");
return 0;
}
- select_frame (fid, fr->level);
+ select_frame (frame, fr->level);
return(1);
}
{
stop_signal = inf_status->stop_signal;
stop_pc = inf_status->stop_pc;
- stop_frame_address = inf_status->stop_frame_address;
stop_step = inf_status->stop_step;
stop_stack_dummy = inf_status->stop_stack_dummy;
stopped_by_random_signal = inf_status->stopped_by_random_signal;
add_info ("signals", signals_info,
"What debugger does when program gets various signals.\n\
-Specify a signal number as argument to print info on that signal only.");
+Specify a signal as argument to print info on that signal only.");
add_info_alias ("handle", "signals", 0);
add_com ("handle", class_run, handle_command,
- "Specify how to handle a signal.\n\
-Args are signal numbers and actions to apply to those signals.\n\
-Signal numbers may be numeric (ex. 11) or symbolic (ex. SIGSEGV).\n\
-Numeric ranges may be specified with the form LOW-HIGH (ex. 14-21).\n\
+ concat ("Specify how to handle a signal.\n\
+Args are signals and actions to apply to those signals.\n\
+Symbolic signals (e.g. SIGSEGV) are recommended but numeric signals\n\
+from 1-15 are allowed for compatibility with old versions of GDB.\n\
+Numeric ranges may be specified with the form LOW-HIGH (e.g. 1-5).\n\
The special arg \"all\" is recognized to mean all signals except those\n\
-used by the debugger, typically SIGTRAP and SIGINT.\n\
-Recognized actions include \"stop\", \"nostop\", \"print\", \"noprint\",\n\
+used by the debugger, typically SIGTRAP and SIGINT.\n",
+"Recognized actions include \"stop\", \"nostop\", \"print\", \"noprint\",\n\
\"pass\", \"nopass\", \"ignore\", or \"noignore\".\n\
Stop means reenter debugger if this signal happens (implies print).\n\
Print means print a message if this signal happens.\n\
Pass means let program see this signal; otherwise program doesn't know.\n\
Ignore is a synonym for nopass and noignore is a synonym for pass.\n\
-Pass and Stop may be combined.");
+Pass and Stop may be combined.", NULL));
stop_command = add_cmd ("stop", class_obscure, not_just_help_class_command,
"There is no `stop' command, but you can set a hook on `stop'.\n\
signal_print[TARGET_SIGNAL_POLL] = 0;
signal_stop[TARGET_SIGNAL_URG] = 0;
signal_print[TARGET_SIGNAL_URG] = 0;
+
+#ifdef SOLIB_ADD
+ add_show_from_set
+ (add_set_cmd ("stop-on-solib-events", class_support, var_zinteger,
+ (char *) &stop_on_solib_events,
+ "Set stopping for shared library events.\n\
+If nonzero, gdb will give control to the user when the dynamic linker\n\
+notifies gdb of shared library events. The most common event of interest\n\
+to the user would be loading/unloading of a new library.\n",
+ &setlist),
+ &showlist);
+#endif
}