/* Target-struct-independent code to start (run) and stop an inferior process.
- Copyright 1986-1989, 1991-1999 Free Software Foundation, Inc.
+ Copyright 1986-1989, 1991-2000 Free Software Foundation, Inc.
This file is part of GDB.
#include "frame.h"
#include "inferior.h"
#include "breakpoint.h"
-#include "wait.h"
+#include "gdb_wait.h"
#include "gdbcore.h"
#include "gdbcmd.h"
#include "target.h"
#include "symfile.h" /* for overlay functions */
#include "top.h"
#include <signal.h>
-#include "event-loop.h"
+#include "inf-loop.h"
/* Prototypes for local functions */
static void sig_print_header (void);
-static void resume_cleanups (int);
+static void resume_cleanups (void *);
static int hook_stop_stub (void *);
static void set_follow_fork_mode_command (char *arg, int from_tty,
struct cmd_list_element * c);
-static void complete_execution (void);
-
static struct inferior_status *xmalloc_inferior_status (void);
static void free_inferior_status (struct inferior_status *);
static void set_schedlock_func (char *args, int from_tty,
struct cmd_list_element * c);
-static int is_internal_shlib_eventpoint (struct breakpoint * ep);
-
-static int stopped_for_internal_shlib_event (bpstat bs);
-
struct execution_control_state;
static int currently_stepping (struct execution_control_state *ecs);
int inferior_ignoring_startup_exec_events = 0;
int inferior_ignoring_leading_exec_events = 0;
+/* When set, stop the 'step' command if we enter a function which has
+ no line number information. The normal behavior is that we step
+ over such function. */
+int step_stop_if_no_debug = 0;
+
/* In asynchronous mode, but simulating synchronous execution. */
int sync_execution = 0;
when the inferior stopped in a different thread than it had been
running in. */
-static int switched_from_inferior_pid;
-
-/* This will be true for configurations that may actually report an
- inferior pid different from the original. At present this is only
- true for HP-UX native. */
-
-#ifndef MAY_SWITCH_FROM_INFERIOR_PID
-#define MAY_SWITCH_FROM_INFERIOR_PID (0)
-#endif
-
-static int may_switch_from_inferior_pid = MAY_SWITCH_FROM_INFERIOR_PID;
+static int previous_inferior_pid;
/* This is true for configurations that may follow through execl() and
similar functions. At present this is only true for HP-UX native. */
#define DYNAMIC_TRAMPOLINE_NEXTPC(pc) 0
#endif
-/* On SVR4 based systems, determining the callee's address is exceedingly
- difficult and depends on the implementation of the run time loader.
- If we are stepping at the source level, we single step until we exit
- the run time loader code and reach the callee's address. */
+/* If the program uses ELF-style shared libraries, then calls to
+ functions in shared libraries go through stubs, which live in a
+ table called the PLT (Procedure Linkage Table). The first time the
+ function is called, the stub sends control to the dynamic linker,
+ which looks up the function's real address, patches the stub so
+ that future calls will go directly to the function, and then passes
+ control to the function.
+
+ If we are stepping at the source level, we don't want to see any of
+ this --- we just want to skip over the stub and the dynamic linker.
+ The simple approach is to single-step until control leaves the
+ dynamic linker.
+
+ However, on some systems (e.g., Red Hat Linux 5.2) the dynamic
+ linker calls functions in the shared C library, so you can't tell
+ from the PC alone whether the dynamic linker is still running. In
+ this case, we use a step-resume breakpoint to get us past the
+ dynamic linker, as if we were using "next" to step over a function
+ call.
+
+ IN_SOLIB_DYNSYM_RESOLVE_CODE says whether we're in the dynamic
+ linker code or not. Normally, this means we single-step. However,
+ if SKIP_SOLIB_RESOLVER then returns non-zero, then its value is an
+ address where we can place a step-resume breakpoint to get past the
+ linker's symbol resolution function.
+
+ IN_SOLIB_DYNSYM_RESOLVE_CODE can generally be implemented in a
+ pretty portable way, by comparing the PC against the address ranges
+ of the dynamic linker's sections.
+
+ SKIP_SOLIB_RESOLVER is generally going to be system-specific, since
+ it depends on internal details of the dynamic linker. It's usually
+ not too hard to figure out where to put a breakpoint, but it
+ certainly isn't portable. SKIP_SOLIB_RESOLVER should do plenty of
+ sanity checking. If it can't figure things out, returning zero and
+ getting the (possibly confusing) stepping behavior is better than
+ signalling an error, which will obscure the change in the
+ inferior's state. */
#ifndef IN_SOLIB_DYNSYM_RESOLVE_CODE
#define IN_SOLIB_DYNSYM_RESOLVE_CODE(pc) 0
#endif
+#ifndef SKIP_SOLIB_RESOLVER
+#define SKIP_SOLIB_RESOLVER(pc) 0
+#endif
+
/* For SVR4 shared libraries, each call goes through a small piece of
trampoline code in the ".plt" section. IN_SOLIB_CALL_TRAMPOLINE evaluates
to nonzero if we are current stopped in one of these. */
#define INSTRUCTION_NULLIFIED 0
#endif
+/* We can't step off a permanent breakpoint in the ordinary way, because we
+ can't remove it. Instead, we have to advance the PC to the next
+ instruction. This macro should expand to a pointer to a function that
+ does that, or zero if we have no such function. If we don't have a
+ definition for it, we have to report an error. */
+#ifndef SKIP_PERMANENT_BREAKPOINT
+#define SKIP_PERMANENT_BREAKPOINT (default_skip_permanent_breakpoint)
+static void
+default_skip_permanent_breakpoint (void)
+{
+ error_begin ();
+ fprintf_filtered (gdb_stderr, "\
+The program is stopped at a permanent breakpoint, but GDB does not know\n\
+how to step past a permanent breakpoint on this architecture. Try using\n\
+a command like `return' or `jump' to continue execution.\n");
+ return_to_top_level (RETURN_ERROR);
+}
+#endif
+
+
/* Convert the #defines into values. This is temporary until wfi control
flow is completely sorted out. */
#define HAVE_CONTINUABLE_WATCHPOINT 1
#endif
+#ifndef CANNOT_STEP_HW_WATCHPOINTS
+#define CANNOT_STEP_HW_WATCHPOINTS 0
+#else
+#undef CANNOT_STEP_HW_WATCHPOINTS
+#define CANNOT_STEP_HW_WATCHPOINTS 1
+#endif
+
/* Tables of how to react to signals; the user sets them. */
static unsigned char *signal_stop;
follow-fork-mode.) */
static int follow_vfork_when_exec;
-static char *follow_fork_mode_kind_names[] =
+static const char follow_fork_mode_ask[] = "ask";
+static const char follow_fork_mode_both[] = "both";
+static const char follow_fork_mode_child[] = "child";
+static const char follow_fork_mode_parent[] = "parent";
+
+static const char *follow_fork_mode_kind_names[] =
{
-/* ??rehrauer: The "both" option is broken, by what may be a 10.20
- kernel problem. It's also not terribly useful without a GUI to
- help the user drive two debuggers. So for now, I'm disabling
- the "both" option.
- "parent", "child", "both", "ask" };
- */
- "parent", "child", "ask"};
+ follow_fork_mode_ask,
+ /* ??rehrauer: The "both" option is broken, by what may be a 10.20
+ kernel problem. It's also not terribly useful without a GUI to
+ help the user drive two debuggers. So for now, I'm disabling the
+ "both" option. */
+ /* follow_fork_mode_both, */
+ follow_fork_mode_child,
+ follow_fork_mode_parent,
+ NULL
+};
-static char *follow_fork_mode_string = NULL;
+static const char *follow_fork_mode_string = follow_fork_mode_parent;
\f
static void
int followed_child = 0;
/* Which process did the user want us to follow? */
- char *follow_mode =
- savestring (follow_fork_mode_string, strlen (follow_fork_mode_string));
+ const char *follow_mode = follow_fork_mode_string;
/* Or, did the user not know, and want us to ask? */
- if (STREQ (follow_fork_mode_string, "ask"))
+ if (follow_fork_mode_string == follow_fork_mode_ask)
{
- char requested_mode[100];
-
- free (follow_mode);
- error ("\"ask\" mode NYI");
- follow_mode = savestring (requested_mode, strlen (requested_mode));
+ internal_error ("follow_inferior_fork: \"ask\" mode not implemented");
+ /* follow_mode = follow_fork_mode_...; */
}
/* If we're to be following the parent, then detach from child_pid.
We're already following the parent, so need do nothing explicit
for it. */
- if (STREQ (follow_mode, "parent"))
+ if (follow_mode == follow_fork_mode_parent)
{
followed_parent = 1;
/* If we're to be following the child, then attach to it, detach
from inferior_pid, and set inferior_pid to child_pid. */
- else if (STREQ (follow_mode, "child"))
+ else if (follow_mode == follow_fork_mode_child)
{
char child_pid_spelling[100]; /* Arbitrary length. */
/* If we're to be following both parent and child, then fork ourselves,
and attach the debugger clone to the child. */
- else if (STREQ (follow_mode, "both"))
+ else if (follow_mode == follow_fork_mode_both)
{
char pid_suffix[100]; /* Arbitrary length. */
pending_follow.fork_event.saw_parent_fork = 0;
pending_follow.fork_event.saw_child_fork = 0;
-
- free (follow_mode);
}
static void
pending_follow.fork_event.saw_child_exec = 0;
pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
follow_exec (inferior_pid, pending_follow.execd_pathname);
- free (pending_follow.execd_pathname);
+ xfree (pending_follow.execd_pathname);
}
}
/* Things to clean up if we QUIT out of resume (). */
/* ARGSUSED */
static void
-resume_cleanups (int arg)
+resume_cleanups (void *ignore)
{
normal_stop ();
}
-static char schedlock_off[] = "off";
-static char schedlock_on[] = "on";
-static char schedlock_step[] = "step";
-static char *scheduler_mode = schedlock_off;
-static char *scheduler_enums[] =
-{schedlock_off, schedlock_on, schedlock_step};
+static const char schedlock_off[] = "off";
+static const char schedlock_on[] = "on";
+static const char schedlock_step[] = "step";
+static const char *scheduler_mode = schedlock_off;
+static const char *scheduler_enums[] =
+{
+ schedlock_off,
+ schedlock_on,
+ schedlock_step,
+ NULL
+};
static void
set_schedlock_func (char *args, int from_tty, struct cmd_list_element *c)
}
+
+
/* 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
resume (int step, enum target_signal sig)
{
int should_resume = 1;
- struct cleanup *old_cleanups = make_cleanup ((make_cleanup_func)
- resume_cleanups, 0);
+ struct cleanup *old_cleanups = make_cleanup (resume_cleanups, 0);
QUIT;
#ifdef CANNOT_STEP_BREAKPOINT
step = 0;
#endif
+ /* Some targets (e.g. Solaris x86) have a kernel bug when stepping
+ over an instruction that causes a page fault without triggering
+ a hardware watchpoint. The kernel properly notices that it shouldn't
+ stop, because the hardware watchpoint is not triggered, but it forgets
+ the step request and continues the program normally.
+ Work around the problem by removing hardware watchpoints if a step is
+ requested, GDB will check for a hardware watchpoint trigger after the
+ step anyway. */
+ if (CANNOT_STEP_HW_WATCHPOINTS && step && breakpoints_inserted)
+ remove_hw_watchpoints ();
+
+
+ /* Normally, by the time we reach `resume', the breakpoints are either
+ removed or inserted, as appropriate. The exception is if we're sitting
+ at a permanent breakpoint; we need to step over it, but permanent
+ breakpoints can't be removed. So we have to test for it here. */
+ if (breakpoint_here_p (read_pc ()) == permanent_breakpoint_here)
+ SKIP_PERMANENT_BREAKPOINT ();
+
if (SOFTWARE_SINGLE_STEP_P && step)
{
/* Do it the hard way, w/temp breakpoints */
if (should_resume)
{
+ int resume_pid;
+
if (use_thread_step_needed && thread_step_needed)
{
/* We stopped on a BPT instruction;
{
/* Breakpoint deleted: ok to do regular resume
where all the threads either step or continue. */
- target_resume (-1, step, sig);
+ resume_pid = -1;
}
else
{
trap_expected = 1;
step = 1;
}
-
- target_resume (inferior_pid, step, sig);
+ resume_pid = inferior_pid;
}
}
else
{
/* Vanilla resume. */
-
if ((scheduler_mode == schedlock_on) ||
(scheduler_mode == schedlock_step && step != 0))
- target_resume (inferior_pid, step, sig);
+ resume_pid = inferior_pid;
else
- target_resume (-1, step, sig);
+ resume_pid = -1;
}
+ target_resume (resume_pid, step, sig);
}
discard_cleanups (old_cleanups);
step_range_start = 0;
step_range_end = 0;
step_frame_address = 0;
- step_over_calls = -1;
+ step_over_calls = STEP_OVER_UNDEBUGGABLE;
stop_after_trap = 0;
stop_soon_quietly = 0;
proceed_to_finish = 0;
if (step < 0)
stop_after_trap = 1;
- if (addr == (CORE_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
int temp = insert_breakpoints ();
if (temp)
{
- print_sys_errmsg ("ptrace", temp);
+ print_sys_errmsg ("insert_breakpoints", temp);
error ("Cannot insert breakpoints.\n\
-The same program may be running in another process.");
+The same program may be running in another process,\n\
+or you may have requested too many hardware\n\
+breakpoints and/or watchpoints.\n");
}
breakpoints_inserted = 1;
and in any case decode why it stopped, and act accordingly. */
/* Do this only if we are not using the event loop, or if the target
does not support asynchronous execution. */
- if (!async_p || !target_has_async)
+ if (!event_loop_p || !target_can_async_p ())
{
wait_for_inferior ();
normal_stop ();
stop_soon_quietly = 1;
trap_expected = 0;
- /* Go on waiting only in case gdb is not started in async mode, or
- in case the target doesn't support async execution. */
- if (!async_p || !target_has_async)
- {
- wait_for_inferior ();
- normal_stop ();
- }
- else
- {
- /* The 'tar rem' command should always look synchronous,
- i.e. display the prompt only once it has connected and
- started the target. */
- sync_execution = 1;
- push_prompt ("", "", "");
- delete_file_handler (input_fd);
- target_executing = 1;
- }
+ /* Always go on waiting for the target, regardless of the mode. */
+ /* FIXME: cagney/1999-09-23: At present it isn't possible to
+ indicate to wait_for_inferior that a target should timeout if
+ nothing is returned (instead of just blocking). Because of this,
+ targets expecting an immediate response need to, internally, set
+ things up so that the target_wait() is forced to eventually
+ timeout. */
+ /* FIXME: cagney/1999-09-24: It isn't possible for target_open() to
+ differentiate to its caller what the state of the target is after
+ the initial open has been performed. Here we're assuming that
+ the target has stopped. It should be possible to eventually have
+ target_open() return to the caller an indication that the target
+ is currently running and GDB state should be set to the same as
+ for an async run. */
+ wait_for_inferior ();
+ normal_stop ();
}
/* Initialize static vars when a new inferior begins. */
infwait_nonstep_watch_state
};
+/* Why did the inferior stop? Used to print the appropriate messages
+ to the interface from within handle_inferior_event(). */
+enum inferior_stop_reason
+{
+ /* We don't know why. */
+ STOP_UNKNOWN,
+ /* Step, next, nexti, stepi finished. */
+ END_STEPPING_RANGE,
+ /* Found breakpoint. */
+ BREAKPOINT_HIT,
+ /* Inferior terminated by signal. */
+ SIGNAL_EXITED,
+ /* Inferior exited. */
+ EXITED,
+ /* Inferior received signal, and user asked to be notified. */
+ SIGNAL_RECEIVED
+};
+
/* This structure contains what used to be local variables in
wait_for_inferior. Probably many of them can return to being
locals in handle_inferior_event. */
void handle_inferior_event (struct execution_control_state * ecs);
static void check_sigtramp2 (struct execution_control_state *ecs);
+static void step_into_function (struct execution_control_state *ecs);
+static void step_over_function (struct execution_control_state *ecs);
static void stop_stepping (struct execution_control_state *ecs);
static void prepare_to_wait (struct execution_control_state *ecs);
+static void keep_going (struct execution_control_state *ecs);
+static void print_stop_reason (enum inferior_stop_reason stop_reason, int stop_info);
/* Wait for control to return from inferior to debugger.
If inferior gets a signal, we may decide to start it up again
thread_step_needed = 0;
/* We'll update this if & when we switch to a new thread. */
- if (may_switch_from_inferior_pid)
- switched_from_inferior_pid = inferior_pid;
+ previous_inferior_pid = inferior_pid;
overlay_cache_invalid = 1;
struct execution_control_state *async_ecs;
void
-fetch_inferior_event (void)
+fetch_inferior_event (void *client_data)
{
static struct cleanup *old_cleanups;
thread_step_needed = 0;
/* We'll update this if & when we switch to a new thread. */
- if (may_switch_from_inferior_pid)
- switched_from_inferior_pid = inferior_pid;
+ previous_inferior_pid = inferior_pid;
overlay_cache_invalid = 1;
if there are any. */
do_exec_cleanups (old_cleanups);
normal_stop ();
- /* Is there anything left to do for the command issued to
- complete? */
- do_all_continuations ();
- /* Reset things after target has stopped for the async commands. */
- complete_execution ();
+ if (step_multi && stop_step)
+ inferior_event_handler (INF_EXEC_CONTINUE, NULL);
+ else
+ inferior_event_handler (INF_EXEC_COMPLETE, NULL);
}
}
void
init_execution_control_state (struct execution_control_state *ecs)
{
+ /* ecs->another_trap? */
ecs->random_signal = 0;
ecs->remove_breakpoints_on_following_step = 0;
ecs->handling_longjmp = 0; /* FIXME */
insert_breakpoints ();
/* We need to restart all the threads now,
- * unles we're running in scheduler-locked mode.
+ * unless we're running in scheduler-locked mode.
* FIXME: shouldn't we look at currently_stepping ()?
*/
if (scheduler_mode == schedlock_on)
{
add_thread (ecs->pid);
+#ifdef UI_OUT
+ ui_out_text (uiout, "[New ");
+ ui_out_text (uiout, target_pid_or_tid_to_str (ecs->pid));
+ ui_out_text (uiout, "]\n");
+#else
printf_filtered ("[New %s]\n", target_pid_or_tid_to_str (ecs->pid));
+#endif
#if 0
/* NOTE: This block is ONLY meant to be invoked in case of a
case TARGET_WAITKIND_EXITED:
target_terminal_ours (); /* Must do this before mourn anyway */
- annotate_exited (ecs->ws.value.integer);
- if (ecs->ws.value.integer)
- printf_filtered ("\nProgram exited with code 0%o.\n",
- (unsigned int) ecs->ws.value.integer);
- else
- printf_filtered ("\nProgram exited normally.\n");
+ print_stop_reason (EXITED, ecs->ws.value.integer);
/* Record the exit code in the convenience variable $_exitcode, so
that the user can inspect this again later. */
stop_print_frame = 0;
stop_signal = ecs->ws.value.sig;
target_terminal_ours (); /* Must do this before mourn anyway */
- annotate_signalled ();
-
- /* This looks pretty bogus to me. Doesn't TARGET_WAITKIND_SIGNALLED
- mean it is already dead? This has been here since GDB 2.8, so
- perhaps it means rms didn't understand unix waitstatuses?
- For the moment I'm just kludging around this in remote.c
- rather than trying to change it here --kingdon, 5 Dec 1994. */
- target_kill (); /* kill mourns as well */
-
- printf_filtered ("\nProgram terminated with signal ");
- annotate_signal_name ();
- printf_filtered ("%s", target_signal_to_name (stop_signal));
- annotate_signal_name_end ();
- printf_filtered (", ");
- annotate_signal_string ();
- printf_filtered ("%s", target_signal_to_string (stop_signal));
- annotate_signal_string_end ();
- printf_filtered (".\n");
-
- printf_filtered ("The program no longer exists.\n");
- gdb_flush (gdb_stdout);
+
+ /* Note: By definition of TARGET_WAITKIND_SIGNALLED, we shouldn't
+ reach here unless the inferior is dead. However, for years
+ target_kill() was called here, which hints that fatal signals aren't
+ really fatal on some systems. If that's true, then some changes
+ may be needed. */
+ target_mourn_inferior ();
+
+ print_stop_reason (SIGNAL_EXITED, stop_signal);
singlestep_breakpoints_inserted_p = 0; /*SOFTWARE_SINGLE_STEP_P */
stop_stepping (ecs);
return;
stop_pc = read_pc_pid (ecs->pid);
ecs->saved_inferior_pid = inferior_pid;
inferior_pid = ecs->pid;
- stop_bpstat = bpstat_stop_status
- (&stop_pc,
- (DECR_PC_AFTER_BREAK ?
- (prev_pc != stop_pc - DECR_PC_AFTER_BREAK
- && currently_stepping (ecs))
- : 0)
- );
+ stop_bpstat = bpstat_stop_status (&stop_pc, currently_stepping (ecs));
ecs->random_signal = !bpstat_explains_signal (stop_bpstat);
inferior_pid = ecs->saved_inferior_pid;
goto process_event_stop_test;
}
stop_pc = read_pc ();
- stop_bpstat = bpstat_stop_status
- (&stop_pc,
- (DECR_PC_AFTER_BREAK ?
- (prev_pc != stop_pc - DECR_PC_AFTER_BREAK
- && currently_stepping (ecs))
- : 0)
- );
+ stop_bpstat = bpstat_stop_status (&stop_pc, currently_stepping (ecs));
ecs->random_signal = !bpstat_explains_signal (stop_bpstat);
goto process_event_stop_test;
/* This causes the eventpoints and symbol table to be reset. Must
do this now, before trying to determine whether to stop. */
follow_exec (inferior_pid, pending_follow.execd_pathname);
- free (pending_follow.execd_pathname);
+ xfree (pending_follow.execd_pathname);
stop_pc = read_pc_pid (ecs->pid);
ecs->saved_inferior_pid = inferior_pid;
inferior_pid = ecs->pid;
- stop_bpstat = bpstat_stop_status
- (&stop_pc,
- (DECR_PC_AFTER_BREAK ?
- (prev_pc != stop_pc - DECR_PC_AFTER_BREAK
- && currently_stepping (ecs))
- : 0)
- );
+ stop_bpstat = bpstat_stop_status (&stop_pc, currently_stepping (ecs));
ecs->random_signal = !bpstat_explains_signal (stop_bpstat);
inferior_pid = ecs->saved_inferior_pid;
goto process_event_stop_test;
case TARGET_WAITKIND_STOPPED:
stop_signal = ecs->ws.value.sig;
break;
+
+ /* We had an event in the inferior, but we are not interested
+ in handling it at this level. The lower layers have already
+ done what needs to be done, if anything. This case can
+ occur only when the target is async or extended-async. One
+ of the circumstamces for this to happen is when the
+ inferior produces output for the console. The inferior has
+ not stopped, and we are ignoring the event. */
+ case TARGET_WAITKIND_IGNORE:
+ ecs->wait_some_more = 1;
+ return;
}
/* We may want to consider not doing a resume here in order to give
/* It's a SIGTRAP or a signal we're interested in. Switch threads,
and fall into the rest of wait_for_inferior(). */
- /* Save infrun state for the old thread. */
- save_infrun_state (inferior_pid, prev_pc,
- prev_func_start, prev_func_name,
- trap_expected, step_resume_breakpoint,
- through_sigtramp_breakpoint,
- step_range_start, step_range_end,
- step_frame_address, ecs->handling_longjmp,
- ecs->another_trap,
- ecs->stepping_through_solib_after_catch,
- ecs->stepping_through_solib_catchpoints,
- ecs->stepping_through_sigtramp);
-
- if (may_switch_from_inferior_pid)
- switched_from_inferior_pid = inferior_pid;
+ /* Caution: it may happen that the new thread (or the old one!)
+ is not in the thread list. In this case we must not attempt
+ to "switch context", or we run the risk that our context may
+ be lost. This may happen as a result of the target module
+ mishandling thread creation. */
+
+ if (in_thread_list (inferior_pid) && in_thread_list (ecs->pid))
+ { /* Perform infrun state context switch: */
+ /* Save infrun state for the old thread. */
+ save_infrun_state (inferior_pid, prev_pc,
+ prev_func_start, prev_func_name,
+ trap_expected, step_resume_breakpoint,
+ through_sigtramp_breakpoint,
+ step_range_start, step_range_end,
+ step_frame_address, ecs->handling_longjmp,
+ ecs->another_trap,
+ ecs->stepping_through_solib_after_catch,
+ ecs->stepping_through_solib_catchpoints,
+ ecs->stepping_through_sigtramp);
+
+ /* Load infrun state for the new thread. */
+ load_infrun_state (ecs->pid, &prev_pc,
+ &prev_func_start, &prev_func_name,
+ &trap_expected, &step_resume_breakpoint,
+ &through_sigtramp_breakpoint,
+ &step_range_start, &step_range_end,
+ &step_frame_address, &ecs->handling_longjmp,
+ &ecs->another_trap,
+ &ecs->stepping_through_solib_after_catch,
+ &ecs->stepping_through_solib_catchpoints,
+ &ecs->stepping_through_sigtramp);
+ }
inferior_pid = ecs->pid;
- /* Load infrun state for the new thread. */
- load_infrun_state (inferior_pid, &prev_pc,
- &prev_func_start, &prev_func_name,
- &trap_expected, &step_resume_breakpoint,
- &through_sigtramp_breakpoint,
- &step_range_start, &step_range_end,
- &step_frame_address, &ecs->handling_longjmp,
- &ecs->another_trap,
- &ecs->stepping_through_solib_after_catch,
- &ecs->stepping_through_solib_catchpoints,
- &ecs->stepping_through_sigtramp);
-
if (context_hook)
context_hook (pid_to_thread_id (ecs->pid));
- printf_filtered ("[Switching to %s]\n", target_pid_to_str (ecs->pid));
flush_cached_frames ();
}
/* See if there is a breakpoint at the current PC. */
stop_bpstat = bpstat_stop_status
(&stop_pc,
- (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
- and 3) we didn't hit a breakpoint in a signal handler
- without an intervening stop in sigtramp, which is
- detected by a new stack pointer value below
- any usual function calling stack adjustments. */
+ /* Pass TRUE if our reason for stopping is something other
+ than hitting a breakpoint. We do this by checking that
+ 1) stepping is going on and 2) we didn't hit a breakpoint
+ in a signal handler without an intervening stop in
+ sigtramp, which is detected by a new stack pointer value
+ below any usual function calling stack adjustments. */
(currently_stepping (ecs)
- && prev_pc != stop_pc - DECR_PC_AFTER_BREAK
&& !(step_range_end
- && INNER_THAN (read_sp (), (step_sp - 16)))) :
- 0)
+ && INNER_THAN (read_sp (), (step_sp - 16))))
);
/* Following in case break condition called a
function. */
{
trap_expected = 1;
stop_signal = TARGET_SIGNAL_0;
- goto keep_going;
+ keep_going (ecs);
+ return;
}
}
else if (ecs->ws.kind == TARGET_WAITKIND_VFORKED)
{
if (ecs->random_signal) /* I.e., no catchpoint triggered for this. */
{
- stop_signal = TARGET_SIGNAL_0;
- goto keep_going;
+ stop_signal = TARGET_SIGNAL_0;
+ keep_going (ecs);
+ return;
}
}
else if (ecs->ws.kind == TARGET_WAITKIND_EXECD)
{
trap_expected = 1;
stop_signal = TARGET_SIGNAL_0;
- goto keep_going;
+ keep_going (ecs);
+ return;
}
}
{
printed = 1;
target_terminal_ours_for_output ();
- annotate_signal ();
- printf_filtered ("\nProgram received signal ");
- annotate_signal_name ();
- printf_filtered ("%s", target_signal_to_name (stop_signal));
- annotate_signal_name_end ();
- printf_filtered (", ");
- annotate_signal_string ();
- printf_filtered ("%s", target_signal_to_string (stop_signal));
- annotate_signal_string_end ();
- printf_filtered (".\n");
- gdb_flush (gdb_stdout);
+ print_stop_reason (SIGNAL_RECEIVED, stop_signal);
}
if (signal_stop[stop_signal])
{
that case, when we reach this point, there is already a
step-resume breakpoint established, right where it should be:
immediately after the function call the user is "next"-ing
- over. If we jump to step_over_function now, two bad things
+ over. If we call step_over_function now, two bad things
happen:
- we'll create a new breakpoint, at wherever the current
the HP-UX maintainer to furnish a fix that doesn't break other
platforms. --JimB, 20 May 1999 */
check_sigtramp2 (ecs);
+ keep_going (ecs);
+ return;
}
/* Handle cases caused by hitting a breakpoint. */
remove_breakpoints ();
breakpoints_inserted = 0;
if (!GET_LONGJMP_TARGET (&jmp_buf_pc))
- goto keep_going;
+ {
+ keep_going (ecs);
+ return;
+ }
/* Need to blow away step-resume breakpoint, as it
interferes with us */
#endif /* 0 */
set_longjmp_resume_breakpoint (jmp_buf_pc, NULL);
ecs->handling_longjmp = 1; /* FIXME */
- goto keep_going;
+ keep_going (ecs);
+ return;
case BPSTAT_WHAT_CLEAR_LONGJMP_RESUME:
case BPSTAT_WHAT_CLEAR_LONGJMP_RESUME_SINGLE:
step_frame_address)))
{
ecs->another_trap = 1;
- goto keep_going;
+ keep_going (ecs);
+ return;
}
#endif /* 0 */
disable_longjmp_breakpoint ();
dynamically loaded objects (among other things). */
if (stop_on_solib_events)
{
- stop_print_frame = 0;
stop_stepping (ecs);
return;
}
if (SOLIB_IN_DYNAMIC_LINKER (ecs->pid, stop_pc))
{
ecs->another_trap = 1;
- goto keep_going;
+ keep_going (ecs);
+ return;
}
#endif
/* Else, stop and report the catchpoint(s) whose triggering
/* I'm not sure whether this needs to be check_sigtramp2 or
whether it could/should be keep_going. */
check_sigtramp2 (ecs);
- goto keep_going;
+ keep_going (ecs);
+ return;
}
if (step_range_end == 0)
/* I'm not sure whether this needs to be check_sigtramp2 or
whether it could/should be keep_going. */
check_sigtramp2 (ecs);
- goto keep_going;
+ keep_going (ecs);
+ return;
}
/* If stepping through a line, keep going if still within it.
/* We might be doing a BPSTAT_WHAT_SINGLE and getting a signal.
So definately need to check for sigtramp here. */
check_sigtramp2 (ecs);
- goto keep_going;
+ keep_going (ecs);
+ return;
}
/* We stepped out of the stepping range. */
loader dynamic symbol resolution code, we keep on single stepping
until we exit the run time loader code and reach the callee's
address. */
- if (step_over_calls < 0 && IN_SOLIB_DYNSYM_RESOLVE_CODE (stop_pc))
- goto keep_going;
+ if (step_over_calls == STEP_OVER_UNDEBUGGABLE && IN_SOLIB_DYNSYM_RESOLVE_CODE (stop_pc))
+ {
+ CORE_ADDR pc_after_resolver = SKIP_SOLIB_RESOLVER (stop_pc);
+
+ if (pc_after_resolver)
+ {
+ /* Set up a step-resume breakpoint at the address
+ indicated by SKIP_SOLIB_RESOLVER. */
+ struct symtab_and_line sr_sal;
+ INIT_SAL (&sr_sal);
+ sr_sal.pc = pc_after_resolver;
+
+ check_for_old_step_resume_breakpoint ();
+ step_resume_breakpoint =
+ set_momentary_breakpoint (sr_sal, NULL, bp_step_resume);
+ if (breakpoints_inserted)
+ insert_breakpoints ();
+ }
+
+ keep_going (ecs);
+ return;
+ }
/* We can't update step_sp every time through the loop, because
reading the stack pointer would slow down stepping too much.
/* We just stepped out of a signal handler and into
its calling trampoline.
- Normally, we'd jump to step_over_function from
+ Normally, we'd call step_over_function from
here, but for some reason GDB can't unwind the
stack correctly to find the real PC for the point
user code where the signal trampoline will return
step_range_end = (step_range_start = prev_pc) + 1;
ecs->remove_breakpoints_on_following_step = 1;
- goto keep_going;
+ keep_going (ecs);
+ return;
}
if (stop_pc == ecs->stop_func_start /* Quick test */
{
/* It's a subroutine call. */
- if (step_over_calls == 0)
+ if (step_over_calls == STEP_OVER_NONE)
{
/* 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;
+ print_stop_reason (END_STEPPING_RANGE, 0);
stop_stepping (ecs);
return;
}
- if (step_over_calls > 0 || IGNORE_HELPER_CALL (stop_pc))
- /* We're doing a "next". */
- goto step_over_function;
+ if (step_over_calls == STEP_OVER_ALL || IGNORE_HELPER_CALL (stop_pc))
+ {
+ /* We're doing a "next". */
+
+ if (IN_SIGTRAMP (stop_pc, ecs->stop_func_name)
+ && INNER_THAN (step_frame_address, read_sp()))
+ /* We stepped out of a signal handler, and into its
+ calling trampoline. This is misdetected as a
+ subroutine call, but stepping over the signal
+ trampoline isn't such a bad idea. In order to do
+ that, we have to ignore the value in
+ step_frame_address, since that doesn't represent the
+ frame that'll reach when we return from the signal
+ trampoline. Otherwise we'll probably continue to the
+ end of the program. */
+ step_frame_address = 0;
+
+ step_over_function (ecs);
+ keep_going (ecs);
+ return;
+ }
/* If we are in a function call trampoline (a stub between
the calling routine and the real function), locate the real
step_resume_breakpoint =
set_momentary_breakpoint (xxx, NULL, bp_step_resume);
insert_breakpoints ();
- goto keep_going;
+ keep_going (ecs);
+ return;
}
}
tmp_sal = find_pc_line (ecs->stop_func_start, 0);
if (tmp_sal.line != 0)
- goto step_into_function;
- }
-
- step_over_function:
- /* A subroutine call has happened. */
- {
- /* We've just entered a callee, and we wish to resume until it
- returns to the caller. Setting a step_resume breakpoint on
- the return address will catch a return from the callee.
-
- However, if the callee is recursing, we want to be careful
- not to catch returns of those recursive calls, but only of
- THIS instance of the call.
-
- To do this, we set the step_resume bp's frame to our current
- caller's frame (step_frame_address, which is set by the "next"
- or "until" command, before execution begins). */
- struct symtab_and_line sr_sal;
-
- INIT_SAL (&sr_sal); /* initialize to zeros */
- sr_sal.pc =
- ADDR_BITS_REMOVE (SAVED_PC_AFTER_CALL (get_current_frame ()));
- sr_sal.section = find_pc_overlay (sr_sal.pc);
-
- check_for_old_step_resume_breakpoint ();
- step_resume_breakpoint =
- set_momentary_breakpoint (sr_sal, get_current_frame (),
- bp_step_resume);
-
- if (!IN_SOLIB_DYNSYM_RESOLVE_CODE (sr_sal.pc))
- step_resume_breakpoint->frame = step_frame_address;
-
- 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. */
- {
- struct symtab *s;
-
- s = find_pc_symtab (stop_pc);
- if (s && s->language != language_asm)
- ecs->stop_func_start = SKIP_PROLOGUE (ecs->stop_func_start);
+ {
+ step_into_function (ecs);
+ return;
+ }
}
- ecs->sal = find_pc_line (ecs->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 (if it is still
- within the function). Otherwise, just go to end of prologue. */
-#ifdef PROLOGUE_FIRSTLINE_OVERLAP
- /* no, don't either. It skips any code that's
- legitimately on the first line. */
-#else
- if (ecs->sal.end && ecs->sal.pc != ecs->stop_func_start && ecs->sal.end < ecs->stop_func_end)
- ecs->stop_func_start = ecs->sal.end;
-#endif
- if (ecs->stop_func_start == stop_pc)
+ /* If we have no line number and the step-stop-if-no-debug
+ is set, we stop the step so that the user has a chance to
+ switch in assembly mode. */
+ if (step_over_calls == STEP_OVER_UNDEBUGGABLE && step_stop_if_no_debug)
{
- /* We are already there: stop now. */
stop_step = 1;
+ print_stop_reason (END_STEPPING_RANGE, 0);
stop_stepping (ecs);
return;
}
- else
- /* Put the step-breakpoint there and go until there. */
- {
- struct symtab_and_line sr_sal;
- INIT_SAL (&sr_sal); /* initialize to zeroes */
- sr_sal.pc = ecs->stop_func_start;
- sr_sal.section = find_pc_overlay (ecs->stop_func_start);
- /* Do not specify what the fp should be when we stop
- since on some machines the prologue
- is where the new fp value is established. */
- check_for_old_step_resume_breakpoint ();
- step_resume_breakpoint =
- set_momentary_breakpoint (sr_sal, NULL, bp_step_resume);
- if (breakpoints_inserted)
- insert_breakpoints ();
+ step_over_function (ecs);
+ keep_going (ecs);
+ return;
- /* 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. */
/* It is stepi or nexti. We always want to stop stepping after
one instruction. */
stop_step = 1;
+ print_stop_reason (END_STEPPING_RANGE, 0);
stop_stepping (ecs);
return;
}
/* Restart without fiddling with the step ranges or
other state. */
- goto keep_going;
+ keep_going (ecs);
+ return;
}
}
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;
+ print_stop_reason (END_STEPPING_RANGE, 0);
stop_stepping (ecs);
return;
}
That is said to make things like for (;;) statements work
better. */
stop_step = 1;
+ print_stop_reason (END_STEPPING_RANGE, 0);
stop_stepping (ecs);
return;
}
in which after skipping the prologue we better stop even though
we will be in mid-line. */
stop_step = 1;
+ print_stop_reason (END_STEPPING_RANGE, 0);
stop_stepping (ecs);
return;
}
step_frame_address = current_frame;
}
- 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. */
-
- /* ??rehrauer: ttrace on HP-UX theoretically allows one to debug
- a vforked child beetween its creation and subsequent exit or
- call to exec(). However, I had big problems in this rather
- creaky exec engine, getting that to work. The fundamental
- problem is that I'm trying to debug two processes via an
- engine that only understands a single process with possibly
- multiple threads.
-
- Hence, this spot is known to have problems when
- target_can_follow_vfork_prior_to_exec returns 1. */
-
- /* Save the pc before execution, to compare with pc after stop. */
- prev_pc = read_pc (); /* Might have been DECR_AFTER_BREAK */
- prev_func_start = ecs->stop_func_start; /* Ok, since if DECR_PC_AFTER
- BREAK is defined, the
- original pc would not have
- been at the start of a
- function. */
- prev_func_name = ecs->stop_func_name;
-
- if (ecs->update_step_sp)
- step_sp = read_sp ();
- ecs->update_step_sp = 0;
-
- /* If we did not do break;, it means we should keep
- running the inferior and not return to debugger. */
-
- if (trap_expected && stop_signal != TARGET_SIGNAL_TRAP)
- {
- /* 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 (ecs), stop_signal);
- }
- else
- {
- /* 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. */
- /* 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
- && through_sigtramp_breakpoint == NULL
- && ecs->remove_breakpoints_on_following_step)
- {
- ecs->remove_breakpoints_on_following_step = 0;
- remove_breakpoints ();
- breakpoints_inserted = 0;
- }
- else if (!breakpoints_inserted &&
- (through_sigtramp_breakpoint != NULL || !ecs->another_trap))
- {
- breakpoints_failed = insert_breakpoints ();
- if (breakpoints_failed)
- {
- stop_stepping (ecs);
- return;
- }
- breakpoints_inserted = 1;
- }
-
- trap_expected = ecs->another_trap;
-
- /* Do not deliver SIGNAL_TRAP (except when the user
- explicitly specifies that such a signal should be
- delivered to the target program).
-
- Typically, this would occure when a user is debugging a
- target monitor on a simulator: the target monitor sets a
- breakpoint; the simulator encounters this break-point and
- halts the simulation handing control to GDB; GDB, noteing
- that the break-point isn't valid, returns control back to
- the simulator; the simulator then delivers the hardware
- equivalent of a SIGNAL_TRAP to the program being
- debugged. */
-
- if (stop_signal == TARGET_SIGNAL_TRAP
- && !signal_program[stop_signal])
- stop_signal = TARGET_SIGNAL_0;
-
-#ifdef SHIFT_INST_REGS
- /* I'm not sure when this following segment applies. I do know,
- now, that we shouldn't rewrite the regs when we were stopped
- by a random signal from the inferior process. */
- /* FIXME: Shouldn't this be based on the valid bit of the SXIP?
- (this is only used on the 88k). */
-
- if (!bpstat_explains_signal (stop_bpstat)
- && (stop_signal != TARGET_SIGNAL_CHLD)
- && !stopped_by_random_signal)
- SHIFT_INST_REGS ();
-#endif /* SHIFT_INST_REGS */
-
- resume (currently_stepping (ecs), stop_signal);
- }
-
- prepare_to_wait (ecs);
- return;
+ keep_going (ecs);
} /* extra brace, to preserve old indentation */
-
- stop_stepping (ecs);
}
/* Are we in the middle of stepping? */
}
}
+/* Subroutine call with source code we should not step over. Do step
+ to the first line of code in it. */
+
+static void
+step_into_function (struct execution_control_state *ecs)
+{
+ struct symtab *s;
+ struct symtab_and_line sr_sal;
+
+ s = find_pc_symtab (stop_pc);
+ if (s && s->language != language_asm)
+ ecs->stop_func_start = SKIP_PROLOGUE (ecs->stop_func_start);
+
+ ecs->sal = find_pc_line (ecs->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 (if it is still within the function).
+ Otherwise, just go to end of prologue. */
+#ifdef PROLOGUE_FIRSTLINE_OVERLAP
+ /* no, don't either. It skips any code that's legitimately on the
+ first line. */
+#else
+ if (ecs->sal.end
+ && ecs->sal.pc != ecs->stop_func_start
+ && ecs->sal.end < ecs->stop_func_end)
+ ecs->stop_func_start = ecs->sal.end;
+#endif
+
+ if (ecs->stop_func_start == stop_pc)
+ {
+ /* We are already there: stop now. */
+ stop_step = 1;
+ print_stop_reason (END_STEPPING_RANGE, 0);
+ stop_stepping (ecs);
+ return;
+ }
+ else
+ {
+ /* Put the step-breakpoint there and go until there. */
+ INIT_SAL (&sr_sal); /* initialize to zeroes */
+ sr_sal.pc = ecs->stop_func_start;
+ sr_sal.section = find_pc_overlay (ecs->stop_func_start);
+ /* Do not specify what the fp should be when we stop since on
+ some machines the prologue is where the new fp value is
+ established. */
+ check_for_old_step_resume_breakpoint ();
+ step_resume_breakpoint =
+ set_momentary_breakpoint (sr_sal, NULL, bp_step_resume);
+ if (breakpoints_inserted)
+ insert_breakpoints ();
+
+ /* And make sure stepping stops right away then. */
+ step_range_end = step_range_start;
+ }
+ keep_going (ecs);
+}
+
+/* We've just entered a callee, and we wish to resume until it returns
+ to the caller. Setting a step_resume breakpoint on the return
+ address will catch a return from the callee.
+
+ However, if the callee is recursing, we want to be careful not to
+ catch returns of those recursive calls, but only of THIS instance
+ of the call.
+
+ To do this, we set the step_resume bp's frame to our current
+ caller's frame (step_frame_address, which is set by the "next" or
+ "until" command, before execution begins). */
+
+static void
+step_over_function (struct execution_control_state *ecs)
+{
+ struct symtab_and_line sr_sal;
+
+ INIT_SAL (&sr_sal); /* initialize to zeros */
+ sr_sal.pc = ADDR_BITS_REMOVE (SAVED_PC_AFTER_CALL (get_current_frame ()));
+ sr_sal.section = find_pc_overlay (sr_sal.pc);
+
+ check_for_old_step_resume_breakpoint ();
+ step_resume_breakpoint =
+ set_momentary_breakpoint (sr_sal, get_current_frame (), bp_step_resume);
+
+ if (step_frame_address && !IN_SOLIB_DYNSYM_RESOLVE_CODE (sr_sal.pc))
+ step_resume_breakpoint->frame = step_frame_address;
+
+ if (breakpoints_inserted)
+ insert_breakpoints ();
+}
+
static void
stop_stepping (struct execution_control_state *ecs)
{
ecs->wait_some_more = 0;
}
+/* This function handles various cases where we need to continue
+ waiting for the inferior. */
+/* (Used to be the keep_going: label in the old wait_for_inferior) */
+
+static void
+keep_going (struct execution_control_state *ecs)
+{
+ /* ??rehrauer: ttrace on HP-UX theoretically allows one to debug a
+ vforked child between its creation and subsequent exit or call to
+ exec(). However, I had big problems in this rather creaky exec
+ engine, getting that to work. The fundamental problem is that
+ I'm trying to debug two processes via an engine that only
+ understands a single process with possibly multiple threads.
+
+ Hence, this spot is known to have problems when
+ target_can_follow_vfork_prior_to_exec returns 1. */
+
+ /* Save the pc before execution, to compare with pc after stop. */
+ prev_pc = read_pc (); /* Might have been DECR_AFTER_BREAK */
+ prev_func_start = ecs->stop_func_start; /* Ok, since if DECR_PC_AFTER
+ BREAK is defined, the
+ original pc would not have
+ been at the start of a
+ function. */
+ prev_func_name = ecs->stop_func_name;
+
+ if (ecs->update_step_sp)
+ step_sp = read_sp ();
+ ecs->update_step_sp = 0;
+
+ /* If we did not do break;, it means we should keep running the
+ inferior and not return to debugger. */
+
+ if (trap_expected && stop_signal != TARGET_SIGNAL_TRAP)
+ {
+ /* 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 (ecs), stop_signal);
+ }
+ else
+ {
+ /* 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. */
+ /* 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
+ && through_sigtramp_breakpoint == NULL
+ && ecs->remove_breakpoints_on_following_step)
+ {
+ ecs->remove_breakpoints_on_following_step = 0;
+ remove_breakpoints ();
+ breakpoints_inserted = 0;
+ }
+ else if (!breakpoints_inserted &&
+ (through_sigtramp_breakpoint != NULL || !ecs->another_trap))
+ {
+ breakpoints_failed = insert_breakpoints ();
+ if (breakpoints_failed)
+ {
+ stop_stepping (ecs);
+ return;
+ }
+ breakpoints_inserted = 1;
+ }
+
+ trap_expected = ecs->another_trap;
+
+ /* Do not deliver SIGNAL_TRAP (except when the user explicitly
+ specifies that such a signal should be delivered to the
+ target program).
+
+ Typically, this would occure when a user is debugging a
+ target monitor on a simulator: the target monitor sets a
+ breakpoint; the simulator encounters this break-point and
+ halts the simulation handing control to GDB; GDB, noteing
+ that the break-point isn't valid, returns control back to the
+ simulator; the simulator then delivers the hardware
+ equivalent of a SIGNAL_TRAP to the program being debugged. */
+
+ if (stop_signal == TARGET_SIGNAL_TRAP
+ && !signal_program[stop_signal])
+ stop_signal = TARGET_SIGNAL_0;
+
+#ifdef SHIFT_INST_REGS
+ /* I'm not sure when this following segment applies. I do know,
+ now, that we shouldn't rewrite the regs when we were stopped
+ by a random signal from the inferior process. */
+ /* FIXME: Shouldn't this be based on the valid bit of the SXIP?
+ (this is only used on the 88k). */
+
+ if (!bpstat_explains_signal (stop_bpstat)
+ && (stop_signal != TARGET_SIGNAL_CHLD)
+ && !stopped_by_random_signal)
+ SHIFT_INST_REGS ();
+#endif /* SHIFT_INST_REGS */
+
+ resume (currently_stepping (ecs), stop_signal);
+ }
+
+ prepare_to_wait (ecs);
+}
+
/* This function normally comes after a resume, before
handle_inferior_event exits. It takes care of any last bits of
housekeeping, and sets the all-important wait_some_more flag. */
ecs->wait_some_more = 1;
}
-/* This function returns TRUE if ep is an internal breakpoint
- set to catch generic shared library (aka dynamically-linked
- library) events. (This is *NOT* the same as a catchpoint for a
- shlib event. The latter is something a user can set; this is
- something gdb sets for its own use, and isn't ever shown to a
- user.) */
-static int
-is_internal_shlib_eventpoint (struct breakpoint *ep)
-{
- return
- (ep->type == bp_shlib_event)
- ;
-}
-
-/* This function returns TRUE if bs indicates that the inferior
- stopped due to a shared library (aka dynamically-linked library)
- event. */
-
-static int
-stopped_for_internal_shlib_event (bpstat bs)
-{
- /* Note that multiple eventpoints may've caused the stop. Any
- that are associated with shlib events will be accepted. */
- for (; bs != NULL; bs = bs->next)
- {
- if ((bs->breakpoint_at != NULL)
- && is_internal_shlib_eventpoint (bs->breakpoint_at))
- return 1;
- }
-
- /* If we get here, then no candidate was found. */
- return 0;
-}
-\f
-/* Reset proper settings after an asynchronous command has finished.
- If the execution command was in synchronous mode, register stdin
- with the event loop, and reset the prompt. */
-
+/* Print why the inferior has stopped. We always print something when
+ the inferior exits, or receives a signal. The rest of the cases are
+ dealt with later on in normal_stop() and print_it_typical(). Ideally
+ there should be a call to this function from handle_inferior_event()
+ each time stop_stepping() is called.*/
static void
-complete_execution (void)
+print_stop_reason (enum inferior_stop_reason stop_reason, int stop_info)
{
- extern int cleanup_sigint_signal_handler (void);
-
- target_executing = 0;
- if (sync_execution)
- {
- add_file_handler (input_fd, call_readline, 0);
- pop_prompt ();
- sync_execution = 0;
- cleanup_sigint_signal_handler ();
- display_gdb_prompt (0);
- }
- else
+ switch (stop_reason)
{
- if (exec_done_display_p)
- printf_unfiltered ("completed.\n");
+ case STOP_UNKNOWN:
+ /* We don't deal with these cases from handle_inferior_event()
+ yet. */
+ break;
+ case END_STEPPING_RANGE:
+ /* We are done with a step/next/si/ni command. */
+ /* For now print nothing. */
+#ifdef UI_OUT
+ /* Print a message only if not in the middle of doing a "step n"
+ operation for n > 1 */
+ if (!step_multi || !stop_step)
+ if (interpreter_p && strcmp (interpreter_p, "mi") == 0)
+ ui_out_field_string (uiout, "reason", "end-stepping-range");
+#endif
+ break;
+ case BREAKPOINT_HIT:
+ /* We found a breakpoint. */
+ /* For now print nothing. */
+ break;
+ case SIGNAL_EXITED:
+ /* The inferior was terminated by a signal. */
+#ifdef UI_OUT
+ annotate_signalled ();
+ if (interpreter_p && strcmp (interpreter_p, "mi") == 0)
+ ui_out_field_string (uiout, "reason", "exited-signalled");
+ ui_out_text (uiout, "\nProgram terminated with signal ");
+ annotate_signal_name ();
+ ui_out_field_string (uiout, "signal-name", target_signal_to_name (stop_info));
+ annotate_signal_name_end ();
+ ui_out_text (uiout, ", ");
+ annotate_signal_string ();
+ ui_out_field_string (uiout, "signal-meaning", target_signal_to_string (stop_info));
+ annotate_signal_string_end ();
+ ui_out_text (uiout, ".\n");
+ ui_out_text (uiout, "The program no longer exists.\n");
+#else
+ annotate_signalled ();
+ printf_filtered ("\nProgram terminated with signal ");
+ annotate_signal_name ();
+ printf_filtered ("%s", target_signal_to_name (stop_info));
+ annotate_signal_name_end ();
+ printf_filtered (", ");
+ annotate_signal_string ();
+ printf_filtered ("%s", target_signal_to_string (stop_info));
+ annotate_signal_string_end ();
+ printf_filtered (".\n");
+
+ printf_filtered ("The program no longer exists.\n");
+ gdb_flush (gdb_stdout);
+#endif
+ break;
+ case EXITED:
+ /* The inferior program is finished. */
+#ifdef UI_OUT
+ annotate_exited (stop_info);
+ if (stop_info)
+ {
+ if (interpreter_p && strcmp (interpreter_p, "mi") == 0)
+ ui_out_field_string (uiout, "reason", "exited");
+ ui_out_text (uiout, "\nProgram exited with code ");
+ ui_out_field_fmt (uiout, "exit-code", "0%o", (unsigned int) stop_info);
+ ui_out_text (uiout, ".\n");
+ }
+ else
+ {
+ if (interpreter_p && strcmp (interpreter_p, "mi") == 0)
+ ui_out_field_string (uiout, "reason", "exited-normally");
+ ui_out_text (uiout, "\nProgram exited normally.\n");
+ }
+#else
+ annotate_exited (stop_info);
+ if (stop_info)
+ printf_filtered ("\nProgram exited with code 0%o.\n",
+ (unsigned int) stop_info);
+ else
+ printf_filtered ("\nProgram exited normally.\n");
+#endif
+ break;
+ case SIGNAL_RECEIVED:
+ /* Signal received. The signal table tells us to print about
+ it. */
+#ifdef UI_OUT
+ annotate_signal ();
+ ui_out_text (uiout, "\nProgram received signal ");
+ annotate_signal_name ();
+ ui_out_field_string (uiout, "signal-name", target_signal_to_name (stop_info));
+ annotate_signal_name_end ();
+ ui_out_text (uiout, ", ");
+ annotate_signal_string ();
+ ui_out_field_string (uiout, "signal-meaning", target_signal_to_string (stop_info));
+ annotate_signal_string_end ();
+ ui_out_text (uiout, ".\n");
+#else
+ annotate_signal ();
+ printf_filtered ("\nProgram received signal ");
+ annotate_signal_name ();
+ printf_filtered ("%s", target_signal_to_name (stop_info));
+ annotate_signal_name_end ();
+ printf_filtered (", ");
+ annotate_signal_string ();
+ printf_filtered ("%s", target_signal_to_string (stop_info));
+ annotate_signal_string_end ();
+ printf_filtered (".\n");
+ gdb_flush (gdb_stdout);
+#endif
+ break;
+ default:
+ internal_error ("print_stop_reason: unrecognized enum value");
+ break;
}
}
+\f
/* Here to return control to GDB when the inferior stops for real.
Print appropriate messages, remove breakpoints, give terminal our modes.
(Note that there's no point in saying anything if the inferior
has exited!) */
- if (may_switch_from_inferior_pid
- && (switched_from_inferior_pid != inferior_pid)
+ if ((previous_inferior_pid != inferior_pid)
&& target_has_execution)
{
target_terminal_ours_for_output ();
- printf_filtered ("[Switched to %s]\n",
+ printf_filtered ("[Switching to %s]\n",
target_pid_or_tid_to_str (inferior_pid));
- switched_from_inferior_pid = inferior_pid;
+ previous_inferior_pid = inferior_pid;
}
/* Make sure that the current_frame's pc is correct. This
if (breakpoints_failed)
{
target_terminal_ours_for_output ();
- print_sys_errmsg ("ptrace", breakpoints_failed);
+ print_sys_errmsg ("While inserting breakpoints", breakpoints_failed);
printf_filtered ("Stopped; cannot insert breakpoints.\n\
-The same program may be running in another process.\n");
+The same program may be running in another process,\n\
+or you may have requested too many hardware breakpoints\n\
+and/or watchpoints.\n");
}
if (target_has_execution && breakpoints_inserted)
target_terminal_ours ();
- /* Did we stop because the user set the stop_on_solib_events
- variable? (If so, we report this as a generic, "Stopped due
- to shlib event" message.) */
- if (stopped_for_internal_shlib_event (stop_bpstat))
- {
- printf_filtered ("Stopped due to shared library event\n");
- }
-
/* Look up the hook_stop and run it if it exists. */
- if (stop_command && stop_command->hook)
+ if (stop_command && stop_command->hook_pre)
{
- catch_errors (hook_stop_stub, stop_command->hook,
+ catch_errors (hook_stop_stub, stop_command->hook_pre,
"Error while running hook_stop:\n", RETURN_MASK_ALL);
}
bpstat_print() contains the logic deciding in detail
what to print, based on the event(s) that just occurred. */
- if (stop_print_frame)
+ if (stop_print_frame
+ && selected_frame)
{
int bpstat_ret;
int source_flag;
+ int do_frame_printing = 1;
bpstat_ret = bpstat_print (stop_bpstat);
- /* bpstat_print() returned one of:
- -1: Didn't print anything
- 0: Printed preliminary "Breakpoint n, " message, desires
- location tacked on
- 1: Printed something, don't tack on location */
-
- if (bpstat_ret == -1)
- if (stop_step
- && step_frame_address == FRAME_FP (get_current_frame ())
- && step_start_function == find_pc_function (stop_pc))
- source_flag = -1; /* finished step, just print source line */
- else
- source_flag = 1; /* print location and source line */
- else if (bpstat_ret == 0) /* hit bpt, desire location */
- source_flag = 1; /* print location and source line */
- else /* bpstat_ret == 1, hit bpt, do not desire location */
- source_flag = -1; /* just print source line */
+ switch (bpstat_ret)
+ {
+ case PRINT_UNKNOWN:
+ if (stop_step
+ && step_frame_address == FRAME_FP (get_current_frame ())
+ && step_start_function == find_pc_function (stop_pc))
+ source_flag = SRC_LINE; /* finished step, just print source line */
+ else
+ source_flag = SRC_AND_LOC; /* print location and source line */
+ break;
+ case PRINT_SRC_AND_LOC:
+ source_flag = SRC_AND_LOC; /* print location and source line */
+ break;
+ case PRINT_SRC_ONLY:
+ source_flag = SRC_LINE;
+ break;
+ case PRINT_NOTHING:
+ do_frame_printing = 0;
+ break;
+ default:
+ internal_error ("Unknown value.");
+ }
+#ifdef UI_OUT
+ /* For mi, have the same behavior every time we stop:
+ print everything but the source line. */
+ if (interpreter_p && strcmp (interpreter_p, "mi") == 0)
+ source_flag = LOC_AND_ADDRESS;
+#endif
+#ifdef UI_OUT
+ if (interpreter_p && strcmp (interpreter_p, "mi") == 0)
+ ui_out_field_int (uiout, "thread-id", pid_to_thread_id (inferior_pid));
+#endif
/* The behavior of this routine with respect to the source
flag is:
- -1: Print only source line
- 0: Print only location
- 1: Print location and source line */
- show_and_print_stack_frame (selected_frame, -1, source_flag);
+ SRC_LINE: Print only source line
+ LOCATION: Print only location
+ SRC_AND_LOC: Print location and source line */
+ if (do_frame_printing)
+ show_and_print_stack_frame (selected_frame, -1, source_flag);
/* Display the auto-display expressions. */
do_displays ();
return signal_program[signo];
}
+int signal_stop_update (signo, state)
+ int signo;
+ int state;
+{
+ int ret = signal_stop[signo];
+ signal_stop[signo] = state;
+ return ret;
+}
+
+int signal_print_update (signo, state)
+ int signo;
+ int state;
+{
+ int ret = signal_print[signo];
+ signal_print[signo] = state;
+ return ret;
+}
+
+int signal_pass_update (signo, state)
+ int signo;
+ int state;
+{
+ int ret = signal_program[signo];
+ signal_program[signo] = state;
+ return ret;
+}
+
static void
sig_print_header (void)
{
else
printf_filtered ("Invalid signal handling flag.\n");
if (argBuf)
- free (argBuf);
+ xfree (argBuf);
}
}
do_cleanups (old_chain);
{
/* No, try numeric. */
oursig =
- target_signal_from_command (parse_and_eval_address (signum_exp));
+ target_signal_from_command (parse_and_eval_long (signum_exp));
}
sig_print_info (oursig);
return;
CORE_ADDR step_range_start;
CORE_ADDR step_range_end;
CORE_ADDR step_frame_address;
- int step_over_calls;
+ enum step_over_calls_kind step_over_calls;
CORE_ADDR step_resume_break_address;
int stop_after_trap;
int stop_soon_quietly;
static void
free_inferior_status (struct inferior_status *inf_status)
{
- free (inf_status->registers);
- free (inf_status->stop_registers);
- free (inf_status);
+ xfree (inf_status->registers);
+ xfree (inf_status->stop_registers);
+ xfree (inf_status);
}
void
free_inferior_status (inf_status);
}
+static void
+do_restore_inferior_status_cleanup (void *sts)
+{
+ restore_inferior_status (sts);
+}
+
+struct cleanup *
+make_cleanup_restore_inferior_status (struct inferior_status *inf_status)
+{
+ return make_cleanup (do_restore_inferior_status_cleanup, inf_status);
+}
+
void
discard_inferior_status (struct inferior_status *inf_status)
{
free_inferior_status (inf_status);
}
-static void
-set_follow_fork_mode_command (char *arg, int from_tty,
- struct cmd_list_element *c)
-{
- if (!STREQ (arg, "parent") &&
- !STREQ (arg, "child") &&
- !STREQ (arg, "both") &&
- !STREQ (arg, "ask"))
- error ("follow-fork-mode must be one of \"parent\", \"child\", \"both\" or \"ask\".");
-
- if (follow_fork_mode_string != NULL)
- free (follow_fork_mode_string);
- follow_fork_mode_string = savestring (arg, strlen (arg));
-}
\f
static void
build_infrun (void)
c = add_set_enum_cmd ("follow-fork-mode",
class_run,
follow_fork_mode_kind_names,
- (char *) &follow_fork_mode_string,
+ &follow_fork_mode_string,
/* ??rehrauer: The "both" option is broken, by what may be a 10.20
kernel problem. It's also not terribly useful without a GUI to
help the user drive two debuggers. So for now, I'm disabling
/* c->function.sfunc = ; */
add_show_from_set (c, &showlist);
- set_follow_fork_mode_command ("parent", 0, NULL);
-
c = add_set_enum_cmd ("scheduler-locking", class_run,
scheduler_enums, /* array of string names */
- (char *) &scheduler_mode, /* current mode */
+ &scheduler_mode, /* current mode */
"Set mode for locking scheduler during execution.\n\
off == no locking (threads may preempt at any time)\n\
on == full locking (no thread except the current thread may run)\n\
c->function.sfunc = set_schedlock_func; /* traps on target vector */
add_show_from_set (c, &showlist);
+
+ c = add_set_cmd ("step-mode", class_run,
+ var_boolean, (char*) &step_stop_if_no_debug,
+"Set mode of the step operation. When set, doing a step over a\n\
+function without debug line information will stop at the first\n\
+instruction of that function. Otherwise, the function is skipped and\n\
+the step command stops at a different source line.",
+ &setlist);
+ add_show_from_set (c, &showlist);
}