aboutsummaryrefslogtreecommitdiff
path: root/gdb/infrun.c
diff options
context:
space:
mode:
authorPedro Alves <pedro@palves.net>2021-11-30 19:52:11 +0000
committerPedro Alves <pedro@palves.net>2023-11-13 14:16:11 +0000
commit7ac958f267898ba87ae67ea04f94a3fda24567dc (patch)
treed085d38a04a17dbd2e4f14d18b6f62a5cb2b6647 /gdb/infrun.c
parentef980d654ba22aad6b5b301179dd105f522b56a1 (diff)
downloadbinutils-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.c41
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);