[v4,2/2] gdb/infrun: handle already-exited threads when attempting to stop

Message ID 2aff425c4d358fe95f67c2898bd8f7cd578d5bcb.1581065751.git.tankut.baris.aktemur@intel.com
State New
Headers show
Series
  • Handling already-exited threads in 'stop_all_threads'
Related show

Commit Message

Tankut Baris Aktemur Feb. 7, 2020, 12:04 p.m.
In stop_all_threads, GDB sends signals to other threads in an attempt
to stop them.  While in a typical scenario the expected wait status is
TARGET_WAITKIND_STOPPED, it is possible that the thread GDB attempted
to stop has already terminated.  If so, a waitstatus other than
TARGET_WAITKIND_STOPPED would be received.  Handle this case
appropriately.

If a wait status that denotes thread termination is ignored, GDB goes
into an infinite loop in stop_all_threads.
E.g.:

~~~
$ gdb ./a.out
(gdb) start
...
(gdb) add-inferior -exec ./a.out
...
(gdb) inferior 2
...
(gdb) start
...
(gdb) set schedule-multiple on
(gdb) set debug infrun 2
(gdb) continue
Continuing.
infrun: clear_proceed_status_thread (process 23419)
infrun: clear_proceed_status_thread (process 23703)
infrun: proceed (addr=0xffffffffffffffff, signal=GDB_SIGNAL_DEFAULT)
infrun: proceed: resuming process 23419
infrun: resume (step=0, signal=GDB_SIGNAL_0), trap_expected=0, current thread [process 23419] at 0x55555555514e
infrun: infrun_async(1)
infrun: prepare_to_wait
infrun: proceed: resuming process 23703
infrun: resume (step=0, signal=GDB_SIGNAL_0), trap_expected=0, current thread [process 23703] at 0x55555555514e
infrun: prepare_to_wait
infrun: target_wait (-1.0.0, status) =
infrun:   23703.23703.0 [process 23703],
infrun:   status->kind = exited, status = 0
infrun: handle_inferior_event status->kind = exited, status = 0
[Inferior 2 (process 23703) exited normally]
infrun: stop_waiting
infrun: stop_all_threads
infrun: stop_all_threads, pass=0, iterations=0
infrun:   process 23419 executing, need stop
infrun: target_wait (-1.0.0, status) =
infrun:   23419.23419.0 [process 23419],
infrun:   status->kind = exited, status = 0
infrun: stop_all_threads status->kind = exited, status = 0 process 23419
infrun:   process 23419 executing, already stopping
infrun: target_wait (-1.0.0, status) =
infrun:   -1.0.0 [process -1],
infrun:   status->kind = no-resumed
infrun: stop_all_threads status->kind = no-resumed process -1
infrun:   process 23419 executing, already stopping
infrun: target_wait (-1.0.0, status) =
infrun:   -1.0.0 [process -1],
infrun:   status->kind = no-resumed
infrun: stop_all_threads status->kind = no-resumed process -1
infrun:   process 23419 executing, already stopping
infrun: target_wait (-1.0.0, status) =
infrun:   -1.0.0 [process -1],
infrun:   status->kind = no-resumed
infrun: stop_all_threads status->kind = no-resumed process -1
infrun:   process 23419 executing, already stopping
infrun: target_wait (-1.0.0, status) =
infrun:   -1.0.0 [process -1],
infrun:   status->kind = no-resumed
infrun: stop_all_threads status->kind = no-resumed process -1
infrun:   process 23419 executing, already stopping
~~~

And this polling goes on forever.  This patch prevents the infinite
looping behavior.  For the same scenario above, we obtain the
following behavior:

