воскресенье, 11 декабря 2011 г.

Перехват сообщений SIGSEGV (Segmentation Fault) и SIGFPE (Floating point exception)

Разрабатывал на днях класс древовидного списка CTree, как раз создавал конструктор копирования.
Случайно забыл обнулить указатель в конструкторе копирования, что привело в дальнейшем к не всегда выпадающей ошибке доступа к памяти.
Разработка велась в Qt Creator. И ошибка доступа памяти приходила сообщением от ОС в виде сигнала SIGSEGV.

Свою ошибку я вычислил, после подробной пошаговой отладки приложения. Не обнулённый указатель был обнулён.

SIGSEGV - сигнал, посылаемый процессу при попытке обращения к несуществующей памяти или обращения с нарушением прав доступа (Segmentation Fault).
Данная ошибка может возникать при попытке обратиться к методу или члену по невалидному указателю, или при попытке обратиться к чужой области памяти.
К ошибке доступа памяти SIGSEGV могут привести следующие примеры кода:
// Пример 1. Не всегда может привести 
// к вызову SIGSEGV (Segmentation Fault)
myclass *ptr = new myclass;
delete ptr;
delete ptr;
// Пример 2. Более вероятно, 
// что сразу приведёт к SIGSEGV (Segmentation Fault)
int *ptr = (int*)(0x000000000);
delete ptr;
Главной неприятностью этих ошибок является то, что программа может молча упасть, и вам будет не понятна причина падения, можно даже не заметить падение (В моём случае падал консольный тест, выполнялись не все тесты, но слов об ошибке не было), либо в виндовс ошибка может вызвать аварийное завершение вашего приложения вида


Кстати, помимо ошибки доступа памяти (при работе с указателями), можно получить и ошибку математических вычислений. Такая ошибка сопровождается приходом сигнала SIGFPE. Самый простой пример - это деление на нуль:
int x;
x = 1/0;
В этом случае также можно получить молчаливое или аварийное завершение программы. (Молчаливое завершение намного хуже, т.к. вы можете его попросту не заметить и ничего об этом не узнать).

Как сделать так, чтобы программа не завершалась молча?

Как перехватить ошибку доступа к памяти?
Как перехватить деление на ноль?
Ответ далее.

Для этого надо перехватить сигналы SIGSEGV, SIGFPE. (Кстати, не все сигналы могут быть перехвачены, более подробную информацию можно найти в соответствующей справке по функции signal).

За основу следующего примера был взят пример с "Обработка Segmentation Fault в C++".

Перехват сигналов SIGSEGV, SIGFPE в C++:
#include <windows.h>
#include <signal.h>

void handler_sigsegv(int signum)
{
    MessageBoxA(NULL,"SIGSEGV Error!","POSIX Signal",MB_ICONSTOP);
    // открепить обработчик и явно завершить приложение
    signal(signum, SIG_DFL);
    exit(3);
}

void handler_sigfpe(int signum)
{
    MessageBoxA(NULL,"SIGFPE Error!","POSIX Signal",MB_ICONSTOP);
    // открепить обработчик и явно завершить приложение
    signal(signum, SIG_DFL);
    exit(3);
}

void fall1()
{
    int* p = 0x00000000;
    *p = 10;
}
void fall2()
{
    int x;
    x = 1/0;
}

int main()
{
    // установим наши обработчики на два сигнала
    signal(SIGSEGV, handler_sigsegv);
    signal(SIGFPE, handler_sigfpe);

    // вызовем одну из ошибок
    fall1();
    fall2();

    return 0;
}
Внимание: в документации описан перечень безопасных функций, которые можно безопасно вызвать из обработчика сигнала SIGSEGV. Также не следует продолжать выполнять какие-то критические действия, т.к. память уже повреждена (какие именно повреждения - неизвестно).

Пример я компилировал в Qt Creator под операционной системой Windows, и он работал, ошибки отлавливались. Спасибо хабра-форуму, который подсказал решение этой проблемы. В результате работы примера, при возникновении ошибок, будут всплывать диалоговые окна, оповещающие об ошибке.


Для перехвата исключения деления на ноль, можно использовать технологию SEH, но возможно она работает только для Windows. И она также может перехватить не все сигналы.

По вопросам выделения памяти и "двойного" delete, можно почитать: http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.2

2 комментария:

  1. Пытаюсь использовать данный метод для отлова исключений "Segmentation Fault". Так вот, если пытаюсь отловить вне блока try, то все перехватывается как и описано у Вас, но если данное исключение возникает в блоке try, то оно не отлавливается. Просто валится - и все.((

    ОтветитьУдалить
  2. Распечатка СМС (WhatsApp,Viber) от 300р.
    Детализация звонков 300р.
    Пробить номер 300р.
    Гарантия!
    Реальные доказательства!
    Оплата по факту.

    sms-ya@live.ru
    WWW.SMS-YA.RU


    Распечатка СМС (WhatsApp,Viber) от 300р.
    Детализация звонков 300р.
    Пробить номер 300р.
    Гарантия!
    Реальные доказательства!
    Оплата по факту.

    sms-ya@live.ru
    WWW.SMS-YA.RU


    Распечатка СМС (WhatsApp,Viber) от 300р.
    Детализация звонков 300р.
    Пробить номер 300р.
    Гарантия!
    Реальные доказательства!
    Оплата по факту.

    sms-ya@live.ru
    WWW.SMS-YA.RU


    Распечатка СМС (WhatsApp,Viber) от 300р.
    Детализация звонков 300р.
    Пробить номер 300р.
    Гарантия!
    Реальные доказательства!
    Оплата по факту.

    sms-ya@live.ru
    WWW.SMS-YA.RU


    Распечатка СМС (WhatsApp,Viber) от 300р.
    Детализация звонков 300р.
    Пробить номер 300р.
    Гарантия!
    Реальные доказательства!
    Оплата по факту.

    sms-ya@live.ru
    WWW.SMS-YA.RU




    ОтветитьУдалить