LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   Catching SIGSEGV - handelling segmentation faults (https://www.linuxquestions.org/questions/programming-9/catching-sigsegv-handelling-segmentation-faults-646359/)

telg 06-02-2008 04:51 AM

Catching SIGSEGV - handelling segmentation faults
 
I have been messing around with signals lately and i was wondering if there is a nice simple way to pass arguments into a signal handler.

This is so it can tell you information about how the program died - almost throwing and catching exceptions in java. However, I can't find how the signal handeller will be able to take arguments from the location where the program crashed.
Code:

#include<signal.h>
#include<stdio.h>
void print(void)
{
        printf("%d\n",__LINE__);
        exit(-1);
}
int main(){
        signal(SIGSEGV,print);
        int x[2];
        x[1000000]=2;
}

but i want __LINE__ to be passed into print from where the program crashes

Does anyone know a method of doing this? Does one exist? If so, it could be very helpful for many...

pinniped 06-02-2008 06:14 AM

That's what a core file and debugger are for. You usually have to unwind the stack a bit (if the stack wasn't damaged) before you find the line in your program which was the initial cause of the problem (usually you pass a bad pointer to a library function).

What you want to do can be accomplished by changing a global variable, but it is cumbersome, ugly, and not very useful. Learning to use a debugger is far more useful and doesn't make your beautiful code look like Jabba the Hutt's mother.

telg 06-02-2008 10:43 AM

i see.
so the signal handler can only take in parameters of int and void but nothing else. hmm, i guess that answers no to any simple mechanism.

osor 06-02-2008 04:33 PM

Quote:

Originally Posted by telg (Post 3172165)
i see.
so the signal handler can only take in parameters of int and void but nothing else. hmm, i guess that answers no to any simple mechanism.

Well, yes and no. First, a process cannot catch its own SIGSEGV AFAIK. For this, you need to be tracing the process (in e.g., a debugger).

If you use the newer signal functions (e.g, sigaction() rather than plain-old signal()), however, you can get a little more information passed to your handler besides the signal number itself. In particular, if you set the flag SA_SIGINFO, your signal catching function will need to have signature:
Code:

void func(int signo, siginfo_t *info, ucontext_t *context);
The siginfo_t structure contains information such as who (what PID) sent the signal and the address of the faulting instruction. The ucontext_t contains the context in which the process was before the signal was caught (this includes stack state and architecture-specific values).

telg 06-02-2008 07:12 PM

ok,

i gave this a try like this

Code:

#include<signal.h>
#include<stdio.h>
#include<string.h>
#include<ucontext.h>
void print(int sig, siginfo_t *info, ucontext_t *context)
{
        printf("%d\n",__LINE__);
        printf("%d %d %d\n",info->si_pid, info->si_addr, context->uc_stack);
        exit(-1);
}
int main(){
        struct sigaction sa;
        //sa.sa_handler=print;
        sa.sa_sigaction=print;// warning here: assignment from incompatible pointer type
        sa.sa_flags=SA_SIGINFO;
        sigaction(SIGSEGV,&sa,NULL);
        int x[2];
        x[10000000]=2;
}

gives output of

Code:

7
-1033747112 -1033747112 0

but i'm pretty sure that's not right.
Is this the correct usage of the handler and the related structures?

pinniped 06-02-2008 08:17 PM

As I said before, the simplest way to get the line number is to have extensive assignments to a global variable, so you can forget about which of your lines resulted in the fault. Plus, (as I've also said), the fault is very likely to be generated from within a call to a library, in which case you have no line number to refer to (unless you compiled yourself or used a version with the debug info not stripped + you have the source on hand).

Although sigaction does give you a lot more info, you have to build in some debugger features simply to display the info you want. The big question is: WHY? There is absolutely no advantage over a core file + debugger, or a debugger attached to a process. People have spent many years building features into debuggers, so use them. The only advantage I can think of is to give a partial stack trace which developers might hope to recover some useful information from, such as the case with Microsoft. But if you do that, be prepared to be inundated with useless bug reports.

osor 06-02-2008 08:45 PM

Quote:

Originally Posted by telg (Post 3172619)
but i'm pretty sure that's not right.
Is this the correct usage of the handler and the related structures?

You haven’t addressed the point pinniped made (in particular, why not use a debugger). As for the correctness of the information, you are not guaranteed meaningful values for all struct members, and, additionally, you have to be able to interpret properly.

The statement:
Code:

printf("%d\n",__LINE__);
Will always print a 7, since __LINE__ is seven in that file. I am not sure how that was supposed to help you debug. The other information (such as pid is useless, since this signal was not generated by a kill() call).

When you use gdb (and compile with the greatest number of debugging symbols available), not only will it give you a stack trace, it will show the exact line number (and moreover, the exact assembly instruction) where the problem occurs.

osor 06-02-2008 08:57 PM

Btw, just for fun, here is a program which outputs the maximum possible amount of information which may be gleaned through siginfo (with the exception of the largely irrelevant floating-point state). It is targeted toward the i386 arch.
Code:

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>

static const char *gregs[] = {
        "GS",
        "FS",
        "ES",
        "DS",
        "EDI",
        "ESI",
        "EBP",
        "ESP",
        "EBX",
        "EDX",
        "ECX",
        "EAX",
        "TRAPNO",
        "ERR",
        "EIP",
        "CS",
        "EFL",
        "UESP",
        "SS"
};

void print(int sig, siginfo_t *info, void *c)
{
        ucontext_t *context = c;

        fprintf(stderr,
                "si_signo:  %d\n"
                "si_code:  %s\n"
                "si_errno:  %d\n"
                "si_pid:    %d\n"
                "si_uid:    %d\n"
                "si_addr:  %p\n"
                "si_status: %d\n"
                "si_band:  %ld\n",
                info->si_signo,
                (info->si_code == SEGV_MAPERR) ? "SEGV_MAPERR" : "SEGV_ACCERR",
                info->si_errno, info->si_pid, info->si_uid, info->si_addr,
                info->si_status, info->si_band
        );

        fprintf(stderr,
                "uc_flags:  0x%x\n"
                "ss_sp:    %p\n"
                "ss_size:  %d\n"
                "ss_flags:  0x%X\n",
                context->uc_flags,
                context->uc_stack.ss_sp,
                context->uc_stack.ss_size,
                context->uc_stack.ss_flags
        );

        fprintf(stderr, "General Registers:\n");
        for(int i = 0; i < 19; i++)
                fprintf(stderr, "\t%7s: 0x%x\n", gregs[i], context->uc_mcontext.gregs[i]);
        fprintf(stderr, "\tOLDMASK: 0x%x\n", context->uc_mcontext.oldmask);
        fprintf(stderr, "\t    CR2: 0x%x\n", context->uc_mcontext.cr2);

        exit(-1);
}

int main()
{
        struct sigaction sa;
        sigemptyset (&sa.sa_mask);
        sa.sa_flags = SA_SIGINFO;
        sa.sa_sigaction = print;

        sigaction(SIGSEGV, &sa, NULL);

        int x[2];
        x[10000000]=2;

        return 0;
}

I haven’t checked for complete correctness, but it passes the “works for me” test.


All times are GMT -5. The time now is 10:00 PM.