diff options
author | Pedro Alves <palves@redhat.com> | 2015-08-07 17:23:58 +0100 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2015-08-07 17:23:58 +0100 |
commit | 4d9d9d0423ed611fa6d620ca3aa088fc16a0d59e (patch) | |
tree | 12db443f6ea89572c1107dd0a04750dc6ea1aa73 | |
parent | c2829269f5af8a860b54ceac3596610b1f51fee5 (diff) | |
download | gdb-4d9d9d0423ed611fa6d620ca3aa088fc16a0d59e.zip gdb-4d9d9d0423ed611fa6d620ca3aa088fc16a0d59e.tar.gz gdb-4d9d9d0423ed611fa6d620ca3aa088fc16a0d59e.tar.bz2 |
Use keep_going in proceed and start_step_over too
The main motivation of this patch is sharing more code between the
proceed (starting the inferior for the first time) and keep_going
(restarting the inferior after handling an event) paths and using the
step_over_chain queue now embedded in the thread_info object for
pending in-line step-overs too (instead of just for displaced
stepping).
So this commit:
- splits out a new keep_going_pass_signal function out of keep_going
that is just like keep_going except for the bits that clear the
signal to pass if the signal is set to "handle nopass".
- makes proceed use keep_going too.
- Makes start_step_over use keep_going_pass_signal instead of lower
level displaced stepping things.
One user visible change: if inserting breakpoints while trying to
proceed fails, we now get:
(gdb) si
Warning:
Could not insert hardware watchpoint 7.
Could not insert hardware breakpoints:
You may have requested too many hardware breakpoints/watchpoints.
Command aborted.
(gdb)
while before we only saw warnings with no indication that the command
was cancelled:
(gdb) si
Warning:
Could not insert hardware watchpoint 7.
Could not insert hardware breakpoints:
You may have requested too many hardware breakpoints/watchpoints.
(gdb)
Tested on x86_64-linux-gnu, ppc64-linux-gnu and s390-linux-gnu.
gdb/ChangeLog:
2015-08-07 Pedro Alves <palves@redhat.com>
* gdbthread.h (struct thread_info) <prev_pc>: Extend comment.
* infrun.c (struct execution_control_state): Move higher up in the
file.
(reset_ecs): New function.
(start_step_over): Now returns int. Rewrite to use
keep_going_pass_signal instead of manually starting a displaced step.
(resume): Don't call set_running here. If displaced stepping
can't start now, clear trap_expected.
(find_thread_needs_step_over): Delete function.
(proceed): Set up finish_thread_state_cleanup. Call set_running.
If the current thread needs a step over, push it in the step-over
chain. Don't set insert breakpoints nor call resume directly
here. Instead rewrite to use start_step_over and
keep_going_pass_signal.
(finish_step_over): New function.
(handle_signal_stop): Call finish_step_over instead of
start_step_over.
(switch_back_to_stepped_thread): If the event thread needs another
step-over do that first. Use start_step_over.
(keep_going_pass_signal): New function, factored out from ...
(keep_going): ... here.
(_initialize_infrun): Comment moved here.
* thread.c (set_running_thread): New function.
(set_running, finish_thread_state): Use set_running_thread.
-rw-r--r-- | gdb/ChangeLog | 27 | ||||
-rw-r--r-- | gdb/gdbthread.h | 6 | ||||
-rw-r--r-- | gdb/infrun.c | 574 | ||||
-rw-r--r-- | gdb/thread.c | 52 |
4 files changed, 351 insertions, 308 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index f7c559b..3a6668a 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,32 @@ 2015-08-07 Pedro Alves <palves@redhat.com> + * gdbthread.h (struct thread_info) <prev_pc>: Extend comment. + * infrun.c (struct execution_control_state): Move higher up in the + file. + (reset_ecs): New function. + (start_step_over): Now returns int. Rewrite to use + keep_going_pass_signal instead of manually starting a displaced step. + (resume): Don't call set_running here. If displaced stepping + can't start now, clear trap_expected. + (find_thread_needs_step_over): Delete function. + (proceed): Set up finish_thread_state_cleanup. Call set_running. + If the current thread needs a step over, push it in the step-over + chain. Don't set insert breakpoints nor call resume directly + here. Instead rewrite to use start_step_over and + keep_going_pass_signal. + (finish_step_over): New function. + (handle_signal_stop): Call finish_step_over instead of + start_step_over. + (switch_back_to_stepped_thread): If the event thread needs another + step-over do that first. Use start_step_over. + (keep_going_pass_signal): New function, factored out from ... + (keep_going): ... here. + (_initialize_infrun): Comment moved here. + * thread.c (set_running_thread): New function. + (set_running, finish_thread_state): Use set_running_thread. + +2015-08-07 Pedro Alves <palves@redhat.com> + * gdbthread.h (struct thread_info) <step_over_prev, step_over_next>: New fields. (thread_step_over_chain_enqueue, thread_step_over_chain_remove) diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h index 2602e8b..f881712 100644 --- a/gdb/gdbthread.h +++ b/gdb/gdbthread.h @@ -206,8 +206,10 @@ struct thread_info /* Internal stepping state. */ - /* Record the pc of the thread the last time it stopped. This is - maintained by proceed and keep_going, and used in + /* Record the pc of the thread the last time it was resumed. (It + can't be done on stop as the PC may change since the last stop, + e.g., "return" command, or "p $pc = 0xf000"). This is maintained + by proceed and keep_going, and among other things, it's used in adjust_pc_after_break to distinguish a hardware single-step SIGTRAP from a breakpoint SIGTRAP. */ CORE_ADDR prev_pc; diff --git a/gdb/infrun.c b/gdb/infrun.c index 9cd897a..94075ce 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -1842,31 +1842,61 @@ displaced_step_fixup (ptid_t event_ptid, enum gdb_signal signal) displaced->step_ptid = null_ptid; } -/* Are there any pending step-over requests? If so, run all we can - now. */ +/* Data to be passed around while handling an event. This data is + discarded between events. */ +struct execution_control_state +{ + ptid_t ptid; + /* The thread that got the event, if this was a thread event; NULL + otherwise. */ + struct thread_info *event_thread; + + struct target_waitstatus ws; + int stop_func_filled_in; + CORE_ADDR stop_func_start; + CORE_ADDR stop_func_end; + const char *stop_func_name; + int wait_some_more; + + /* True if the event thread hit the single-step breakpoint of + another thread. Thus the event doesn't cause a stop, the thread + needs to be single-stepped past the single-step breakpoint before + we can switch back to the original stepping thread. */ + int hit_singlestep_breakpoint; +}; + +/* Clear ECS and set it to point at TP. */ static void +reset_ecs (struct execution_control_state *ecs, struct thread_info *tp) +{ + memset (ecs, 0, sizeof (*ecs)); + ecs->event_thread = tp; + ecs->ptid = tp->ptid; +} + +static void keep_going_pass_signal (struct execution_control_state *ecs); +static void prepare_to_wait (struct execution_control_state *ecs); +static int thread_still_needs_step_over (struct thread_info *tp); + +/* Are there any pending step-over requests? If so, run all we can + now and return true. Otherwise, return false. */ + +static int start_step_over (void) { struct thread_info *tp, *next; for (tp = step_over_queue_head; tp != NULL; tp = next) { - ptid_t ptid; - struct displaced_step_inferior_state *displaced; - struct regcache *regcache; - struct gdbarch *gdbarch; - CORE_ADDR actual_pc; - struct address_space *aspace; - struct inferior *inf = find_inferior_ptid (tp->ptid); + struct execution_control_state ecss; + struct execution_control_state *ecs = &ecss; next = thread_step_over_chain_next (tp); - displaced = get_displaced_stepping_state (inf->pid); - /* If this inferior already has a displaced step in process, don't start a new one. */ - if (!ptid_equal (displaced->step_ptid, null_ptid)) + if (displaced_step_in_progress (ptid_get_pid (tp->ptid))) continue; thread_step_over_chain_remove (tp); @@ -1878,73 +1908,57 @@ start_step_over (void) "infrun: step-over queue now empty\n"); } - ptid = tp->ptid; - context_switch (ptid); - - regcache = get_thread_regcache (ptid); - actual_pc = regcache_read_pc (regcache); - aspace = get_regcache_aspace (regcache); - gdbarch = get_regcache_arch (regcache); - - if (breakpoint_here_p (aspace, actual_pc)) + if (tp->control.trap_expected || tp->executing) { - if (debug_displaced) - fprintf_unfiltered (gdb_stdlog, - "displaced: stepping queued %s now\n", - target_pid_to_str (ptid)); - - displaced_step_prepare (ptid); - - if (debug_displaced) - { - CORE_ADDR actual_pc = regcache_read_pc (regcache); - gdb_byte buf[4]; - - fprintf_unfiltered (gdb_stdlog, "displaced: run %s: ", - paddress (gdbarch, actual_pc)); - read_memory (actual_pc, buf, sizeof (buf)); - displaced_step_dump_bytes (gdb_stdlog, buf, sizeof (buf)); - } - - if (gdbarch_displaced_step_hw_singlestep (gdbarch, - displaced->step_closure)) - target_resume (ptid, 1, GDB_SIGNAL_0); - else - target_resume (ptid, 0, GDB_SIGNAL_0); - - /* Done, we're stepping a thread. */ + internal_error (__FILE__, __LINE__, + "[%s] has inconsistent state: " + "trap_expected=%d, executing=%d\n", + target_pid_to_str (tp->ptid), + tp->control.trap_expected, + tp->executing); } - else - { - int step; - struct thread_info *tp = inferior_thread (); - /* The breakpoint we were sitting under has since been - removed. */ - tp->control.trap_expected = 0; - - /* Go back to what we were trying to do. */ - step = currently_stepping (tp); - - if (step) - step = maybe_software_singlestep (gdbarch, actual_pc); + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: resuming [%s] for step-over\n", + target_pid_to_str (tp->ptid)); + + /* keep_going_pass_signal skips the step-over if the breakpoint + is no longer inserted. In all-stop, we want to keep looking + for a thread that needs a step-over instead of resuming TP, + because we wouldn't be able to resume anything else until the + target stops again. In non-stop, the resume always resumes + only TP, so it's OK to let the thread resume freely. */ + if (!non_stop && !thread_still_needs_step_over (tp)) + continue; - if (debug_displaced) - fprintf_unfiltered (gdb_stdlog, - "displaced: breakpoint is gone: %s, step(%d)\n", - target_pid_to_str (tp->ptid), step); + switch_to_thread (tp->ptid); + reset_ecs (ecs, tp); + keep_going_pass_signal (ecs); - target_resume (ptid, step, GDB_SIGNAL_0); - tp->suspend.stop_signal = GDB_SIGNAL_0; + if (!ecs->wait_some_more) + error (_("Command aborted.")); - /* This request was discarded. See if there's any other - thread waiting for its turn. */ + if (!non_stop) + { + /* On all-stop, shouldn't have resumed unless we needed a + step over. */ + gdb_assert (tp->control.trap_expected + || tp->step_after_step_resume_breakpoint); + + /* With remote targets (at least), in all-stop, we can't + issue any further remote commands until the program stops + again. */ + return 1; } - /* A new displaced stepping sequence started. Maybe we can - start a displaced step on a thread of other process. - Continue looking. */ + /* Either the thread no longer needed a step-over, or a new + displaced stepping sequence started. Even in the latter + case, continue looking. Maybe we can also start another + displaced step on a thread of other process. */ } + + return 0; } /* Update global variables holding ptids to hold NEW_PTID if they were @@ -2281,13 +2295,11 @@ resume (enum gdb_signal sig) if (!displaced_step_prepare (inferior_ptid)) { - /* Got placed in displaced stepping queue. Will be resumed - later when all the currently queued displaced stepping - requests finish. The thread is not executing at this - point, and the call to set_executing will be made later. - But we need to call set_running here, since from the - user/frontend's point of view, threads were set running. */ - set_running (user_visible_resume_ptid (user_step), 1); + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "Got placed in step-over queue\n"); + + tp->control.trap_expected = 0; discard_cleanups (old_cleanups); return; } @@ -2362,12 +2374,6 @@ resume (enum gdb_signal sig) by applying increasingly restricting conditions. */ resume_ptid = user_visible_resume_ptid (user_step); - /* Even if RESUME_PTID is a wildcard, and we end up resuming less - (e.g., we might need to step over a breakpoint), from the - user/frontend's point of view, all threads in RESUME_PTID are now - running. */ - set_running (resume_ptid, 1); - /* Maybe resume a single thread after all. */ if ((step || thread_has_single_step_breakpoints_set (tp)) && tp->control.trap_expected) @@ -2576,48 +2582,6 @@ schedlock_applies (struct thread_info *tp) && tp->control.stepping_command)); } -/* Look a thread other than EXCEPT that has previously reported a - breakpoint event, and thus needs a step-over in order to make - progress. Returns NULL is none is found. */ - -static struct thread_info * -find_thread_needs_step_over (struct thread_info *except) -{ - struct thread_info *tp, *current; - - /* With non-stop mode on, threads are always handled individually. */ - gdb_assert (! non_stop); - - current = inferior_thread (); - - /* If scheduler locking applies, we can avoid iterating over all - threads. */ - if (schedlock_applies (except)) - { - if (except != current - && thread_still_needs_step_over (current)) - return current; - - return NULL; - } - - ALL_NON_EXITED_THREADS (tp) - { - /* Ignore the EXCEPT thread. */ - if (tp == except) - continue; - /* Ignore threads of processes we're not resuming. */ - if (!sched_multi - && ptid_get_pid (tp->ptid) != ptid_get_pid (inferior_ptid)) - continue; - - if (thread_still_needs_step_over (tp)) - return tp; - } - - return NULL; -} - /* Basic routine for continuing the program in various fashions. ADDR is the address to resume at, or -1 for resume where stopped. @@ -2638,6 +2602,11 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal) struct thread_info *tp; CORE_ADDR pc; struct address_space *aspace; + ptid_t resume_ptid; + struct execution_control_state ecss; + struct execution_control_state *ecs = &ecss; + struct cleanup *old_chain; + int started; /* If we're stopped at a fork/vfork, follow the branch set by the "set follow-fork-mode" command; otherwise, we'll just proceed @@ -2700,7 +2669,23 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal) (next/step/etc.), we'll want to print stop event output to the MI console channel (the stepped-to line, etc.), as if the user entered the execution command on a real GDB console. */ - inferior_thread ()->control.command_interp = command_interp (); + tp->control.command_interp = command_interp (); + + resume_ptid = user_visible_resume_ptid (tp->control.stepping_command); + + /* If an exception is thrown from this point on, make sure to + propagate GDB's knowledge of the executing state to the + frontend/user running state. */ + old_chain = make_cleanup (finish_thread_state_cleanup, &resume_ptid); + + /* Even if RESUME_PTID is a wildcard, and we end up resuming fewer + threads (e.g., we might need to set threads stepping over + breakpoints first), from the user/frontend's point of view, all + threads in RESUME_PTID are now running. Unless we're calling an + inferior function, as in that case we pretend the inferior + doesn't run at all. */ + if (!tp->control.in_infcall) + set_running (resume_ptid, 1); if (debug_infrun) fprintf_unfiltered (gdb_stdlog, @@ -2708,91 +2693,92 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal) paddress (gdbarch, addr), gdb_signal_to_symbol_string (siggnal)); - if (non_stop) - /* In non-stop, each thread is handled individually. The context - must already be set to the right thread here. */ - ; - else + annotate_starting (); + + /* Make sure that output from GDB appears before output from the + inferior. */ + gdb_flush (gdb_stdout); + + /* In a multi-threaded task we may select another thread and + then continue or step. + + But if a thread that we're resuming had stopped at a breakpoint, + it will immediately cause another breakpoint stop without any + execution (i.e. it will report a breakpoint hit incorrectly). So + we must step over it first. + + Look for threads other than the current (TP) that reported a + breakpoint hit and haven't been resumed yet since. */ + + /* If scheduler locking applies, we can avoid iterating over all + threads. */ + if (!non_stop && !schedlock_applies (tp)) { - struct thread_info *step_over; + struct thread_info *current = tp; + + ALL_NON_EXITED_THREADS (tp) + { + /* Ignore the current thread here. It's handled + afterwards. */ + if (tp == current) + continue; - /* In a multi-threaded task we may select another thread and - then continue or step. + /* Ignore threads of processes we're not resuming. */ + if (!ptid_match (tp->ptid, resume_ptid)) + continue; - But if the old thread was stopped at a breakpoint, it will - immediately cause another breakpoint stop without any - execution (i.e. it will report a breakpoint hit incorrectly). - So we must step over it first. + if (!thread_still_needs_step_over (tp)) + continue; + + gdb_assert (!thread_is_in_step_over_chain (tp)); - Look for a thread other than the current (TP) that reported a - breakpoint hit and hasn't been resumed yet since. */ - step_over = find_thread_needs_step_over (tp); - if (step_over != NULL) - { if (debug_infrun) fprintf_unfiltered (gdb_stdlog, "infrun: need to step-over [%s] first\n", - target_pid_to_str (step_over->ptid)); + target_pid_to_str (tp->ptid)); - /* Store the prev_pc for the stepping thread too, needed by - switch_back_to_stepped_thread. */ - tp->prev_pc = regcache_read_pc (get_current_regcache ()); - switch_to_thread (step_over->ptid); - tp = step_over; + thread_step_over_chain_enqueue (tp); } - } - - /* If we need to step over a breakpoint, and we're not using - displaced stepping to do so, insert all breakpoints (watchpoints, - etc.) but the one we're stepping over, step one instruction, and - then re-insert the breakpoint when that step is finished. */ - if (tp->stepping_over_breakpoint && !use_displaced_stepping (gdbarch)) - { - struct regcache *regcache = get_current_regcache (); - set_step_over_info (get_regcache_aspace (regcache), - regcache_read_pc (regcache), 0); + tp = current; } - else - clear_step_over_info (); - insert_breakpoints (); + /* Enqueue the current thread last, so that we move all other + threads over their breakpoints first. */ + if (tp->stepping_over_breakpoint) + thread_step_over_chain_enqueue (tp); - tp->control.trap_expected = tp->stepping_over_breakpoint; + /* If the thread isn't started, we'll still need to set its prev_pc, + so that switch_back_to_stepped_thread knows the thread hasn't + advanced. Must do this before resuming any thread, as in + all-stop/remote, once we resume we can't send any other packet + until the target stops again. */ + tp->prev_pc = regcache_read_pc (regcache); - annotate_starting (); + started = start_step_over (); - /* Make sure that output from GDB appears before output from the - inferior. */ - gdb_flush (gdb_stdout); + if (step_over_info_valid_p ()) + { + /* Either this thread started a new in-line step over, or some + other thread was already doing one. In either case, don't + resume anything else until the step-over is finished. */ + } + else if (started && !non_stop) + { + /* A new displaced stepping sequence was started. In all-stop, + we can't talk to the target anymore until it next stops. */ + } + else if (!tp->executing && !thread_is_in_step_over_chain (tp)) + { + /* The thread wasn't started, and isn't queued, run it now. */ + reset_ecs (ecs, tp); + switch_to_thread (tp->ptid); + keep_going_pass_signal (ecs); + if (!ecs->wait_some_more) + error ("Command aborted."); + } - /* Refresh prev_pc value just prior to resuming. This used to be - done in stop_waiting, however, setting prev_pc there did not handle - scenarios such as inferior function calls or returning from - a function via the return command. In those cases, the prev_pc - value was not set properly for subsequent commands. The prev_pc value - is used to initialize the starting line number in the ecs. With an - invalid value, the gdb next command ends up stopping at the position - represented by the next line table entry past our start position. - On platforms that generate one line table entry per line, this - is not a problem. However, on the ia64, the compiler generates - extraneous line table entries that do not increase the line number. - When we issue the gdb next command on the ia64 after an inferior call - or a return command, we often end up a few instructions forward, still - within the original line we started. - - An attempt was made to refresh the prev_pc at the same time the - execution_control_state is initialized (for instance, just before - waiting for an inferior event). But this approach did not work - because of platforms that use ptrace, where the pc register cannot - be read unless the inferior is stopped. At that point, we are not - guaranteed the inferior is stopped and so the regcache_read_pc() call - can fail. Setting the prev_pc value here ensures the value is updated - correctly when the inferior is stopped. */ - tp->prev_pc = regcache_read_pc (get_current_regcache ()); - - /* Resume inferior. */ - resume (tp->suspend.stop_signal); + discard_cleanups (old_chain); /* Wait for it to stop (if not standalone) and in any case decode why it stopped, and act accordingly. */ @@ -2860,28 +2846,6 @@ init_wait_for_inferior (void) } -/* Data to be passed around while handling an event. This data is - discarded between events. */ -struct execution_control_state -{ - ptid_t ptid; - /* The thread that got the event, if this was a thread event; NULL - otherwise. */ - struct thread_info *event_thread; - - struct target_waitstatus ws; - int stop_func_filled_in; - CORE_ADDR stop_func_start; - CORE_ADDR stop_func_end; - const char *stop_func_name; - int wait_some_more; - - /* True if the event thread hit the single-step breakpoint of - another thread. Thus the event doesn't cause a stop, the thread - needs to be single-stepped past the single-step breakpoint before - we can switch back to the original stepping thread. */ - int hit_singlestep_breakpoint; -}; static void handle_inferior_event (struct execution_control_state *ecs); @@ -2895,7 +2859,6 @@ static void check_exception_resume (struct execution_control_state *, static void end_stepping_range (struct execution_control_state *ecs); static void stop_waiting (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 process_event_stop_test (struct execution_control_state *ecs); static int switch_back_to_stepped_thread (struct execution_control_state *ecs); @@ -4271,6 +4234,34 @@ handle_inferior_event (struct execution_control_state *ecs) value_free_to_mark (mark); } +/* Called when we get an event that may finish an in-line or + out-of-line (displaced stepping) step-over started previously. */ + +static void +finish_step_over (struct execution_control_state *ecs) +{ + displaced_step_fixup (ecs->ptid, + ecs->event_thread->suspend.stop_signal); + + if (step_over_info_valid_p ()) + { + /* If we're stepping over a breakpoint with all threads locked, + then only the thread that was stepped should be reporting + back an event. */ + gdb_assert (ecs->event_thread->control.trap_expected); + + if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP) + clear_step_over_info (); + } + + if (!non_stop) + return; + + /* Start a new step-over in another thread if there's one that + needs it. */ + start_step_over (); +} + /* Come here when the program has stopped with a signal. */ static void @@ -4287,9 +4278,7 @@ handle_signal_stop (struct execution_control_state *ecs) /* Do we need to clean up the state of a thread that has completed a displaced single-step? (Doing so usually affects the PC, so do it here, before we set stop_pc.) */ - displaced_step_fixup (ecs->ptid, - ecs->event_thread->suspend.stop_signal); - start_step_over (); + finish_step_over (ecs); /* If we either finished a single-step or hit a breakpoint, but the user wanted this thread to be stopped, pretend we got a @@ -5639,7 +5628,6 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs) { struct thread_info *tp; struct thread_info *stepping_thread; - struct thread_info *step_over; /* If any thread is blocked on some internal breakpoint, and we simply need to step over that breakpoint to get it going @@ -5682,14 +5670,20 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs) return 1; } - /* Otherwise, we no longer expect a trap in the current thread. - Clear the trap_expected flag before switching back -- this is - what keep_going does as well, if we call it. */ - ecs->event_thread->control.trap_expected = 0; - - /* Likewise, clear the signal if it should not be passed. */ - if (!signal_program[ecs->event_thread->suspend.stop_signal]) - ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0; + /* If this thread needs yet another step-over (e.g., stepping + through a delay slot), do it first before moving on to + another thread. */ + if (thread_still_needs_step_over (ecs->event_thread)) + { + if (debug_infrun) + { + fprintf_unfiltered (gdb_stdlog, + "infrun: thread [%s] still needs step-over\n", + target_pid_to_str (ecs->event_thread->ptid)); + } + keep_going (ecs); + return 1; + } /* If scheduler locking applies even if not stepping, there's no need to walk over threads. Above we've checked whether the @@ -5699,12 +5693,26 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs) if (schedlock_applies (ecs->event_thread)) return 0; - /* Look for the stepping/nexting thread, and check if any other - thread other than the stepping thread needs to start a - step-over. Do all step-overs before actually proceeding with + /* Otherwise, we no longer expect a trap in the current thread. + Clear the trap_expected flag before switching back -- this is + what keep_going does as well, if we call it. */ + ecs->event_thread->control.trap_expected = 0; + + /* Likewise, clear the signal if it should not be passed. */ + if (!signal_program[ecs->event_thread->suspend.stop_signal]) + ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0; + + /* Do all pending step-overs before actually proceeding with step/next/etc. */ + if (start_step_over ()) + { + prepare_to_wait (ecs); + return 1; + } + + /* Look for the stepping/nexting thread. */ stepping_thread = NULL; - step_over = NULL; + ALL_NON_EXITED_THREADS (tp) { /* Ignore threads of processes we're not resuming. */ @@ -5736,37 +5744,6 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs) stepping_thread = tp; } - else if (thread_still_needs_step_over (tp)) - { - step_over = tp; - - /* At the top we've returned early if the event thread - is stepping. If some other thread not the event - thread is stepping, then scheduler locking can't be - in effect, and we can resume this thread. No need to - keep looking for the stepping thread then. */ - break; - } - } - - if (step_over != NULL) - { - tp = step_over; - if (debug_infrun) - { - fprintf_unfiltered (gdb_stdlog, - "infrun: need to step-over [%s]\n", - target_pid_to_str (tp->ptid)); - } - - /* Only the stepping thread should have this set. */ - gdb_assert (tp->control.step_range_end == 0); - - ecs->ptid = tp->ptid; - ecs->event_thread = tp; - switch_to_thread (ecs->ptid); - keep_going (ecs); - return 1; } if (stepping_thread != NULL) @@ -5865,7 +5842,7 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs) fprintf_unfiltered (gdb_stdlog, "infrun: expected thread still " "hasn't advanced\n"); - keep_going (ecs); + keep_going_pass_signal (ecs); } return 1; @@ -6281,24 +6258,32 @@ stop_waiting (struct execution_control_state *ecs) ecs->wait_some_more = 0; } -/* Called when we should continue running the inferior, because the - current event doesn't cause a user visible stop. This does the - resuming part; waiting for the next event is done elsewhere. */ +/* Like keep_going, but passes the signal to the inferior, even if the + signal is set to nopass. */ static void -keep_going (struct execution_control_state *ecs) +keep_going_pass_signal (struct execution_control_state *ecs) { /* Make sure normal_stop is called if we get a QUIT handled before reaching resume. */ struct cleanup *old_cleanups = make_cleanup (resume_cleanups, 0); + gdb_assert (ptid_equal (ecs->event_thread->ptid, inferior_ptid)); + /* Save the pc before execution, to compare with pc after stop. */ ecs->event_thread->prev_pc = regcache_read_pc (get_thread_regcache (ecs->ptid)); - if (ecs->event_thread->control.trap_expected - && ecs->event_thread->suspend.stop_signal != GDB_SIGNAL_TRAP) + if (ecs->event_thread->control.trap_expected) { + struct thread_info *tp = ecs->event_thread; + + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: %s has trap_expected set, " + "resuming to collect trap\n", + target_pid_to_str (tp->ptid)); + /* We haven't yet gotten our trap, and either: intercepted a non-signal event (e.g., a fork); or took a signal which we are supposed to pass through to the inferior. Simply @@ -6369,20 +6354,6 @@ keep_going (struct execution_control_state *ecs) ecs->event_thread->control.trap_expected = (remove_bp || remove_wps); - /* Do not deliver GDB_SIGNAL_TRAP (except when the user - explicitly specifies that such a signal should be delivered - to the target program). Typically, that would occur when a - user is debugging a target monitor on a simulator: the target - monitor sets a breakpoint; the simulator encounters this - breakpoint and halts the simulation handing control to GDB; - GDB, noting that the stop address doesn't map to any known - breakpoint, returns control back to the simulator; the - simulator then delivers the hardware equivalent of a - GDB_SIGNAL_TRAP to the program being debugged. */ - if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP - && !signal_program[ecs->event_thread->suspend.stop_signal]) - ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0; - discard_cleanups (old_cleanups); resume (ecs->event_thread->suspend.stop_signal); } @@ -6390,6 +6361,22 @@ keep_going (struct execution_control_state *ecs) prepare_to_wait (ecs); } +/* Called when we should continue running the inferior, because the + current event doesn't cause a user visible stop. This does the + resuming part; waiting for the next event is done elsewhere. */ + +static void +keep_going (struct execution_control_state *ecs) +{ + if (ecs->event_thread->control.trap_expected + && ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP) + ecs->event_thread->control.trap_expected = 0; + + if (!signal_program[ecs->event_thread->suspend.stop_signal]) + ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0; + keep_going_pass_signal (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. */ @@ -7790,8 +7777,19 @@ leave it stopped or free to run as needed."), signal_catch[i] = 0; } - /* Signals caused by debugger's own actions - should not be given to the program afterwards. */ + /* Signals caused by debugger's own actions should not be given to + the program afterwards. + + Do not deliver GDB_SIGNAL_TRAP by default, except when the user + explicitly specifies that it should be delivered to the target + program. Typically, that would occur when a user is debugging a + target monitor on a simulator: the target monitor sets a + breakpoint; the simulator encounters this breakpoint and halts + the simulation handing control to GDB; GDB, noting that the stop + address doesn't map to any known breakpoint, returns control back + to the simulator; the simulator then delivers the hardware + equivalent of a GDB_SIGNAL_TRAP to the program being + debugged. */ signal_program[GDB_SIGNAL_TRAP] = 0; signal_program[GDB_SIGNAL_INT] = 0; diff --git a/gdb/thread.c b/gdb/thread.c index 28e5ef8..3e3f419 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -852,44 +852,62 @@ thread_change_ptid (ptid_t old_ptid, ptid_t new_ptid) observer_notify_thread_ptid_changed (old_ptid, new_ptid); } +/* Helper for set_running, that marks one thread either running or + stopped. */ + +static int +set_running_thread (struct thread_info *tp, int running) +{ + int started = 0; + + if (running && tp->state == THREAD_STOPPED) + started = 1; + tp->state = running ? THREAD_RUNNING : THREAD_STOPPED; + + if (!running) + { + /* If the thread is now marked stopped, remove it from + the step-over queue, so that we don't try to resume + it until the user wants it to. */ + if (tp->step_over_next != NULL) + thread_step_over_chain_remove (tp); + } + + return started; +} + void set_running (ptid_t ptid, int running) { struct thread_info *tp; int all = ptid_equal (ptid, minus_one_ptid); + int any_started = 0; /* We try not to notify the observer if no thread has actually changed the running state -- merely to reduce the number of messages to frontend. Frontend is supposed to handle multiple *running just fine. */ if (all || ptid_is_pid (ptid)) { - int any_started = 0; - for (tp = thread_list; tp; tp = tp->next) if (all || ptid_get_pid (tp->ptid) == ptid_get_pid (ptid)) { if (tp->state == THREAD_EXITED) continue; - if (running && tp->state == THREAD_STOPPED) + + if (set_running_thread (tp, running)) any_started = 1; - tp->state = running ? THREAD_RUNNING : THREAD_STOPPED; } - if (any_started) - observer_notify_target_resumed (ptid); } else { - int started = 0; - tp = find_thread_ptid (ptid); - gdb_assert (tp); + gdb_assert (tp != NULL); gdb_assert (tp->state != THREAD_EXITED); - if (running && tp->state == THREAD_STOPPED) - started = 1; - tp->state = running ? THREAD_RUNNING : THREAD_STOPPED; - if (started) - observer_notify_target_resumed (ptid); + if (set_running_thread (tp, running)) + any_started = 1; } + if (any_started) + observer_notify_target_resumed (ptid); } static int @@ -1008,9 +1026,8 @@ finish_thread_state (ptid_t ptid) continue; if (all || ptid_get_pid (ptid) == ptid_get_pid (tp->ptid)) { - if (tp->executing && tp->state == THREAD_STOPPED) + if (set_running_thread (tp, tp->executing)) any_started = 1; - tp->state = tp->executing ? THREAD_RUNNING : THREAD_STOPPED; } } } @@ -1020,9 +1037,8 @@ finish_thread_state (ptid_t ptid) gdb_assert (tp); if (tp->state != THREAD_EXITED) { - if (tp->executing && tp->state == THREAD_STOPPED) + if (set_running_thread (tp, tp->executing)) any_started = 1; - tp->state = tp->executing ? THREAD_RUNNING : THREAD_STOPPED; } } |