aboutsummaryrefslogtreecommitdiff
path: root/gdb/infrun.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/infrun.c')
-rw-r--r--gdb/infrun.c943
1 files changed, 943 insertions, 0 deletions
diff --git a/gdb/infrun.c b/gdb/infrun.c
new file mode 100644
index 0000000..e980cec
--- /dev/null
+++ b/gdb/infrun.c
@@ -0,0 +1,943 @@
+/* Start and stop the inferior process, for GDB.
+ Copyright (C) 1986, 1987 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!
+*/
+
+#include "defs.h"
+#include "initialize.h"
+#include "param.h"
+#include "symtab.h"
+#include "frame.h"
+#include "inferior.h"
+#include "wait.h"
+
+#include <stdio.h>
+#include <signal.h>
+#include <a.out.h>
+
+#ifdef UMAX_PTRACE
+#include <sys/param.h>
+#include <sys/ptrace.h>
+#endif UMAX_PTRACE
+
+extern char *sys_siglist[];
+extern int errno;
+
+/* Tables of how to react to signals; the user sets them. */
+
+static char signal_stop[NSIG];
+static char signal_print[NSIG];
+static char signal_program[NSIG];
+
+/* Nonzero if breakpoints are now inserted in the inferior. */
+
+static int breakpoints_inserted;
+
+/* Function inferior was in as of last step command. */
+
+static struct symbol *step_start_function;
+
+/* This is the sequence of bytes we insert for a breakpoint. */
+
+static char break_insn[] = BREAKPOINT;
+
+/* Nonzero => address for special breakpoint for resuming stepping. */
+
+static CORE_ADDR step_resume_break_address;
+
+/* Original contents of the byte where the special breakpoint is. */
+
+static char step_resume_break_shadow[sizeof break_insn];
+
+/* Nonzero means the special breakpoint is a duplicate
+ so it has not itself been inserted. */
+
+static int step_resume_break_duplicate;
+
+/* 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. */
+
+static int trap_expected;
+
+/* 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. */
+
+static int stop_after_attach;
+
+/* Nonzero if pc has been changed by the debugger
+ since the inferior stopped. */
+
+int pc_changed;
+
+/* Save register contents here when about to pop a stack dummy frame. */
+
+char stop_registers[REGISTER_BYTES];
+
+/* Nonzero if program stopped due to error trying to insert breakpoints. */
+
+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 ();
+
+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'. */
+
+void
+clear_proceed_status ()
+{
+ trap_expected = 0;
+ step_range_start = 0;
+ step_range_end = 0;
+ step_frame = 0;
+ step_over_calls = -1;
+ step_resume_break_address = 0;
+ stop_after_trap = 0;
+ stop_after_attach = 0;
+
+ /* Discard any remaining commands left by breakpoint we had stopped at. */
+ clear_breakpoint_commands ();
+}
+
+/* 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,
+ 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.
+ You should probably set various step_... variables
+ before calling here, if you are stepping.
+
+ You should call clear_proceed_status before calling proceed. */
+
+void
+proceed (addr, signal, step)
+ CORE_ADDR addr;
+ int signal;
+ int step;
+{
+ int oneproc = 0;
+
+ if (step > 0)
+ step_start_function = find_pc_function (read_pc ());
+ if (step < 0)
+ stop_after_trap = 1;
+
+ if (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 ()))
+ {
+ oneproc = 1;
+ /* We will get a trace trap after one instruction.
+ Continue it automatically and insert breakpoints then. */
+ trap_expected = 1;
+ }
+ }
+ else
+ write_register (PC_REGNUM, addr);
+
+ if (!oneproc)
+ {
+ int temp = insert_breakpoints ();
+ if (temp)
+ {
+ print_sys_errmsg ("ptrace", temp);
+ error ("Cannot insert breakpoints.\n\
+The same program may be running in another process.");
+ }
+ breakpoints_inserted = 1;
+ }
+
+ /* Install inferior's terminal modes. */
+ terminal_inferior ();
+
+ if (signal >= 0)
+ stop_signal = signal;
+ /* 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);
+
+ /* Wait for it to stop (if not standalone)
+ and in any case decode why it stopped, and act accordingly. */
+
+ wait_for_inferior ();
+ 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. */
+ 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 ();
+
+ wait_for_inferior ();
+ normal_stop ();
+}
+
+#ifdef ATTACH_DETACH
+
+/* Attach to process PID, then initialize for debugging it
+ and wait for the trace-trap that results from attaching. */
+
+void
+attach_program (pid)
+ int pid;
+{
+ attach (pid);
+ inferior_pid = pid;
+
+ mark_breakpoints_out ();
+ terminal_init_inferior ();
+ clear_proceed_status ();
+ stop_after_attach = 1;
+ /*proceed (-1, 0, -2);*/
+ wait_for_inferior ();
+ normal_stop ();
+}
+#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
+ instead of returning. That is why there is a loop in this function.
+ When this function actually returns it means the inferior
+ should be left stopped and GDB should read more commands. */
+
+static void
+wait_for_inferior ()
+{
+ register int pid;
+ 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;
+ struct symtab_and_line sal;
+ int prev_pc;
+
+ while (1)
+ {
+ prev_pc = read_pc ();
+ pid = wait (&w);
+ 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;
+
+ /* 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. */
+
+ if (WIFEXITED (w))
+ {
+ terminal_ours_for_output ();
+ if (WRETCODE (w))
+ printf ("\nProgram exited with code 0%o.\n", WRETCODE (w));
+ else
+ printf ("\nProgram exited normally.\n");
+ fflush (stdout);
+ inferior_died ();
+ stop_print_frame = 0;
+ break;
+ }
+ else if (!WIFSTOPPED (w))
+ {
+ kill_inferior ();
+ 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");
+ fflush (stdout);
+ break;
+ }
+ else
+ {
+ 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)
+ {
+ 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;
+ }
+ }
+
+ 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;
+ }
+ }
+ 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;
+
+ if (stop_signal >= NSIG
+ || signal_print[stop_signal])
+ {
+ printed = 1;
+ terminal_ours_for_output ();
+ printf ("\nProgram received signal %d, %s\n",
+ stop_signal,
+ stop_signal < NSIG
+ ? sys_siglist[stop_signal]
+ : "(undocumented)");
+ 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)
+ {
+ /* 0x1000000 is set in stop_breakpoint as returned by
+ breakpoint_status_p to indicate a silent breakpoint. */
+ if (stop_breakpoint > 0 && stop_breakpoint & 0x1000000)
+ {
+ stop_breakpoint &= ~0x1000000;
+ stop_print_frame = 0;
+ }
+ 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;
+ }
+ /* 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;
+ }
+
+ /* 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. */
+ }
+
+ /* 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)
+ {
+ stop_print_frame = 0;
+ stop_stack_dummy = 1;
+ break;
+ }
+
+ 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)
+ {
+ /* 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;
+ }
+ }
+
+ /* 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)
+ {
+ newfun = find_pc_function (stop_pc);
+ if (newfun)
+ {
+ newfun_pc = BLOCK_START (SYMBOL_BLOCK_VALUE (newfun))
+ + FUNCTION_START_OFFSET;
+ }
+ 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)
+ step_resume_break_address = sal.end;
+ else
+ 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;
+ }
+ }
+ }
+
+ /* If we did not do break;, it means we should keep
+ running the inferior and not return to debugger. */
+
+ /* 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. */
+
+ if (trap_expected > 1)
+ {
+ trap_expected--;
+ running_in_shell = 1;
+ resume (0, 0);
+ }
+ else if (running_in_shell && stop_signal == SIGSEGV)
+ {
+ resume (0, SIGSEGV);
+ }
+ else
+ {
+ /* Here, we are not awaiting another exec to get
+ the program we really want to debug.
+ Insert breakpoints now, unless we are trying
+ to one-proceed past a breakpoint. */
+ running_in_shell = 0;
+ if (!breakpoints_inserted && !another_trap)
+ {
+ insert_step_breakpoint ();
+ breakpoints_failed = insert_breakpoints ();
+ if (breakpoints_failed)
+ break;
+ breakpoints_inserted = 1;
+ }
+
+ trap_expected = another_trap;
+
+ if (stop_signal == SIGTRAP)
+ stop_signal = 0;
+
+ resume ((step_range_end && !step_resume_break_address)
+ || trap_expected,
+ stop_signal);
+ }
+ }
+}
+
+/* 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
+normal_stop ()
+{
+ if (breakpoints_failed)
+ {
+ terminal_ours_for_output ();
+ print_sys_errmsg ("ptrace", breakpoints_failed);
+ printf ("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 (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\
+Further execution is probably impossible.\n");
+ }
+
+ breakpoints_inserted = 0;
+
+ /* 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);
+
+ if (step_multi && stop_step)
+ return;
+
+ terminal_ours ();
+
+ if (running_in_shell)
+ {
+ if (stop_signal == SIGSEGV)
+ 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 (inferior_pid == 0)
+ return;
+
+ /* Select innermost stack frame except on return from a stack dummy routine,
+ or if the program has exited. */
+ if (!stop_stack_dummy)
+ {
+ select_frame (stop_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));
+ /* Display the auto-display expressions. */
+ do_displays ();
+ }
+ }
+
+ /* Save the function value return registers
+ We might be about to restore their previous contents. */
+ read_register_bytes (0, stop_registers, REGISTER_BYTES);
+
+ if (stop_stack_dummy)
+ {
+ /* Pop the empty frame that contains the stack dummy. */
+ POP_FRAME;
+ select_frame (read_register (FP_REGNUM), 0);
+ }
+}
+
+static void
+insert_step_breakpoint ()
+{
+ 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);
+ }
+}
+
+static void
+remove_step_breakpoint ()
+{
+ if (step_resume_break_address && !step_resume_break_duplicate)
+ write_memory (step_resume_break_address, step_resume_break_shadow,
+ sizeof break_insn);
+}
+
+/* Specify how various signals in the inferior should be handled. */
+
+static void
+handle_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ register char *p = args;
+ int signum;
+ register int digits, wordlen;
+
+ if (!args)
+ error_no_arg ("signal to handle");
+
+ while (*p)
+ {
+ /* 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++);
+
+ /* If it is all digits, it is signal number to operate on. */
+ if (digits == wordlen)
+ {
+ signum = atoi (p);
+ if (signum == SIGTRAP || signum == SIGINT)
+ {
+ if (!query ("Signal %d is used by the debugger.\nAre you sure you want to change it? ", signum))
+ error ("Not confirmed.");
+ }
+ }
+ 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))
+ {
+ signal_stop[signum] = 1;
+ signal_print[signum] = 1;
+ }
+ 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))
+ {
+ signal_print[signum] = 0;
+ signal_stop[signum] = 0;
+ }
+ 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
+ {
+ p[wordlen] = 0;
+ error ("Unrecognized flag word: \"%s\".", p);
+ }
+
+ /* Find start of next word. */
+ p += wordlen;
+ while (*p == ' ' || *p == '\t') p++;
+ }
+
+ 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]);
+ }
+}
+
+/* Print current contents of the tables set by the handle command. */
+
+static void
+signals_info (signum_exp)
+ char *signum_exp;
+{
+ register int i;
+ printf ("Number\tStop\tPrint\tPass to program\tDescription\n");
+
+ 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]);
+ return;
+ }
+
+ printf ("\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]);
+ }
+
+ printf ("\nUse the \"handle\" command to change these tables.\n");
+}
+
+static
+initialize ()
+{
+ register int i;
+
+ 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_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\
+Stop means reenter debugger if this signal happens (implies print).\n\
+Pass means let program see this signal; otherwise program doesn't know.\n\
+Pass and Stop may be combined.");
+
+ for (i = 0; i < NSIG; i++)
+ {
+ signal_stop[i] = 1;
+ signal_print[i] = 1;
+ signal_program[i] = 1;
+ }
+
+ /* Signals caused by debugger's own actions
+ should not be given to the program afterwards. */
+ signal_program[SIGTRAP] = 0;
+ signal_program[SIGINT] = 0;
+
+ /* Signals that are not errors should not normally enter the debugger. */
+#ifdef SIGALRM
+ signal_stop[SIGALRM] = 0;
+ signal_print[SIGALRM] = 0;
+#endif /* SIGALRM */
+#ifdef SIGVTALRM
+ signal_stop[SIGVTALRM] = 0;
+ signal_print[SIGVTALRM] = 0;
+#endif /* SIGVTALRM */
+#ifdef SIGPROF
+ signal_stop[SIGPROF] = 0;
+ signal_print[SIGPROF] = 0;
+#endif /* SIGPROF */
+#ifdef SIGCHLD
+ signal_stop[SIGCHLD] = 0;
+ signal_print[SIGCHLD] = 0;
+#endif /* SIGCHLD */
+#ifdef SIGCLD
+ signal_stop[SIGCLD] = 0;
+ signal_print[SIGCLD] = 0;
+#endif /* SIGCLD */
+#ifdef SIGIO
+ signal_stop[SIGIO] = 0;
+ signal_print[SIGIO] = 0;
+#endif /* SIGIO */
+#ifdef SIGURG
+ signal_stop[SIGURG] = 0;
+ signal_print[SIGURG] = 0;
+#endif /* SIGURG */
+}
+
+END_FILE