[v2,15/16] GNU/Linux: Interrupt/Ctrl-C with SIGSTOP instead of SIGINT [PR gdb/9425, PR gdb/14559]

Message ID 20210614212410.1612666-16-pedro@palves.net
State New
Headers show
Series
  • Interrupting programs that block/ignore SIGINT
Related show

Commit Message

Pedro Alves June 14, 2021, 9:24 p.m.
After the "Always put inferiors in their own terminal/session
[gdb/9425, gdb/14559]" change, when a user types "Ctrl-C" while the
inferior is running, GDB is the one who gets the SIGINT, not the
inferior process.  GDB then forwards the SIGINT to the inferior with
target_pass_ctrlc.

That was necessary but not not sufficient to fix PRs gdb/9425,
gdb/14559, because if a program blocks SIGINT with e.g. sigprocmask,
then if GDB sends it a SIGINT, the signal isn't ever delivered to the
process, so ptrace does not intercept it.  You type Ctrl-C, but
nothing happens.  Similarly, if a program uses sigwait to wait for
SIGINT, and the program receives a SIGINT, the SIGINT is _not_
intercepted by ptrace, it goes straight to the inferior.

Now that the Ctrl-C results in a SIGINT sent to GDB instead of the
inferior, we can make GDB interrupt the program any other way we like.
This patch makes non-stop-capable ports interrupt the program with
stop_all_threads / target_stop (i.e., SIGSTOP) instead of
target_pass_ctrlc (i.e., SIGINT), which always works -- SIGSTOP can't
be blocked/ignored.  (In the future GDB may even switch to
PTRACE_INTERRUPT on Linux, though that's a project of its own.)

Another advantage here is with multi-target -- currently, because GDB
relies on Ctrl-C stopping one thread, and then stopping all other
threads in reaction to that stop, target_pass_ctrlc tries to find one
inferior with a thread that is running, in any target.  If the
selected target for some reason fails to process the Ctrl-C request,
then the Ctrl-C ends up lost.  The mechanism implemented in this patch
is different -- we never have to pick a thread, inferior or target --
we're going to stop everything, so we end up in stop_all_threads.

For non-stop, the patch preserves the current behavior of only
stopping one thread in reaction to Ctrl-C, so it can still happen that
the thread that GDB selects to stop disappears and the Ctrl-C ends up
being lost.  However, because now GDB always sees the SIGINT first, we
can change how Ctrl-C behaves there too.  We could even make it
configurable -- for example, it could be reasonable that Ctrl-C simply
drops the CLI back to the prompt, without stopping anything at all.
That might be interesting for "set observer-mode on", at least.

This commit has a user-visible behavior change in all-stop mode --
when you interrupt the program with Ctrl-C, instead of:

  Thread 1 "threads" received signal SIGINT, Interrupt.

You'll get:

  Thread 1 "threads" stopped.

Which is what you already got with the "interrupt" command in non-stop
mode.

If you really want to pass a SIGINT to the program, you can then issue:

  (gdb) signal SIGINT

This commit also adjusts the testsuite to cope with that output
alternative.

With this change, the gdb.base/sigint-sigwait.exp and
gdb.base/sigint-masked-out.exp testcases now pass cleanly on
GNU/Linux, on both native debugging and gdbserver + "maint set
target-non-stop on", so the kfails are adjusted accordingly.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	PR gdb/9425
	PR gdb/14559
	* event-top.c (default_quit_handler): Mark infrun event-loop
	handler with mark_infrun_async_event_handler_ctrl_c instead of
	passing the Ctrl-C to the target directly with target_pass_ctrlc.
	* infcmd.c (interrupt_target_1): On non-stop targets, Mark infrun
	event-loop handler with
	mark_infrun_async_event_handler_interrupt_all instead of using
	target_interrupt.
	* infrun.c (interrupt_all_requested, switch_to_stop_thread)
	(sync_interrupt_all)
	(mark_infrun_async_event_handler_interrupt_all)
	(mark_infrun_async_event_handler_ctrl_c): New.
	(infrun_async_inferior_event_handler): Handle Ctrl-C/interrupt
	requests.
	* infrun.h (mark_infrun_async_event_handler_interrupt_all)
	(mark_infrun_async_event_handler_ctrl_c): Declare.
	* linux-nat.c (wait_for_signal): Don't handle Ctrl-C here.
	(linux_nat_wait_1): Handle it here, by marking the infrun event
	handler, and returning TARGET_WAITKIND_IGNORE with the quit flag
	still set.
	* target.c (maybe_pass_ctrlc): New.
	(target_terminal::inferior, target_terminal::restore_inferior):
	Use it.
	(target_pass_ctrlc): Ass there's no non-stop target pushed.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <pedro@palves.net>

	PR gdb/9425
	PR gdb/14559
	* gdb.base/bp-cmds-continue-ctrl-c.exp: Expect "stopped" in
	alternative to "signal SIGINT".
	* gdb.base/interrupt-daemon-attach.exp: Likewise.
	* gdb.base/interrupt-daemon.exp: Likewise.
	* gdb.base/interrupt-noterm.exp: Likewise.
	* gdb.base/interrupt.exp: Likewise.
	* gdb.base/random-signal.exp: Likewise.
	* gdb.base/range-stepping.exp: Likewise.
	* gdb.gdb/selftest.exp: Likewise.
	* gdb.mi/new-ui-mi-sync.exp: Likewise.
	* gdb.multi/multi-target-interrupt.exp: Likewise.
	* gdb.multi/multi-target-no-resumed.exp: Likewise.
	* gdb.multi/multi-term-settings.exp: Likewise.
	* gdb.server/reconnect-ctrl-c.exp: Likewise.
	* gdb.threads/async.exp: Likewise.
	* gdb.threads/continue-pending-status.exp: Likewise.
	* gdb.threads/leader-exit.exp: Likewise.
	* gdb.threads/manythreads.exp: Likewise.
	* gdb.threads/pthreads.exp: Likewise.
	* gdb.threads/schedlock.exp: Likewise.
	* gdb.threads/sigthread.exp: Likewise.

	* lib/gdb.exp (can_interrupt_blocked_sigint): New.
	* gdb.base/sigint-masked-out.exp (test_ctrl_c)
	(test_interrupt_cmd): Use can_interrupt_blocked_sigint, and don't
	kfail if true.
	* gdb.base/sigint-sigwait.exp (test_ctrl_c, test_interrupt_cmd):
	Likewise.

Change-Id: I83c1b6a20deea1f1909156adde1d60b8f6f2629b
---
 gdb/event-top.c                               |  10 +-
 gdb/infcmd.c                                  |  34 +++-
 gdb/infrun.c                                  | 149 +++++++++++++++++-
 gdb/infrun.h                                  |  12 ++
 gdb/linux-nat.c                               |  39 +++--
 gdb/target.c                                  |  24 ++-
 .../gdb.base/bp-cmds-continue-ctrl-c.exp      |   3 +
 .../gdb.base/interrupt-daemon-attach.exp      |   2 +-
 gdb/testsuite/gdb.base/interrupt-daemon.exp   |   4 +-
 gdb/testsuite/gdb.base/interrupt-noterm.exp   |   2 +-
 gdb/testsuite/gdb.base/interrupt.exp          |   4 +-
 gdb/testsuite/gdb.base/random-signal.exp      |   3 +-
 gdb/testsuite/gdb.base/range-stepping.exp     |   2 +-
 gdb/testsuite/gdb.base/sigint-masked-out.exp  |  12 +-
 gdb/testsuite/gdb.base/sigint-sigwait.exp     |  12 +-
 gdb/testsuite/gdb.gdb/selftest.exp            |   4 +-
 gdb/testsuite/gdb.mi/new-ui-mi-sync.exp       |   2 +-
 .../gdb.multi/multi-target-interrupt.exp      |   2 +-
 .../gdb.multi/multi-target-no-resumed.exp     |   2 +-
 .../gdb.multi/multi-term-settings.exp         |   2 +-
 gdb/testsuite/gdb.server/reconnect-ctrl-c.exp |   4 +-
 gdb/testsuite/gdb.threads/async.exp           |   2 +-
 .../gdb.threads/continue-pending-status.exp   |   2 +-
 gdb/testsuite/gdb.threads/leader-exit.exp     |   2 +-
 gdb/testsuite/gdb.threads/manythreads.exp     |   4 +-
 gdb/testsuite/gdb.threads/pthreads.exp        |   2 +-
 gdb/testsuite/gdb.threads/schedlock.exp       |  11 +-
 gdb/testsuite/gdb.threads/sigthread.exp       |   2 +-
 gdb/testsuite/lib/gdb.exp                     |  19 +++
 29 files changed, 315 insertions(+), 57 deletions(-)

-- 
2.26.2

Comments

Maciej W. Rozycki July 8, 2021, 11:05 p.m. | #1
On Mon, 14 Jun 2021, Pedro Alves wrote:

> This commit has a user-visible behavior change in all-stop mode --

> when you interrupt the program with Ctrl-C, instead of:

> 

>   Thread 1 "threads" received signal SIGINT, Interrupt.

> 

> You'll get:

> 

>   Thread 1 "threads" stopped.

> 

> Which is what you already got with the "interrupt" command in non-stop

> mode.

> 

> If you really want to pass a SIGINT to the program, you can then issue:

> 

>   (gdb) signal SIGINT


 Can you still do:

(gdb) handle SIGINT nostop noprint noignore

instead to make SIGINT go through to the debuggee right away without GDB
getting bothered in any way?  I've had a need to use such a setup numerous
times (normally you can still hit ^Z or ^\ to get GDB interrupted with
SIGTSTP or SIGQUIT respectively instead).

  Maciej
Pedro Alves July 13, 2021, 3:26 p.m. | #2
Hi Maciej,

On 2021-07-09 12:05 a.m., Maciej W. Rozycki wrote:
> On Mon, 14 Jun 2021, Pedro Alves wrote:

> 

>> This commit has a user-visible behavior change in all-stop mode --

>> when you interrupt the program with Ctrl-C, instead of:

>>

>>   Thread 1 "threads" received signal SIGINT, Interrupt.

>>

>> You'll get:

>>

>>   Thread 1 "threads" stopped.

>>

>> Which is what you already got with the "interrupt" command in non-stop

>> mode.

>>

>> If you really want to pass a SIGINT to the program, you can then issue:

>>

>>   (gdb) signal SIGINT

> 

>  Can you still do:

> 

> (gdb) handle SIGINT nostop noprint noignore

> 

> instead to make SIGINT go through to the debuggee right away without GDB

> getting bothered in any way?  I've had a need to use such a setup numerous

> times (normally you can still hit ^Z or ^\ to get GDB interrupted with

> SIGTSTP or SIGQUIT respectively instead).

> 


Yes, in the sense that if a SIGINT it sent to the inferior, it will go
through to the debuggee right away.  However, if you press Ctrl-C in
GDB (which is probably what you're asking), then it will stop the
program regardless of "handle SIGINT".  I've been thinking that to handle
scenarios like these and Eli's emacs scenario, GDB could gain some
command to configure it's own interrupt key.  The inferior's
terminal would still inherit the terminal settings that GDB had when it
was started up, so the terminal's interrupt key as configured when GDB
started (usually ^c) would still raise a SIGINT in the inferior.    So in
your scenario you'd do "set interrupt-key ^g" or some such, like:

 (gdb) handle SIGINT nostop noprint noignore
 (gdb) set interrupt-key ^g

With the above, ctrl-c would result in a SIGINT in the inferior
and ctrl-g would always stop the program, no matter what the program
does with signal masks/dispositions.

Patch

diff --git a/gdb/event-top.c b/gdb/event-top.c
index 002a7dc95e0..9741b23576a 100644
--- a/gdb/event-top.c
+++ b/gdb/event-top.c
@@ -1002,7 +1002,15 @@  default_quit_handler (void)
       if (target_terminal::is_ours ())
 	quit ();
       else
-	target_pass_ctrlc ();
+	{
+	  /* Let the even loop handle the quit/interrupt.  In some
+	     modes (e.g., "set non-stop off" + "maint set
+	     target-non-stop on"), it's not safe to request an
+	     interrupt right now, as we may be in the middle of
+	     handling some other event, and target_stop changes infrun
+	     state.  */
+	  mark_infrun_async_event_handler_ctrl_c ();
+	}
     }
 }
 
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 99ce0ec78fa..72051bdc095 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -2846,7 +2846,39 @@  interrupt_target_1 (bool all_threads)
 	stop_current_target_threads_ns (inferior_ptid);
     }
   else