~~~
...
(gdb) continue
Continuing.
infrun: clear_proceed_status_thread (process 8564)
infrun: clear_proceed_status_thread (process 8652)
infrun: proceed (addr=0xffffffffffffffff, signal=GDB_SIGNAL_DEFAULT)
infrun: proceed: resuming process 8564
infrun: resume (step=0, signal=GDB_SIGNAL_0), trap_expected=0, current thread [process 8564] at 0x55555555514e
infrun: infrun_async(1)
infrun: prepare_to_wait
infrun: proceed: resuming process 8652
infrun: resume (step=0, signal=GDB_SIGNAL_0), trap_expected=0, current thread [process 8652] at 0x55555555514e
infrun: prepare_to_wait
infrun: Found 2 inferiors, starting at #0
infrun: target_wait (-1.0.0, status) =
infrun:   8564.8564.0 [process 8564],
infrun:   status->kind = exited, status = 0
infrun: handle_inferior_event status->kind = exited, status = 0
[Inferior 1 (process 8564) exited normally]
infrun: stop_waiting
infrun: stop_all_threads
infrun: stop_all_threads, pass=0, iterations=0
infrun:   process 8652 executing, need stop
infrun: target_wait (-1.0.0, status) =
infrun:   8652.8652.0 [process 8652],
infrun:   status->kind = exited, status = 0
infrun: stop_all_threads status->kind = exited, status = 0 process 8652
infrun: saving status status->kind = exited, status = 0 for 8652.8652.0
infrun:   process 8652 not executing
infrun: stop_all_threads, pass=1, iterations=1
infrun:   process 8652 not executing
infrun: stop_all_threads done
(gdb) infrun: Using pending wait status status->kind = exited, status = 0 for process 8652.
infrun: target_wait (-1.0.0, status) =
infrun:   8652.8652.0 [process 8652],
infrun:   status->kind = exited, status = 0
infrun: handle_inferior_event status->kind = exited, status = 0
[Inferior 2 (process 8652) exited normally]
infrun: stop_waiting
info inferiors
  Num  Description       Connection           Executable
  1    <null>                                 a.out
* 2    <null>                                 a.out
(gdb) info threads
No threads.
(gdb)
~~~

gdb/ChangeLog:
2020-02-05  Tankut Baris Aktemur  <tankut.baris.aktemur@intel.com>
	    Tom de Vries  <tdevries@suse.de>

	PR threads/25478
	* infrun.c (stop_all_threads): Do NOT ignore
	TARGET_WAITKIND_NO_RESUMED, TARGET_WAITKIND_THREAD_EXITED,
	TARGET_WAITKIND_EXITED, TARGET_WAITKIND_SIGNALLED wait statuses
	received from threads we attempt to stop.

gdb/testsuite/ChangeLog:
2019-11-04  Tankut Baris Aktemur  <tankut.baris.aktemur@intel.com>

	* gdb.multi/multi-exit.c: New file.
	* gdb.multi/multi-exit.exp: New file.
	* gdb.multi/multi-kill.c: New file.
	* gdb.multi/multi-kill.exp: New file.

Change-Id: I7cec98f40283773b79255d998511da434e9cd408
---
 gdb/infrun.c                           | 38 ++++++++++--
 gdb/testsuite/gdb.multi/multi-exit.c   | 22 +++++++
 gdb/testsuite/gdb.multi/multi-exit.exp | 81 +++++++++++++++++++++++++
 gdb/testsuite/gdb.multi/multi-kill.c   | 34 +++++++++++
 gdb/testsuite/gdb.multi/multi-kill.exp | 84 ++++++++++++++++++++++++++
 5 files changed, 254 insertions(+), 5 deletions(-)
 create mode 100644 gdb/testsuite/gdb.multi/multi-exit.c
 create mode 100644 gdb/testsuite/gdb.multi/multi-exit.exp
 create mode 100644 gdb/testsuite/gdb.multi/multi-kill.c
 create mode 100644 gdb/testsuite/gdb.multi/multi-kill.exp

-- 
2.17.1

Comments

Tankut Baris Aktemur via Gdb-patches April 2, 2020, 6 p.m. | #1
Hi,

On 2/7/20 12:04 PM, Tankut Baris Aktemur wrote:

> ...

> (gdb) continue

> Continuing.

> infrun: clear_proceed_status_thread (process 8564)

> infrun: clear_proceed_status_thread (process 8652)

> infrun: proceed (addr=0xffffffffffffffff, signal=GDB_SIGNAL_DEFAULT)

> infrun: proceed: resuming process 8564

> infrun: resume (step=0, signal=GDB_SIGNAL_0), trap_expected=0, current thread [process 8564] at 0x55555555514e

