[PATCH] Supress SIGTTOU when handling errors

Alan Hayward Alan.Hayward@arm.com
Fri May 17 12:47:00 GMT 2019



> On 16 May 2019, at 19:06, Andrew Burgess <andrew.burgess@embecosm.com> wrote:
> 
> * Alan Hayward <Alan.Hayward@arm.com> [2019-05-16 15:51:53 +0000]:
> 
>> [I've seen this on and off over many months on AArch64 and Arm, and am
>> assuming it isn't the intended behaviour. Not sure if this should be at
>> tcdrain or it should be done at a higher level - eg in the terminal
>> handling code]
>> 
>> Calls to error () can cause SIGTTOU to send gdb to the background.
>> 
>> For example, on an Arm build:
>>  (gdb) b main
>>  Breakpoint 1 at 0x10774: file /build/gdb/testsuite/../../../src/binutils-gdb/gdb/testsuite/gdb.base/watchpoint.c, line 174.
>>  (gdb) r
>>  Starting program: /build/gdb/testsuite/outputs/gdb.base/watchpoint/watchpoint
>> 
>>  [1]+  Stopped                 ../gdb ./outputs/gdb.base/watchpoint/watchpoint
>>  localhost$ fg
>>  ../gdb ./outputs/gdb.base/watchpoint/watchpoint
>>  Cannot parse expression `.L1199 4@r4'.
>>  warning: Probes-based dynamic linker interface failed.
>>  Reverting to original interface.
>> 
>> The SIGTTOU is raised whilst inside a syscall during the call to tcdrain.
>> Fix is to use scoped_ignore_sigttou to ensure SIGTTOU is blocked.
>> 
>> In addition fix include comments - job_control is not included via
>> terminal.h
> 
> Maybe someone else knows this better, but this feels like the wrong
> solution to me.
> 
> As I understand it the problem you're seeing could be resolved by
> making sure that the terminal is correctly claimed by GDB at the point
> tcdrain is called.  This would require a call to either
> 'target_terminal::ours ()' or 'target_terminal::ours_for_output ()’.

That makes sense. Wasn’t aware about that code before. 

> 
> I've made several attempts to fix this in the past (non posted
> though), one problem I've previously run into that I've not yet
> understood is that calling ::ours_for_output doesn't seem to be enough
> to prevent the SIGTTOU (I assume from the tcdrain) and only calling
> ::ours is enough.


Playing about with the patch you posted, I also found that ::ours prevents
the signal, but ::ours_for_output doesn’t.  Looks like it’s due to the
following tcsetpgrp not happening:

inflow.c:child_terminal_ours_1 ()

if (job_control && desired_state == target_terminal_state::is_ours)
  {
#ifdef HAVE_TERMIOS_H
    result = tcsetpgrp (0, our_terminal_info.process_group);

> What I've been trying to figure out is what the intended difference
> between ::ours_for_output and ::ours actually is, if we can't always
> write output after calling ::ours_for_output.  Part of me wonders if
> we should just switch to using ::ours in all cases….

Agreed, I’m not sure of the intended differences here.

> 
> Anyway, I think most of the problems you're seeing should be fixed by
> claiming the terminal either permanently (calling ::ours or
> ::ours_for_output) or temporarily using
> target_terminal::scoped_restore_terminal_state.

The problem with that is finding all possible error cases and ensuring
they all all fixed up.

For example, my error was coming from the try/catch/exception_print
in solid-svr4.c:solib_event_probe_action ()

> 
> Like I said, I'm not an expert on this code, so maybe I've
> misunderstood the problem you're solving.
> 

We’re definitely looking at the same issue.

Working back up the call trace from where my change was, all the error
prints first end up in exceptions.c::print_flush () which already has
a call to ::ours_for_output.

I’ve posted two patches below. Both of them fix all my issues, including
your GDB_FAKE_ERROR case.

If ::ours_for_output and ::ours are working as intended, then the first
patch is probably the correct fix.

But, if ::ours_for_output and ::ours are not working as intended, then
possibly more investigation is required to know why patch 2 works.

Alan.



PATCH 1:

gdb/ChangeLog:

2019-05-17  Alan Hayward  <alan.hayward@arm.com>

        * exceptions.c (print_flush): Use target_terminal::ours.

diff --git a/gdb/exceptions.c b/gdb/exceptions.c
index ebdc71d98d..47bfb95153 100644
--- a/gdb/exceptions.c
+++ b/gdb/exceptions.c
@@ -46,7 +46,7 @@ print_flush (void)
   if (current_top_target () != NULL && target_supports_terminal_ours ())
     {
       term_state.emplace ();
-      target_terminal::ours_for_output ();
+      target_terminal::ours ();
     }

   /* We want all output to appear now, before we print the error.  We




PATCH 2:


    gdb/ChangeLog:

    2019-05-17  Alan Hayward  <alan.hayward@arm.com>

            * inflow.c (child_terminal_ours_1): Call tcsetpgrp for
            is_ours_for_output.

diff --git a/gdb/inflow.c b/gdb/inflow.c
index 339b55c0bc..b376e24e86 100644
--- a/gdb/inflow.c
+++ b/gdb/inflow.c
@@ -509,7 +509,9 @@ child_terminal_ours_1 (target_terminal_state desired_state)
       /* If we only want output, then leave the inferior's pgrp in the
         foreground, so that Ctrl-C/Ctrl-Z reach the inferior
         directly.  */
-      if (job_control && desired_state == target_terminal_state::is_ours)
+      if (job_control
+         && (desired_state == target_terminal_state::is_ours
+             || desired_state == target_terminal_state::is_ours_for_output))
        {
 #ifdef HAVE_TERMIOS_H




More information about the Gdb-patches mailing list