-    target_interrupt ();
+    {
+      if (exists_non_stop_target ())
+	{
+	  /* Ignore the interrupt request if everything is already
+	     stopped.  */
+	  auto any_resumed = [] ()
+	    {
+	      for (thread_info *thr : all_non_exited_threads ())
+		{
+		  if (thr->executing)
+		    return true;
+		  if (thr->suspend.waitstatus_pending_p)
+		    return true;
+		}
+	      return false;
+	    };
+
+	  if (any_resumed ())
+	    {
+	      /* Stop all threads, and report one single stop for all
+		 threads.  Since the "interrupt" command works
+		 asynchronously on all other modes (non-stop or true
+		 all-stop + stopping with SIGINT), i.e., the command
+		 finishes and GDB prints the prompt before the target
+		 actually stops, make this mode work the same, by
+		 deferring the actual synchronous stopping work to the
+		 event loop.  */
+	      mark_infrun_async_event_handler_interrupt_all ();
+	    }
+	}
+      else
+	target_interrupt ();
+    }
 
   disable_commit_resumed.reset_and_commit ();
 }
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 3f40fa39b5a..f3f24839f32 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -9440,13 +9440,158 @@  static const struct internalvar_funcs siginfo_funcs =
   NULL
 };
 
-/* Callback for infrun's target events source.  This is marked when a
-   thread has a pending status to process.  */
+/* True if the user used the "interrupt" command and we want to handle
+   the interruption via the event loop instead of immediately.  This
+   is so that "interrupt" always stops the program asynchronously in
+   all the different execution modes.  In particular, in "set non-stop
+   off" + "maint set target-non-stop on" mode, we want to
+   synchronously stop all threads with stop_all_threads, so we delay
+   doing that to the event loop, so that "interrupt" presents a prompt
+   immediately, and then presents the stop afterwards, just like what
+   happens in non-stop mode, or if the target is in true all-stop mode
+   and the interrupting is done by sending a SIGINT to the inferior
+   process.  */
+static bool interrupt_all_requested = false;
+
+/* Pick the thread to report the stop on and to switch to it.  */
+
+static void
+switch_to_stop_thread ()
+{
+  thread_info *stop_thr = nullptr;
+
+  if (previous_thread != nullptr && previous_thread->state == THREAD_RUNNING)
+    stop_thr = previous_thread.get ();
+  else
+    {
+      for (thread_info *thr : all_non_exited_threads ())
+	if (thr->state == THREAD_RUNNING)
+	  {
+	    stop_thr = thr;
+	    break;
+	  }
+    }
+
+  gdb_assert (stop_thr != nullptr);
+
+  switch_to_thread (stop_thr);
+}
+
+/* Synchronously stop all threads, saving interesting events as
+   pending events, and present a normal stop on one of the threads.
+   Preference is given to the "previous thread", which was the thread
+   that the user last resumed.  This is used in "set non-stop off" +
+   "maint set target-non-stop on" mode to stop the target in response
+   to Ctrl-C or the "interrupt" command.  */
+
+static void
+sync_interrupt_all ()
+{
+  /* Events are always processed with the main UI as current UI.  This
+     way, warnings, debug output, etc. are always consistently sent to
+     the main console.  */
+  scoped_restore save_ui = make_scoped_restore (&current_ui, main_ui);
+
+  /* Exposed by gdb.base/paginate-after-ctrl-c-running.exp.  */
+
+  /* Temporarily disable pagination.  Otherwise, the user would be
+     given an option to press 'q' to quit, which would cause an early
+     exit and could leave GDB in a half-baked state.  */
+  scoped_restore save_pagination
+    = make_scoped_restore (&pagination_enabled, false);
+
+  scoped_disable_commit_resumed disable_commit_resumed ("stopping for ctrl-c");
+
+  gdb_assert (!non_stop);
+
+  /* Stop all threads before picking which one to present the stop on
+     -- this is safer than the other way around because otherwise the
+     thread we pick could exit just while we try to stop it.  */
+  stop_all_threads ();
+
+  switch_to_stop_thread ();
+
+  target_waitstatus ws;
+  ws.kind = TARGET_WAITKIND_STOPPED;
+  ws.value.sig = GDB_SIGNAL_0;
+  set_last_target_status (current_inferior ()->process_target (),
+			  inferior_ptid, ws);
+  stopped_by_random_signal = true;
+  stop_print_frame = true;
+  normal_stop ();
+
+  inferior_event_handler (INF_EXEC_COMPLETE);
+
+  /* If a UI was in sync execution mode, and now isn't, restore its
+     prompt (a synchronous execution command has finished, and we're
+     ready for input).  */
+  all_uis_check_sync_execution_done ();
+}
+
+/* See infrun.h.  */
+
+void
+mark_infrun_async_event_handler_interrupt_all ()
+{
+  mark_infrun_async_event_handler ();
+  interrupt_all_requested = true;
+}
+
+/* See infrun.h.  */
+
+void
+mark_infrun_async_event_handler_ctrl_c ()
+{
+  mark_infrun_async_event_handler ();
+  set_quit_flag ();
+}
+
+/* Callback for infrun's target events source.  This is marked either
+   when a thread has a pending status to process, or a target
+   interrupt was requested, either with Ctrl-C or the "interrupt"
+   command and target is in non-stop mode.  */
 
 static void
 infrun_async_inferior_event_handler (gdb_client_data data)
 {
+  /* Handle a Ctrl-C while the inferior has the terminal, or an
+     "interrupt" cmd request.  */
+  if ((!target_terminal::is_ours () && check_quit_flag ())
+      || interrupt_all_requested)
+    {
+      interrupt_all_requested = false;
+
+      if (exists_non_stop_target ())
+	{
+	  if (non_stop)
+	    {
+	      /* Stop one thread, like it would happen if we were
+		 stopping with SIGINT sent to the foreground
+		 process.  */
+	      switch_to_stop_thread ();
+	      interrupt_target_1 (false);
+	    }
+	  else
+	    {
+	      /* Stop all threads, and report one single stop for all
+		 threads.  */
+	      sync_interrupt_all ();
+	    }
+	}
+      else
+	{
+	  /* Pass a Ctrl-C request to the target.  Usually this means
+	     sending a SIGINT to the inferior process.  */
+	  target_pass_ctrlc ();
+	}
+
+      /* Don't clear the event handler yet -- there may be pending
+	 events to process.  */
+      return;
+    }
+
   clear_async_event_handler (infrun_async_inferior_event_token);
+
   inferior_event_handler (INF_REG_EVENT);
 }
 