> infrun: infrun_async(1)

> infrun: prepare_to_wait

> infrun: proceed: resuming process 8652

> infrun: resume (step=0, signal=GDB_SIGNAL_0), trap_expected=0, current thread [process 8652] at 0x55555555514e

> infrun: prepare_to_wait

> infrun: Found 2 inferiors, starting at #0

> infrun: target_wait (-1.0.0, status) =

> infrun:   8564.8564.0 [process 8564],

> infrun:   status->kind = exited, status = 0

> infrun: handle_inferior_event status->kind = exited, status = 0

> [Inferior 1 (process 8564) exited normally]

> infrun: stop_waiting

> infrun: stop_all_threads

> infrun: stop_all_threads, pass=0, iterations=0

> infrun:   process 8652 executing, need stop

> infrun: target_wait (-1.0.0, status) =

> infrun:   8652.8652.0 [process 8652],

> infrun:   status->kind = exited, status = 0

> infrun: stop_all_threads status->kind = exited, status = 0 process 8652

> infrun: saving status status->kind = exited, status = 0 for 8652.8652.0

> infrun:   process 8652 not executing

> infrun: stop_all_threads, pass=1, iterations=1

> infrun:   process 8652 not executing

> infrun: stop_all_threads done


Looks good so far, but ...

> (gdb) infrun: Using pending wait status status->kind = exited, status = 0 for process 8652.

> infrun: target_wait (-1.0.0, status) =

> infrun:   8652.8652.0 [process 8652],

> infrun:   status->kind = exited, status = 0

> infrun: handle_inferior_event status->kind = exited, status = 0

> [Inferior 2 (process 8652) exited normally]


... how come GDB collected the pending event without an intervening
"continue" ?

As previously discussed, the event for the other inferior should be
left pending until that inferior/thread is re-resumed.  That is now
done in your patch.  But, if gdb is consuming the pending event right
after showing the prompt for the first inferior exit, then the second
inferior/thread wasn't correctly marked as NOT resumed.

Once we do that, I think we'll still hit the issues discussed
earlier at <https://sourceware.org/legacy-ml/gdb-patches/2020-01/msg00212.html>
like that:

 (gdb) c
 Continuing.
 Couldn't get registers: No such process.
 (gdb) 

But maybe changes for that have gone in meanwhile and I haven't noticed?


> infrun: stop_waiting

> info inferiors

>   Num  Description       Connection           Executable

>   1    <null>                                 a.out

> * 2    <null>                                 a.out

> (gdb) info threads

> No threads.

> (gdb)

> ~~~

> 

> gdb/ChangeLog:

> 2020-02-05  Tankut Baris Aktemur  <tankut.baris.aktemur@intel.com>

> 	    Tom de Vries  <tdevries@suse.de>

> 

> 	PR threads/25478

> 	* infrun.c (stop_all_threads): Do NOT ignore

> 	TARGET_WAITKIND_NO_RESUMED, TARGET_WAITKIND_THREAD_EXITED,

> 	TARGET_WAITKIND_EXITED, TARGET_WAITKIND_SIGNALLED wait statuses

> 	received from threads we attempt to stop.

> 

> gdb/testsuite/ChangeLog:

> 2019-11-04  Tankut Baris Aktemur  <tankut.baris.aktemur@intel.com>

> 

> 	* gdb.multi/multi-exit.c: New file.

> 	* gdb.multi/multi-exit.exp: New file.

> 	* gdb.multi/multi-kill.c: New file.

> 	* gdb.multi/multi-kill.exp: New file.

> 

> Change-Id: I7cec98f40283773b79255d998511da434e9cd408

> ---

>  gdb/infrun.c                           | 38 ++++++++++--

>  gdb/testsuite/gdb.multi/multi-exit.c   | 22 +++++++

>  gdb/testsuite/gdb.multi/multi-exit.exp | 81 +++++++++++++++++++++++++

>  gdb/testsuite/gdb.multi/multi-kill.c   | 34 +++++++++++

>  gdb/testsuite/gdb.multi/multi-kill.exp | 84 ++++++++++++++++++++++++++

>  5 files changed, 254 insertions(+), 5 deletions(-)

