[review,v2] infrun: handle already-exited threads when attempting to stop

Message ID 20191104102336.331FD25B28@gnutoolchain-gerrit.osci.io
State New
Headers show
Series
  • [review,v2] infrun: handle already-exited threads when attempting to stop
Related show

Commit Message

Simon Marchi (Code Review) Nov. 4, 2019, 10:23 a.m.
Change URL: https://gnutoolchain-gerrit.osci.io/r/c/binutils-gdb/+/133
......................................................................

infrun: handle already-exited threads when attempting to stop

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 1635)
infrun: clear_proceed_status_thread (process 1763)
infrun: proceed (addr=0xffffffffffffffff, signal=GDB_SIGNAL_DEFAULT)
infrun: proceed: resuming process 1635
infrun: resume (step=0, signal=GDB_SIGNAL_0), trap_expected=0, current thread [process 1635] at 0x55555555514e
infrun: infrun_async(1)
infrun: prepare_to_wait
infrun: proceed: resuming process 1763
infrun: resume (step=0, signal=GDB_SIGNAL_0), trap_expected=0, current thread [process 1763] at 0x55555555514e
infrun: prepare_to_wait
infrun: target_wait (-1.0.0, status) =
infrun:   1635.1635.0 [process 1635],
infrun:   status->kind = exited, status = 0
infrun: handle_inferior_event status->kind = exited, status = 0
[Inferior 1 (process 1635) exited normally]
infrun: stop_waiting
infrun: stop_all_threads
infrun: stop_all_threads, pass=0, iterations=0
infrun:   process 1763 executing, need stop
infrun: target_wait (-1.0.0, status) =
infrun:   1763.1763.0 [process 1763],
infrun:   status->kind = exited, status = 0
infrun: stop_all_threads status->kind = exited, status = 0 process 1763
[Inferior 2 (process 1763) exited normally]
infrun: stop_all_threads, pass=1, iterations=1
infrun: stop_all_threads done
(gdb) info inferiors
  Num  Description       Executable
* 1    <null>            /path/to/a.out
  2    <null>            /path/to/a.out
(gdb) info threads
No threads.
(gdb)
~~~

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

	* 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.

Change-Id: I7cec98f40283773b79255d998511da434e9cd408
---
M gdb/infrun.c
1 file changed, 29 insertions(+), 2 deletions(-)




-- 
Gerrit-Project: binutils-gdb
Gerrit-Branch: master
Gerrit-Change-Id: I7cec98f40283773b79255d998511da434e9cd408
Gerrit-Change-Number: 133
Gerrit-PatchSet: 2
Gerrit-Owner: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Gerrit-Reviewer: Luis Machado <luis.machado@linaro.org>
Gerrit-Reviewer: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Gerrit-MessageType: newpatchset

Patch

diff --git a/gdb/infrun.c b/gdb/infrun.c
index f628fd1..45aa1d9 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -4489,8 +4489,35 @@ 
 	      || ws.kind == TARGET_WAITKIND_EXITED
 	      || ws.kind == TARGET_WAITKIND_SIGNALLED)
 	    {
-	      /* All resumed threads exited
-		 or one thread/process exited/signalled.  */
+	      if (event_ptid != minus_one_ptid)
+		{
+		  thread_info *t = find_thread_ptid (event_ptid);
+		  if (t == nullptr)
+		    {
+		      /* This is the first time we see this thread.  */
+		      t = add_thread (event_ptid);
+		    }
+
+		  /* Set the threads as non-executing to avoid infinitely
+		     waiting for them to stop.  */
+		  mark_non_executing_threads (event_ptid, ws);
+
+		  if (ws.kind == TARGET_WAITKIND_NO_RESUMED)
+		    {
+		      /* Do nothing.  Already marked the threads.  */
+		    }
+		  if (ws.kind == TARGET_WAITKIND_THREAD_EXITED)
+		    delete_thread (t);
+		  else
+		    {
+		      /* TARGET_WAITKIND_EXITED or
+			 TARGET_WAITKIND_SIGNALLED.  */
+		      /* Need to restore the context because
+			 handle_inferior_exit switches it.  */
+		      scoped_restore_current_pspace_and_thread restore;
+		      handle_inferior_exit (event_ptid, ws);
+		    }
+		}
 	    }
 	  else
 	    {