Bug 9039

Summary: GDB is not trapping SIGINT. Ctrl+C terminates program when should break gdb.
Product: Process Management Reporter: Jordi Blasi (jordiblasi)
Component: OtherAssignee: process_other
Status: REJECTED INVALID    
Severity: high CC: drow, fairyfar, flavio.etrusco, hi-angel, oleg, randy.dunlap, roland, tromey
Priority: P1    
Hardware: All   
OS: Linux   
Kernel Version: 2.6.20.1 #3 PREEMPT Subsystem:
Regression: --- Bisected commit-id:

Description Jordi Blasi 2007-09-19 02:40:48 UTC
Most recent kernel where this bug did not occur: do not know.
-----------------------------------------------
Distribution: Mandriva Powerpack 2007.
-------------
Hardware Environment: i686
---------------------
Software Environment:
---------------------
gcc (GCC) 4.1.1 20060724 (prerelease) (4.1.1-3mdk)
GNU gdb 6.5

Problem Description: GDB is not trapping SIGINT.
--------------------

I'm having problems getting GDB to not pass SIGINT to my program. In my main thread I do a sigwait for SIGINT. When I'm debugging, while gdb is running I may want to break gdb to set breakpoints or so, then I Ctrl-C (send SIGINT) but gdb does not break, instead SIGINT is passed to my program that gracefully terminates and gdb prints "Program exited normally". Of course "info handle" shows that SIGINT should not be being passed to my program:

(gdb) info handle SIGINT
Signal        Stop      Print   Pass to program Description
SIGINT        Yes       Yes     No              Interrupt

Gdb does not even print SIGINT signal delivery when I think it should.

At first I thought that was a gdb related problem but

http://sourceware.org/ml/gdb/2006-04/msg00010.html points to a kernel issue.

Steps to reproduce: compile and run under gdb, then hit Ctrl+C
-------------------

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <string.h>

 
//-----------------------------------------------------------------------------
void user_catched_sighandler_term(int signal_number)
{}

//-----------------------------------------------------------------------------
void user_catched_sighandler_int(int signal_number)
{}


//  Signal number, signal handler relationship
//-----------------------------------------------------------------------------
static const struct
{
    int m_SigNum;
    void (*m_SigHandler)(int);
}
Signal_handlers[] = {
        { SIGTERM, user_catched_sighandler_term },
        { SIGINT,  user_catched_sighandler_int  },
};

//-----------------------------------------------------------------------------
int main(int argc, char **argv)
{
    sigset_t signal_set;
    int signal;
    struct sigaction s;

    s.sa_flags = SA_RESTART;

    //! Set signal handlers
    //!--------------------------------------------------------------------    
    for(uint sig = 0; sig < sizeof(Signal_handlers)/sizeof(Signal_handlers[0]); sig++)
    {
        s.sa_handler = Signal_handlers[sig].m_SigHandler;

        if(sigaction(Signal_handlers[sig].m_SigNum, &s, NULL) < 0)
        {
            perror("<%s> Error sigaction: ");
            exit(2);
        }
    }

    if(sigfillset(&signal_set) < 0)
    {
        perror("<main> Error sigfillset(): ");
        exit(2);
    }

    while(true)
    {
        //! Wait for queued signals.
        //!--------------------------------------------------------------------
        if(sigwait(&signal_set,&signal) != 0)
        {
            perror("<main> Error sigwait(): ");
            exit(1);
        }

        //! Process signal.
        //!--------------------------------------------------------------------
        switch(signal)
        {
            case SIGTERM:
            case SIGINT: 
                printf("Terminating.\n");
                exit(0);
            break;

            default:
                printf("Unhandled signal [%s]\n",strsignal(signal));
            break;
        }
    }
}
Comment 1 Roland McGrath 2007-09-19 13:42:37 UTC
gdb puts the debugged process in its own pgrp and sets the terminal to that pgrp.  (Try e.g. ps j on the PIDs of gdb and your program being debugged.)

When you hit C-c, the signal goes to the current pgrp, i.e. to the debugged process and not to gdb.  When a signal is delivered, ptrace will intercept it and let gdb decide what to do before it actually reaches the debugged process.  Unless you are using "handle SIGINT nostop", then gdb will stop and give you a prompt here.

However, your program uses sigwait and so the signal is never actually delivered.  Instead, you dequeue it via sigwait without going through actual signal delivery.  ptrace only reports a signal that is about to be delivered.  When you use sigwait, technically the signal is blocked the whole time and never delivered as such.  The fact that ptrace does not report this signal is the expected behavior.