>  create mode 100644 gdb/testsuite/gdb.multi/multi-exit.c

>  create mode 100644 gdb/testsuite/gdb.multi/multi-exit.exp

>  create mode 100644 gdb/testsuite/gdb.multi/multi-kill.c

>  create mode 100644 gdb/testsuite/gdb.multi/multi-kill.exp

> 

> diff --git a/gdb/infrun.c b/gdb/infrun.c

> index 9224376d05e..58a6da9ec32 100644

> --- a/gdb/infrun.c

> +++ b/gdb/infrun.c

> @@ -4799,9 +4799,14 @@ stop_all_threads (void)

>  					"infrun:   %s not executing\n",

>  					target_pid_to_str (t->ptid).c_str ());

>  

> -		  /* The thread may be not executing, but still be

> -		     resumed with a pending status to process.  */

> -		  t->resumed = false;

> +		  if ((t->suspend.waitstatus.kind == TARGET_WAITKIND_SIGNALLED

> +		       || t->suspend.waitstatus.kind == TARGET_WAITKIND_EXITED)

> +		      && t->suspend.waitstatus_pending_p)

> +		    ;

> +		  else

> +		    /* The thread may be not executing, but still be

> +		       resumed with a pending status to process.  */

> +		    t->resumed = false;

>  		}

>  	    }

>  

> @@ -4829,8 +4834,31 @@ stop_all_threads (void)

>  	      || event.ws.kind == TARGET_WAITKIND_EXITED

>  	      || event.ws.kind == TARGET_WAITKIND_SIGNALLED)

