2013-04-15 21 views
10

Załóżmy, że mam kod powodujący błąd segmentacji.wydrukuj przyczynę niepowodzenia segmentacji

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

Jak drukować na wykonywania, adres w pamięci, że błąd wystąpił w segmentacji, a przyczyną usterki segmentacji (dostęp do zakazanego obszaru pamięci, lub coś innego).

Przeczytałem o plikach zrzutu pamięci, ale nie jestem pewien, czy to poprawne rozwiązanie.

Jak mogę to zrobić?

P.S, jestem świadomy zrobić, że mogę to osiągnąć za pomocą gdb lub inny debugger, ale celem jest, aby to zrobić za pomocą kodu, a jedynie kod.

+3

Można użyć funkcji ['backtrace'] (http://linux.die.net/man/3/backtrace). Ale naprawdę polecam uruchomienie programu w debugerze, pozwoli ci nie tylko zobaczyć ślad, ale podejść do stosu wywołań i zbadać zmienne. –

+2

"przeczytaj o plikach zrzutu pamięci" - zdecydowanie polecam. Zrzucają wszystko w pamięci i można je otworzyć za pomocą 'gdb' i właściwego pliku wykonywalnego. To da ci szansę zobaczenia, co dokładnie się stało (chyba że pamięć nie jest pomieszana, ale to dość rzadki przypadek) - zobacz wartości dowolnych zmiennych, ślad wstecz, wątki itp. (Oczywiście, byłoby miło mieć max debugowanie poziom i brak optymalizacji dla tego typu badania) –

+0

hmm .. typ '* ptr' to' char', ale '" hello "to typ' char * '. powinieneś prawdopodobnie przypisać znak ('* ptr = 'h';') lub użyć 'memmove()' lub podobnego, aby przykład był poprawny.jak to jest, pobiera adres stałej ciągowej, rzuca ją na liczbę całkowitą, goli ją do 1 bajtu, a następnie segfaults przypisując ją do '* ptr' – SingleNegationElimination

Odpowiedz

4

Jeśli chcesz poznać przyczynę, możesz zarejestrować sig nal obsługi, coś jak:

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); 
} 

A potem gdzieś trzeba zarejestrować go:

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"); 
    } 

Zasadniczo zarejestrować sygnał, że pożary po SIGSEGV została wydana, i masz jakieś dodatkowe informacje, cytując strona 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 

Są mapa do dwóch podstawowych powodów f lub otrzymanie błędu seg - albo strona, do której wchodziłeś, nie była w ogóle mapowana, albo nie mogłeś wykonać żadnej operacji, którą próbujesz wykonać na tej stronie.

Po uruchomieniu obsługi sygnału wylogowuje się i zastępuje domyślną akcję. Powoduje to, że operacja, która nie została wykonana ponownie, może zostać przechwycona przez normalną trasę. Jest to normalne zachowanie strony błędu (prekursora do uzyskania błędu seg), aby działały strony takie jak popieranie popytu.

2

Jak już tu odpowiedzi: How to generate a stacktrace when my gcc C++ app crashes

można (w przypadku GCC z Linux/BSD przynajmniej) to zrobić dość łatwo:

Przykład kodu:

#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 */ 
} 

Przykâadowa :

# 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

To nie spowoduje już utworzenia zrzutu pamięci, a przynajmniej nie będzie miało pierwotnej przyczyny błędu. Dobrze, masz jakiś stacktrace, ale o znacznie mniejszej wartości. Bez urazy, odpowiadasz tylko na pytanie OP, ale i tak ostrzeżenia się obowiązują ;-). Rozważ także użycie kombinacji 'sprintf' i' write (2, buf, ...) ', aby uniknąć' fprintf', co może być trudne w obsłudze sygnału (przynajmniej w przypadku sygnałów asynchronicznych). Ponadto, nie wywołuję 'exit()', ale tylko '_exit()' lub 'abort()'. Ale YMMV. –

+0

Zgadzam się z tobą Christian, prawdopodobnie możesz włączyć to dla niektórych buildów, jeśli nie chcesz 'wysyłać zrzutu pamięci. Ale osobiście po prostu owinąłbym plik wykonywalny w skrypcie, który pozwala gdb (jeśli jest dostępny) pokazać stacktrace z wygenerowanego zrzutu pamięci ... Wydaje się, że jest to rozsądniejsze rozwiązanie. – Wolph