[PATCH v2 00/16] Interrupting programs that block/ignore SIGINT

Pedro Alves pedro@palves.net
Wed Jun 16 11:27:28 GMT 2021


On 2021-06-15 1:34 p.m., Eli Zaretskii wrote:
>> From: Pedro Alves <pedro@palves.net>
>> Date: Mon, 14 Jun 2021 22:23:54 +0100
>>
>> Currently, on GNU/Linux, it is not possible to interrupt with Ctrl-C
>> programs that block or ignore SIGINT, with e.g., sigprocmask or
>> signal(SIGINT, SIG_IGN).  You type Ctrl-C, but nothing happens.
> 
> For the reasons I explained in my other message, can we please talk
> about "stopping a program" instead of "interrupting"?  Both in the log
> message (and the discussion) and in the documentation, please.  At
> least for me, the use of "interrupt" was confusing.
> 
>> This series address the problem by turning Ctrl-C handling around such
>> that the SIGINT always reaches GDB first, not the inferior.  That is
>> done by making GDB put inferiors in their own terminal/session created
>> by GDB.  I.e., GDB creates a pseudo-terminal master/slave pair, makes
>> the inferior run with the slave as its terminal, and pumps
>> output/input on the master end.
> 
> Is this "pumping" really-truly 100% transparent to the program being
> debugged?  I understand how a program that uses stdio will simply read
> the same stuff that was "pumped" from GDB's terminal, but what about
> other kinds of input and settings?
> 
> For example, some text-mode programs change the settings of the
> terminal via termios -- will those settings be reflected in the GDB's
> terminal when the debuggee reads or writes from its terminal?  Or what
> about support for terminfo functions -- will the commands be passed
> back and forth between the terminals and the user will see the effects
> he/she expects?  Some terminal emulators support advanced features
> like cut/paste -- will that also work with this pumping?  And if the
> program changes the character that causes SIGINT (Emacs does that when
> it runs on a text-mode terminal), will that change the interrupt
> character for GDB as well?

Debugging curses programs works fine, because we "pump" all input/output between
the terminals, escape sequences and all.  E.g., I use the TUI frequently, and
debugging gdb with itself and enabling the TUI in the inferior gdb works fine.  
No other pumping is done, only input/output.

When the new terminal is created, it inherits the terminal settings of the
GDB terminal, but otherwise, the inferior's terminal settings are not
reflected in GDB's terminal settings.  I mean, if the program changes \n -> \r\n
translation in the inferior, that changes the inferior's terminal, and what ends
up output to the pseudo-terminal slave end.  When GDB flushes that into its
own terminal, GDB puts its own terminal in raw mode, so that exactly whatever
characters/bytes the inferior wanted to send to the screen, they end up in
GDB's terminal.  Similarly for the input direction.

Cut/paste works fine for me, but I'm not sure I'm trying what you are
suggesting.  E.g., I debug a GDB that is running in TUI mode, and then I can use
the mouse to select text (shift click drag) and then paste it back (shift middle click).
This is all implemented in the terminal emulator, the application has no idea about it,
it's just text grabbed from the screen, and then input back into the application,
as if you typed it.

Changing the interrupt character does not work.  If I debug emacs in text-mode, and
then press ctrl-c, it drops you back into GDB, like:

 Thread 1 "emacs" stopped.
 0x00007ffff5197246 in __pselect (nfds=9, readfds=0x7fffffffc8a0, writefds=0x7fffffffc920, exceptfds=0x0, timeout=<optimized out>, sigmask=<optimized out>) at ../sysdeps/unix/sysv/linux/pselect.c:48                                                           |
 48      ../sysdeps/unix/sysv/linux/pselect.c: No such file or directory.
 (gdb)

If I press "ctrl-g", I then get "Program stopped by SIGINT":

 Thread 1 "emacs" received signal SIGINT, Interrupt.
 0x00007ffff5197246 in __pselect (nfds=9, readfds=0x7fffffffc8a0, writefds=0x7fffffffc920, exceptfds=0x0, timeout=<optimized out>, sigmask=<optimized out>) at ../sysdeps/unix/sysv/linux/pselect.c:48                                                           |
 48      in ../sysdeps/unix/sysv/linux/pselect.c
 (gdb)

I see this as a feature and it goes back to the "supervisor" idea discussed
in the other thread.  To change the "supervisor" interrupt key, you have to
change the interrupt key in the supervisor's terminal, with e.g., "$ stty intr ^G".
or even "(gdb) shell stty intr ^G".  Not unlike if you had attached to emacs
instead of spawned it.  I guess the emacs's emacs-gdb.gdb file could run that
stty command, so it is run automatically when debugging emacs.  The altered intr key will
remain in effect after gdb exits, though.  (I can reproduce it with current GDB.)
I guess GDB could grow a built-in command for that to avoid it.  