>  	    {

> -	      /* All resumed threads exited

> -		 or one thread/process exited/signalled.  */

> +	      /* Eventing target is null if there were no waitable

> +		 targets.  */

> +	      if (event.target != nullptr)


Instead of this nullptr check, can't we write the conditions as:


	  if (event.ws.kind == TARGET_WAITKIND_NO_RESUMED)
            {
	      /* All resumed threads exited.  */
            }
          else if (event.ws.kind == TARGET_WAITKIND_THREAD_EXITED
                   || event.ws.kind == TARGET_WAITKIND_EXITED
                   || event.ws.kind == TARGET_WAITKIND_SIGNALLED)
	    {
	      /* One thread/process exited/signalled.  */
              ...
	    }

?

> +		{

> +		  thread_info *t = find_thread_ptid (event.target,

> +						     event.ptid);

> +		  /* Check if this is the first time we see this thread.

> +		     Don't bother adding if it individually exited.  */

> +		  if (t == nullptr

> +		      && event.ws.kind != TARGET_WAITKIND_THREAD_EXITED)

> +		    t = add_thread (event.target, event.ptid);

> +


This doesn't look right for all targets.  On native GNU/Linux,
the ptid returned by TARGET_WAITKIND_EXITED happens to be the main
thread's ptid usually, 

 infrun: target_wait (-1.0.0, status) =
 infrun:   7941.7941.0 [process 7941],
           ^^^^^^^^^
 infrun:   status->kind = signalled, signal = GDB_SIGNAL_ALRM

but on e.g., remote targets, it will be just the pid of the process with
tid/lwpid fields == 0, 

 infrun: target_wait (-1.0.0, status) =
 infrun:   8262.0.0 [process 8262],
 infrun:   status->kind = signalled, signal = GDB_SIGNAL_ALRM

(Looks like darwin-nat.c is the same.)

so this will probably add a thread with such a ptid, which seems incorrect.
I think we should still be able to find a thread in the list for the exiting
inferior here, so if we don't find a thread by ptid, I guess we could use the
first non-exited thread in the inferior to save the status.  Otherwise
we'd have to save the status in the inferior or in a separate event list.

Actually, looking at windows-nat.c's, EXIT_PROCESS_DEBUG_EVENT handling,
that deletes the event thread before returning the TARGET_WAITKIND_EXITED
event...  :-/  I'm not sure we can delete that windows_delete_thread
call.  Maybe we can.

BTW, the new test fails with --target_board=native-extended-gdbserver:

 Running /home/pedro/gdb/binutils-gdb/src/gdb/testsuite/gdb.multi/multi-kill.exp ...
 FAIL: gdb.multi/multi-kill.exp: iteration 2: attach to program 1 (got interactive prompt)
 Running /home/pedro/gdb/binutils-gdb/src/gdb/testsuite/gdb.multi/multi-exit.exp ...
 FAIL: gdb.multi/multi-exit.exp: iteration 1: continue (timeout)

Thanks,
Pedro Alves
Tankut Baris Aktemur via Gdb-patches April 6, 2020, 3:45 p.m. | #2
On Thursday, April 2, 2020 8:00 PM, Pedro Alves wrote:
> Hi,

> 

> On 2/7/20 12:04 PM, Tankut Baris Aktemur wrote:

> 

> > ...

> > (gdb) continue

> > Continuing.

> > infrun: clear_proceed_status_thread (process 8564)

> > infrun: clear_proceed_status_thread (process 8652)

> > infrun: proceed (addr=0xffffffffffffffff, signal=GDB_SIGNAL_DEFAULT)

> > infrun: proceed: resuming process 8564

> > infrun: resume (step=0, signal=GDB_SIGNAL_0), trap_expected=0, current thread [process 8564] at 0x55555555514e

> > infrun: infrun_async(1)

> > infrun: prepare_to_wait

> > infrun: proceed: resuming process 8652

> > infrun: resume (step=0, signal=GDB_SIGNAL_0), trap_expected=0, current thread [process 8652] at 0x55555555514e

> > infrun: prepare_to_wait

> > infrun: Found 2 inferiors, starting at #0

> > infrun: target_wait (-1.0.0, status) =

> > infrun:   8564.8564.0 [process 8564],

> > infrun:   status->kind = exited, status = 0

> > infrun: handle_inferior_event status->kind = exited, status = 0

> > [Inferior 1 (process 8564) exited normally]

> > infrun: stop_waiting

> > infrun: stop_all_threads

> > infrun: stop_all_threads, pass=0, iterations=0

> > infrun:   process 8652 executing, need stop

> > infrun: target_wait (-1.0.0, status) =

> > infrun:   8652.8652.0 [process 8652],

> > infrun:   status->kind = exited, status = 0

> > infrun: stop_all_threads status->kind = exited, status = 0 process 8652

> > infrun: saving status status->kind = exited, status = 0 for 8652.8652.0

> > infrun:   process 8652 not executing

> > infrun: stop_all_threads, pass=1, iterations=1

> > infrun:   process 8652 not executing

> > infrun: stop_all_threads done

> 

> Looks good so far, but ...

> 

> > (gdb) infrun: Using pending wait status status->kind = exited, status = 0 for process 8652.

> > infrun: target_wait (-1.0.0, status) =

> > infrun:   8652.8652.0 [process 8652],

> > infrun:   status->kind = exited, status = 0

> > infrun: handle_inferior_event status->kind = exited, status = 0

> > [Inferior 2 (process 8652) exited normally]

> 

> ... how come GDB collected the pending event without an intervening

> "continue" ?

> 

> As previously discussed, the event for the other inferior should be

> left pending until that inferior/thread is re-resumed.  That is now

> done in your patch.  But, if gdb is consuming the pending event right

> after showing the prompt for the first inferior exit, then the second

> inferior/thread wasn't correctly marked as NOT resumed.


The next revision, v5, fixes this.  There, we get the GDB prompt and it stops there.

> Once we do that, I think we'll still hit the issues discussed

> earlier at <https://sourceware.org/legacy-ml/gdb-patches/2020-01/msg00212.html>

> like that:

> 

>  (gdb) c

>  Continuing.

>  Couldn't get registers: No such process.

>  (gdb)

> 

> But maybe changes for that have gone in meanwhile and I haven't noticed?


Unfortunately it exists.  In fact, GDB goes into an infinite query.  I applied
Tom de Vries' patch submitted at

  https://sourceware.org/pipermail/gdb-patches/2020-March/166982.html

It would be great if you could check that patch.

There are also three more places where the PC of the thread is queried when 
the second "continue" command is issued to resume the second inferior.  I added
predecessor patches into the series to address these.

Please see Patch 05's commit message for the output of a sample session.  The
"Couldn't get registers" error message is still seen, but only once.  If the user
issues commands that trigger the register query (e.g.  "info threads"), the error
message will be seen again.  I considered that as a separate problem and did not
attempt to fix it as part of this series.

> > @@ -4829,8 +4834,31 @@ stop_all_threads (void)

> >  	      || event.ws.kind == TARGET_WAITKIND_EXITED

> >  	      || event.ws.kind == TARGET_WAITKIND_SIGNALLED)

> >  	    {

> > -	      /* All resumed threads exited

> > -		 or one thread/process exited/signalled.  */

> > +	      /* Eventing target is null if there were no waitable

> > +		 targets.  */

> > +	      if (event.target != nullptr)

> 

> Instead of this nullptr check, can't we write the conditions as:

> 

> 

> 	  if (event.ws.kind == TARGET_WAITKIND_NO_RESUMED)

>             {

> 	      /* All resumed threads exited.  */

>             }

>           else if (event.ws.kind == TARGET_WAITKIND_THREAD_EXITED

>                    || event.ws.kind == TARGET_WAITKIND_EXITED

>                    || event.ws.kind == TARGET_WAITKIND_SIGNALLED)

> 	    {

> 	      /* One thread/process exited/signalled.  */

>               ...

> 	    }

> 

> ?


I made this change in v5.

> > +		{

> > +		  thread_info *t = find_thread_ptid (event.target,

> > +						     event.ptid);

> > +		  /* Check if this is the first time we see this thread.

> > +		     Don't bother adding if it individually exited.  */

> > +		  if (t == nullptr

> > +		      && event.ws.kind != TARGET_WAITKIND_THREAD_EXITED)

> > +		    t = add_thread (event.target, event.ptid);

> > +

> 

> This doesn't look right for all targets.  On native GNU/Linux,

> the ptid returned by TARGET_WAITKIND_EXITED happens to be the main

> thread's ptid usually,

> 

>  infrun: target_wait (-1.0.0, status) =

>  infrun:   7941.7941.0 [process 7941],

>            ^^^^^^^^^

>  infrun:   status->kind = signalled, signal = GDB_SIGNAL_ALRM

> 

> but on e.g., remote targets, it will be just the pid of the process with

> tid/lwpid fields == 0,

> 

>  infrun: target_wait (-1.0.0, status) =

>  infrun:   8262.0.0 [process 8262],

>  infrun:   status->kind = signalled, signal = GDB_SIGNAL_ALRM

> 

> (Looks like darwin-nat.c is the same.)

> 

> so this will probably add a thread with such a ptid, which seems incorrect.

> I think we should still be able to find a thread in the list for the exiting

> inferior here, so if we don't find a thread by ptid, I guess we could use the

> first non-exited thread in the inferior to save the status.  Otherwise

> we'd have to save the status in the inferior or in a separate event list.


In v5, the first non-exited thread is used.  I also added a test scenario that uses
gdbserver to cover this case.

> Actually, looking at windows-nat.c's, EXIT_PROCESS_DEBUG_EVENT handling,

> that deletes the event thread before returning the TARGET_WAITKIND_EXITED

> event...  :-/  I'm not sure we can delete that windows_delete_thread

> call.  Maybe we can.


Err... I'm not able to comment on this.  In v5, I put a gdb_assert after attempting
to use the first non-exited thread.  This way, at least it can make the problem stand
out instead of seemingly hanging.

> BTW, the new test fails with --target_board=native-extended-gdbserver:

> 

>  Running /home/pedro/gdb/binutils-gdb/src/gdb/testsuite/gdb.multi/multi-kill.exp ...

>  FAIL: gdb.multi/multi-kill.exp: iteration 2: attach to program 1 (got interactive prompt)

>  Running /home/pedro/gdb/binutils-gdb/src/gdb/testsuite/gdb.multi/multi-exit.exp ...

>  FAIL: gdb.multi/multi-exit.exp: iteration 1: continue (timeout)


I added a guard at the beginning of the test, inspired from multi-target.exp, to exit the test
if multiple inferiors cannot be defined.

Thanks.
-Baris


Intel Deutschland GmbH
Registered Address: Am Campeon 10-12, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Christin Eisenschmid, Gary Kershaw
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

Patch

diff --git a/gdb/infrun.c b/gdb/infrun.c
index 9224376d05e..58a6da9ec32 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -4799,9 +4799,14 @@  stop_all_threads (void)
 					"infrun:   %s not executing\n",
 					target_pid_to_str (t->ptid).c_str ());
 
-		  /* The thread may be not executing, but still be
-		     resumed with a pending status to process.  */
-		  t->resumed = false;
+		  if ((t->suspend.waitstatus.kind == TARGET_WAITKIND_SIGNALLED
+		       || t->suspend.waitstatus.kind == TARGET_WAITKIND_EXITED)
+		      && t->suspend.waitstatus_pending_p)
+		    ;
+		  else
+		    /* The thread may be not executing, but still be
+		       resumed with a pending status to process.  */
+		    t->resumed = false;
 		}
 	    }
 
