binutils-gdb/gdb/testsuite/gdb.threads
Pedro Alves e0c01ce66d Don't stop all threads prematurely after first step of "step N"
In all-stop mode, when the target is itself in non-stop mode (like
GNU/Linux), if you use the "step N" (or "stepi/next/nexti N") to step
a thread a number of times:

 (gdb) help step
 step, s
 Step program until it reaches a different source line.
 Usage: step [N]
 Argument N means step N times (or till program stops for another reason).

... GDB prematurely stops all threads after the first step, and
doesn't re-resume them for the subsequent N-1 steps.  It's as if for
the 2nd and subsequent steps, the command was running with
scheduler-locking enabled.

This can be observed with the testcase added by this commit, which
looks like this:

 static pthread_barrier_t barrier;

 static void *
 thread_func (void *arg)
 {
   pthread_barrier_wait (&barrier);
   return NULL;
 }

 int
 main ()
 {
   pthread_t thread;
   int ret;

   pthread_barrier_init (&barrier, NULL, 2);

   /* We run to this line below, and then issue "next 3".  That should
      step over the 3 lines below and land on the return statement.  If
      GDB prematurely stops the thread_func thread after the first of
      the 3 nexts (and never resumes it again), then the join won't
      ever return.  */
   pthread_create (&thread, NULL, thread_func, NULL); /* set break here */
   pthread_barrier_wait (&barrier);
   pthread_join (thread, NULL);

   return 0;
 }

The test hangs and times out without the GDB fix:

 (gdb) next 3
 [New Thread 0x7ffff7d89700 (LWP 525772)]
 FAIL: gdb.threads/step-N-all-progress.exp: non-stop=off: target-non-stop=on: next 3 (timeout)

The problem is a core gdb bug.

When you do "step/stepi/next/nexti N", GDB internally creates a
thread_fsm object and associates it with the stepping thread.  For the
stepping commands, the FSM's class is step_command_fsm.  That object
is what keeps track of how many steps are left to make.  When one step
finishes, handle_inferior_event calls stop_waiting and returns, and
then fetch_inferior_event calls the "should_stop" method of the event
thread's FSM.  The implementation of that method decrements the
steps-left counter.  If the counter is 0, it returns true and we
proceed to presenting the stop to the user.  If it isn't 0 yet, then
the method returns false, indicating to fetch_inferior_event to "keep
going".

Focusing now on when the first step finishes -- we're in "all-stop"
mode, with the target in non-stop mode.  When a step finishes,
handle_inferior_event calls stop_waiting, which itself calls
stop_all_threads to stop everything.  I.e., after the first step
completes, all threads are stopped, before handle_inferior_event
returns.  And after that, now in fetch_inferior_event, we consult the
thread's thread_fsm::should_stop, which as we've seen, for the first
step returns false -- i.e., we need to keep_going for another step.
However, since the target is in non-stop mode, keep_going resumes
_only_ the current thread.  All the other threads remain stopped,
inadvertently.

If the target is in non-stop mode, we don't actually need to stop all
threads right after each step finishes, and then re-resume them again.
We can instead defer stopping all threads until all the steps are
completed.

So fix this by delaying the stopping of all threads until after we
called the FSM's "should_stop" method.  I.e., move it from
stop_waiting, to handle_inferior_events's callers,
fetch_inferior_event and wait_for_inferior.

New test included.  Tested on x86-64 GNU/Linux native and gdbserver.

