diff options
author | Pedro Alves <pedro@palves.net> | 2021-11-30 19:52:11 +0000 |
---|---|---|
committer | Pedro Alves <pedro@palves.net> | 2023-11-13 14:16:11 +0000 |
commit | 7ac958f267898ba87ae67ea04f94a3fda24567dc (patch) | |
tree | d085d38a04a17dbd2e4f14d18b6f62a5cb2b6647 /gdb/infrun.c | |
parent | ef980d654ba22aad6b5b301179dd105f522b56a1 (diff) | |
download | binutils-7ac958f267898ba87ae67ea04f94a3fda24567dc.zip binutils-7ac958f267898ba87ae67ea04f94a3fda24567dc.tar.gz binutils-7ac958f267898ba87ae67ea04f94a3fda24567dc.tar.bz2 |
Don't resume new threads if scheduler-locking is in effect
If scheduler-locking is in effect, e.g., with "set scheduler-locking
on", and you step over a function that spawns a new thread, the new
thread is allowed to run free, at least until some event is hit, at
which point, whether the new thread is re-resumed depends on a number
of seemingly random factors. E.g., if the target is all-stop, and the
parent thread hits a breakpoint, and GDB decides the breakpoint isn't
interesting to report to the user, then the parent thread is resumed,
but the new thread is left stopped.
I think that letting the new threads run with scheduler-locking
enabled is a defect. This commit fixes that, making use of the new
clone events on Linux, and of target_thread_events() on targets where
new threads have no connection to the thread that spawned them.
Testcase and documentation changes included.
Approved-By: Eli Zaretskii <eliz@gnu.org>
Reviewed-By: Andrew Burgess <aburgess@redhat.com>
Change-Id: Ie12140138b37534b7fc1d904da34f0f174aa11ce
Diffstat (limited to 'gdb/infrun.c')
-rw-r--r-- | gdb/infrun.c | 41 |
1 files changed, 33 insertions, 8 deletions
diff --git a/gdb/infrun.c b/gdb/infrun.c index 409c224..943ea88 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -107,6 +107,8 @@ static bool start_step_over (void); static bool step_over_info_valid_p (void); +static bool schedlock_applies (struct thread_info *tp); + /* Asynchronous signal handler registered as event loop source for when we have pending events ready to be passed to the core. */ static struct async_event_handler *infrun_async_inferior_event_token; @@ -1961,7 +1963,13 @@ static void update_thread_events_after_step_over (thread_info *event_thread, const target_waitstatus &event_status) { - if (target_supports_set_thread_options (0)) + if (schedlock_applies (event_thread)) + { + /* If scheduler-locking applies, continue reporting + thread-created/thread-cloned events. */ + return; + } + else if (target_supports_set_thread_options (0)) { /* We can control per-thread options. Disable events for the event thread, unless the thread is gone. */ @@ -2535,9 +2543,14 @@ do_target_resume (ptid_t resume_ptid, bool step, enum gdb_signal sig) to start stopped. We need to release the displaced stepping buffer if the stepped thread exits, so we also enable thread-exit events. + + - If scheduler-locking applies, threads that the current thread + spawns should remain halted. It's not strictly necessary to + enable thread-exit events in this case, but it doesn't hurt. */ if (step_over_info_valid_p () - || displaced_step_in_progress_thread (tp)) + || displaced_step_in_progress_thread (tp) + || schedlock_applies (tp)) { gdb_thread_options options = GDB_THREAD_OPTION_CLONE | GDB_THREAD_OPTION_EXIT; @@ -2546,6 +2559,13 @@ do_target_resume (ptid_t resume_ptid, bool step, enum gdb_signal sig) else target_thread_events (true); } + else + { + if (target_supports_set_thread_options (0)) + tp->set_thread_options (0); + else if (!displaced_step_in_progress_any_thread ()) + target_thread_events (false); + } /* If we're resuming more than one thread simultaneously, then any thread other than the leader is being set to run free. Clear any @@ -6295,16 +6315,21 @@ handle_inferior_event (struct execution_control_state *ecs) parent->set_running (false); /* If resuming the child, mark it running. */ - if (ecs->ws.kind () == TARGET_WAITKIND_THREAD_CLONED - || (follow_child || (!detach_fork && (non_stop || sched_multi)))) + if ((ecs->ws.kind () == TARGET_WAITKIND_THREAD_CLONED + && !schedlock_applies (ecs->event_thread)) + || (ecs->ws.kind () != TARGET_WAITKIND_THREAD_CLONED + && (follow_child + || (!detach_fork && (non_stop || sched_multi))))) child->set_running (true); /* In non-stop mode, also resume the other branch. */ if ((ecs->ws.kind () == TARGET_WAITKIND_THREAD_CLONED - && target_is_non_stop_p ()) - || (!detach_fork && (non_stop - || (sched_multi - && target_is_non_stop_p ())))) + && target_is_non_stop_p () + && !schedlock_applies (ecs->event_thread)) + || (ecs->ws.kind () != TARGET_WAITKIND_THREAD_CLONED + && (!detach_fork && (non_stop + || (sched_multi + && target_is_non_stop_p ()))))) { if (follow_child) switch_to_thread (parent); |