@@ -4829,8 +4834,31 @@  stop_all_threads (void)
 	      || event.ws.kind == TARGET_WAITKIND_EXITED
 	      || event.ws.kind == TARGET_WAITKIND_SIGNALLED)
 	    {
-	      /* All resumed threads exited
-		 or one thread/process exited/signalled.  */
+	      /* Eventing target is null if there were no waitable
+		 targets.  */
+	      if (event.target != nullptr)
+		{
+		  thread_info *t = find_thread_ptid (event.target,
+						     event.ptid);
+		  /* Check if this is the first time we see this thread.
+		     Don't bother adding if it individually exited.  */
+		  if (t == nullptr
+		      && event.ws.kind != TARGET_WAITKIND_THREAD_EXITED)
+		    t = add_thread (event.target, event.ptid);
+
+		  if (t != nullptr)
+		    {
+		      /* Set the threads as non-executing to avoid
+			 another stop attempt on them.  */
+		      mark_non_executing_threads (event.target, event.ptid,
+						  event.ws);
+		      save_waitstatus (t, &event.ws);
+		      /* This was cleared in mark_non_executing_threads.
+			 Set it so this pending event is considered by
+			 do_target_wait.  */
+		      t->resumed = true;
+		    }
+		}
 	    }
 	  else
 	    {
diff --git a/gdb/testsuite/gdb.multi/multi-exit.c b/gdb/testsuite/gdb.multi/multi-exit.c
new file mode 100644
index 00000000000..f4825c8a7c1
--- /dev/null
+++ b/gdb/testsuite/gdb.multi/multi-exit.c
@@ -0,0 +1,22 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2020 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int
+main ()
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.multi/multi-exit.exp b/gdb/testsuite/gdb.multi/multi-exit.exp
new file mode 100644
index 00000000000..959f0e8740a
--- /dev/null
+++ b/gdb/testsuite/gdb.multi/multi-exit.exp
@@ -0,0 +1,81 @@ 
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2020 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test receiving TARGET_WAITKIND_EXITED events from multiple
+# inferiors.  In all stop-mode, upon receiving the exit event from one
+# of the inferiors, GDB will try to stop the other inferior, too.  So,
+# a stop request will be sent.  Receiving a TARGET_WAITKIND_EXITED
+# status kind as a response to that stop request instead of a
+# TARGET_WAITKIND_STOPPED should be handled by GDB without problems.
+
+standard_testfile
+
+# This is a test specific for native GDB's ability to stop all
+# threads.
+if {![can_spawn_for_attach]} {
+    return 0
+}
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile]} {
+    return -1
+}
+
+gdb_test "add-inferior -exec $binfile" "Added inferior 2.*" \
+    "add the second inferior"
+
+proc test {} {
+    # Start the processes separately.
+    gdb_test_no_output "set schedule-multiple off"
+
+    gdb_test "inferior 1" ".*Switching to inferior 1.*"
+    if {![runto_main]} {
+	fail "starting inferior 1"
+	return -1
+    }
+
+    gdb_test "inferior 2" ".*Switching to inferior 2.*"
+    if {![runto_main]} {
+	fail "starting inferior 2"
+	return -1
+    }
+
+    # Now continue both processes.
+    gdb_test_no_output "set schedule-multiple on"
+
+    # We want GDB to complete the command and return the prompt
+    # instead of going into an infinite loop.
+    # Both inferiors exit.
+    global inferior_exited_re gdb_prompt
+    set count 0
+    gdb_test_multiple "continue" "continue" {
+	-re "$inferior_exited_re normally\]\[\r\n\]+" {
+	    incr count
+	    if {$count == 2} {
+		pass $gdb_test_name
+	    } else {
+		exp_continue
+	    }
+	}
+	-re "$gdb_prompt " {
+	    exp_continue
+	}
+    }
+}
+
+with_test_prefix "iteration 1" test
+# Repeat to also test re-runnability.
+with_test_prefix "iteration 2" test
diff --git a/gdb/testsuite/gdb.multi/multi-kill.c b/gdb/testsuite/gdb.multi/multi-kill.c
new file mode 100644
index 00000000000..3622c499202
--- /dev/null
+++ b/gdb/testsuite/gdb.multi/multi-kill.c
@@ -0,0 +1,34 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2020 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* This program is intended to be started outside of gdb, and then
+   attached to by gdb.  */
+
+#include <unistd.h>
+
+int
+main ()
+{
+  /* Don't run forever in case GDB crashes and DejaGNU fails to kill
+     this program.  */
+  alarm (10);
+
+  while (1)
+    ;
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.multi/multi-kill.exp b/gdb/testsuite/gdb.multi/multi-kill.exp
new file mode 100644
index 00000000000..6d509d9f0ba
--- /dev/null
+++ b/gdb/testsuite/gdb.multi/multi-kill.exp
@@ -0,0 +1,84 @@ 
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2020 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test receiving TARGET_WAITKIND_SIGNALLED events from multiple
+# inferiors.  In all stop-mode, upon receiving the exit event from one
+# of the inferiors, GDB will try to stop the other inferior, too.  So,
+# a stop request will be sent.  Receiving a TARGET_WAITKIND_SIGNALLED
+# status kind as a response to that stop request instead of a
+# TARGET_WAITKIND_STOPPED should be handled by GDB without problems.
+
+standard_testfile
+
+# This is a test specific for native GDB's ability to stop all
+# threads.
+if {![can_spawn_for_attach]} {
+    return 0
+}
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile]} {
+    return -1
+}
+
+gdb_test "add-inferior -exec $binfile" "Added inferior 2.*" \
+    "add the second inferior"
+
+# We want both processes in a running state.
+gdb_test_no_output "set schedule-multiple on"
+
+proc test {} {
+    # Start the programs, attach to them, then kill both from outside.
+    global binfile
+    global gdb_prompt
+
+    set spawn_id_list [spawn_wait_for_attach [list $binfile $binfile]]
+    set test_spawn_id1 [lindex $spawn_id_list 0]
+    set test_spawn_id2 [lindex $spawn_id_list 1]
+    set testpid1 [spawn_id_get_pid $test_spawn_id1]
+    set testpid2 [spawn_id_get_pid $test_spawn_id2]
+
+    gdb_test "inferior 1" ".*Switching to inferior 1.*"
+
+    gdb_test "attach $testpid1" \
+	"Attaching to program: .*, process $testpid1.*(in|at).*" \
+	"attach to program 1"
+
+    gdb_test "inferior 2" ".*Switching to inferior 2.*"
+
+    gdb_test "attach $testpid2" \
+	"Attaching to program: .*, process $testpid2.*(in|at).*" \
+	"attach to program 2"
+
+    gdb_test_multiple "continue" "continue processes" {
+	-re "Continuing.\[\r\n\]+" {
+	    # Kill both processes at once.
+	    remote_exec build "kill -9 ${testpid1} ${testpid2}"
+	    exp_continue
+	}
+	-re "Program terminated with signal.*$gdb_prompt" {
+	    pass $gdb_test_name
+	}
+    }
+
+    # Make sure that the processes are gone.
+    kill_wait_spawned_process $test_spawn_id1
+    kill_wait_spawned_process $test_spawn_id2
+}
+
+with_test_prefix "iteration 1" test
+# Repeat to also test re-runnability.
+with_test_prefix "iteration 2" test