diff --git a/gdb/infrun.h b/gdb/infrun.h
index 5d791bdc5b4..64fdfc7ab60 100644
--- a/gdb/infrun.h
+++ b/gdb/infrun.h
@@ -255,6 +255,18 @@  extern void infrun_async (int enable);
    loop.  */
 extern void mark_infrun_async_event_handler (void);
 
+/* Like mark_infrun_async_event_handler, and ask the event loop to
+   stop all threads, in response to an "interrupt" command.  */
+extern void mark_infrun_async_event_handler_interrupt_all ();
+
+/* Like mark_infrun_async_event_handler, and ask the event loop to
+   handle a "Ctrl-C" interruption request.  In some modes (e.g., "set
+   non-stop off" + "maint set target-non-stop on"), we interrupt the
+   target with target_stop, and it's not safe to use that right away,
+   as we may be in the middle of handling some other event, and
+   target_stop changes infrun state.  */
+extern void mark_infrun_async_event_handler_ctrl_c ();
+
 /* The global chain of threads that need to do a step-over operation
    to get past e.g., a breakpoint.  */
 extern struct thread_info *global_thread_step_over_chain_head;
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index d31bcd98e84..a40b3c30012 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -2142,19 +2142,6 @@  wait_for_signal ()
 {
   linux_nat_debug_printf ("about to sigsuspend");
   sigsuspend (&suspend_mask);
-
-  /* If the quit flag is set, it means that the user pressed Ctrl-C
-     and we're debugging a process that is running on a separate
-     terminal, so we must forward the Ctrl-C to the inferior.  (If the
-     inferior is sharing GDB's terminal, then the Ctrl-C reaches the
-     inferior directly.)  We must do this here because functions that
-     need to block waiting for a signal loop forever until there's an
-     event to report before returning back to the event loop.  */
-  if (!target_terminal::is_ours ())
-    {
-      if (check_quit_flag ())
-	target_pass_ctrlc ();
-    }
 }
 
 /* Wait for LP to stop.  Returns the wait status, or 0 if the LWP has
@@ -3317,8 +3304,32 @@  linux_nat_wait_1 (ptid_t ptid, struct target_waitstatus *ourstatus,
       /* We shouldn't end up here unless we want to try again.  */
       gdb_assert (lp == NULL);
 