Change-Id: Iaad50dcfea4464c84bdbac853a89df92ade6ae01
2022-07-20 15:10:24 +01:00
..
access-mem-running-thread-exit.c
access-mem-running-thread-exit.exp gdb: Fix DUPLICATE and PATH regressions throughout 2022-05-25 13:44:12 +01:00
async.c
async.exp
attach-into-signal.c
attach-into-signal.exp gdb/testsuite: remove unneeded calls to get_compiler_info 2022-06-24 15:07:29 +01:00
attach-many-short-lived-threads.c
attach-many-short-lived-threads.exp
attach-non-stop.c
attach-non-stop.exp
attach-slow-waitpid.c
attach-slow-waitpid.exp [gdb/testsuite] Run two test-cases with ASAN_OPTIONS=verify_asan_link_order=0 2022-07-12 13:58:31 +02:00
attach-stopped.c
attach-stopped.exp gdb/testsuite: remove unneeded calls to get_compiler_info 2022-06-24 15:07:29 +01:00
bp_in_thread.c
bp_in_thread.exp
break-while-running.c
break-while-running.exp
check-libthread-db.c
check-libthread-db.exp
clone-attach-detach.c
clone-attach-detach.exp
clone-new-thread-event.c
clone-new-thread-event.exp
clone-thread_db.c
clone-thread_db.exp
continue-pending-after-query.c
continue-pending-after-query.exp
continue-pending-status.c
continue-pending-status.exp
corethreads.c
corethreads.exp
create-fail.c
create-fail.exp
current-lwp-dead.c
current-lwp-dead.exp
detach-step-over.c
detach-step-over.exp
dlopen-libpthread-lib.c
dlopen-libpthread.c
dlopen-libpthread.exp
execl1.c
execl.c
execl.exp
fork-child-threads.c
fork-child-threads.exp
fork-plus-threads.c
fork-plus-threads.exp
fork-thread-pending.c
fork-thread-pending.exp
forking-threads-plus-breakpoint.c
forking-threads-plus-breakpoint.exp
gcore-stale-thread.c
gcore-stale-thread.exp
gcore-thread.exp
hand-call-in-threads.c
hand-call-in-threads.exp
hand-call-new-thread.c
hand-call-new-thread.exp
ia64-sigill.c
ia64-sigill.exp
info-threads-cur-sal-2.c
info-threads-cur-sal.c
info-threads-cur-sal.exp
interrupt-while-step-over.c
interrupt-while-step-over.exp
interrupted-hand-call.c
interrupted-hand-call.exp
kill.c
kill.exp
killed-outside.c
killed-outside.exp
killed.c
killed.exp
leader-exit.c
leader-exit.exp
linux-dp.c
linux-dp.exp
local-watch-wrong-thread.c
local-watch-wrong-thread.exp
manythreads.c
manythreads.exp [gdb/testsuite] Fix gdb.threads/manythreads.exp with check-read1 2022-06-04 11:16:22 +02:00
multi-create-ns-info-thr.exp
multi-create.c
multi-create.exp
multiple-step-overs.c
multiple-step-overs.exp
multiple-successive-infcall.c
multiple-successive-infcall.exp gdb/testsuite: remove unneeded calls to get_compiler_info 2022-06-24 15:07:29 +01:00
names.c
names.exp
next-bp-other-thread.c
next-bp-other-thread.exp
next-fork-other-thread.c
next-fork-other-thread.exp
next-while-other-thread-longjmps.c
next-while-other-thread-longjmps.exp
no-unwaited-for-left.c
no-unwaited-for-left.exp Tighten gdb.threads/no-unwaited-for-left.exp regexps 2022-07-13 22:29:25 +01:00
non-ldr-exc-1.c
non-ldr-exc-1.exp
non-ldr-exc-2.c
non-ldr-exc-2.exp
non-ldr-exc-3.c
non-ldr-exc-3.exp
non-ldr-exc-4.c
non-ldr-exc-4.exp
non-ldr-exit.c
non-ldr-exit.exp
non-stop-fair-events.c
non-stop-fair-events.exp
omp-par-scope.c
omp-par-scope.exp
omp-task.c
omp-task.exp
pending-fork-event-detach-ns.c
pending-fork-event-detach-ns.exp
pending-fork-event-detach-touch-file.c
pending-fork-event-detach.c
pending-fork-event-detach.exp
pending-step.c
pending-step.exp
print-threads.c
print-threads.exp
process-dies-while-detaching.c
process-dies-while-detaching.exp
process-dies-while-handling-bp.c
process-dies-while-handling-bp.exp
process-exit-status-is-leader-exit-status.c
process-exit-status-is-leader-exit-status.exp
pthread_cond_wait.c
pthread_cond_wait.exp
pthreads.c
pthreads.exp
queue-signal.c
queue-signal.exp
reconnect-signal.c
reconnect-signal.exp
schedlock-thread-exit.c
schedlock-thread-exit.exp
schedlock.c
schedlock.exp
siginfo-threads.c
siginfo-threads.exp
signal-command-handle-nopass.c
signal-command-handle-nopass.exp
signal-command-multiple-signals-pending.c
signal-command-multiple-signals-pending.exp
signal-delivered-right-thread.c
signal-delivered-right-thread.exp
signal-sigtrap.c
signal-sigtrap.exp
signal-while-stepping-over-bp-other-thread.c
signal-while-stepping-over-bp-other-thread.exp
sigstep-threads.c
sigstep-threads.exp gdb: Fix DUPLICATE and PATH regressions throughout 2022-05-25 13:44:12 +01:00
sigthread.c
sigthread.exp
slow-waitpid.c
staticthreads.c
staticthreads.exp
step-bg-decr-pc-switch-thread.c
step-bg-decr-pc-switch-thread.exp
step-N-all-progress.c Don't stop all threads prematurely after first step of "step N" 2022-07-20 15:10:24 +01:00
step-N-all-progress.exp Don't stop all threads prematurely after first step of "step N" 2022-07-20 15:10:24 +01:00
step-over-exec-execd.c
step-over-exec.c
step-over-exec.exp
step-over-lands-on-breakpoint.c
step-over-lands-on-breakpoint.exp
step-over-trips-on-watchpoint.c
step-over-trips-on-watchpoint.exp
stepi-random-signal.c
stepi-random-signal.exp
stop-with-handle.c
stop-with-handle.exp
switch-threads.c
switch-threads.exp
thread_check.c
thread_check.exp
thread_events.c
thread_events.exp
thread-execl.c
thread-execl.exp
thread-find.exp
thread-specific-bp.c
thread-specific-bp.exp
thread-specific.c
thread-specific.exp
thread-unwindonsignal.exp
threadapply.c
threadapply.exp
threxit-hop-specific.c
threxit-hop-specific.exp
tid-reuse.c
tid-reuse.exp
tls2.c
tls-core.c
tls-core.exp
tls-main.c
tls-nodebug-pie.c
tls-nodebug-pie.exp
tls-nodebug.c
tls-nodebug.exp
tls-shared.c
tls-shared.exp gdb/testsuite: remove global gcc_compiled from gdb.exp 2022-06-24 15:07:28 +01:00
tls-so_extern_main.c
tls-so_extern.c
tls-so_extern.exp gdb/testsuite: remove global gcc_compiled from gdb.exp 2022-06-24 15:07:28 +01:00
tls-var-main.c
tls-var.c
tls-var.exp
tls.c
tls.exp gdb/testsuite: resolve duplicate test names in gdb.threads/tls.exp 2022-06-09 13:40:07 +01:00
vfork-follow-child-exec.c
vfork-follow-child-exec.exp
vfork-follow-child-exit.c
vfork-follow-child-exit.exp
vfork-multi-inferior-sleep.c
vfork-multi-inferior.c
vfork-multi-inferior.exp
vfork-multi-thread.c
vfork-multi-thread.exp
watchpoint-fork-child.c
watchpoint-fork-mt.c
watchpoint-fork-parent.c
watchpoint-fork-st.c
watchpoint-fork.exp
watchpoint-fork.h
watchthreads2.c
watchthreads2.exp
watchthreads-reorder.c
watchthreads-reorder.exp
watchthreads.c
watchthreads.exp
wp-replication.c
wp-replication.exp