process.
Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995,
- 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
- Free Software Foundation, Inc.
+ 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
+ 2008 Free Software Foundation, Inc.
This file is part of GDB.
static struct cmd_list_element *stop_command;
-/* 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;
-/* Nonzero if we are expecting a trace trap and should proceed from it. */
+/* Nonzero if we are presently stepping over a breakpoint.
+
+ If we hit a breakpoint or watchpoint, and then continue,
+ we need to single step the current thread with breakpoints
+ disabled, to avoid hitting the same breakpoint or
+ watchpoint again. And we should step just a single
+ thread and keep other threads stopped, so that
+ other threads don't miss breakpoints while they are removed.
+
+ So, this variable simultaneously means that we need to single
+ step the current thread, keep other threads stopped, and that
+ breakpoints should be removed while we step.
-static int trap_expected;
+ This variable is set either:
+ - in proceed, when we resume inferior on user's explicit request
+ - in keep_going, if handle_inferior_event decides we need to
+ step over breakpoint.
+
+ The variable is cleared in clear_proceed_status, called every
+ time before we call proceed. The proceed calls wait_for_inferior,
+ which calls handle_inferior_event in a loop, and until
+ wait_for_inferior exits, this variable is changed only by keep_going. */
+
+static int stepping_over_breakpoint;
/* Nonzero if we want to give control to the user when we're notified
of shared library events by the dynamic linker. */
static ptid_t saved_singlestep_ptid;
static int stepping_past_singlestep_breakpoint;
-/* Similarly, if we are stepping another thread past a breakpoint,
- save the original thread here so that we can resume stepping it later. */
-static ptid_t stepping_past_breakpoint_ptid;
-static int stepping_past_breakpoint;
+/* If not equal to null_ptid, this means that after stepping over breakpoint
+ is finished, we need to switch to deferred_step_ptid, and step it.
+
+ The use case is when one thread has hit a breakpoint, and then the user
+ has switched to another thread and issued 'step'. We need to step over
+ breakpoint in the thread which hit the breakpoint, but then continue
+ stepping the thread user has selected. */
+static ptid_t deferred_step_ptid;
\f
/* Things to clean up if we QUIT out of resume (). */
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)
+ if (CANNOT_STEP_HW_WATCHPOINTS && step)
remove_hw_watchpoints ();
/* Most targets can step a breakpoint instruction, thus
executing it normally. But if this one cannot, just
continue and we will hit it anyway. */
- if (step && breakpoints_inserted && breakpoint_here_p (read_pc ()))
+ if (step && breakpoint_inserted_here_p (read_pc ()))
step = 0;
}
target_resume (resume_ptid, step, sig);
void
clear_proceed_status (void)
{
- trap_expected = 0;
+ stepping_over_breakpoint = 0;
step_range_start = 0;
step_range_end = 0;
step_frame_id = null_frame_id;
/* If stepping, remember current thread to switch back to. */
if (step)
{
- stepping_past_breakpoint = 1;
- stepping_past_breakpoint_ptid = inferior_ptid;
+ deferred_step_ptid = inferior_ptid;
}
/* Switch back to WAIT_PID thread. */
if (oneproc)
/* We will get a trace trap after one instruction.
Continue it automatically and insert breakpoints then. */
- trap_expected = 1;
+ stepping_over_breakpoint = 1;
else
- {
- insert_breakpoints ();
- /* If we get here there was no call to error() in
- insert breakpoints -- so they were inserted. */
- breakpoints_inserted = 1;
- }
+ insert_breakpoints ();
if (siggnal != TARGET_SIGNAL_DEFAULT)
stop_signal = siggnal;
init_thread_list ();
init_wait_for_inferior ();
stop_soon = STOP_QUIETLY_REMOTE;
- trap_expected = 0;
+ stepping_over_breakpoint = 0;
/* Always go on waiting for the target, regardless of the mode. */
/* FIXME: cagney/1999-09-23: At present it isn't possible to
/* These are meaningless until the first time through wait_for_inferior. */
prev_pc = 0;
- breakpoints_inserted = 0;
breakpoint_init_inferior (inf_starting);
/* Don't confuse first call to proceed(). */
clear_proceed_status ();
stepping_past_singlestep_breakpoint = 0;
- stepping_past_breakpoint = 0;
+ deferred_step_ptid = null_ptid;
+
+ target_last_wait_ptid = minus_one_ptid;
}
\f
/* This enum encodes possible reasons for doing a target_wait, so that
{
struct target_waitstatus ws;
struct target_waitstatus *wp;
- int another_trap;
+ /* Should we step over breakpoint next time keep_going
+ is called? */
+ int stepping_over_breakpoint;
int random_signal;
CORE_ADDR stop_func_start;
CORE_ADDR stop_func_end;
void
init_execution_control_state (struct execution_control_state *ecs)
{
- ecs->another_trap = 0;
+ ecs->stepping_over_breakpoint = 0;
ecs->random_signal = 0;
ecs->step_after_step_resume_breakpoint = 0;
ecs->handling_longjmp = 0; /* FIXME */
{ /* Perform infrun state context switch: */
/* Save infrun state for the old thread. */
save_infrun_state (inferior_ptid, prev_pc,
- trap_expected, step_resume_breakpoint,
+ stepping_over_breakpoint, step_resume_breakpoint,
step_range_start,
step_range_end, &step_frame_id,
- ecs->handling_longjmp, ecs->another_trap,
+ ecs->handling_longjmp, ecs->stepping_over_breakpoint,
ecs->stepping_through_solib_after_catch,
ecs->stepping_through_solib_catchpoints,
ecs->current_line, ecs->current_symtab);
/* Load infrun state for the new thread. */
load_infrun_state (ecs->ptid, &prev_pc,
- &trap_expected, &step_resume_breakpoint,
+ &stepping_over_breakpoint, &step_resume_breakpoint,
&step_range_start,
&step_range_end, &step_frame_id,
- &ecs->handling_longjmp, &ecs->another_trap,
+ &ecs->handling_longjmp, &ecs->stepping_over_breakpoint,
&ecs->stepping_through_solib_after_catch,
&ecs->stepping_through_solib_catchpoints,
&ecs->current_line, &ecs->current_symtab);
target_last_wait_ptid = ecs->ptid;
target_last_waitstatus = *ecs->wp;
+ /* Always clear state belonging to the previous time we stopped. */
+ stop_stack_dummy = 0;
+
adjust_pc_after_break (ecs);
switch (ecs->infwait_state)
add_thread (ecs->ptid);
ui_out_text (uiout, "[New ");
- ui_out_text (uiout, target_pid_or_tid_to_str (ecs->ptid));
+ ui_out_text (uiout, target_pid_to_str (ecs->ptid));
ui_out_text (uiout, "]\n");
}
established. */
if (stop_soon == NO_STOP_QUIETLY)
{
- int breakpoints_were_inserted;
-
/* Remove breakpoints, SOLIB_ADD might adjust
breakpoint addresses via breakpoint_re_set. */
- breakpoints_were_inserted = breakpoints_inserted;
- if (breakpoints_inserted)
- remove_breakpoints ();
- breakpoints_inserted = 0;
+ remove_breakpoints ();
/* Check for any newly added shared libraries if we're
supposed to be adding them automatically. Switch
for "catch load". */
/* Reinsert breakpoints and continue. */
- if (breakpoints_were_inserted)
- {
- insert_breakpoints ();
- breakpoints_inserted = 1;
- }
+ insert_breakpoints ();
}
/* If we are skipping through a shell, or through shared library
stepping_past_singlestep_breakpoint = 0;
- if (stepping_past_breakpoint)
+ if (!ptid_equal (deferred_step_ptid, null_ptid))
{
- stepping_past_breakpoint = 0;
-
/* If we stopped for some other reason than single-stepping, ignore
the fact that we were supposed to switch back. */
if (stop_signal == TARGET_SIGNAL_TRAP)
{
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog,
- "infrun: stepping_past_breakpoint\n");
+ "infrun: handling deferred step\n");
/* Pull the single step breakpoints out of the target. */
if (singlestep_breakpoints_inserted_p)
/* Note: We do not call context_switch at this point, as the
context is already set up for stepping the original thread. */
- switch_to_thread (stepping_past_breakpoint_ptid);
+ switch_to_thread (deferred_step_ptid);
+ deferred_step_ptid = null_ptid;
/* Suppress spurious "Switching to ..." message. */
previous_inferior_ptid = inferior_ptid;
prepare_to_wait (ecs);
return;
}
+
+ deferred_step_ptid = null_ptid;
}
/* See if a thread hit a thread-specific breakpoint that was meant for
/* Check if a regular breakpoint has been hit before checking
for a potential single step breakpoint. Otherwise, GDB will
not see this breakpoint hit when stepping onto breakpoints. */
- if (breakpoints_inserted && breakpoint_here_p (stop_pc))
+ if (regular_breakpoint_inserted_here_p (stop_pc))
{
ecs->random_signal = 0;
if (!breakpoint_thread_match (stop_pc, ecs->ptid))
process until the child exits (well, okay, not
then either :-) or execs. */
if (remove_status != 0)
- {
- /* FIXME! This is obviously non-portable! */
- write_pc_pid (stop_pc + 4, ecs->ptid);
- /* We need to restart all the threads now,
- * unles we're running in scheduler-locked mode.
- * Use currently_stepping to determine whether to
- * step or continue.
- */
- /* FIXME MVS: is there any reason not to call resume()? */
- if (scheduler_mode == schedlock_on)
- target_resume (ecs->ptid,
- currently_stepping (ecs), TARGET_SIGNAL_0);
- else
- target_resume (RESUME_ALL,
- currently_stepping (ecs), TARGET_SIGNAL_0);
- prepare_to_wait (ecs);
- return;
- }
+ error (_("Cannot step over breakpoint hit in wrong thread"));
else
{ /* Single step */
- breakpoints_inserted = 0;
if (!ptid_equal (inferior_ptid, ecs->ptid))
context_switch (ecs);
ecs->waiton_ptid = ecs->ptid;
ecs->wp = &(ecs->ws);
- ecs->another_trap = 1;
+ ecs->stepping_over_breakpoint = 1;
ecs->infwait_state = infwait_thread_hop_state;
keep_going (ecs);
&ecs->stop_func_start, &ecs->stop_func_end);
ecs->stop_func_start
+= gdbarch_deprecated_function_start_offset (current_gdbarch);
- ecs->another_trap = 0;
+ ecs->stepping_over_breakpoint = 0;
bpstat_clear (&stop_bpstat);
stop_step = 0;
- stop_stack_dummy = 0;
stop_print_frame = 1;
ecs->random_signal = 0;
stopped_by_random_signal = 0;
if (stop_signal == TARGET_SIGNAL_TRAP
- && trap_expected
+ && stepping_over_breakpoint
&& gdbarch_single_step_through_delay_p (current_gdbarch)
&& currently_stepping (ecs))
{
- /* We're trying to step of a breakpoint. Turns out that we're
+ /* We're trying to step off a breakpoint. Turns out that we're
also on an instruction that needs to be stepped multiple
times before it's been fully executing. E.g., architectures
with a delay slot. It needs to be stepped twice, once for
{
/* The user issued a continue when stopped at a breakpoint.
Set up for another trap and get out of here. */
- ecs->another_trap = 1;
+ ecs->stepping_over_breakpoint = 1;
keep_going (ecs);
return;
}
/* The user issued a step when stopped at a breakpoint.
Maybe we should stop, maybe we should not - the delay
slot *might* correspond to a line of source. In any
- case, don't decide that here, just set ecs->another_trap,
- making sure we single-step again before breakpoints are
- re-inserted. */
- ecs->another_trap = 1;
+ case, don't decide that here, just set
+ ecs->stepping_over_breakpoint, making sure we
+ single-step again before breakpoints are re-inserted. */
+ ecs->stepping_over_breakpoint = 1;
}
}
The alternatives are:
1) break; to really stop and return to the debugger,
2) drop through to start up again
- (set ecs->another_trap to 1 to single step once)
+ (set ecs->stepping_over_breakpoint to 1 to single step once)
3) set ecs->random_signal to 1, and the decision between 1 and 2
will be made according to the signal handling tables. */
stack. */
if (stop_signal == TARGET_SIGNAL_TRAP
- || (breakpoints_inserted
+ || (breakpoint_inserted_here_p (stop_pc)
&& (stop_signal == TARGET_SIGNAL_ILL
|| stop_signal == TARGET_SIGNAL_SEGV
|| stop_signal == TARGET_SIGNAL_EMT))
return;
}
- /* Don't even think about breakpoints if just proceeded over a
- breakpoint. */
- if (stop_signal == TARGET_SIGNAL_TRAP && trap_expected)
- {
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "infrun: trap expected\n");
- bpstat_clear (&stop_bpstat);
- }
- else
- {
- /* See if there is a breakpoint at the current PC. */
- stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
-
- /* Following in case break condition called a
- function. */
- stop_print_frame = 1;
- }
+ /* See if there is a breakpoint at the current PC. */
+ stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
+
+ /* Following in case break condition called a
+ function. */
+ stop_print_frame = 1;
/* NOTE: cagney/2003-03-29: These two checks for a random signal
at one stage in the past included checks for an inferior
if (stop_signal == TARGET_SIGNAL_TRAP)
ecs->random_signal
= !(bpstat_explains_signal (stop_bpstat)
- || trap_expected
+ || stepping_over_breakpoint
|| (step_range_end && step_resume_breakpoint == NULL));
else
{
stop_signal = TARGET_SIGNAL_0;
if (prev_pc == read_pc ()
- && !breakpoints_inserted
&& breakpoint_here_p (read_pc ())
+ && !breakpoint_inserted_here_p (read_pc ())
&& step_resume_breakpoint == NULL)
{
/* We were just starting a new sequence, attempting to
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME\n");
disable_longjmp_breakpoint ();
- remove_breakpoints ();
- breakpoints_inserted = 0;
if (!gdbarch_get_longjmp_target_p (current_gdbarch)
|| !gdbarch_get_longjmp_target (current_gdbarch,
get_current_frame (), &jmp_buf_pc))
case BPSTAT_WHAT_CLEAR_LONGJMP_RESUME_SINGLE:
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_CLEAR_LONGJMP_RESUME\n");
- remove_breakpoints ();
- breakpoints_inserted = 0;
disable_longjmp_breakpoint ();
ecs->handling_longjmp = 0; /* FIXME */
if (what.main_action == BPSTAT_WHAT_CLEAR_LONGJMP_RESUME)
case BPSTAT_WHAT_SINGLE:
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_SINGLE\n");
- if (breakpoints_inserted)
- remove_breakpoints ();
- breakpoints_inserted = 0;
- ecs->another_trap = 1;
+ ecs->stepping_over_breakpoint = 1;
/* Still need to check other stuff, at least the case
where we are stepping and step out of the right range. */
break;
were trying to single-step off a breakpoint. Go back
to doing that. */
ecs->step_after_step_resume_breakpoint = 0;
- remove_breakpoints ();
- breakpoints_inserted = 0;
- ecs->another_trap = 1;
+ ecs->stepping_over_breakpoint = 1;
keep_going (ecs);
return;
}
/* Remove breakpoints, we eventually want to step over the
shlib event breakpoint, and SOLIB_ADD might adjust
breakpoint addresses via breakpoint_re_set. */
- if (breakpoints_inserted)
- remove_breakpoints ();
- breakpoints_inserted = 0;
+ remove_breakpoints ();
/* Check for any newly added shared libraries if we're
supposed to be adding them automatically. Switch
/* Be sure to lift all breakpoints, so the inferior does
actually step past this point... */
- ecs->another_trap = 1;
+ ecs->stepping_over_breakpoint = 1;
break;
}
else
{
/* We want to step over this breakpoint, then keep going. */
- ecs->another_trap = 1;
+ ecs->stepping_over_breakpoint = 1;
break;
}
}
{
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: stepping in dynamic linker\n");
- ecs->another_trap = 1;
+ ecs->stepping_over_breakpoint = 1;
keep_going (ecs);
return;
}
new line in mid-statement, we continue stepping. This makes
things like for(;;) statements work better.) */
- if (ecs->stop_func_end && ecs->sal.end >= ecs->stop_func_end)
- {
- /* If this is the last line of the function, don't keep stepping
- (it would probably step us out of the function).
- This is particularly necessary for a one-line function,
- in which after skipping the prologue we better stop even though
- we will be in mid-line. */
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "infrun: stepped to a different function\n");
- stop_step = 1;
- print_stop_reason (END_STEPPING_RANGE, 0);
- stop_stepping (ecs);
- return;
- }
step_range_start = ecs->sal.pc;
step_range_end = ecs->sal.end;
step_frame_id = get_frame_id (get_current_frame ());
{
return ((!ecs->handling_longjmp
&& ((step_range_end && step_resume_breakpoint == NULL)
- || trap_expected))
+ || stepping_over_breakpoint))
|| ecs->stepping_through_solib_after_catch
|| bpstat_should_step ());
}
step_resume_breakpoint = set_momentary_breakpoint (sr_sal, sr_id,
bp_step_resume);
- if (breakpoints_inserted)
- insert_breakpoints ();
}
/* Insert a "step-resume breakpoint" at RETURN_FRAME.pc. This is used
/* 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)
+ if (stepping_over_breakpoint && 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
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! */
+ We're going to run this baby now!
- if (!breakpoints_inserted && !ecs->another_trap)
+ Note that insert_breakpoints won't try to re-insert
+ already inserted breakpoints. Therefore, we don't
+ care if breakpoints were already inserted, or not. */
+
+ if (ecs->stepping_over_breakpoint)
+ {
+ remove_breakpoints ();
+ }
+ else
{
+ struct gdb_exception e;
/* Stop stepping when inserting breakpoints
has failed. */
- if (insert_breakpoints () != 0)
+ TRY_CATCH (e, RETURN_MASK_ERROR)
+ {
+ insert_breakpoints ();
+ }
+ if (e.reason < 0)
{
stop_stepping (ecs);
return;
}
- breakpoints_inserted = 1;
}
- trap_expected = ecs->another_trap;
+ stepping_over_breakpoint = ecs->stepping_over_breakpoint;
/* Do not deliver SIGNAL_TRAP (except when the user explicitly
specifies that such a signal should be delivered to the
{
target_terminal_ours_for_output ();
printf_filtered (_("[Switching to %s]\n"),
- target_pid_or_tid_to_str (inferior_ptid));
+ target_pid_to_str (inferior_ptid));
previous_inferior_ptid = inferior_ptid;
}
gdbarch_decr_pc_after_break needs to just go away. */
deprecated_update_frame_pc_hack (get_current_frame (), read_pc ());
- if (target_has_execution && breakpoints_inserted)
+ if (target_has_execution)
{
if (remove_breakpoints ())
{
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. */
int stop_step;
int stop_stack_dummy;
int stopped_by_random_signal;
- int trap_expected;
+ int stepping_over_breakpoint;
CORE_ADDR step_range_start;
CORE_ADDR step_range_end;
struct frame_id step_frame_id;
inf_status->stop_step = stop_step;
inf_status->stop_stack_dummy = stop_stack_dummy;
inf_status->stopped_by_random_signal = stopped_by_random_signal;
- inf_status->trap_expected = trap_expected;
+ inf_status->stepping_over_breakpoint = stepping_over_breakpoint;
inf_status->step_range_start = step_range_start;
inf_status->step_range_end = step_range_end;
inf_status->step_frame_id = step_frame_id;
stop_step = inf_status->stop_step;
stop_stack_dummy = inf_status->stop_stack_dummy;
stopped_by_random_signal = inf_status->stopped_by_random_signal;
- trap_expected = inf_status->trap_expected;
+ stepping_over_breakpoint = inf_status->stepping_over_breakpoint;
step_range_start = inf_status->step_range_start;
step_range_end = inf_status->step_range_end;
step_frame_id = inf_status->step_frame_id;