-      /* Block until we get an event reported with SIGCHLD.  */
+      /* Block until we get an event reported with SIGCHLD or a SIGINT
+	 interrupt.  */
       wait_for_signal ();
+
+      /* If the quit flag is set, it means that the user pressed
+	 Ctrl-C and we're debugging a process that is running on a
+	 separate terminal, so we must forward the Ctrl-C to the
+	 inferior.  (If the inferior is sharing GDB's terminal, then
+	 the Ctrl-C reaches the inferior directly.)  If we were
+	 interrupted by Ctrl-C, return back to the event loop and let
+	 it handle interrupting the target (or targets).  */
+
+      if (!target_terminal::is_ours () && check_quit_flag ())
+	{
+	  mark_infrun_async_event_handler_ctrl_c ();
+
+	  linux_nat_debug_printf ("exit (quit flag)");
+
+	  /* If we got a SIGCHLD, need to end up here again. */
+	  async_file_mark ();
+
+	  ourstatus->kind = TARGET_WAITKIND_IGNORE;
+
+	  restore_child_signals_mask (&prev_mask);
+	  return minus_one_ptid;
+	}
     }
 
   gdb_assert (lp);
diff --git a/gdb/target.c b/gdb/target.c
index 68d7e7c12d7..ab4dc7bd5dd 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -927,6 +927,21 @@  target_terminal::init (void)
   m_terminal_state = target_terminal_state::is_ours;
 }
 
