2013-04-15 10 views
10

Diciamo che ho un codice che causa un errore di segmentazione.stampa il motivo dell'errore di segmentazione

char * ptr = NULL; 
*ptr = "hello"; /* this will cause a segmentation fault */ 

Come esegue la stampa delle runtime, l'indirizzo in memoria che la colpa di segmentazione si è verificato a, e il motivo per il guasto di segmentazione (accedendo alla regione di memoria proibita, o altro).

Ho letto dei file di dump di base, ma non sono sicuro che sia la soluzione corretta.

Come posso fare questo?

P.S, sono a conoscenza del fatto che posso ottenerlo utilizzando gdb o un altro debugger, ma lo scopo è farlo utilizzando codice e solo codice.

+3

È possibile utilizzare la funzione ['backtrace'] (http://linux.die.net/man/3/backtrace). Ma in realtà ti raccomando di eseguire il tuo programma in un debugger, ti permetterà non solo di vedere il backtrace, ma di salire sullo stack delle chiamate ed esaminare le variabili. –

+2

"leggi i file di dump di base": li raccomando vivamente. Scaricano tutto nella memoria e puoi quindi aprirli con 'gdb' e l'eseguibile corretto. Questo ti darà la possibilità di vedere cosa è successo esattamente (a meno che la memoria non sia incasinata, ma è un caso piuttosto raro) - vedi i valori di qualsiasi variabile, backtrace, thread, ecc (ovviamente, sarebbe bello avere un debug massimo livello e nessuna ottimizzazione per questo tipo di indagine) –

+0

hmm .. il tipo di '* ptr' è' char', ma '" ciao "il tipo di' s è 'char *'. probabilmente dovresti assegnare un carattere ('* ptr = 'h';') o usare un 'memmove()' o simile per essere corretto nell'esempio.così com'è, prende l'indirizzo della costante di stringa, lo converte in numero intero, lo riduce a 1 byte e quindi segfaults assegnandolo a '* ptr' – SingleNegationElimination

risposta

4

Se si desidera conoscere la causa è possibile registrare un sig handler nale, qualcosa di simile:

void handler(int signum, siginfo_t *info, void *context) 
{ 
    struct sigaction action = { 
    .sa_handler = SIG_DFL, 
    .sa_sigaction = NULL, 
    .sa_mask = 0, 
    .sa_flags = 0, 
    .sa_restorer = NULL 
    }; 

    fprintf(stderr, "Fault address: %p\n", info->si_addr); 
    switch (info->si_code) { 
    case SEGV_MAPERR: 
    fprintf(stderr, "Address not mapped.\n"); 
    break; 

    case SEGV_ACCERR: 
    fprintf(stderr, "Access to this address is not allowed.\n"); 
    break; 

    default: 
    fprintf(stderr, "Unknown reason.\n"); 
    break; 
    } 

    /* unregister and let the default action occur */ 
    sigaction(SIGSEGV, &action, NULL); 
} 

E poi da qualche parte è necessario registrarlo:

struct sigaction action = { 
    .sa_handler = NULL, 
    .sa_sigaction = handler, 
    .sa_mask = 0, 
    .sa_flags = SA_SIGINFO, 
    .sa_restorer = NULL 
    }; 


    if (sigaction(SIGSEGV, &action, NULL) < 0) { 
    perror("sigaction"); 
    } 

In pratica si registra un segnale che viene generato quando SIGSEGV è consegnato, e si ottiene un po 'di informazioni aggiuntive, per citare la pagina man:

The following values can be placed in si_code for a SIGSEGV signal: 

     SEGV_MAPERR address not mapped to object 

     SEGV_ACCERR invalid permissions for mapped object 

Questi mappa per due motivi fondamentali f o ottenendo un errore di seg, o la pagina a cui si è eseguito l'accesso non è stata mappata affatto o non è stato consentito eseguire alcuna operazione che si è tentato di eseguire su quella pagina.

Qui, dopo che il gestore del segnale si è attivato, si annulla automaticamente e sostituisce l'azione predefinita. Ciò causa l'operazione che non è stata eseguita nuovamente in modo che possa essere catturata dal percorso normale. Questo è il comportamento normale di un errore di pagina (il precursore per ottenere un errore di seg) in modo che funzioni come il paging della domanda funzionino.

2

Come già risposto qui: How to generate a stacktrace when my gcc C++ app crashes

È possibile (nel caso di GCC con Linux/BSD almeno) fare questo abbastanza facile:

codice Esempio: uscita

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


void handler(int sig) { 
    void *array[10]; 
    size_t size; 

    // get void*'s for all entries on the stack 
    size = backtrace(array, 10); 

    // print out all the frames to stderr 
    fprintf(stderr, "Error: signal %d:\n", sig); 
    backtrace_symbols_fd(array, size, 2); 
    exit(1); 
} 

int main(int argc, char **argv) { 
    signal(SIGSEGV, handler); // install our handler 

    char * ptr = NULL; 
    *ptr = "hello"; /* this will cause a segmentation fault */ 
} 

Esempio :

# gcc -g -rdynamic -o test test.c 
# ./test 
Error: signal 11: 
0 test        0x000000010e99dcfa handler + 42 
1 libsystem_c.dylib     0x00007fff95c1194a _sigtramp + 26 
2 ???         0x0000000000000000 0x0 + 0 
3 libdyld.dylib      0x00007fff8fa177e1 start + 0 
4 ???         0x0000000000000001 0x0 + 1 
+0

Questo non produrrà più un core dump, almeno non uno con la causa originale dell'errore. Bene, hai una sorta di stacktrace, ma con molto meno valore. Senza offesa, stiamo solo rispondendo alla domanda degli OP, ma gli avvertimenti si applicano comunque ;-). Inoltre, considera l'uso di una combinazione di 'sprintf' e' write (2, buf, ...) 'per evitare' fprintf' che potrebbe essere complicato in un gestore di segnali (beh almeno per i segnali asincroni). Inoltre, non chiamerei 'exit()' ma solo '_exit()' o 'abort()'. Ma YMMV. –

+0

Sono d'accordo con te Christian, probabilmente potresti abilitarlo per certe build se davvero _ non vuoi mandare un core dump. Ma personalmente vorrei solo racchiudere l'eseguibile in uno script che permetta a gdb (se disponibile) di mostrare lo stacktrace dal core dump generato ... Sembra una soluzione molto più sensata. – Wolph