X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Finfrun.c;h=37447334eb0d5c34857e02a02019c107191de5fc;hb=cf3e377e61e7861677252feb4d06ba8fcea1e5c1;hp=596e17c4abc0b68102602d76bfa86ca4b53964cb;hpb=3bf57d210832b28e9361990830eb722a619f031b;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/infrun.c b/gdb/infrun.c index 596e17c4ab..37447334eb 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -1,79 +1,216 @@ -/* Start and stop the inferior process, for GDB. - Copyright (C) 1986, 1987, 1988 Free Software Foundation, Inc. - -GDB is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY. No author or distributor accepts responsibility to anyone -for the consequences of using it or for whether it serves any -particular purpose or works at all, unless he says so in writing. -Refer to the GDB General Public License for full details. - -Everyone is granted permission to copy, modify and redistribute GDB, -but only under the conditions described in the GDB General Public -License. A copy of this license is supposed to have been given to you -along with GDB so you can know your rights and responsibilities. It -should be in a file named COPYING. Among other things, the copyright -notice and this notice must be preserved on all copies. - -In other words, go ahead and share GDB, but don't try to stop -anyone else from sharing it farther. Help stamp out software hoarding! +/* Target-struct-independent code to start (run) and stop an inferior process. + Copyright 1986, 1987, 1988, 1989, 1991, 1992, 1993 + Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +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. */ + +/* Notes on the algorithm used in wait_for_inferior to determine if we + just did a subroutine call when stepping. We have the following + information at that point: + + Current and previous (just before this step) pc. + Current and previous sp. + Current and previous start of current function. + + If the starts of the functions don't match, then + + a) We did a subroutine call. + + In this case, the pc will be at the beginning of a function. + + b) We did a subroutine return. + + Otherwise. + + c) We did a longjmp. + + If we did a longjump, we were doing "nexti", since a next would + have attempted to skip over the assembly language routine in which + the longjmp is coded and would have simply been the equivalent of a + continue. I consider this ok behaivior. We'd like one of two + things to happen if we are doing a nexti through the longjmp() + routine: 1) It behaves as a stepi, or 2) It acts like a continue as + above. Given that this is a special case, and that anybody who + thinks that the concept of sub calls is meaningful in the context + of a longjmp, I'll take either one. Let's see what happens. + + Acts like a subroutine return. I can handle that with no problem + at all. + + -->So: If the current and previous beginnings of the current + function don't match, *and* the pc is at the start of a function, + we've done a subroutine call. If the pc is not at the start of a + function, we *didn't* do a subroutine call. + + -->If the beginnings of the current and previous function do match, + either: + + a) We just did a recursive call. + + In this case, we would be at the very beginning of a + function and 1) it will have a prologue (don't jump to + before prologue, or 2) (we assume here that it doesn't have + a prologue) there will have been a change in the stack + pointer over the last instruction. (Ie. it's got to put + the saved pc somewhere. The stack is the usual place. In + a recursive call a register is only an option if there's a + prologue to do something with it. This is even true on + register window machines; the prologue sets up the new + window. It might not be true on a register window machine + where the call instruction moved the register window + itself. Hmmm. One would hope that the stack pointer would + also change. If it doesn't, somebody send me a note, and + I'll work out a more general theory. + bug-gdb@prep.ai.mit.edu). This is true (albeit slipperly + so) on all machines I'm aware of: + + m68k: Call changes stack pointer. Regular jumps don't. + + sparc: Recursive calls must have frames and therefor, + prologues. + + vax: All calls have frames and hence change the + stack pointer. + + b) We did a return from a recursive call. I don't see that we + have either the ability or the need to distinguish this + from an ordinary jump. The stack frame will be printed + when and if the frame pointer changes; if we are in a + function without a frame pointer, it's the users own + lookout. + + c) We did a jump within a function. We assume that this is + true if we didn't do a recursive call. + + d) We are in no-man's land ("I see no symbols here"). We + don't worry about this; it will make calls look like simple + jumps (and the stack frames will be printed when the frame + pointer moves), which is a reasonably non-violent response. */ #include "defs.h" -#include "initialize.h" -#include "param.h" +#include +#include #include "symtab.h" #include "frame.h" #include "inferior.h" +#include "breakpoint.h" #include "wait.h" +#include "gdbcore.h" +#include "gdbcmd.h" +#include "target.h" -#include #include -#include + +/* unistd.h is needed to #define X_OK */ +#ifdef USG +#include +#else #include +#endif -#ifdef UMAX_PTRACE -#include -#include -#endif UMAX_PTRACE +/* Prototypes for local functions */ -extern char *sys_siglist[]; -extern int errno; +static void +signals_info PARAMS ((char *, int)); -/* Tables of how to react to signals; the user sets them. */ +static void +handle_command PARAMS ((char *, int)); -static char signal_stop[NSIG]; -static char signal_print[NSIG]; -static char signal_program[NSIG]; +static void +sig_print_info PARAMS ((int)); -/* Nonzero if breakpoints are now inserted in the inferior. */ +static void +sig_print_header PARAMS ((void)); -static int breakpoints_inserted; +static void +resume_cleanups PARAMS ((int)); -/* Function inferior was in as of last step command. */ +static int +hook_stop_stub PARAMS ((char *)); -static struct symbol *step_start_function; +/* 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 + +/* For SVR4 shared libraries, each call goes through a small piece of + trampoline code in the ".init" section. IN_SOLIB_TRAMPOLINE evaluates + to nonzero if we are current stopped in one of these. */ +#ifndef IN_SOLIB_TRAMPOLINE +#define IN_SOLIB_TRAMPOLINE(pc,name) 0 +#endif + +/* On some systems, the PC may be left pointing at an instruction that won't + actually be executed. This is usually indicated by a bit in the PSW. If + we find ourselves in such a state, then we step the target beyond the + nullified instruction before returning control to the user so as to avoid + confusion. */ + +#ifndef INSTRUCTION_NULLIFIED +#define INSTRUCTION_NULLIFIED 0 +#endif + +/* Tables of how to react to signals; the user sets them. */ + +static unsigned char *signal_stop; +static unsigned char *signal_print; +static unsigned char *signal_program; -/* This is the sequence of bytes we insert for a breakpoint. */ +#define SET_SIGS(nsigs,sigs,flags) \ + do { \ + int signum = (nsigs); \ + while (signum-- > 0) \ + if ((sigs)[signum]) \ + (flags)[signum] = 1; \ + } while (0) -static char break_insn[] = BREAKPOINT; +#define UNSET_SIGS(nsigs,sigs,flags) \ + do { \ + int signum = (nsigs); \ + while (signum-- > 0) \ + if ((sigs)[signum]) \ + (flags)[signum] = 0; \ + } while (0) -/* Nonzero => address for special breakpoint for resuming stepping. */ -static CORE_ADDR step_resume_break_address; +/* Command list pointer for the "stop" placeholder. */ -/* Original contents of the byte where the special breakpoint is. */ +static struct cmd_list_element *stop_command; -static char step_resume_break_shadow[sizeof break_insn]; +/* Nonzero if breakpoints are now inserted in the inferior. */ + +static int breakpoints_inserted; -/* Nonzero means the special breakpoint is a duplicate - so it has not itself been inserted. */ +/* Function inferior was in as of last step command. */ -static int step_resume_break_duplicate; +static struct symbol *step_start_function; -/* Nonzero if we are expecting a trace trap and should proceed from it. - 2 means expecting 2 trace traps and should continue both times. - That occurs when we tell sh to exec the program: we will get - a trap after the exec of sh and a second when the program is exec'd. */ +/* Nonzero if we are expecting a trace trap and should proceed from it. */ static int trap_expected; @@ -86,22 +223,24 @@ static int trap_expected_after_continue; /* Nonzero means expecting a trace trap and should stop the inferior and return silently when it happens. */ -static int stop_after_trap; - -/* Nonzero means expecting a trace trap due to attaching to a process. */ +int stop_after_trap; -static int stop_after_attach; +/* Nonzero means expecting a trap and caller will handle it themselves. + It is used after attach, due to attaching to a process; + when running in the shell before the child program has been exec'd; + and when running some kinds of remote stuff (FIXME?). */ -/* Nonzero if pc has been changed by the debugger - since the inferior stopped. */ +int stop_soon_quietly; -int pc_changed; +/* Nonzero if proceed is being used for a "finish" command or a similar + situation when stop_registers should be saved. */ -/* Nonzero if debugging a remote machine via a serial link or ethernet. */ +int proceed_to_finish; -int remote_debugging; - -/* Save register contents here when about to pop a stack dummy frame. */ +/* Save register contents here when about to pop a stack dummy frame, + if-and-only-if proceed_to_finish is set. + Thus this contains the return value from the called function (assuming + values are returned in a register). */ char stop_registers[REGISTER_BYTES]; @@ -109,20 +248,65 @@ char stop_registers[REGISTER_BYTES]; static int breakpoints_failed; -/* Nonzero if inferior is in sh before our program got exec'd. */ - -static int running_in_shell; - /* Nonzero after stop if current stack frame should be printed. */ static int stop_print_frame; -static void insert_step_breakpoint (); -static void remove_step_breakpoint (); -static void wait_for_inferior (); -static void normal_stop (); +#ifdef NO_SINGLE_STEP +extern int one_stepped; /* From machine dependent code */ +extern void single_step (); /* Same. */ +#endif /* NO_SINGLE_STEP */ + + +/* Things to clean up if we QUIT out of resume (). */ +/* ARGSUSED */ +static void +resume_cleanups (arg) + int arg; +{ + normal_stop (); +} + +/* Resume the inferior, but allow a QUIT. This is useful if the user + wants to interrupt some lengthy single-stepping operation + (for child processes, the SIGINT goes to the inferior, and so + we get a SIGINT random_signal, but for remote debugging and perhaps + other targets, that's not true). + + STEP nonzero if we should step (zero to continue instead). + SIG is the signal to give the inferior (zero for none). */ +void +resume (step, sig) + int step; + int sig; +{ + struct cleanup *old_cleanups = make_cleanup (resume_cleanups, 0); + QUIT; + +#ifdef CANNOT_STEP_BREAKPOINT + /* Most targets can step a breakpoint instruction, thus executing it + normally. But if this one cannot, just continue and we will hit + it anyway. */ + if (step && breakpoints_inserted && breakpoint_here_p (read_pc ())) + step = 0; +#endif + +#ifdef NO_SINGLE_STEP + if (step) { + single_step(sig); /* Do it the hard way, w/temp breakpoints */ + step = 0; /* ...and don't ask hardware to do it. */ + } +#endif + + /* Handle any optimized stores to the inferior NOW... */ +#ifdef DO_DEFERRED_STORES + DO_DEFERRED_STORES; +#endif + + target_resume (inferior_pid, step, sig); + discard_cleanups (old_cleanups); +} -START_FILE /* Clear out all variables saying what to do when inferior is continued. First do this, then set the ones you want, then call `proceed'. */ @@ -133,20 +317,21 @@ clear_proceed_status () trap_expected = 0; step_range_start = 0; step_range_end = 0; - step_frame = 0; + step_frame_address = 0; step_over_calls = -1; - step_resume_break_address = 0; stop_after_trap = 0; - stop_after_attach = 0; + stop_soon_quietly = 0; + proceed_to_finish = 0; + breakpoint_proceeded = 1; /* We're about to proceed... */ - /* Discard any remaining commands left by breakpoint we had stopped at. */ - clear_breakpoint_commands (); + /* Discard any remaining commands or status from previous stop. */ + bpstat_clear (&stop_bpstat); } /* Basic routine for continuing the program in various fashions. ADDR is the address to resume at, or -1 for resume where stopped. - SIGNAL is the signal to give it, or 0 for none, + SIGGNAL is the signal to give it, or 0 for none, or -1 for act according to how it stopped. STEP is nonzero if should trap after one instruction. -1 means return after that and print nothing. @@ -156,9 +341,9 @@ clear_proceed_status () You should call clear_proceed_status before calling proceed. */ void -proceed (addr, signal, step) +proceed (addr, siggnal, step) CORE_ADDR addr; - int signal; + int siggnal; int step; { int oneproc = 0; @@ -168,17 +353,17 @@ proceed (addr, signal, step) if (step < 0) stop_after_trap = 1; - if (addr == -1) + if (addr == (CORE_ADDR)-1) { /* If there is a breakpoint at the address we will resume at, step one instruction before inserting breakpoints so that we do not stop right away. */ - if (!pc_changed && breakpoint_here_p (read_pc ())) + if (breakpoint_here_p (read_pc ())) oneproc = 1; } else - write_register (PC_REGNUM, addr); + write_pc (addr); if (trap_expected_after_continue) { @@ -207,17 +392,17 @@ The same program may be running in another process."); } /* Install inferior's terminal modes. */ - terminal_inferior (); + target_terminal_inferior (); - if (signal >= 0) - stop_signal = signal; + if (siggnal >= 0) + stop_signal = siggnal; /* If this signal should not be seen by program, give it zero. Used for debugging signals. */ else if (stop_signal < NSIG && !signal_program[stop_signal]) stop_signal= 0; /* Resume inferior. */ - resume (oneproc || step, stop_signal); + resume (oneproc || step || bpstat_should_step (), stop_signal); /* Wait for it to stop (if not standalone) and in any case decode why it stopped, and act accordingly. */ @@ -226,94 +411,54 @@ The same program may be running in another process."); normal_stop (); } -/* Writing the inferior pc as a register calls this function - to inform infrun that the pc has been set in the debugger. */ - -writing_pc (val) - CORE_ADDR val; -{ - stop_pc = val; - pc_changed = 1; -} - -/* Start an inferior process for the first time. - Actually it was started by the fork that created it, - but it will have stopped one instruction after execing sh. - Here we must get it up to actual execution of the real program. */ - -start_inferior () -{ - /* We will get a trace trap after one instruction. - Continue it automatically. Eventually (after shell does an exec) - it will get another trace trap. Then insert breakpoints and continue. */ - trap_expected = 2; - running_in_shell = 0; /* Set to 1 at first SIGTRAP, 0 at second. */ - trap_expected_after_continue = 0; - breakpoints_inserted = 0; - mark_breakpoints_out (); - - /* Set up the "saved terminal modes" of the inferior - based on what modes we are starting it with. */ - terminal_init_inferior (); - - /* Install inferior's terminal modes. */ - terminal_inferior (); - - if (remote_debugging) - { - trap_expected = 0; - fetch_inferior_registers(); - set_current_frame (read_register(FP_REGNUM)); - stop_frame = get_current_frame(); - inferior_pid = 3; - if (insert_breakpoints()) - fatal("Can't insert breakpoints"); - breakpoints_inserted = 1; - proceed(-1, -1, 0); - } - else - { - wait_for_inferior (); - 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_sp; +static CORE_ADDR prev_func_start; +static char *prev_func_name; + /* Start remote-debugging of a machine over a serial link. */ void start_remote () { + init_wait_for_inferior (); clear_proceed_status (); - running_in_shell = 0; + stop_soon_quietly = 1; trap_expected = 0; - inferior_pid = 3; - breakpoints_inserted = 0; - mark_breakpoints_out (); wait_for_inferior (); - normal_stop(); + normal_stop (); } -#ifdef ATTACH_DETACH - -/* Attach to process PID, then initialize for debugging it - and wait for the trace-trap that results from attaching. */ +/* Initialize static vars when a new inferior begins. */ void -attach_program (pid) - int pid; +init_wait_for_inferior () { - attach (pid); - inferior_pid = pid; + /* 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; - mark_breakpoints_out (); - terminal_init_inferior (); - clear_proceed_status (); - stop_after_attach = 1; - /*proceed (-1, 0, -2);*/ - wait_for_inferior (); - normal_stop (); + trap_expected_after_continue = 0; + breakpoints_inserted = 0; + breakpoint_init_inferior (); + stop_signal = 0; /* Don't confuse first call to proceed(). */ +} + +static void +delete_breakpoint_current_contents (arg) + PTR arg; +{ + struct breakpoint **breakpointp = (struct breakpoint **)arg; + if (*breakpointp != NULL) + delete_breakpoint (*breakpointp); } -#endif /* ATTACH_DETACH */ /* Wait for control to return from inferior to debugger. If inferior gets a signal, we may decide to start it up again @@ -321,380 +466,788 @@ attach_program (pid) When this function actually returns it means the inferior should be left stopped and GDB should read more commands. */ -static void +void wait_for_inferior () { - register int pid; + struct cleanup *old_cleanups; WAITTYPE w; - CORE_ADDR pc; - int tem; int another_trap; int random_signal; - CORE_ADDR stop_sp; - int stop_step_resume_break; - int newmisc; - int newfun_pc; - struct symbol *newfun; + CORE_ADDR stop_sp = 0; + CORE_ADDR stop_func_start; + char *stop_func_name; + CORE_ADDR prologue_pc = 0, tmp; struct symtab_and_line sal; - int prev_pc; - - prev_pc = read_pc (); + int remove_breakpoints_on_following_step = 0; + int current_line; + int handling_longjmp = 0; /* FIXME */ + struct breakpoint *step_resume_breakpoint = NULL; + int pid; + + old_cleanups = make_cleanup (delete_breakpoint_current_contents, + &step_resume_breakpoint); + sal = find_pc_line(prev_pc, 0); + current_line = sal.line; + + /* Are we stepping? */ +#define CURRENTLY_STEPPING() ((step_resume_breakpoint == NULL \ + && !handling_longjmp \ + && (step_range_end \ + || trap_expected)) \ + || bpstat_should_step ()) while (1) { - if (remote_debugging) - remote_wait (&w); - else - { - pid = wait (&w); - if (pid != inferior_pid) - continue; - } + /* Clean up saved state that will become invalid. */ + flush_cached_frames (); + registers_changed (); - pc_changed = 0; - fetch_inferior_registers (); - stop_pc = read_pc (); - set_current_frame (read_register (FP_REGNUM)); - stop_frame = get_current_frame (); - stop_sp = read_register (SP_REGNUM); - another_trap = 0; - stop_breakpoint = 0; - stop_step = 0; - stop_stack_dummy = 0; - stop_print_frame = 1; - stop_step_resume_break = 0; - random_signal = 0; - breakpoints_failed = 0; + pid = target_wait (&w); - /* Look at the cause of the stop, and decide what to do. - The alternatives are: - 1) break; to really stop and return to the debugger, - 2) drop through to start up again - (set another_trap to 1 to single step once) - 3) set random_signal to 1, and the decision between 1 and 2 - will be made according to the signal handling tables. */ +#ifdef SIGTRAP_STOP_AFTER_LOAD + + /* Somebody called load(2), and it gave us a "trap signal after load". + Ignore it gracefully. */ + SIGTRAP_STOP_AFTER_LOAD (w); +#endif + + /* See if the process still exists; clean up if it doesn't. */ if (WIFEXITED (w)) { - terminal_ours_for_output (); - if (WRETCODE (w)) - printf ("\nProgram exited with code 0%o.\n", WRETCODE (w)); + target_terminal_ours (); /* Must do this before mourn anyway */ + if (WEXITSTATUS (w)) + printf_filtered ("\nProgram exited with code 0%o.\n", + (unsigned int)WEXITSTATUS (w)); else - printf ("\nProgram exited normally.\n"); + if (!batch_mode()) + printf_filtered ("\nProgram exited normally.\n"); fflush (stdout); - inferior_died (); + target_mourn_inferior (); +#ifdef NO_SINGLE_STEP + one_stepped = 0; +#endif stop_print_frame = 0; break; } else if (!WIFSTOPPED (w)) { - kill_inferior (); + char *signame; + stop_print_frame = 0; stop_signal = WTERMSIG (w); - terminal_ours_for_output (); - printf ("\nProgram terminated with signal %d, %s\n", - stop_signal, - stop_signal < NSIG - ? sys_siglist[stop_signal] - : "(undocumented)"); - printf ("The inferior process no longer exists.\n"); + target_terminal_ours (); /* Must do this before mourn anyway */ + target_kill (); /* kill mourns as well */ +#ifdef PRINT_RANDOM_SIGNAL + printf_filtered ("\nProgram terminated: "); + PRINT_RANDOM_SIGNAL (stop_signal); +#else + printf_filtered ("\nProgram terminated with signal "); + signame = strsigno (stop_signal); + if (signame == NULL) + printf_filtered ("%d", stop_signal); + else + /* Do we need to print the number in addition to the name? */ + printf_filtered ("%s (%d)", signame, stop_signal); + printf_filtered (", %s\n", safe_strsignal (stop_signal)); +#endif + printf_filtered ("The program no longer exists.\n"); fflush (stdout); +#ifdef NO_SINGLE_STEP + one_stepped = 0; +#endif break; } - else + + if (pid != inferior_pid) { - stop_signal = WSTOPSIG (w); - - /* 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. */ - - if (stop_signal == SIGTRAP - || (breakpoints_inserted && - (stop_signal == SIGILL - || stop_signal == SIGEMT)) - || stop_after_attach) + int printed = 0; + + if (!in_thread_list (pid)) { - if (stop_signal == SIGTRAP && stop_after_trap) - { - stop_print_frame = 0; - break; - } - if (stop_after_attach) - break; - /* Don't even think about breakpoints - if still running the shell that will exec the program - or if just proceeded over a breakpoint. */ - if (stop_signal == SIGTRAP && trap_expected) - stop_breakpoint = 0; - else - /* See if there is a breakpoint at the current PC. */ -#if DECR_PC_AFTER_BREAK - /* Notice the case of stepping through a jump - that leads just after a breakpoint. - Don't confuse that with hitting the breakpoint. - What we check for is that 1) stepping is going on - and 2) the pc before the last insn does not match - the address of the breakpoint before the current pc. */ - if (!(prev_pc != stop_pc - DECR_PC_AFTER_BREAK - && step_range_end && !step_resume_break_address)) -#endif /* DECR_PC_AFTER_BREAK not zero */ - { - select_frame (stop_frame, 0); /* For condition exprs. */ - stop_breakpoint = breakpoint_stop_status (stop_pc, stop_frame); - /* Following in case break condition called a function. */ - stop_print_frame = 1; - if (stop_breakpoint && DECR_PC_AFTER_BREAK) - { - stop_pc -= DECR_PC_AFTER_BREAK; - write_register (PC_REGNUM, stop_pc); - pc_changed = 0; - } - } - /* See if we stopped at the special breakpoint for - stepping over a subroutine call. */ - if (stop_pc - DECR_PC_AFTER_BREAK == step_resume_break_address) - { - stop_step_resume_break = 1; - if (DECR_PC_AFTER_BREAK) - { - stop_pc -= DECR_PC_AFTER_BREAK; - write_register (PC_REGNUM, stop_pc); - pc_changed = 0; - } - } + fprintf (stderr, "[New %s]\n", target_pid_to_str (pid)); + add_thread (pid); - if (stop_signal == SIGTRAP) - random_signal - = !(stop_breakpoint || trap_expected - || stop_step_resume_break - || (stop_sp INNER_THAN stop_pc && stop_pc INNER_THAN stop_frame) - || (step_range_end && !step_resume_break_address)); - else - { - random_signal - = !(stop_breakpoint || stop_step_resume_break); - if (!random_signal) - stop_signal = SIGTRAP; - } + target_resume (pid, 0, 0); + continue; } else - random_signal = 1; - - /* For the program's own signals, act according to - the signal handling tables. */ - - if (random_signal - && !(running_in_shell && stop_signal == SIGSEGV)) { - /* Signal not for debugging purposes. */ - int printed = 0; + stop_signal = WSTOPSIG (w); - if (stop_signal >= NSIG - || signal_print[stop_signal]) + if (stop_signal >= NSIG || signal_print[stop_signal]) { + char *signame; + printed = 1; - terminal_ours_for_output (); - printf ("\nProgram received signal %d, %s\n", - stop_signal, - stop_signal < NSIG - ? sys_siglist[stop_signal] - : "(undocumented)"); + target_terminal_ours_for_output (); + printf_filtered ("\nProgram received signal "); + signame = strsigno (stop_signal); + if (signame == NULL) + printf_filtered ("%d", stop_signal); + else + printf_filtered ("%s (%d)", signame, stop_signal); + printf_filtered (", %s\n", safe_strsignal (stop_signal)); + fflush (stdout); } - if (stop_signal >= NSIG - || signal_stop[stop_signal]) - break; - /* If not going to stop, give terminal back - if we took it away. */ - else if (printed) - terminal_inferior (); - } - - /* Handle cases caused by hitting a breakpoint. */ - if (!random_signal - && (stop_breakpoint || stop_step_resume_break)) - { - /* Does a breakpoint want us to stop? */ - if (stop_breakpoint && stop_breakpoint != -1) + if (stop_signal >= NSIG || signal_stop[stop_signal]) { - /* 0x1000000 is set in stop_breakpoint as returned by - breakpoint_status_p to indicate a silent breakpoint. */ - if (stop_breakpoint > 0 && stop_breakpoint & 0x1000000) + 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) { - stop_breakpoint &= ~0x1000000; - stop_print_frame = 0; + delete_breakpoint (step_resume_breakpoint); + step_resume_breakpoint = NULL; } - break; - } - /* But if we have hit the step-resumption breakpoint, - remove it. It has done its job getting us here. */ - if (stop_step_resume_break - && (step_frame == 0 || stop_frame == step_frame)) - { - remove_step_breakpoint (); - step_resume_break_address = 0; + 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; } - /* Otherwise, must remove breakpoints and single-step - to get us past the one we hit. */ else { - remove_breakpoints (); - remove_step_breakpoint (); - breakpoints_inserted = 0; - another_trap = 1; - } + if (printed) + target_terminal_inferior (); + + /* Clear the signal if it should not be passed. */ + if (signal_program[stop_signal] == 0) + stop_signal = 0; - /* We come here if we hit a breakpoint but should not - stop for it. Possibly we also were stepping - and should stop for that. So fall through and - test for stepping. But, if not stepping, - do not stop. */ + target_resume (pid, 0, stop_signal); + continue; + } } + } + +#ifdef NO_SINGLE_STEP + if (one_stepped) + single_step (0); /* This actually cleans up the ss */ +#endif /* NO_SINGLE_STEP */ + +/* If PC is pointing at a nullified instruction, then step beyond it so that + the user won't be confused when GDB appears to be ready to execute it. */ - /* If this is the breakpoint at the end of a stack dummy, - just stop silently. */ - if (stop_sp INNER_THAN stop_pc && stop_pc INNER_THAN stop_frame) + if (INSTRUCTION_NULLIFIED) + { + resume (1, 0); + continue; + } + + stop_pc = read_pc (); + set_current_frame ( create_new_frame (read_fp (), stop_pc)); + + stop_frame_address = FRAME_FP (get_current_frame ()); + stop_sp = read_sp (); + stop_func_start = 0; + stop_func_name = 0; + /* Don't care about return value; stop_func_start and stop_func_name + will both be 0 if it doesn't work. */ + find_pc_partial_function (stop_pc, &stop_func_name, &stop_func_start, + (CORE_ADDR *)NULL); + stop_func_start += FUNCTION_START_OFFSET; + another_trap = 0; + bpstat_clear (&stop_bpstat); + stop_step = 0; + stop_stack_dummy = 0; + stop_print_frame = 1; + random_signal = 0; + stopped_by_random_signal = 0; + breakpoints_failed = 0; + + /* Look at the cause of the stop, and decide what to do. + The alternatives are: + 1) break; to really stop and return to the debugger, + 2) drop through to start up again + (set another_trap to 1 to single step once) + 3) set random_signal to 1, and the decision between 1 and 2 + will be made according to the signal handling tables. */ + + stop_signal = WSTOPSIG (w); + + /* 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. */ + + if (stop_signal == SIGTRAP + || (breakpoints_inserted && + (stop_signal == SIGILL +#ifdef SIGEMT + || stop_signal == SIGEMT +#endif + )) + || stop_soon_quietly) + { + if (stop_signal == SIGTRAP && stop_after_trap) { stop_print_frame = 0; - stop_stack_dummy = 1; -#ifdef HP9K320 - trap_expected_after_continue = 1; -#endif break; } + if (stop_soon_quietly) + break; + + /* Don't even think about breakpoints + if just proceeded over a breakpoint. + + However, if we are trying to proceed over a breakpoint + and end up in sigtramp, then step_resume_breakpoint + will be set and we should check whether we've hit the + step breakpoint. */ + if (stop_signal == SIGTRAP && trap_expected + && step_resume_breakpoint == NULL) + bpstat_clear (&stop_bpstat); + else + { + /* See if there is a breakpoint at the current PC. */ + stop_bpstat = bpstat_stop_status + (&stop_pc, stop_frame_address, +#if DECR_PC_AFTER_BREAK + /* Notice the case of stepping through a jump + that lands just after a breakpoint. + Don't confuse that with hitting the breakpoint. + What we check for is that 1) stepping is going on + and 2) the pc before the last insn does not match + the address of the breakpoint before the current pc. */ + (prev_pc != stop_pc - DECR_PC_AFTER_BREAK + && CURRENTLY_STEPPING ()) +#else /* DECR_PC_AFTER_BREAK zero */ + 0 +#endif /* DECR_PC_AFTER_BREAK zero */ + ); + /* Following in case break condition called a + function. */ + stop_print_frame = 1; + } - if (step_resume_break_address) - /* Having a step-resume breakpoint overrides anything - else having to do with stepping commands until - that breakpoint is reached. */ - ; - /* If stepping through a line, keep going if still within it. */ - else if (!random_signal - && step_range_end - && stop_pc >= step_range_start - && stop_pc < step_range_end) + if (stop_signal == SIGTRAP) + random_signal + = !(bpstat_explains_signal (stop_bpstat) + || trap_expected +#ifndef CALL_DUMMY_BREAKPOINT_OFFSET + || PC_IN_CALL_DUMMY (stop_pc, stop_sp, stop_frame_address) +#endif /* No CALL_DUMMY_BREAKPOINT_OFFSET. */ + || (step_range_end && step_resume_breakpoint == NULL)); + else { - /* Don't step through the return from a function - unless that is the first instruction stepped through. */ - if (ABOUT_TO_RETURN (stop_pc)) - { - stop_step = 1; - break; - } + 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. */ +#ifndef CALL_DUMMY_BREAKPOINT_OFFSET + || PC_IN_CALL_DUMMY (stop_pc, stop_sp, stop_frame_address) +#endif /* No CALL_DUMMY_BREAKPOINT_OFFSET. */ + ); + if (!random_signal) + stop_signal = SIGTRAP; } + } + else + random_signal = 1; - /* We stepped out of the stepping range. See if that was due - to a subroutine call that we should proceed to the end of. */ - else if (!random_signal && step_range_end) + /* For the program's own signals, act according to + the signal handling tables. */ + + if (random_signal) + { + /* Signal not for debugging purposes. */ + int printed = 0; + + stopped_by_random_signal = 1; + + if (stop_signal >= NSIG + || signal_print[stop_signal]) { - newfun = find_pc_function (stop_pc); - newmisc = -1; - if (newfun) - { - newfun_pc = BLOCK_START (SYMBOL_BLOCK_VALUE (newfun)) - + FUNCTION_START_OFFSET; - } + char *signame; + printed = 1; + target_terminal_ours_for_output (); +#ifdef PRINT_RANDOM_SIGNAL + PRINT_RANDOM_SIGNAL (stop_signal); +#else + printf_filtered ("\nProgram received signal "); + signame = strsigno (stop_signal); + if (signame == NULL) + printf_filtered ("%d", stop_signal); else - { - newmisc = find_pc_misc_function (stop_pc); - if (newmisc >= 0) - newfun_pc = misc_function_vector[newmisc].address - + FUNCTION_START_OFFSET; - else newfun_pc = 0; - } - if (stop_pc == newfun_pc - && (step_over_calls > 0 || (step_over_calls && newfun == 0))) - { - /* A subroutine call has happened. */ - /* Set a special breakpoint after the return */ - step_resume_break_address = SAVED_PC_AFTER_CALL (stop_frame); - step_resume_break_duplicate - = breakpoint_here_p (step_resume_break_address); - if (breakpoints_inserted) - insert_step_breakpoint (); - } - /* Subroutine call with source code we should not step over. - Do step to the first line of code in it. */ - else if (stop_pc == newfun_pc && step_over_calls) - { - SKIP_PROLOGUE (newfun_pc); - sal = find_pc_line (newfun_pc, 0); - /* Use the step_resume_break to step until - the end of the prologue, even if that involves jumps - (as it seems to on the vax under 4.2). */ - /* If the prologue ends in the middle of a source line, - continue to the end of that source line. - Otherwise, just go to end of prologue. */ - if (sal.end && sal.pc != newfun_pc) - newfun_pc = sal.end; - - if (newfun_pc == stop_pc) - /* We are already there: stop now. */ - stop_step = 1; - else - /* Put the step-breakpoint there and go until there. */ - { - step_resume_break_address = newfun_pc; - - step_resume_break_duplicate - = breakpoint_here_p (step_resume_break_address); - if (breakpoints_inserted) - insert_step_breakpoint (); - /* 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_frame = 0; - /* And make sure stepping stops right away then. */ - step_range_end = step_range_start; - } - } - /* No subroutince call; stop now. */ - else - { - stop_step = 1; - break; - } + /* Do we need to print the number as well as the name? */ + printf_filtered ("%s (%d)", signame, stop_signal); + printf_filtered (", %s\n", safe_strsignal (stop_signal)); +#endif /* PRINT_RANDOM_SIGNAL */ + fflush (stdout); } + if (stop_signal >= NSIG + || signal_stop[stop_signal]) + break; + /* If not going to stop, give terminal back + if we took it away. */ + else if (printed) + target_terminal_inferior (); + + /* Clear the signal if it should not be passed. */ + if (signal_program[stop_signal] == 0) + stop_signal = 0; + + /* I'm not sure whether this needs to be check_sigtramp2 or + whether it could/should be keep_going. */ + goto check_sigtramp2; } - /* Save the pc before execution, to compare with pc after stop. */ - prev_pc = read_pc (); + /* Handle cases caused by hitting a breakpoint. */ + { + CORE_ADDR jmp_buf_pc; + struct bpstat_what what; - /* If we did not do break;, it means we should keep - running the inferior and not return to debugger. */ + what = bpstat_what (stop_bpstat); + + if (what.call_dummy) + { + stop_stack_dummy = 1; +#ifdef HP_OS_BUG + trap_expected_after_continue = 1; +#endif + } + + switch (what.main_action) + { + case BPSTAT_WHAT_SET_LONGJMP_RESUME: + /* If we hit the breakpoint at longjmp, disable it for the + duration of this command. Then, install a temporary + breakpoint at the target of the jmp_buf. */ + disable_longjmp_breakpoint(); + remove_breakpoints (); + breakpoints_inserted = 0; + if (!GET_LONGJMP_TARGET(&jmp_buf_pc)) goto keep_going; + + /* Need to blow away step-resume breakpoint, as it + interferes with us */ + if (step_resume_breakpoint != NULL) + { + delete_breakpoint (step_resume_breakpoint); + step_resume_breakpoint = NULL; + what.step_resume = 0; + } + +#if 0 + /* FIXME - Need to implement nested temporary breakpoints */ + if (step_over_calls > 0) + set_longjmp_resume_breakpoint(jmp_buf_pc, + get_current_frame()); + else +#endif /* 0 */ + set_longjmp_resume_breakpoint(jmp_buf_pc, NULL); + handling_longjmp = 1; /* FIXME */ + goto keep_going; + + case BPSTAT_WHAT_CLEAR_LONGJMP_RESUME: + case BPSTAT_WHAT_CLEAR_LONGJMP_RESUME_SINGLE: + remove_breakpoints (); + breakpoints_inserted = 0; +#if 0 + /* FIXME - Need to implement nested temporary breakpoints */ + if (step_over_calls + && (stop_frame_address + INNER_THAN step_frame_address)) + { + another_trap = 1; + goto keep_going; + } +#endif /* 0 */ + disable_longjmp_breakpoint(); + handling_longjmp = 0; /* FIXME */ + if (what.main_action == BPSTAT_WHAT_CLEAR_LONGJMP_RESUME) + break; + /* else fallthrough */ + + case BPSTAT_WHAT_SINGLE: + if (breakpoints_inserted) + remove_breakpoints (); + breakpoints_inserted = 0; + another_trap = 1; + /* Still need to check other stuff, at least the case + where we are stepping and step out of the right range. */ + break; + + case BPSTAT_WHAT_STOP_NOISY: + stop_print_frame = 1; + /* We are about to nuke the step_resume_breakpoint via the + cleanup chain, so no need to worry about it here. */ + goto stop_stepping; + + case BPSTAT_WHAT_STOP_SILENT: + stop_print_frame = 0; + /* We are about to nuke the step_resume_breakpoint via the + cleanup chain, so no need to worry about it here. */ + goto stop_stepping; + + case BPSTAT_WHAT_KEEP_CHECKING: + break; + } + + if (what.step_resume) + { + delete_breakpoint (step_resume_breakpoint); + step_resume_breakpoint = NULL; + + /* If were waiting for a trap, hitting the step_resume_break + doesn't count as getting it. */ + if (trap_expected) + another_trap = 1; + } + } + + /* We come here if we hit a breakpoint but should not + stop for it. Possibly we also were stepping + and should stop for that. So fall through and + test for stepping. But, if not stepping, + do not stop. */ + +#ifndef CALL_DUMMY_BREAKPOINT_OFFSET + /* 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 (PC_IN_CALL_DUMMY (stop_pc, stop_sp, stop_frame_address) + && !step_range_end) + { + stop_print_frame = 0; + stop_stack_dummy = 1; +#ifdef HP_OS_BUG + trap_expected_after_continue = 1; +#endif + break; + } +#endif /* No CALL_DUMMY_BREAKPOINT_OFFSET. */ + + if (step_resume_breakpoint) + /* Having a step-resume breakpoint overrides anything + else having to do with stepping commands until + that breakpoint is reached. */ + /* I suspect this could/should be keep_going, because if the + check_sigtramp2 check succeeds, then it will put in another + step_resume_breakpoint, and we aren't (yet) prepared to nest + them. */ + goto check_sigtramp2; + + if (step_range_end == 0) + /* Likewise if we aren't even stepping. */ + /* I'm not sure whether this needs to be check_sigtramp2 or + whether it could/should be keep_going. */ + goto check_sigtramp2; + + /* If stepping through a line, keep going if still within it. */ + if (stop_pc >= step_range_start + && stop_pc < step_range_end + /* 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))) + { + /* 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. */ + + /* Did we just take a signal? */ + if (IN_SIGTRAMP (stop_pc, stop_func_name) + && !IN_SIGTRAMP (prev_pc, prev_func_name)) + { + /* This code is needed at least in the following case: + The user types "next" and then a signal arrives (before + the "next" is done). */ + /* We've just taken a signal; go until we are back to + the point where we took it and one more. */ + { + struct symtab_and_line sr_sal; + + sr_sal.pc = prev_pc; + sr_sal.symtab = NULL; + sr_sal.line = 0; + step_resume_breakpoint = + set_momentary_breakpoint (sr_sal, get_current_frame (), + bp_step_resume); + if (breakpoints_inserted) + insert_breakpoints (); + } + + /* If this is stepi or nexti, make sure that the stepping range + gets us past that instruction. */ + if (step_range_end == 1) + /* FIXME: Does this run afoul of the code below which, if + we step into the middle of a line, resets the stepping + range? */ + step_range_end = (step_range_start = prev_pc) + 1; + + remove_breakpoints_on_following_step = 1; + goto keep_going; + } + + if (stop_func_start) + { + /* Do this after the IN_SIGTRAMP check; it might give + an error. */ + prologue_pc = stop_func_start; + SKIP_PROLOGUE (prologue_pc); + } + + /* ==> See comments at top of file on this algorithm. <==*/ + + if ((stop_pc == stop_func_start + || IN_SOLIB_TRAMPOLINE (stop_pc, stop_func_name)) + && (stop_func_start != prev_func_start + || prologue_pc != stop_func_start + || stop_sp != prev_sp)) + { + /* It's a subroutine call. */ + + if (step_over_calls == 0) + { + /* I presume that step_over_calls is only 0 when we're + supposed to be stepping at the assembly language level + ("stepi"). Just stop. */ + stop_step = 1; + break; + } + + if (step_over_calls > 0) + /* We're doing a "next". */ + goto step_over_function; + + /* 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) + stop_func_start = tmp; + + /* 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 (stop_func_start, 0); + if (tmp_sal.line != 0) + goto step_into_function; + } + +step_over_function: + /* A subroutine call has happened. */ + { + /* Set a special breakpoint after the return */ + struct symtab_and_line sr_sal; + sr_sal.pc = + ADDR_BITS_REMOVE + (SAVED_PC_AFTER_CALL (get_current_frame ())); + sr_sal.symtab = NULL; + sr_sal.line = 0; + step_resume_breakpoint = + set_momentary_breakpoint (sr_sal, get_current_frame (), + bp_step_resume); + if (breakpoints_inserted) + insert_breakpoints (); + } + goto keep_going; + +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); + 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 + (as it seems to on the vax under 4.2). */ + /* If the prologue ends in the middle of a source line, + continue to the end of that source line. + 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 (sal.end && sal.pc != stop_func_start) + stop_func_start = sal.end; +#endif + + if (stop_func_start == stop_pc) + { + /* We are already there: stop now. */ + stop_step = 1; + break; + } + else + /* Put the step-breakpoint there and go until there. */ + { + struct symtab_and_line sr_sal; + + sr_sal.pc = stop_func_start; + 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 (); + + /* And make sure stepping stops right away then. */ + step_range_end = step_range_start; + } + goto keep_going; + } + + /* We've wandered out of the step range (but haven't done a + subroutine call or return). (Is that true? I think we get + here if we did a return and maybe a longjmp). */ + + sal = find_pc_line(stop_pc, 0); + + if (step_range_end == 1) + { + /* It is stepi or nexti. We always want to stop stepping after + one instruction. */ + stop_step = 1; + break; + } + + if (sal.line == 0) + { + /* We have no line number information. That means to stop + stepping (does this always happen right after one instruction, + when we do "s" in a function with no line numbers, + or can this happen as a result of a return or longjmp?). */ + stop_step = 1; + break; + } + + if (stop_pc == sal.pc && current_line != sal.line) + { + /* We are at the start of a different line. So stop. Note that + we don't stop if we step into the middle of a different line. + That is said to make things like for (;;) statements work + better. */ + stop_step = 1; + break; + } - /* If trap_expected is 2, it means continue once more - and insert breakpoints at the next trap. - If trap_expected is 1 and the signal was SIGSEGV, it means - the shell is doing some memory allocation--just resume it - with SIGSEGV. - Otherwise insert breakpoints now, and possibly single step. */ + /* We aren't done stepping. - if (trap_expected > 1) + Optimize by setting the stepping range to the line. + (We might not be in the original line, but if we entered a + new line in mid-statement, we continue stepping. This makes + things like for(;;) statements work better.) */ + step_range_start = sal.pc; + step_range_end = sal.end; + goto keep_going; + + check_sigtramp2: + if (trap_expected + && IN_SIGTRAMP (stop_pc, stop_func_name) + && !IN_SIGTRAMP (prev_pc, prev_func_name)) { - trap_expected--; - running_in_shell = 1; - resume (0, 0); + /* What has happened here is that we have just stepped the inferior + with a signal (because it is a signal which shouldn't make + us stop), thus stepping into sigtramp. + + So we need to set a step_resume_break_address breakpoint + and continue until we hit it, and then step. FIXME: This should + be more enduring than a step_resume breakpoint; we should know + that we will later need to keep going rather than re-hitting + the breakpoint here (see testsuite/gdb.t06/signals.exp where + it says "exceedingly difficult"). */ + struct symtab_and_line sr_sal; + + sr_sal.pc = prev_pc; + sr_sal.symtab = NULL; + sr_sal.line = 0; + step_resume_breakpoint = + set_momentary_breakpoint (sr_sal, get_current_frame (), + bp_step_resume); + if (breakpoints_inserted) + insert_breakpoints (); + + remove_breakpoints_on_following_step = 1; + another_trap = 1; } - else if (running_in_shell && stop_signal == SIGSEGV) + + keep_going: + /* Come to this label when you need to resume the inferior. + It's really much cleaner to do a goto than a maze of if-else + conditions. */ + + /* Save the pc before execution, to compare with pc after stop. */ + prev_pc = read_pc (); /* Might have been DECR_AFTER_BREAK */ + prev_func_start = 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 = stop_func_name; + prev_sp = stop_sp; + + /* If we did not do break;, it means we should keep + running the inferior and not return to debugger. */ + + if (trap_expected && stop_signal != SIGTRAP) { - resume (0, SIGSEGV); + /* We took a signal (which we are supposed to pass through to + the inferior, else we'd have done a break above) and we + haven't yet gotten our trap. Simply continue. */ + resume (CURRENTLY_STEPPING (), stop_signal); } else { - /* Here, we are not awaiting another exec to get - the program we really want to debug. + /* Either the trap was not expected, but we are continuing + anyway (the user asked that this signal be passed to the + child) + -- or -- + The signal was SIGTRAP, e.g. it was our signal, but we + decided we should resume from it. + + We're going to run this baby now! + Insert breakpoints now, unless we are trying to one-proceed past a breakpoint. */ - running_in_shell = 0; - if (!breakpoints_inserted && !another_trap) + /* If we've just finished a special step resume and we don't + want to hit a breakpoint, pull em out. */ + if (step_resume_breakpoint == NULL && + remove_breakpoints_on_following_step) + { + remove_breakpoints_on_following_step = 0; + remove_breakpoints (); + breakpoints_inserted = 0; + } + else if (!breakpoints_inserted && + (step_resume_breakpoint != NULL || !another_trap)) { - insert_step_breakpoint (); breakpoints_failed = insert_breakpoints (); if (breakpoints_failed) break; @@ -706,43 +1259,68 @@ wait_for_inferior () if (stop_signal == SIGTRAP) stop_signal = 0; - resume ((step_range_end && !step_resume_break_address) - || trap_expected, - stop_signal); +#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 != SIGCLD) + && !stopped_by_random_signal) + SHIFT_INST_REGS(); +#endif /* SHIFT_INST_REGS */ + + resume (CURRENTLY_STEPPING (), stop_signal); } } + + stop_stepping: + if (target_has_execution) + { + /* 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 = stop_func_start; + prev_func_name = stop_func_name; + prev_sp = stop_sp; + } + do_cleanups (old_cleanups); } /* Here to return control to GDB when the inferior stops for real. Print appropriate messages, remove breakpoints, give terminal our modes. - RUNNING_IN_SHELL nonzero means the shell got a signal before - exec'ing the program we wanted to run. STOP_PRINT_FRAME nonzero means print the executing frame (pc, function, args, file, line number and line text). BREAKPOINTS_FAILED nonzero means stop was due to error attempting to insert breakpoints. */ -static void +void normal_stop () { + /* 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 ())->pc = read_pc (); + if (breakpoints_failed) { - terminal_ours_for_output (); + target_terminal_ours_for_output (); print_sys_errmsg ("ptrace", breakpoints_failed); - printf ("Stopped; cannot insert breakpoints.\n\ + printf_filtered ("Stopped; cannot insert breakpoints.\n\ The same program may be running in another process.\n"); } - if (inferior_pid) - remove_step_breakpoint (); - - if (inferior_pid && breakpoints_inserted) + if (target_has_execution && breakpoints_inserted) if (remove_breakpoints ()) { - terminal_ours_for_output (); - printf ("Cannot remove breakpoints because program is no longer writable.\n\ -It must be running in another process.\n\ + target_terminal_ours_for_output (); + printf_filtered ("Cannot remove breakpoints because program is no longer writable.\n\ +It might be running in another process.\n\ Further execution is probably impossible.\n"); } @@ -751,97 +1329,118 @@ Further execution is probably impossible.\n"); /* Delete the breakpoint we stopped at, if it wants to be deleted. Delete any breakpoint that is to be deleted at the next stop. */ - breakpoint_auto_delete (stop_breakpoint); + breakpoint_auto_delete (stop_bpstat); /* If an auto-display called a function and that got a signal, delete that auto-display to avoid an infinite recursion. */ - delete_current_display (); + if (stopped_by_random_signal) + disable_current_display (); if (step_multi && stop_step) return; - terminal_ours (); + target_terminal_ours (); - if (running_in_shell) - { - if (stop_signal == SIGSEGV) - { - char *exec_file = (char *) get_exec_file (1); + /* Look up the hook_stop and run it if it exists. */ - if (access (exec_file, X_OK) != 0) - printf ("The file \"%s\" is not executable.\n", exec_file); - else - printf ("\ -You have just encountered a bug in \"sh\". GDB starts your program\n\ -by running \"sh\" with a command to exec your program.\n\ -This is so that \"sh\" will process wildcards and I/O redirection.\n\ -This time, \"sh\" crashed.\n\ -\n\ -One known bug in \"sh\" bites when the environment takes up a lot of space.\n\ -Try \"info env\" to see the environment; then use \"unset-env\" to kill\n\ -some variables whose values are large; then do \"run\" again.\n\ -\n\ -If that works, you might want to put those \"unset-env\" commands\n\ -into a \".gdbinit\" file in this directory so they will happen every time.\n"); - } - /* Don't confuse user with his program's symbols on sh's data. */ - stop_print_frame = 0; + if (stop_command->hook) + { + catch_errors (hook_stop_stub, (char *)stop_command->hook, + "Error while running hook_stop:\n", RETURN_MASK_ALL); } - if (inferior_pid == 0) + if (!target_has_stack) return; /* Select innermost stack frame except on return from a stack dummy routine, - or if the program has exited. */ + or if the program has exited. Print it without a level number if + we have changed functions or hit a breakpoint. Print source line + if we have one. */ if (!stop_stack_dummy) { - select_frame (stop_frame, 0); + select_frame (get_current_frame (), 0); if (stop_print_frame) { - if (stop_breakpoint > 0) - printf ("\nBpt %d, ", stop_breakpoint); - print_sel_frame (stop_step - && step_frame == stop_frame - && step_start_function == find_pc_function (stop_pc)); + int source_only; + + source_only = bpstat_print (stop_bpstat); + source_only = source_only || + ( stop_step + && step_frame_address == stop_frame_address + && step_start_function == find_pc_function (stop_pc)); + + print_stack_frame (selected_frame, -1, source_only? -1: 1); + /* Display the auto-display expressions. */ do_displays (); } } - /* Save the function value return registers + /* Save the function value return registers, if we care. We might be about to restore their previous contents. */ - read_register_bytes (0, stop_registers, REGISTER_BYTES); + if (proceed_to_finish) + read_register_bytes (0, stop_registers, REGISTER_BYTES); if (stop_stack_dummy) { - /* Pop the empty frame that contains the 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; - select_frame (read_register (FP_REGNUM), 0); + select_frame (get_current_frame (), 0); } } + +static int +hook_stop_stub (cmd) + char *cmd; +{ + execute_user_command ((struct cmd_list_element *)cmd, 0); + return (0); +} +int signal_stop_state (signo) + int signo; +{ + return ((signo >= 0 && signo < NSIG) ? signal_stop[signo] : 0); +} + +int signal_print_state (signo) + int signo; +{ + return ((signo >= 0 && signo < NSIG) ? signal_print[signo] : 0); +} + +int signal_pass_state (signo) + int signo; +{ + return ((signo >= 0 && signo < NSIG) ? signal_program[signo] : 0); +} + static void -insert_step_breakpoint () +sig_print_header () { - if (step_resume_break_address && !step_resume_break_duplicate) - { - read_memory (step_resume_break_address, - step_resume_break_shadow, sizeof break_insn); - write_memory (step_resume_break_address, - break_insn, sizeof break_insn); - } + printf_filtered ("Signal\t\tStop\tPrint\tPass to program\tDescription\n"); } static void -remove_step_breakpoint () +sig_print_info (number) + int number; { - if (step_resume_break_address && !step_resume_break_duplicate) - write_memory (step_resume_break_address, step_resume_break_shadow, - sizeof break_insn); + char *name; + + if ((name = strsigno (number)) == NULL) + printf_filtered ("%d\t\t", number); + else + printf_filtered ("%s (%d)\t", name, number); + printf_filtered ("%s\t", signal_stop[number] ? "Yes" : "No"); + printf_filtered ("%s\t", signal_print[number] ? "Yes" : "No"); + printf_filtered ("%s\t\t", signal_program[number] ? "Yes" : "No"); + printf_filtered ("%s\n", safe_strsignal (number)); } - + /* Specify how various signals in the inferior should be handled. */ static void @@ -849,141 +1448,378 @@ handle_command (args, from_tty) char *args; int from_tty; { - register char *p = args; - int signum; - register int digits, wordlen; + char **argv; + int digits, wordlen; + int sigfirst, signum, siglast; + int allsigs; + int nsigs; + unsigned char *sigs; + struct cleanup *old_chain; + + if (args == NULL) + { + error_no_arg ("signal to handle"); + } + + /* Allocate and zero an array of flags for which signals to handle. */ - if (!args) - error_no_arg ("signal to handle"); + nsigs = signo_max () + 1; + sigs = (unsigned char *) alloca (nsigs); + memset (sigs, 0, nsigs); - while (*p) + /* Break the command line up into args. */ + + argv = buildargv (args); + if (argv == NULL) + { + nomem (0); + } + old_chain = make_cleanup (freeargv, (char *) argv); + + /* Walk through the args, looking for signal numbers, signal names, and + actions. Signal numbers and signal names may be interspersed with + actions, with the actions being performed for all signals cumulatively + specified. Signal ranges can be specified as -. */ + + while (*argv != NULL) { - /* Find the end of the next word in the args. */ - for (wordlen = 0; p[wordlen] && p[wordlen] != ' ' && p[wordlen] != '\t'; - wordlen++); - for (digits = 0; p[digits] >= '0' && p[digits] <= '9'; digits++); + wordlen = strlen (*argv); + for (digits = 0; isdigit ((*argv)[digits]); digits++) {;} + allsigs = 0; + sigfirst = siglast = -1; - /* If it is all digits, it is signal number to operate on. */ - if (digits == wordlen) + if (wordlen >= 1 && !strncmp (*argv, "all", wordlen)) + { + /* Apply action to all signals except those used by the + debugger. Silently skip those. */ + allsigs = 1; + sigfirst = 0; + siglast = nsigs - 1; + } + else if (wordlen >= 1 && !strncmp (*argv, "stop", wordlen)) + { + SET_SIGS (nsigs, sigs, signal_stop); + SET_SIGS (nsigs, sigs, signal_print); + } + else if (wordlen >= 1 && !strncmp (*argv, "ignore", wordlen)) { - signum = atoi (p); - if (signum == SIGTRAP || signum == SIGINT) + UNSET_SIGS (nsigs, sigs, signal_program); + } + else if (wordlen >= 2 && !strncmp (*argv, "print", wordlen)) + { + SET_SIGS (nsigs, sigs, signal_print); + } + else if (wordlen >= 2 && !strncmp (*argv, "pass", wordlen)) + { + SET_SIGS (nsigs, sigs, signal_program); + } + else if (wordlen >= 3 && !strncmp (*argv, "nostop", wordlen)) + { + UNSET_SIGS (nsigs, sigs, signal_stop); + } + else if (wordlen >= 3 && !strncmp (*argv, "noignore", wordlen)) + { + SET_SIGS (nsigs, sigs, signal_program); + } + else if (wordlen >= 4 && !strncmp (*argv, "noprint", wordlen)) + { + UNSET_SIGS (nsigs, sigs, signal_print); + UNSET_SIGS (nsigs, sigs, signal_stop); + } + else if (wordlen >= 4 && !strncmp (*argv, "nopass", wordlen)) + { + UNSET_SIGS (nsigs, sigs, signal_program); + } + else if (digits > 0) + { + sigfirst = siglast = atoi (*argv); + if ((*argv)[digits] == '-') { - if (!query ("Signal %d is used by the debugger.\nAre you sure you want to change it? ", signum)) - error ("Not confirmed."); + siglast = atoi ((*argv) + digits + 1); + } + if (sigfirst > siglast) + { + /* Bet he didn't figure we'd think of this case... */ + signum = sigfirst; + 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 if (signum == 0) - error ("First argument is not a signal number."); - - /* Else, if already got a signal number, look for flag words - saying what to do for it. */ - else if (!strncmp (p, "stop", wordlen)) + else if ((signum = strtosigno (*argv)) != 0) { - signal_stop[signum] = 1; - signal_print[signum] = 1; + sigfirst = siglast = signum; } - else if (wordlen >= 2 && !strncmp (p, "print", wordlen)) - signal_print[signum] = 1; - else if (wordlen >= 2 && !strncmp (p, "pass", wordlen)) - signal_program[signum] = 1; - else if (!strncmp (p, "ignore", wordlen)) - signal_program[signum] = 0; - else if (wordlen >= 3 && !strncmp (p, "nostop", wordlen)) - signal_stop[signum] = 0; - else if (wordlen >= 4 && !strncmp (p, "noprint", wordlen)) + else { - signal_print[signum] = 0; - signal_stop[signum] = 0; + /* Not a number and not a recognized flag word => complain. */ + error ("Unrecognized or ambiguous flag word: \"%s\".", *argv); } - else if (wordlen >= 4 && !strncmp (p, "nopass", wordlen)) - signal_program[signum] = 0; - else if (wordlen >= 3 && !strncmp (p, "noignore", wordlen)) - signal_program[signum] = 1; - /* Not a number and not a recognized flag word => complain. */ - else + + /* If any signal numbers or symbol names were found, set flags for + which signals to apply actions to. */ + + for (signum = sigfirst; signum >= 0 && signum <= siglast; signum++) { - p[wordlen] = 0; - error ("Unrecognized flag word: \"%s\".", p); + switch (signum) + { + case SIGTRAP: + case SIGINT: + if (!allsigs && !sigs[signum]) + { + if (query ("%s is used by the debugger.\nAre you sure you want to change it? ", strsigno (signum))) + { + sigs[signum] = 1; + } + else + { + printf ("Not confirmed, unchanged.\n"); + fflush (stdout); + } + } + break; + default: + sigs[signum] = 1; + break; + } } - /* Find start of next word. */ - p += wordlen; - while (*p == ' ' || *p == '\t') p++; + argv++; } + target_notice_signals(); + if (from_tty) { /* Show the results. */ - printf ("Number\tStop\tPrint\tPass to program\tDescription\n"); - printf ("%d\t", signum); - printf ("%s\t", signal_stop[signum] ? "Yes" : "No"); - printf ("%s\t", signal_print[signum] ? "Yes" : "No"); - printf ("%s\t\t", signal_program[signum] ? "Yes" : "No"); - printf ("%s\n", sys_siglist[signum]); + sig_print_header (); + for (signum = 0; signum < nsigs; signum++) + { + if (sigs[signum]) + { + sig_print_info (signum); + } + } } + + do_cleanups (old_chain); } /* Print current contents of the tables set by the handle command. */ static void -signals_info (signum_exp) +signals_info (signum_exp, from_tty) char *signum_exp; + int from_tty; { register int i; - printf ("Number\tStop\tPrint\tPass to program\tDescription\n"); + sig_print_header (); if (signum_exp) { - i = parse_and_eval_address (signum_exp); - printf ("%d\t", i); - printf ("%s\t", signal_stop[i] ? "Yes" : "No"); - printf ("%s\t", signal_print[i] ? "Yes" : "No"); - printf ("%s\t\t", signal_program[i] ? "Yes" : "No"); - printf ("%s\n", sys_siglist[i]); + /* First see if this is a symbol name. */ + i = strtosigno (signum_exp); + if (i == 0) + { + /* Nope, maybe it's an address which evaluates to a signal + number. */ + i = parse_and_eval_address (signum_exp); + if (i >= NSIG || i < 0) + error ("Signal number out of bounds."); + } + sig_print_info (i); return; } - printf ("\n"); + printf_filtered ("\n"); for (i = 0; i < NSIG; i++) { QUIT; - if (i > 0 && i % 16 == 0) - { - printf ("[Type Return to see more]"); - fflush (stdout); - read_line (); - } - printf ("%d\t", i); - printf ("%s\t", signal_stop[i] ? "Yes" : "No"); - printf ("%s\t", signal_print[i] ? "Yes" : "No"); - printf ("%s\t\t", signal_program[i] ? "Yes" : "No"); - printf ("%s\n", sys_siglist[i]); + + sig_print_info (i); } - printf ("\nUse the \"handle\" command to change these tables.\n"); + printf_filtered ("\nUse the \"handle\" command to change these tables.\n"); } -static -initialize () +/* Save all of the information associated with the inferior<==>gdb + connection. INF_STATUS is a pointer to a "struct inferior_status" + (defined in inferior.h). */ + +void +save_inferior_status (inf_status, restore_stack_info) + struct inferior_status *inf_status; + int restore_stack_info; +{ + 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; + 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_over_calls = step_over_calls; + inf_status->stop_after_trap = stop_after_trap; + inf_status->stop_soon_quietly = stop_soon_quietly; + /* 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_i_s is called. */ + inf_status->stop_bpstat = stop_bpstat; + stop_bpstat = bpstat_copy (stop_bpstat); + inf_status->breakpoint_proceeded = breakpoint_proceeded; + inf_status->restore_stack_info = restore_stack_info; + inf_status->proceed_to_finish = proceed_to_finish; + + memcpy (inf_status->stop_registers, stop_registers, REGISTER_BYTES); + + read_register_bytes (0, inf_status->registers, REGISTER_BYTES); + + record_selected_frame (&(inf_status->selected_frame_address), + &(inf_status->selected_level)); + return; +} + +struct restore_selected_frame_args { + FRAME_ADDR frame_address; + int level; +}; + +static int restore_selected_frame PARAMS ((char *)); + +/* Restore the selected frame. args is really a struct + 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; + int level = fr->level; + + fid = 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 || + level != 0) + { + warning ("Unable to restore previously selected frame.\n"); + return 0; + } + select_frame (fid, fr->level); + return(1); +} + +void +restore_inferior_status (inf_status) + struct inferior_status *inf_status; +{ + 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; + 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_over_calls = inf_status->step_over_calls; + stop_after_trap = inf_status->stop_after_trap; + stop_soon_quietly = inf_status->stop_soon_quietly; + bpstat_clear (&stop_bpstat); + stop_bpstat = inf_status->stop_bpstat; + breakpoint_proceeded = inf_status->breakpoint_proceeded; + proceed_to_finish = inf_status->proceed_to_finish; + + memcpy (stop_registers, inf_status->stop_registers, REGISTER_BYTES); + + /* The inferior can be gone if the user types "print exit(0)" + (and perhaps other times). */ + if (target_has_execution) + write_register_bytes (0, inf_status->registers, REGISTER_BYTES); + + /* The inferior can be gone if the user types "print exit(0)" + (and perhaps other times). */ + + /* FIXME: If we are being called after stopping in a function which + is called from gdb, we should not be trying to restore the + selected frame; it just prints a spurious error message (The + message is useful, however, in detecting bugs in gdb (like if gdb + clobbers the stack)). In fact, should we be restoring the + inferior status at all in that case? . */ + + if (target_has_stack && inf_status->restore_stack_info) + { + struct restore_selected_frame_args fr; + fr.level = inf_status->selected_level; + fr.frame_address = inf_status->selected_frame_address; + /* The point of catch_errors is that if the stack is clobbered, + walking the stack might encounter a garbage pointer and error() + trying to dereference it. */ + if (catch_errors (restore_selected_frame, &fr, + "Unable to restore previously selected frame:\n", + RETURN_MASK_ERROR) == 0) + /* Error in restoring the selected frame. Select the innermost + frame. */ + select_frame (get_current_frame (), 0); + } +} + + +void +_initialize_infrun () { register int i; + register int numsigs; 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."); + add_info_alias ("handle", "signals", 0); add_com ("handle", class_run, handle_command, "Specify how to handle a signal.\n\ -Args are signal number followed by flags.\n\ -Flags allowed are \"stop\", \"print\", \"pass\",\n\ - \"nostop\", \"noprint\" or \"nopass\".\n\ -Print means print a message if this signal happens.\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\ +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\ +\"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."); - for (i = 0; i < NSIG; i++) + 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\ +This allows you to set a list of commands to be run each time execution\n\ +of the program stops.", &cmdlist); + + numsigs = signo_max () + 1; + signal_stop = (unsigned char *) + xmalloc (sizeof (signal_stop[0]) * numsigs); + signal_print = (unsigned char *) + xmalloc (sizeof (signal_print[0]) * numsigs); + signal_program = (unsigned char *) + xmalloc (sizeof (signal_program[0]) * numsigs); + for (i = 0; i < numsigs; i++) { signal_stop[i] = 1; signal_print[i] = 1; @@ -1025,5 +1861,3 @@ Pass and Stop may be combined."); signal_print[SIGURG] = 0; #endif /* SIGURG */ } - -END_FILE