+/* Called after switching the terminal to the inferior.  If the user
+   hit C-c before, pretend that it was hit right here.  Non-stop
+   targets however are more complicated though, as interruption with
+   "set non-stop off" + "maint set target-non-stop on" wants to stop
+   all threads individually with target_stop (and synchronously wait
+   for the stops), so we let the event loop handle it, when it's
+   recursion-safe to do so.  */
+
+static void
+maybe_pass_ctrlc ()
+{
+  if (!exists_non_stop_target () && check_quit_flag ())
+    target_pass_ctrlc ();
+}
+
 /* See target/target.h.  */
 
 void
@@ -961,8 +976,7 @@  target_terminal::inferior (void)
 
   /* If the user hit C-c before, pretend that it was hit right
      here.  */
-  if (check_quit_flag ())
-    target_pass_ctrlc ();
+  maybe_pass_ctrlc ();
 }
 
 /* See target/target.h.  */
@@ -998,8 +1012,7 @@  target_terminal::restore_inferior (void)
 
   /* If the user hit C-c before, pretend that it was hit right
      here.  */
-  if (check_quit_flag ())
-    target_pass_ctrlc ();
+  maybe_pass_ctrlc ();
 }
 
 /* Switch terminal state to DESIRED_STATE, either is_ours, or
@@ -3797,6 +3810,9 @@  target_interrupt ()
 void
 target_pass_ctrlc (void)
 {
+  /* Non-stop targets interrupt programs with target_stop instead.  */
+  gdb_assert (!exists_non_stop_target ());
+
   /* Pass the Ctrl-C to the first target that has a thread
      running.  */
   for (inferior *inf : all_inferiors ())
