/* 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"
static void sig_print_header (void);
-static void resume_cleanups (int);
+static void resume_cleanups (void *);
static int hook_stop_stub (void *);
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;
#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
/* 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 (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
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;
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;
/* Always go on waiting for the target, regardless of the mode. */
/* FIXME: cagney/1999-09-23: At present it isn't possible to
- indicate th wait_for_inferior that a target should timeout if
+ 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
struct execution_control_state *async_ecs;
void
-fetch_inferior_event (client_data)
- void *client_data;
+fetch_inferior_event (void *client_data)
{
static struct cleanup *old_cleanups;
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
stop_signal = ecs->ws.value.sig;
target_terminal_ours (); /* Must do this before mourn anyway */
- /* 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 */
+ /* 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 */
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. */
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))
+ if (step_over_calls == STEP_OVER_UNDEBUGGABLE && IN_SOLIB_DYNSYM_RESOLVE_CODE (stop_pc))
{
CORE_ADDR pc_after_resolver = SKIP_SOLIB_RESOLVER (stop_pc);
{
/* 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
return;
}
- if (step_over_calls > 0 || IGNORE_HELPER_CALL (stop_pc))
+ 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;
return;
}
}
+
+ /* If we have no line number and the step-stop-if-no-debug
+ is set, we stop the step so that the user has a chance to
+ switch in assembly mode. */
+ if (step_over_calls == STEP_OVER_UNDEBUGGABLE && step_stop_if_no_debug)
+ {
+ stop_step = 1;
+ print_stop_reason (END_STEPPING_RANGE, 0);
+ stop_stepping (ecs);
+ return;
+ }
+
step_over_function (ecs);
keep_going (ecs);
return;
step_resume_breakpoint =
set_momentary_breakpoint (sr_sal, get_current_frame (), bp_step_resume);
- if (!IN_SOLIB_DYNSYM_RESOLVE_CODE (sr_sal.pc))
+ if (step_frame_address && !IN_SOLIB_DYNSYM_RESOLVE_CODE (sr_sal.pc))
step_resume_breakpoint->frame = step_frame_address;
if (breakpoints_inserted)
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. */
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 ("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 ();
annotate_signal_string_end ();
printf_filtered (".\n");
gdb_flush (gdb_stdout);
+#endif
break;
default:
internal_error ("print_stop_reason: unrecognized enum value");
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)
/* 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;
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 */
+ source_flag = SRC_LINE; /* finished step, just print source line */
else
- source_flag = 1; /* print location and source line */
+ source_flag = SRC_AND_LOC; /* print location and source line */
break;
case PRINT_SRC_AND_LOC:
- source_flag = 1; /* print location and source line */
+ source_flag = SRC_AND_LOC; /* print location and source line */
break;
case PRINT_SRC_ONLY:
- source_flag = -1;
+ source_flag = SRC_LINE;
break;
case PRINT_NOTHING:
do_frame_printing = 0;
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 */
+ 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);
{
/* 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;
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);
}