But, shouldn't GDB restore terminal settings on exit?  That's easy to do, GDB
already saves the initial terminal settings when it starts up, to pass them down
to inferiors when their started.  See patch at the bottom.  That does restore
the intr key.

> 
>> The series will then make GDB interrupt the program with SIGSTOP
>> instead of SIGINT, which always works even if the inferior
>> blocks/ignores SIGINT -- SIGSTOP can't be ignored.
> 
> And if the user then types "continue", will SIGINT be delivered to the
> program, or will the Ctrl-C keystroke be "swallowed" by GDB?

Already answered elsewhere, but yes, it will be swallowed, just like today
by default.

> 
>> Having the inferior in its own terminal also means that GDB is in
>> control of when inferior output is flushed to the screen.  When
>> debugging with the CLI, this means that inferior output is now never
>> interpersed with GDB's output in an unreadable fashion.
> 
> I guess this will be done on the screen line level?  Because if a
> program uses terminfo or curses to write to different parts of the
> screen, the outputs of the program and GDB will mix anyhow, just not
> on the same screen line, right?
> 

Yeah, all I meant is that the inferior and GDB won't be concurrently writing
to the screen at the same time,  E.g., if you continue the program in the
background (continue&), and the program prints to the screen, if you do e.g. "help all",
the inferior output won't be flushed until all the help text is printed.
I should probably just drop that sentence, it's not that important.

Pedro Alves

>From c98f0866f70b473399193f45186cd7fbdb43e3aa Mon Sep 17 00:00:00 2001
From: Pedro Alves <pedro@palves.net>
Date: Wed, 16 Jun 2021 12:12:52 +0100
Subject: [PATCH] restore terminal settings on exit

Change-Id: I7afb648d6a17ee7588b1963518a5a79b20ebb8c9
---
 gdb/inflow.c   | 11 ++++++++++-
 gdb/terminal.h |  6 +++++-
 gdb/top.c      |  4 +++-
 3 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/gdb/inflow.c b/gdb/inflow.c
index 35a51b26fb5..f100bd58dfc 100644
--- a/gdb/inflow.c
+++ b/gdb/inflow.c
@@ -207,7 +207,7 @@ static bool input_fd_redirected = false;
 /* See terminal.h.  */
 
 void
-set_initial_gdb_ttystate (void)
+save_initial_gdb_ttystate (void)
 {
   /* Note we can't do any of this in _initialize_inflow because at
      that point stdin_serial has not been created yet.  */
@@ -227,6 +227,15 @@ set_initial_gdb_ttystate (void)
     }
 }
 
+/* See terminal.h.  */
+
+void
+restore_initial_gdb_ttystate ()
+{
+  if (initial_gdb_ttystate != NULL)
+    serial_set_tty_state (stdin_serial, initial_gdb_ttystate);
+}
+
 /* Does GDB have a terminal (on stdin)?  */
 
 static int
diff --git a/gdb/terminal.h b/gdb/terminal.h
index f8ef29bc2a9..8661a8fc519 100644
--- a/gdb/terminal.h
+++ b/gdb/terminal.h
@@ -44,6 +44,10 @@ extern void gdb_save_tty_state (void);
 
 /* Take a snapshot of our initial tty state before readline/ncurses
    have had a chance to alter it.  */
-extern void set_initial_gdb_ttystate (void);
+extern void save_initial_gdb_ttystate (void);
+
+/* Restore the terminal settings back to what they were at GDB
+   startup.  */
+extern void restore_initial_gdb_ttystate ();
 
 #endif /* !defined (TERMINAL_H) */
diff --git a/gdb/top.c b/gdb/top.c
index 6e0f43d2fd9..ee6b45774a4 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -1841,6 +1841,8 @@ quit_force (int *exit_arg, int from_tty)
       exception_print (gdb_stderr, ex);
     }
 
+  restore_initial_gdb_ttystate ();
+
   exit (exit_code);
 }
 
@@ -2401,7 +2403,7 @@ gdb_init ()
 
   /* Take a snapshot of our tty state before readline/ncurses have had a chance
      to alter it.  */
-  set_initial_gdb_ttystate ();
+  save_initial_gdb_ttystate ();
 
   async_init_signals ();
 

-- 
2.26.2



More information about the Gdb-patches mailing list