diff --git a/gdb/testsuite/gdb.base/bp-cmds-continue-ctrl-c.exp b/gdb/testsuite/gdb.base/bp-cmds-continue-ctrl-c.exp
index d21f580af87..e7e7a4c43a6 100644
--- a/gdb/testsuite/gdb.base/bp-cmds-continue-ctrl-c.exp
+++ b/gdb/testsuite/gdb.base/bp-cmds-continue-ctrl-c.exp
@@ -87,6 +87,9 @@  proc do_test {} {
 	    -re "Program received signal SIGINT.*\r\n$gdb_prompt $" {
 		send_log "$internal_pass (SIGINT)\n"
 	    }
+	    -re "Program stopped.*\r\n$gdb_prompt $" {
+		send_log "$internal_pass (stopped)\n"
+	    }
 	    -re "Quit\r\n$gdb_prompt $" {
 		send_log "$internal_pass (Quit)\n"
 
diff --git a/gdb/testsuite/gdb.base/interrupt-daemon-attach.exp b/gdb/testsuite/gdb.base/interrupt-daemon-attach.exp
index e43bfe4f4ad..840bbec2dc1 100644
--- a/gdb/testsuite/gdb.base/interrupt-daemon-attach.exp
+++ b/gdb/testsuite/gdb.base/interrupt-daemon-attach.exp
@@ -81,7 +81,7 @@  proc do_test {} {
 	}
 
 	after 500 {send_gdb "\003"}
-	gdb_test "" "(Program|Thread .*) received signal SIGINT.*" \
+	gdb_test "" "(Program|Thread .*) (received signal SIGINT|stopped).*" \
 	    "stop with control-c"
 
 	remote_exec host "kill -9 $child_pid"
diff --git a/gdb/testsuite/gdb.base/interrupt-daemon.exp b/gdb/testsuite/gdb.base/interrupt-daemon.exp
index 2270ab0cbc2..0b0b6dc3e80 100644
--- a/gdb/testsuite/gdb.base/interrupt-daemon.exp
+++ b/gdb/testsuite/gdb.base/interrupt-daemon.exp
@@ -53,7 +53,7 @@  proc do_test {} {
 
 	set test "ctrl-c stops process"
 	gdb_test_multiple "" $test {
-	    -re "received signal SIGINT.*\r\n$gdb_prompt $" {
+	    -re "(received signal SIGINT|stopped).*\r\n$gdb_prompt $" {
 		pass $test
 	    }
 	}
@@ -79,7 +79,7 @@  proc do_test {} {
 
 	set test "interrupt cmd stops process"
 	gdb_test_multiple "" $test {
-	    -re "received signal SIGINT" {
+	    -re "(received signal SIGINT|stopped)" {
 		pass $test
 	    }
 	}
diff --git a/gdb/testsuite/gdb.base/interrupt-noterm.exp b/gdb/testsuite/gdb.base/interrupt-noterm.exp
index 5cfc6171dd4..089b43305fe 100644
--- a/gdb/testsuite/gdb.base/interrupt-noterm.exp
+++ b/gdb/testsuite/gdb.base/interrupt-noterm.exp
@@ -67,7 +67,7 @@  gdb_test_multiple $test $test {
 
 set test "inferior received SIGINT"
 gdb_test_multiple "" $test {
-    -re "\r\nProgram received signal SIGINT.*" {
+    -re "\r\nProgram (received signal SIGINT|stopped).*" {
 	# This appears after the prompt, which was already consumed
 	# above.
 	pass $test
diff --git a/gdb/testsuite/gdb.base/interrupt.exp b/gdb/testsuite/gdb.base/interrupt.exp
index 41788ccade2..30ca6d44285 100644
--- a/gdb/testsuite/gdb.base/interrupt.exp
+++ b/gdb/testsuite/gdb.base/interrupt.exp
@@ -82,7 +82,7 @@  if ![file exists $binfile] then {
 	send_gdb "\003"
 	set msg "send_gdb control C"
 	gdb_test_multiple "" $msg {
-	    -re "Program received signal SIGINT.*$gdb_prompt $" {
+	    -re "Program (received signal SIGINT|stopped).*$gdb_prompt $" {
 		pass $msg
 	    }
 	}
@@ -166,7 +166,7 @@  if ![file exists $binfile] then {
 	    set msg "Send Control-C, second time"
 	    send_gdb "\003"
 	    gdb_test_multiple "" "$msg" {
-		-re "Program received signal SIGINT.*$gdb_prompt $" {
+		-re "Program (received signal SIGINT|stopped).*$gdb_prompt $" {
 		    pass "$msg"
 		}
 	    }
diff --git a/gdb/testsuite/gdb.base/random-signal.exp b/gdb/testsuite/gdb.base/random-signal.exp
index 8511e83fce5..4c8806ce367 100644
--- a/gdb/testsuite/gdb.base/random-signal.exp
+++ b/gdb/testsuite/gdb.base/random-signal.exp
@@ -47,7 +47,8 @@  proc do_test {} {
     # For this to work we must be sure to consume the "Continuing."
     # message first, or GDB's signal handler may not be in place.
     after 500 {send_gdb "\003"}
-    gdb_test "" "Program received signal SIGINT.*" "stop with control-c"
+    gdb_test "" "Program (received signal SIGINT|stopped).*" \
+	"stop with control-c"
 }
 
 # With native debugging and "run" (with job control), the ctrl-c
diff --git a/gdb/testsuite/gdb.base/range-stepping.exp b/gdb/testsuite/gdb.base/range-stepping.exp
index c1ba8ef71cb..21bd7326370 100644
--- a/gdb/testsuite/gdb.base/range-stepping.exp
+++ b/gdb/testsuite/gdb.base/range-stepping.exp
@@ -193,7 +193,7 @@  if ![target_info exists gdb,nointerrupts] {
 		incr vcont_r_counter
 		exp_continue
 	    }
-	    -re "Program received signal SIGINT.*$gdb_prompt $" {
+	    -re "Program (received signal SIGINT|stopped).*$gdb_prompt $" {
 		pass $test
 	    }
 	}
diff --git a/gdb/testsuite/gdb.base/sigint-masked-out.exp b/gdb/testsuite/gdb.base/sigint-masked-out.exp
index 0391152e93a..77a599d12ef 100644
--- a/gdb/testsuite/gdb.base/sigint-masked-out.exp
+++ b/gdb/testsuite/gdb.base/sigint-masked-out.exp
@@ -36,6 +36,8 @@  proc_with_prefix test_ctrl_c {} {
 	return
     }
 
+    set can_interrupt [can_interrupt_blocked_sigint]
+
     gdb_test_multiple "continue" "" {
 	-re "Continuing" {
 	    pass $gdb_test_name
@@ -52,7 +54,9 @@  proc_with_prefix test_ctrl_c {} {
 	    pass $gdb_test_name
 	}
 	timeout {
-	    setup_kfail "gdb/9425" *-*-*
+	    if {!$can_interrupt} {
+		setup_kfail "gdb/9425" *-*-*
+	    }
 	    fail "$gdb_test_name (timeout)"
 	}
     }
@@ -71,6 +75,8 @@  proc_with_prefix test_interrupt_cmd {} {
 	return
     }
 
+    set can_interrupt [can_interrupt_blocked_sigint]
+
     gdb_test_multiple "continue&" "" {
 	-re "Continuing\\.\r\n$gdb_prompt " {
 	    pass $gdb_test_name
@@ -91,7 +97,9 @@  proc_with_prefix test_interrupt_cmd {} {
 	    pass $gdb_test_name
 	}
 	timeout {
-	    setup_kfail "gdb/14559" *-*-*
+	    if {!$can_interrupt} {
+		setup_kfail "gdb/14559" *-*-*
+	    }
 	    fail "$gdb_test_name (timeout)"
 	}
     }
diff --git a/gdb/testsuite/gdb.base/sigint-sigwait.exp b/gdb/testsuite/gdb.base/sigint-sigwait.exp
index 1dd706fc1a4..e37a2d2cbef 100644
--- a/gdb/testsuite/gdb.base/sigint-sigwait.exp
+++ b/gdb/testsuite/gdb.base/sigint-sigwait.exp
@@ -37,6 +37,8 @@  proc_with_prefix test_ctrl_c {} {
 	return
     }
 
+    set can_interrupt [can_interrupt_blocked_sigint]
+
     gdb_test_multiple "continue" "" {
 	-re "Continuing" {
 	    pass $gdb_test_name
@@ -54,7 +56,9 @@  proc_with_prefix test_ctrl_c {} {
 	    pass $gdb_test_name
 	}
 	-re -wrap "Inferior.*exited normally.*" {
-	    setup_kfail "gdb/9425" *-*-*
+	    if {!$can_interrupt} {
+		setup_kfail "gdb/9425" *-*-*
+	    }
 	    fail "$gdb_test_name (the program exited)"
 	}
     }
@@ -73,6 +77,8 @@  proc_with_prefix test_interrupt_cmd {} {
 	return
     }
 
+    set can_interrupt [can_interrupt_blocked_sigint]
+
     gdb_test_multiple "continue&" "" {
 	-re "Continuing\\.\r\n$gdb_prompt " {
 	    pass $gdb_test_name
@@ -95,7 +101,9 @@  proc_with_prefix test_interrupt_cmd {} {
 	    pass $gdb_test_name
 	}
 	-re "Inferior.*exited normally" {
-	    setup_kfail "gdb/14559" *-*-*
+	    if {!$can_interrupt} {
+		setup_kfail "gdb/14559" *-*-*
+	    }
 	    fail "$gdb_test_name (the program exited)"
 	}
     }
diff --git a/gdb/testsuite/gdb.gdb/selftest.exp b/gdb/testsuite/gdb.gdb/selftest.exp
index bee3010bca1..7679131b8e7 100644
--- a/gdb/testsuite/gdb.gdb/selftest.exp
+++ b/gdb/testsuite/gdb.gdb/selftest.exp
@@ -97,7 +97,7 @@  proc test_with_self { } {
 	send_gdb "\003"
 	# "Thread 1" is displayed iff Guile support is linked in.
 	gdb_expect {
-	    -re "(Thread .*|Program) received signal SIGINT.*$gdb_prompt $" {
+	    -re "(Thread .*|Program) (received signal SIGINT|stopped).*$gdb_prompt $" {
 		pass "$description"
 	    }
 	    -re ".*$gdb_prompt $" {
@@ -119,7 +119,7 @@  proc test_with_self { } {
     set description "send ^C to child process again"
     send_gdb "\003"
     gdb_expect {
-	-re "(Thread .*|Program) received signal SIGINT.*$gdb_prompt $" {
+	-re "(Thread .*|Program) (received signal SIGINT|stopped).*$gdb_prompt $" {
 	    pass "$description"
 	}
 	-re ".*$gdb_prompt $" {
diff --git a/gdb/testsuite/gdb.mi/new-ui-mi-sync.exp b/gdb/testsuite/gdb.mi/new-ui-mi-sync.exp
index 887bd60abcd..8eadc2f6f22 100644
--- a/gdb/testsuite/gdb.mi/new-ui-mi-sync.exp
+++ b/gdb/testsuite/gdb.mi/new-ui-mi-sync.exp
@@ -92,7 +92,7 @@  proc do_test {sync_command} {
 	gdb_test_multiple "interrupt" "$message" {
 	    -re "$gdb_prompt " {
 		gdb_test_multiple "" "$message" {
-		    -re "received signal SIGINT" {
+		    -re "(received signal SIGINT|stopped)" {
 			pass $message
 		    }
 		}
diff --git a/gdb/testsuite/gdb.multi/multi-target-interrupt.exp b/gdb/testsuite/gdb.multi/multi-target-interrupt.exp
index cafd60d31b1..36b47a40c1a 100644
--- a/gdb/testsuite/gdb.multi/multi-target-interrupt.exp
+++ b/gdb/testsuite/gdb.multi/multi-target-interrupt.exp
@@ -46,7 +46,7 @@  proc test_ctrlc {} {
 
 	set msg "send_gdb control C"
 	gdb_test_multiple "" $msg {
-	    -re "received signal SIGINT.*$gdb_prompt $" {
+	    -re "(received signal SIGINT|stopped).*$gdb_prompt $" {
 		pass $msg
 	    }
 	}
diff --git a/gdb/testsuite/gdb.multi/multi-target-no-resumed.exp b/gdb/testsuite/gdb.multi/multi-target-no-resumed.exp
index 6e84d9a6949..0ce26f28199 100644
--- a/gdb/testsuite/gdb.multi/multi-target-no-resumed.exp
+++ b/gdb/testsuite/gdb.multi/multi-target-no-resumed.exp
@@ -59,7 +59,7 @@  proc test_no_resumed_infs {inf_A inf_B} {
     # Now stop the program (all targets).
     send_gdb "\003"
     gdb_test_multiple "" "send_gdb control C" {
-	-re "received signal SIGINT.*$gdb_prompt $" {
+	-re "(received signal SIGINT|stopped).*$gdb_prompt $" {
 	    pass $gdb_test_name
 	}
     }
diff --git a/gdb/testsuite/gdb.multi/multi-term-settings.exp b/gdb/testsuite/gdb.multi/multi-term-settings.exp
index 5275391a73c..7796d508083 100644
--- a/gdb/testsuite/gdb.multi/multi-term-settings.exp
+++ b/gdb/testsuite/gdb.multi/multi-term-settings.exp
@@ -278,7 +278,7 @@  proc coretest {inf1_how inf2_how} {
     if {$expect_ttou} {
 	gdb_test "" "Quit" "stop with control-c (Quit)"
     } else {
-	gdb_test "" "received signal SIGINT.*" "stop with control-c (SIGINT)"
+	gdb_test "" "(received signal SIGINT|stopped).*" "stop with control-c (SIGINT)"
     }
 
     # Useful for debugging in case the Ctrl-C above fails.
diff --git a/gdb/testsuite/gdb.server/reconnect-ctrl-c.exp b/gdb/testsuite/gdb.server/reconnect-ctrl-c.exp
index 11aa5147c78..2121c47e873 100644
--- a/gdb/testsuite/gdb.server/reconnect-ctrl-c.exp
+++ b/gdb/testsuite/gdb.server/reconnect-ctrl-c.exp
@@ -49,7 +49,7 @@  with_test_prefix "preparation" {
   gdb_test "disconnect" ".*"
 }
 
-# Connect, continue, send Ctrl-C and expect a SIGINT stop.
+# Connect, continue, send Ctrl-C and expect a stop.
 
 proc connect_continue_ctrl_c {} {
     global gdbserver_protocol gdbserver_gdbport
@@ -67,7 +67,7 @@  proc connect_continue_ctrl_c {} {
     }
 
     after 1000 {send_gdb "\003"}
-    gdb_test "" "Program received signal SIGINT.*" "stop with control-c"
+    gdb_test "" "Program (received signal SIGINT|stopped).*" "stop with control-c"
 }
 
 with_test_prefix "first" {
diff --git a/gdb/testsuite/gdb.threads/async.exp b/gdb/testsuite/gdb.threads/async.exp
index c49deb1f8b5..f7be1271b7b 100644
--- a/gdb/testsuite/gdb.threads/async.exp
+++ b/gdb/testsuite/gdb.threads/async.exp
@@ -80,7 +80,7 @@  proc test_current_thread {expected_thr} {
     gdb_test_multiple $test $test {
 	-re "^interrupt\r\n$gdb_prompt " {
 	    gdb_test_multiple "" $test {
-		-re "Thread .* received signal SIGINT, Interrupt\\." {
+		-re "Thread .* (received signal SIGINT, Interrupt|stopped)\\." {
 		    pass $test
 		}
 	    }
diff --git a/gdb/testsuite/gdb.threads/continue-pending-status.exp b/gdb/testsuite/gdb.threads/continue-pending-status.exp
index c0321c39d11..ca82290e260 100644
--- a/gdb/testsuite/gdb.threads/continue-pending-status.exp
+++ b/gdb/testsuite/gdb.threads/continue-pending-status.exp
@@ -116,7 +116,7 @@  for {set i 0} {$i < $attempts} {incr i} {
 
 	set msg "caught interrupt"
 	gdb_test_multiple "" $msg {
-	    -re "Thread .* received signal SIGINT.*$gdb_prompt $" {
+	    -re "Thread .* (received signal SIGINT|stopped).*$gdb_prompt $" {
 		pass $msg
 	    }
 	}
diff --git a/gdb/testsuite/gdb.threads/leader-exit.exp b/gdb/testsuite/gdb.threads/leader-exit.exp
index fe06e6d4f29..fb76bba3e3b 100644
--- a/gdb/testsuite/gdb.threads/leader-exit.exp
+++ b/gdb/testsuite/gdb.threads/leader-exit.exp
@@ -55,7 +55,7 @@  send_gdb "\003"
 
 set test "caught interrupt"
 gdb_test_multiple "" $test {
-    -re "Thread .* received signal SIGINT.*$gdb_prompt $" {
+    -re "Thread .* (received signal SIGINT|stopped).*$gdb_prompt $" {
 	pass $test
     }
 }
diff --git a/gdb/testsuite/gdb.threads/manythreads.exp b/gdb/testsuite/gdb.threads/manythreads.exp
index b0004b912b7..992c46a3874 100644
--- a/gdb/testsuite/gdb.threads/manythreads.exp
+++ b/gdb/testsuite/gdb.threads/manythreads.exp
@@ -56,7 +56,7 @@  gdb_test_multiple "continue" "first continue" {
 # we don't lose GDB's output while we do it.
 remote_expect host 1 { timeout { } }
 
-# Send a Ctrl-C and wait for the SIGINT.
+# Send a Ctrl-C and wait for the stop.
 
 proc interrupt_and_wait { message } {
     global gdb_prompt
@@ -70,7 +70,7 @@  proc interrupt_and_wait { message } {
 	-re "\\\[\[^\]\]* exited\\\]\r\n" {
 	    exp_continue
 	}
-	-re " received signal SIGINT.*$gdb_prompt $" {
+	-re " (received signal SIGINT|stopped).*$gdb_prompt $" {
 	    pass "$message"
 	}
 	-re "$gdb_prompt $" {
diff --git a/gdb/testsuite/gdb.threads/pthreads.exp b/gdb/testsuite/gdb.threads/pthreads.exp
index 86258b1d874..4bf8544db52 100644
--- a/gdb/testsuite/gdb.threads/pthreads.exp
+++ b/gdb/testsuite/gdb.threads/pthreads.exp
@@ -204,7 +204,7 @@  proc check_control_c {} {
     send_gdb "\003"
     set description "Stopped with a ^C"
     gdb_expect {
-	-re "Thread .* received signal SIGINT.*$gdb_prompt $" {
+	-re "Thread .* (received signal SIGINT|stopped).*$gdb_prompt $" {
 	    pass $description
 	}
 	-re "Quit.*$gdb_prompt $" {
diff --git a/gdb/testsuite/gdb.threads/schedlock.exp b/gdb/testsuite/gdb.threads/schedlock.exp
index 90c70587220..ab25c3d0388 100644
--- a/gdb/testsuite/gdb.threads/schedlock.exp
+++ b/gdb/testsuite/gdb.threads/schedlock.exp
@@ -68,14 +68,9 @@  proc stop_process { description } {
   # For this to work we must be sure to consume the "Continuing."
   # message first, or GDB's signal handler may not be in place.
   after 1000 {send_gdb "\003"}
-  gdb_expect {
-    -re "Thread .* received signal SIGINT.*$gdb_prompt $"
-      {
-	pass $description
-      }
-    timeout
-      {
-	fail "$description (timeout)"
+  gdb_test_multiple "" $description {
+      -re "Thread .* (received signal SIGINT|stopped).*$gdb_prompt $" {
+	  pass $description
       }
   }
 }
diff --git a/gdb/testsuite/gdb.threads/sigthread.exp b/gdb/testsuite/gdb.threads/sigthread.exp
index 867aef9a710..43f39406380 100644
--- a/gdb/testsuite/gdb.threads/sigthread.exp
+++ b/gdb/testsuite/gdb.threads/sigthread.exp
@@ -49,4 +49,4 @@  after 500 {send_gdb "\003"}
 
 # Make sure we do not get an internal error from hitting Control-C
 # while many signals are flying back and forth.
-gdb_test "" "Thread .* received signal SIGINT.*" "stop with control-c"
+gdb_test "" "Thread .* (received signal SIGINT|stopped).*" "stop with control-c"
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index d8c684c7238..5aeebb3704b 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -2870,6 +2870,25 @@  gdb_caching_proc supports_memtag {
     return 0
 }
 
+# Return true if the target is able to interrupt a program that blocks
+# SIGINT.
+proc can_interrupt_blocked_sigint {} {
+    if {[istarget *-*-linux*]} {
+	gdb_test_multiple "maint show target-non-stop" "" {
+	    -re "(is|currently) on.*$::gdb_prompt $" {
+	    }
+	    -re "(is|currently) off.*$::gdb_prompt $" {
+		# GDBserver still tries to interrupt programs with
+		# SIGINT.
+		return 0
+	    }
+	}
+    }
+
+    # Assume possible.
+    return 1
+}
+
 # Return 1 if the target supports hardware single stepping.
 
 proc can_hardware_single_step {} {