It could indeed be useful to let debuggers intercept signals being dequeued by sigwait.  But, it would be wrong to change the existing ptrace interface to report these the same way it reports signals being dequeued for delivery.  The existing ptrace indication of a signal says that the reporting thread will run the handler or default action, and that is what debuggers now expect that it means.  Using the same report for a signal that was swallowed by sigwait could confuse existing debuggers.  A report may be desireable, but it will have to be requested differently from the existing vanilla ptrace signal case.

For your situation, you are not really interested in the sigwait reporting per se.  What you have asked for is to get a terminal signal to wake up gdb.  That is entirely doable within gdb's domain already.  You might be able to get it to happen by using gdb's "attach" and/or "tty" commands.  If not, that is an issue with gdb that you can try to get fixed.  If it provides some way to interrupt gdb that does not involve any signals generated for the debugged process, you are fine.  It could do that by not changing the pgrp of gdb's controlling tty so that a C-c sends SIGINT to gdb and not to the debugged process, or by giving some other means to wake up gdb.  (To avoid changing gdb's ctty's pgrp, you'd have to use a different tty for the debugged process if it does any terminal i/o of its own.)
Comment 2 Randy Dunlap 2007-09-19 14:27:20 UTC
So we can change this to Rejected / Invalid ??
Comment 3 Roland McGrath 2007-09-19 14:28:37 UTC
Yes, there is no bug as such here.
Comment 4 Daniel Jacobowitz 2007-09-20 04:28:24 UTC
I'm afraid I disagree with Roland.  GDB, for one, does not assume that the handler or default action will run.  I definitely expected any debugger to be woken before sigwait returns.  This is not "delivery" of the signal in the kernel's terminology, but the signal is propogated from the sender to the recipient, a.k.a., delivered.

Roland, do you have a specific example of a debugger which makes that assumption?  Does Frysk?
Comment 5 Roland McGrath 2007-09-20 13:35:55 UTC
At least some versions of strace did expect that behavior (and use a kludgey/racey check on /proc/pid/status contents to see if a handler is installed).  Anyway, I really was talking about the kernel semantics of the feature.  It never before reported for signals that are not being delivered (in the traditional and standard sense of taking a signal's action).
Comment 6 Daniel Jacobowitz 2007-09-20 13:53:41 UTC
That's certainly true; it never has.  But you can see where I'm going - I think that's a bug in the kernel, and it would be better for debuggers (which are currently limited to ptrace) if it did so.

If you're worried about existing behavior, it wouldn't be hard to make this optional.  It could be enabled by a ptrace option flag (we have bits to spare) and indicated by a high bit in the status code, just like clone events.  I think that's overkill but I'm certainly willing to be persuaded.
Comment 7 Roland McGrath 2007-09-20 14:39:52 UTC
I do think it would be wrongly breaking compatibility to conflate this new sigwait reporting with real signal delivery reporting.
Comment 8 Tom Tromey 2013-11-26 19:52:35 UTC
Could this bug please be reopened and turned into a ptrace
feature request?  This could be made compatible by adding
a new PTRACE_O_* flag and a new PTRACE_EVENT_* value for
reporting.

This has come up several times from users who are confused
because they type C-c to their program and gdb does not
respond.  After investigation it turns out that their program
is either blocked in sigwait or is accepting SIGINT via
signalfd.
Comment 9 Oleg Nesterov 2013-11-29 17:50:01 UTC
So far I agree with Roland, I do not think it makes sense to change
the kernel...

But I understand that it is too easy to blame userspace and deny the
problem ;)

(In reply to Tom Tromey from comment #8)
>
> Could this bug please be reopened and turned into a ptrace
> feature request?  This could be made compatible by adding
> a new PTRACE_O_* flag and a new PTRACE_EVENT_* value for
> reporting.

Please, could you describe the desired semantics of the new
potential PTRACE_O/EVENT in details?

Or do you really want just the new ptrace report from sigwait
and signalfd_read ?
Comment 10 FairyFar 2022-03-24 10:57:09 UTC
I had a similar problem and I have solved it.

First, write a `gdb` script, the file name is `sighhandler.gdb`. The script file is in the project directory:  

gdb_sigwait/src/sighandler.gdb

Then, `source` the above script file in the `gdb` initialization file (`~/.gdbinit`), for example:

source ~/gdb_sigwait/src/sighandler.gdb

The `gdb` script is too long. 
So, I created a github project to introduce and store the related code: 

https://github.com/fairyfar/gdb_sigwait