public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Andrew Burgess <aburgess@redhat.com>
To: Simon Marchi <simon.marchi@polymtl.ca>, gdb-patches@sourceware.org
Subject: Re: [PATCH 1/2] gdb/infrun: make fetch_inferior_event restore thread if exited or signalled
Date: Mon, 25 Apr 2022 18:21:07 +0100	[thread overview]
Message-ID: <87sfq1oz98.fsf@redhat.com> (raw)
In-Reply-To: <20220424032049.1021263-2-simon.marchi@polymtl.ca>

Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> writes:

> Commit 152a1749566 ("gdb: prune inferiors at end of
> fetch_inferior_event, fix intermittent failure of
> gdb.threads/fork-plus-threads.exp") introduced some follow-fork-related
> test failures, such as:
>
>     info inferiors^M
>       Num  Description       Connection           Executable        ^M
>     * 1    process 634972    1 (native)           /home/simark/build/binutils-gdb-one-target/gdb/testsuite/outputs/gdb.base/foll-fork/foll-fork ^M
>       2    process 634975    1 (native)           /home/simark/build/binutils-gdb-one-target/gdb/testsuite/outputs/gdb.base/foll-fork/foll-fork ^M
>     (gdb) PASS: gdb.base/foll-fork.exp: follow-fork-mode=parent: detach-on-fork=off: cmd=next 2: test_follow_fork: info inferiors
>     inferior 2^M
>     [Switching to inferior 2 [process 634975] (/home/simark/build/binutils-gdb-one-target/gdb/testsuite/outputs/gdb.base/foll-fork/foll-fork)]^M
>     [Switching to thread 2.1 (Thread 0x7ffff7c9a740 (LWP 634975))]^M
>     #0  0x00007ffff7d7abf7 in _Fork () from /usr/lib/libc.so.6^M
>     (gdb) PASS: gdb.base/foll-fork.exp: follow-fork-mode=parent: detach-on-fork=off: cmd=next 2: test_follow_fork: inferior 2
>     continue^M
>     Continuing.^M
>     [Inferior 2 (process 634975) exited normally]^M
>     [Switching to Thread 0x7ffff7c9a740 (LWP 634972)]^M
>     (gdb) PASS: gdb.base/foll-fork.exp: follow-fork-mode=parent: detach-on-fork=off: cmd=next 2: test_follow_fork: continue until exit at continue unfollowed inferior to end
>     break callee^M
>     Breakpoint 2 at 0x555555555160: file /home/simark/src/binutils-gdb/gdb/testsuite/gdb.base/foll-fork.c, line 9.^M
>     (gdb) FAIL: gdb.base/foll-fork.exp: follow-fork-mode=parent: detach-on-fork=off: cmd=next 2: test_follow_fork: break callee
>
> What happens here is:
>
>  - inferior 2 is selected
>  - we continue, leading to inferior 2's exit
>  - we set breakpoint, expect 2 locations, but only one location is
>    resolved
>
> Reading between the lines, we understand that inferior 2 got pruned,
> when it shouldn't have been.
>
> The issue can be reproduced by hand with:
>
>     $ ./gdb -q --data-directory=data-directory testsuite/outputs/gdb.base/foll-fork/foll-fork -ex "set detach-on-fork off" -ex start -ex "next 2" -ex "inferior 2" -ex "set debug infrun"
>     ...
>     Temporary breakpoint 1, main () at /home/simark/src/binutils-gdb/gdb/testsuite/gdb.base/foll-fork.c:14
>     14        int  v = 5;
>     [New inferior 2 (process 637627)]
>     [Thread debugging using libthread_db enabled]
>     Using host libthread_db library "/usr/lib/../lib/libthread_db.so.1".
>     17        if (pid == 0) /* set breakpoint here */
>     [Switching to inferior 2 [process 637627] (/home/simark/build/binutils-gdb-one-target/gdb/testsuite/outputs/gdb.base/foll-fork/foll-fork)]
>     [Switching to thread 2.1 (Thread 0x7ffff7c9a740 (LWP 637627))]
>     #0  0x00007ffff7d7abf7 in _Fork () from /usr/lib/libc.so.6
>     (gdb) continue
>     Continuing.
>     [infrun] clear_proceed_status_thread: 637627.637627.0
>     [infrun] proceed: enter
>       [infrun] proceed: addr=0xffffffffffffffff, signal=GDB_SIGNAL_DEFAULT
>       [infrun] scoped_disable_commit_resumed: reason=proceeding
>       [infrun] start_step_over: enter
>         [infrun] start_step_over: stealing global queue of threads to step, length = 0
>         [infrun] operator(): step-over queue now empty
>       [infrun] start_step_over: exit
>       [infrun] proceed: start: resuming threads, all-stop-on-top-of-non-stop
>         [infrun] proceed: resuming 637627.637627.0
>         [infrun] resume_1: step=0, signal=GDB_SIGNAL_0, trap_expected=0, current thread [637627.637627.0] at 0x7ffff7d7abf7
>         [infrun] do_target_resume: resume_ptid=637627.637627.0, step=0, sig=GDB_SIGNAL_0
>         [infrun] infrun_async: enable=1
>         [infrun] prepare_to_wait: prepare_to_wait
>       [infrun] proceed: end: resuming threads, all-stop-on-top-of-non-stop
>       [infrun] reset: reason=proceeding
>       [infrun] maybe_set_commit_resumed_all_targets: enabling commit-resumed for target native
>       [infrun] maybe_call_commit_resumed_all_targets: calling commit_resumed for target native
>       [infrun] maybe_call_commit_resumed_all_targets: calling commit_resumed for target native
>     [infrun] proceed: exit
>     [infrun] fetch_inferior_event: enter
>       [infrun] scoped_disable_commit_resumed: reason=handling event
>       [infrun] do_target_wait: Found 2 inferiors, starting at #1
>       [infrun] random_pending_event_thread: None found.
>       [infrun] print_target_wait_results: target_wait (-1.0.0 [process -1], status) =
>       [infrun] print_target_wait_results:   637627.637627.0 [process 637627],
>       [infrun] print_target_wait_results:   status->kind = EXITED, exit_status = 0
>       [infrun] handle_inferior_event: status->kind = EXITED, exit_status = 0
>     [Inferior 2 (process 637627) exited normally]
>       [infrun] stop_waiting: stop_waiting
>       [infrun] stop_all_threads: start: reason=presenting stop to user in all-stop, inf=-1
>         [infrun] stop_all_threads: pass=0, iterations=0
>         [infrun] stop_all_threads:   637624.637624.0 not executing
>         [infrun] stop_all_threads: pass=1, iterations=1
>         [infrun] stop_all_threads:   637624.637624.0 not executing
>         [infrun] stop_all_threads: done
>       [infrun] stop_all_threads: end: reason=presenting stop to user in all-stop, inf=-1
>     [Switching to Thread 0x7ffff7c9a740 (LWP 637624)]
>       [infrun] infrun_async: enable=0
>       [infrun] reset: reason=handling event
>       [infrun] maybe_set_commit_resumed_all_targets: not requesting commit-resumed for target native, no resumed threads
>     (gdb) [infrun] fetch_inferior_event: exit
>     (gdb) info inferiors
>       Num  Description       Connection           Executable
>     * 1    process 637624    1 (native)           /home/simark/build/binutils-gdb-one-target/gdb/testsuite/outputs/gdb.base/foll-fork/foll-fork
>     (gdb) i th
>       Id   Target Id                                      Frame
>     * 1    Thread 0x7ffff7c9a740 (LWP 637624) "foll-fork" main () at /home/simark/src/binutils-gdb/gdb/testsuite/gdb.base/foll-fork.c:17
>
> After handling the EXITED event for inferior 2, inferior 2 should have
> stayed the current inferior, which should have prevented it from getting
> pruned.  When debugging, we find that when getting at the
> prune_inferiors call, the current inferior is inferior 1.  Further
> debugging shows that prior to the call to
> clean_up_just_stopped_threads_fsms, the current inferior is inferior 2,
> and after, it's inferior 1.  Then, back in fetch_inferior_event, the
> restore_thread object is disabled, due to:
>
> 	    /* If we got a TARGET_WAITKIND_NO_RESUMED event, then the
> 	       previously selected thread is gone.  We have two
> 	       choices - switch to no thread selected, or restore the
> 	       previously selected thread (now exited).  We chose the
> 	       later, just because that's what GDB used to do.  After
> 	       this, "info threads" says "The current thread <Thread
> 	       ID 2> has terminated." instead of "No thread
> 	       selected.".  */
> 	    if (!non_stop
> 		&& cmd_done
> 		&& ecs->ws.kind () != TARGET_WAITKIND_NO_RESUMED)
> 	      restore_thread.dont_restore ();
>
> So in the end, inferior 1 stays current, and inferior 2 gets wrongfully
> pruned.
>
> I'd say clean_up_just_stopped_threads_fsms is the culprit here.  It
> actually attempts to restore the event_thread to be current at the end,
> after the loop (I presume the current thread on entry is always supposed
> to be the event thread).  But in this case, the event is of kind EXITED,
> and ecs->event_thread is not set, so the current inferior isn't
> restored.
>
> Fix that by using scoped_restore_current_thread.  If there is no current
> thread, scoped_restore_current_thread will still restore the current
> inferior, and that's what we want.
>
> Random note: the thread_info object for inferior 2's thread is never
> freed.  It is held (by refcount) by the restore_thread object in
> fetch_inferior_event, while the inferior's thread list gets cleared, in
> the exit event processing.  When the refcount reaches 0 (when the
> restore_thread object is destroyed), there's nothing that actually
> deletes the thread_info object.  And I think that nothing in GDB points
> to it anymore, so it leaks.  I don't want to fix that in this patch, but
> thought it would be good to mention it, in case somebody has an idea for
> how to fix that.

Could you file this in bugzilla?

The rest of the change LGTM.

Thanks,
Andrew

>
> Change-Id: Ibc7df543e2c46aad5f3b9250b28c3fb5912be4e8
> ---
>  gdb/infrun.c | 10 +++++++---
>  1 file changed, 7 insertions(+), 3 deletions(-)
>
> diff --git a/gdb/infrun.c b/gdb/infrun.c
> index 4e7ca803c798..6dccb7a2ff2a 100644
> --- a/gdb/infrun.c
> +++ b/gdb/infrun.c
> @@ -4052,12 +4052,19 @@ reinstall_readline_callback_handler_cleanup ()
>  static void
>  clean_up_just_stopped_threads_fsms (struct execution_control_state *ecs)
>  {
> +  /* The first clean_up call below assumes the event thread is the current
> +     one.  */
> +  if (ecs->event_thread != nullptr)
> +    gdb_assert (ecs->event_thread == inferior_thread ());
> +
>    if (ecs->event_thread != nullptr
>        && ecs->event_thread->thread_fsm () != nullptr)
>      ecs->event_thread->thread_fsm ()->clean_up (ecs->event_thread);
>  
>    if (!non_stop)
>      {
> +      scoped_restore_current_thread restore_thread;
> +
>        for (thread_info *thr : all_non_exited_threads ())
>  	{
>  	  if (thr->thread_fsm () == nullptr)
> @@ -4068,9 +4075,6 @@ clean_up_just_stopped_threads_fsms (struct execution_control_state *ecs)
>  	  switch_to_thread (thr);
>  	  thr->thread_fsm ()->clean_up (thr);
>  	}
> -
> -      if (ecs->event_thread != nullptr)
> -	switch_to_thread (ecs->event_thread);
>      }
>  }
>  
> -- 
> 2.35.2


  reply	other threads:[~2022-04-25 17:21 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-04-24  3:20 [PATCH 0/2] Fix regressions caused by prune_threads patch Simon Marchi
2022-04-24  3:20 ` [PATCH 1/2] gdb/infrun: make fetch_inferior_event restore thread if exited or signalled Simon Marchi
2022-04-25 17:21   ` Andrew Burgess [this message]
2022-04-26 12:43     ` Simon Marchi
2022-04-29 12:37   ` Pedro Alves
2022-04-29 13:25     ` Simon Marchi
2022-04-24  3:20 ` [PATCH 2/2] gdb/remote: return early from remote_check_symbols if waiting for stop reply Simon Marchi
2022-04-26 10:27   ` Andrew Burgess
2022-04-26 14:15     ` Simon Marchi
2022-04-27 10:11       ` Andrew Burgess
2022-04-27 14:01         ` Simon Marchi
2022-04-29 12:50   ` Pedro Alves
2022-04-29 14:53     ` Simon Marchi
2022-04-29 15:39       ` Pedro Alves
2022-04-29 15:56         ` Simon Marchi
2022-04-29 15:47       ` Pedro Alves

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87sfq1oz98.fsf@redhat.com \
    --to=aburgess@redhat.com \
    --cc=gdb-patches@sourceware.org \
    --cc=simon.marchi@polymtl.ca \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).