Обработка исключений под linux
От: kov_serg Россия  
Дата: 12.08.06 13:28
Оценка: 8 (2)
### 12/08/06 17:03:16 kovserg:
Доброго времени суток.

Возник вопрос досих пор немогу найти решения.
Что нужно сказать gcc что бы следующий код заработал

try {
  int *p=NULL;
  struct A { ~A() { printf("a.dtor\n"); } } a;
  *p=*p;
} catch(...) {
  printf("segmentation fault\n");
}


При возникновении исключения типа access violation или деление на 0
в блок catch(...) программа не попадает, а сразу завершается. При
яем a.dtor не вызывается.

Под windows в VC это решалось ключем компилятора /EHa и кодом вида:

#include <windows.h>
#include <eh.h>
struct MyException {
  MyException(int code) : code {}
  int code;
};
void trans_Exception( unsigned int u, EXCEPTION_POINTERS* exc) {
    throw MyException(u);
}
int init_Exceptions() {
    _set_se_translator(trans_Exception); // compile with: /EHa
    return 1;
}
int auto_init = init_Exceptions();


В если linux обрабатывать сигналы то не получается перевести сигнал
в exception. Главная неясность как при возникновении сигнала SIGSEGV
сделать так что бы вызвались деструкторы для созданных объектов.

Поставил ряд экспериментов с gcc. Суть такая что при возникновении сигнала
access violation просто вызвать throw и дальше программа должна поити по
отлаженной схеме пройтись по unwind tables и грохнуть созданные объекты.
Но как оказалось gcc очень странно генерить кадры исключений. Вообще
непонятно как. Но повсей видимости привязывается к месту возникновения
исключения. Решение подобной проблеммы видел только для Tru64
там есть встроенная функция преобразования сигналов в exception
exc_raise_signal_exception
http://www.camk.edu.pl/doc/ccc/Programmers_Guide/XCPTCHPX.HTM

Проделать такое в SuSe, Derbian и RedHat неудалось там такого
даже близко нету . Но ведь неможет такого быть что бы подобная проблемма
не возникала до меня.

Вот код для проведения экспериментов по преобразованию signal -> exception

#include <signal.h>
#include <iostream>
#include <stdexcept>
using namespace std;

static void av_throw() {
    //cout << "prepare to die\n";
    throw 0xDEAD;
}

void emu_call(sigcontext *regs,int subr,int ret_addr) {
    regs->esp-=sizeof(int);
    *(int*)regs->esp=ret_addr;
    regs->eip=subr;
}

static void sig_action(int signum, siginfo_t *si, void* unk) {
    sigcontext *regs=(sigcontext*)((int*)unk+5); // экспериментально подобрано
    //printf("sig action %d\n",signum);
    //printf("old: eip=%08X esp=%08X\n",regs->eip,regs->esp);

    //regs->eip+=7;  // 1. это работает (просто пропускаем место аварии)
    //regs->eip=(int)av_throw; // 2. симулим jmp -- это падает -> signal_abort
    { // 3.  симулим call
        emu_call(regs,(int)av_throw,regs->eip+12); // 3a. с этим адресом возврата работает!!!
        //emu_call(regs,(int)av_throw,regs->eip+7); // 3b. с этим вызывает abort_signal
                                                    // 3b. если добавить ключ -fnon-call-exceptions то тоже работает. правда почему непонятно
        //emu_call(regs,(int)av_throw,regs->eip); // 3с.  с этим вызывает abort_signal
        // видимо его exception_frames как то связаны с адресом возрата
    }
    //throw 0x33; // 4. просто приводит к abort_signal
    //printf("new: eip=%08X esp=%08X\n",regs->eip,regs->esp);
    //sigreturn(regs); // warning: sigreturn is not implemented and will always fail
}

void __throw() {
    throw 0x12378;
}

void test() {
    struct A {
        ~A() { cout<<"a is dead\n"; }
    } a;
    int *p=NULL;
    *p=*p;
    __throw();
}

void sig_test() {
    struct sigaction sa, old_sa;
    sa.sa_sigaction = &sig_action;
    sa.sa_flags     = SA_SIGINFO;
    cout<<"install signal action\n";
    if (sigaction(SIGSEGV, &sa, &old_sa) == -1) throw runtime_error("can't set signal handler");
    try {
      test();
    } catch(int x) {
        cout<<"catch.int="<<x<<endl;
    } catch(...) {
        cout<<"catch.something\n";
    }
}

Вариант 3a. работает. Вариант 3b может работать а может и падать в зависимости от ключа компиляции, вариант 3c всёгда падает.

3a.
install signal action
a is dead
catch.int=57005
Press Enter to continue!

3c.
install signal action
terminate called after throwing an instance of 'int'
/bin/sh: line 1:  7628 Aborted                 ./threads_tst
Press Enter to continue!


Вот что генерит gcc для void test() собственно осюда смещения +7 и +12
Dump of assembler code for function _Z4testv:
0x0804bb7a <test()+0>:     push   %ebp
0x0804bb7b <test()+1>:     mov    %esp,%ebp
0x0804bb7d <test()+3>:     push   %ebx                        //
0x0804bb7e <test()+4>:     sub    $0x14,%esp                  //
0x0804bb81 <test()+7>:     movl   $0x0,0xfffffff8(%ebp)       //
0x0804bb88 <test()+14>:    mov    0xfffffff8(%ebp),%eax       //
0x0804bb8b <test()+17>:    mov    (%eax),%edx                 // +0   -- page fault point
0x0804bb8d <test()+19>:    mov    0xfffffff8(%ebp),%eax       //
0x0804bb90 <test()+22>:    mov    %edx,(%eax)                 //
0x0804bb92 <test()+24>:    call   0x804bb2c <__throw()>       // +7   -- point1
0x0804bb97 <test()+29>:    lea    0xfffffff7(%ebp),%eax       // +12  -- point2
0x0804bb9a <test()+32>:    call   0x804bb5a <test()::A::~A()> // 
0x0804bb9f <test()+37>:    jmp    0x804bbbd <test()+67>       //
0x0804bba1 <test()+39>:    mov    %eax,0xffffffe8(%ebp)       // повидимому unwind handler
0x0804bba4 <test()+42>:    mov    0xffffffe8(%ebp),%ebx       // правда как он его находит
0x0804bba7 <test()+45>:    lea    0xfffffff7(%ebp),%eax       // пока остаётся загадкой :)
0x0804bbaa <test()+48>:    call   0x804bb5a <test()::A::~A()> // a.dtor()
0x0804bbaf <test()+53>:    mov    %ebx,0xffffffe8(%ebp)       //
0x0804bbb2 <test()+56>:    sub    $0xc,%esp                   //
0x0804bbb5 <test()+59>:    pushl  0xffffffe8(%ebp)            //
0x0804bbb8 <test()+62>:    call   0x80495c8 <std::runtime_error::~runtime_error()+384> // передача управления дальше
0x0804bbbd <test()+67>:    mov    0xfffffffc(%ebp),%ebx  
0x0804bbc0 <test()+70>:    leave  
0x0804bbc1 <test()+71>:    ret    
End of assembler dump.

Может кто знает как gcc строит свои unwind tables (как он привязан к стеку).
Или може есть какой ключ компиляции который позволяет сменить способ постраения unwind table,
Что бы при возникновении access violation или деления на ноль код обрабатывался по схеме как в 3a ??
Люди help ! очень надо! незнаю что делать.



30.08.06 15:30: Перенесено модератором из 'C/C++' — Павел Кузнецов
Re: Обработка исключений под linux
От: Аноним  
Дата: 12.08.06 14:20
Оценка:
А почему нельзя обрабатывать асинхронный сигнал, зачем надо переводить асинхронный сигнал в exception?
Почему нельзя поставить обработчик на сигнал и в нем удалять все необходимые объекты?
Re[2]: Обработка исключений под linux
От: kov_serg Россия  
Дата: 12.08.06 14:30
Оценка:
Здравствуйте, Аноним, Вы писали:

А>А почему нельзя обрабатывать асинхронный сигнал, зачем надо переводить асинхронный сигнал в exception?

А>Почему нельзя поставить обработчик на сигнал и в нем удалять все необходимые объекты?

Как ???
Re[3]: Обработка исключений под linux
От: Аноним  
Дата: 12.08.06 16:28
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Здравствуйте, Аноним, Вы писали:


А>>А почему нельзя обрабатывать асинхронный сигнал, зачем надо переводить асинхронный сигнал в exception?

А>>Почему нельзя поставить обработчик на сигнал и в нем удалять все необходимые объекты?

_>Как ???


man 2 signal
man 7 signal
1е — как вызывать. 2е — список доступных сигналов.
Re[4]: Обработка исключений под linux
От: kov_serg Россия  
Дата: 13.08.06 13:30
Оценка:
Здравствуйте, Аноним, Вы писали:

А>>А почему нельзя обрабатывать асинхронный сигнал, зачем надо переводить асинхронный сигнал в exception?

А>>Почему нельзя поставить обработчик на сигнал и в нем удалять все необходимые объекты?
_>Как ???
A> man 2 signal
A> man 7 signal
A> 1е — как вызывать. 2е — список доступных сигналов.

Проблема не в том как вызывать сигналы или какие они вообще есть.

Проблемма в другом. Есть приложение оно запускает несколько потоков
в каждом потоке могут создаваться объекты и вызываться внешние модули.
Всё бы хорошо но при возникновении обращения но NULL или деления на ноль
Если вообще не обрабатывать сигнал приложение немедленно завершается
при этом про все созданные объекты забывают.
Если же при возникновении сигнала просто убивать сглючивший поток то
в памяти накапливаются объекты, файлы, соединения не закрываются.

Цель всего лишь такая при возникновении деления на 0 или segmentation fault
вызывать стандартный throw MyExceptionClass(); Но я немогу вызвать exception
из произвольного места кода. Так например если я просто сгенерю код
для вызова throw например в стеке а потом вызову это подпрограмму.
Программа скажет что случился неожиданный exception и упадёт.
И плевать она хотела на try {} catch() {}. gcc как то привязывается именно
к месту вызова exception. И как его заставить сменить способ генерации
unwind tables. На вариант где я смогу вызвать throw из любого места кода.

И вообще если есть многопроцессорная система и один из них делит на ноль
то обработка исключения ведётся именно этим процессором, а не передаётся другому!
Так что при возникновении signal код выполняется тем же процессором и скорее всего
даже тем же потоком (По крайне мере так должно выглядеть для пользователя).
По крайне мере стек именно этого потока. При обработке сигнала я могу вернуть
выполнение в туже точку. Но даже если я там соберу код который вызывает
throw. А потом верну управление это не сработает.

>>Почему нельзя поставить обработчик на сигнал и в нем удалять все необходимые объекты

Потому что для этого я должен вручную генерить unwind tables. И везде по коду
вставлять поддержку этого безобразия. Это приведёт к громоздкому коду. И не эффективной
работе кода. И вообще почему я должен вручную выполнять работу компилятора ???
Re[5]: Обработка исключений под linux
От: Cyberax Марс  
Дата: 13.08.06 14:02
Оценка: 2 (1)
kov_serg wrote:
> Цель всего лишь такая при возникновении деления на 0 или segmentation fault
> вызывать стандартный throw MyExceptionClass(); Но я немогу вызвать exception
> из произвольного места кода. Так например если я просто сгенерю код
> для вызова throw например в стеке а потом вызову это подпрограмму.
> Программа скажет что случился неожиданный exception и упадёт.
> И плевать она хотела на try {} catch() {}. gcc как то привязывается именно
> к месту вызова exception. И как его заставить сменить способ генерации
> unwind tables. На вариант где я смогу вызвать throw из любого места кода.
Фигней занимаешься. Уже раз 10 написали, что SEGFAULT может повредить
данные, важные для дальнейшего исполнения программы. SEGFAULT -это
ошибка разработчика и патчить ее обработчиками исключений является
кривостью.

Если нужно использовать программы, которые могут упасть — то для этого в
Юниксах придумали специальное средство — fork+IPC. В качестве IPC можно
использовать MPI для многопроцессорных высокопроизводительных систем.
Posted via RSDN NNTP Server 2.0
Sapienti sat!
Re[6]: Обработка исключений под linux
От: kov_serg Россия  
Дата: 14.08.06 06:03
Оценка: +1
Здравствуйте, Cyberax, Вы писали:

...
>> unwind tables. На вариант где я смогу вызвать throw из любого места кода.
C>Фигней занимаешься. Уже раз 10 написали, что SEGFAULT может повредить
C>данные, важные для дальнейшего исполнения программы. SEGFAULT -это
C>ошибка разработчика и патчить ее обработчиками исключений является
C>кривостью.

C>Если нужно использовать программы, которые могут упасть — то для этого в

C>Юниксах придумали специальное средство — fork+IPC. В качестве IPC можно
C>использовать MPI для многопроцессорных высокопроизводительных систем.

Потому и пишу что решения пока не нашел. Я просто надеялся что кто-нибудь
уже сталкивался с такой проблеммой и может что посоветовать. Но пока вижу
только непонимание. "О осинхронное событие ужоснах... Ты что, смерись...
Или иди читай man-ы"
Я пока вижу только одно решение смена компилятора. Мне совершенно не нужно
fork+IPC и подобные прибабахи для много процессорных систем. Должно быть
максимально просто и прозрачно. Мне надо что бы обычная программа которая
использует внешние модули не падала на таких простых ошибках. И компилировалась
как под Windows так и под Linux.
Когда я в первый раз увидел что происходит с моей прогой при делении
на ноль в linux у меня волосы дыбом встали.

try { fast_calc(); } catch (MathOverflow &e) { verbose_calc(log_file); }

Вместо подробного отчета о том где случилось переполнение просто приложение падает.
Причем все потоки тросто аварийно закрываются. Несомненно, Очень Удобно!
Re[5]: Обработка исключений под linux
От: Аноним  
Дата: 15.08.06 08:49
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Здравствуйте, Аноним, Вы писали:


А>>>А почему нельзя обрабатывать асинхронный сигнал, зачем надо переводить асинхронный сигнал в exception?

А>>>Почему нельзя поставить обработчик на сигнал и в нем удалять все необходимые объекты?
_>>Как ???
A>> man 2 signal
A>> man 7 signal
A>> 1е — как вызывать. 2е — список доступных сигналов.

_>Проблема не в том как вызывать сигналы или какие они вообще есть.


_>Проблемма в другом. Есть приложение оно запускает несколько потоков

_>в каждом потоке могут создаваться объекты и вызываться внешние модули.
_>Всё бы хорошо но при возникновении обращения но NULL или деления на ноль
_>Если вообще не обрабатывать сигнал приложение немедленно завершается
_>при этом про все созданные объекты забывают.
_>Если же при возникновении сигнала просто убивать сглючивший поток то
_>в памяти накапливаются объекты, файлы, соединения не закрываются.

Так введите список этих объектов, и удаляйте в обработчике сигнала.
Re[7]: Обработка исключений под linux
От: Аноним  
Дата: 15.08.06 08:53
Оценка:
Здравствуйте, kov_serg, Вы писали:


_>Я пока вижу только одно решение смена компилятора. Мне совершенно не нужно

_>fork+IPC и подобные прибабахи для много процессорных систем.

Вообще-то это обычный путь для Unix систем, и никак не связан с количеством
процессов, fork в Unix очень дешев,
используйте процессы и в Windows,
есть же небось что-нибудь boost::process

_>
_>try { fast_calc(); } catch (MathOverflow &e) { verbose_calc(log_file); }
_>

_>Вместо подробного отчета о том где случилось переполнение просто приложение падает.
_>Причем все потоки тросто аварийно закрываются. Несомненно, Очень Удобно!

Ну предложите механизм, который будет работать одинаково хорошо на всех платформах,
которые поддерживает gcc и linux, и тогда возвращайтесь с критикой.
Re[6]: Обработка исключений под linux
От: demi США  
Дата: 15.08.06 09:11
Оценка:
Здравствуйте, Аноним, Вы писали:

Про линукс я не знаю, но немного расскажу про Винду и VC. Так вот. Такой код:
try
{
   volatile int* p = 0;
   *p = 0;
}
catch (...)
{
   printf("Vse, kranti!\n");
}


Не факт, что он выведет такое замечательное сообщение. Потому что throw это синхронное исключение, а разыменование NULL нет. В последнем случае в терминологии Windows происходит обработка SEH. И именно, системой вызывается функция по адресу FS:[0]. Далее, в зависмости от кода возврата проиходит поиск-выполнение-продолжение работы. VC при компиляции делает так, что в FS:[0] попадает обработчик C++ эксепшенов, который занимается тем, что по type_info ищет нужный обработчик. В данном случае, все проходят фильтр и попадают в тело обрабочика. Понятно, что и SEH туда попадают. Так вот, это багофича MS. Нет строчки в стандарте о том, что асинхронные исключение должны быть перехвачены. Более того, несмотря на то, что это может показаться неплохим поведением, это плохо. Потому что при обработке SEH мы не можем полагаться на некотрые инварианты, гарантируемые языком.
Не стыдно попасть в дерьмо, стыдно в нём остаться!
Re[7]: Обработка исключений под linux
От: kov_serg Россия  
Дата: 15.08.06 12:56
Оценка:
Здравствуйте, demi, Вы писали:

D>Про линукс я не знаю, но немного расскажу про Винду и VC. Так вот. Такой код:

D>
D>try
D>{
D>   volatile int* p = 0;
D>   *p = 0;
D>}
D>catch (...)
D>{
D>   printf("Vse, kranti!\n");
D>}
D>

Я уже писал "Под windows в VC это решалось ключем компилятора /EHa ...". Мне просто нужен аналого под gcc (если есть)

D>Не факт, что он выведет такое замечательное сообщение. Потому что throw это синхронное исключение, а разыменование NULL нет. В последнем случае в терминологии Windows происходит обработка SEH. И именно, системой вызывается функция по адресу FS:[0]. Далее, в зависмости от кода возврата проиходит поиск-выполнение-продолжение работы. VC при компиляции делает так, что в FS:[0] попадает обработчик C++ эксепшенов, который занимается тем, что по type_info ищет нужный обработчик. В данном случае, все проходят фильтр и попадают в тело обрабочика. Понятно, что и SEH туда попадают. Так вот, это багофича MS. Нет строчки в стандарте о том, что асинхронные исключение должны быть перехвачены. Более того, несмотря на то, что это может показаться неплохим поведением, это плохо. Потому что при обработке SEH мы не можем полагаться на некотрые инварианты, гарантируемые языком.


Схему по которой обрабатывает исключения Windows я знаю. Но даже если в linux нету аналога сегмента задачи FS можно же сделать общую функцию вида TaskInformationBlock* getCurrentTIB(); В котором держать EFP-ExceptionFramePointer. А дальше по традиционной схеме. Проблем с реализацией я вобщем-то не вижу. Но переписывать GCC я не собираюсь. И добавлять в код конструкии которые будут делать это за компилятор тоже не очень хочу.

И еще. Я наверно идиот. Но я не понимаю почему деление на 0 это асинхронное событие? Да и PageFault тоже обрабатывается тем же процессором что и вызвал его. Неважно что он в i386 сначала на Ring0 попадает. Для кода пользователя это должно выглядеть как-будто оно случилось там. ( push flags;call intterrupt_handler )
И какие собственно инварианты тут разрушаются я тоже не вижу! Приведите пример где такие инварианты не сохраняются.
Re[8]: Обработка исключений под linux
От: demi США  
Дата: 15.08.06 15:12
Оценка:
_>И еще. Я наверно идиот.
Ну ваши мыслительные способности я думаю как выше среднего.


_>Но я не понимаю почему деление на 0 это асинхронное событие? Да и PageFault тоже обрабатывается тем же процессором что и вызвал _>его. Неважно что он в i386 сначала на Ring0 попадает. Для кода пользователя это должно выглядеть как-будто оно случилось там. ( _>push flags;call intterrupt_handler )

Почему асинхронное? Да потому, что его инициирует и ловит (ну со всеми оговорками) система. PageFault тот же. Вы его средствами языка можете создать? Наверно, нет. Создайте ооогромный массив, бегите по нему. Да случится page-fault поганый. Он отрботается ситемой, страница будет подгружена — но не вы его кидали, и не в состоянии это контролировать (когда кидать). То же самое с FPU. Маскируем прерывания или нет. Как вы сами его возбудите? Тут дело не в том, кто добивается эксепшена (это всегда вы, ваш код), а кто его ВЫРАБАТЫВЕТ. throw — синхронное, потому что его вырабатывает не ОС и железо, а криворукие программисты. Деление на ноль — его тоже находит железо. А если бы это было так:
int idiv(int a, int b)
{
    if (b == 0)
        throw "division by zero";
    return a/b;
}

То это было бы синхронно. Улавливаете в чем фишка?

_>И какие собственно инварианты тут разрушаются я тоже не вижу! Приведите пример где такие инварианты не сохраняются.

К примеру. У нас есть машина, которая в отличие от Wintel архитектуры не так мягко отрабатывает исключение, связанное с выравниванием. Пусть это жесткий эксепшн. И пусть компилятор творит что-то как MSVC — catch(...) его ловит.
int array[8] = {0}; //гарантированно выровнен
int* p = (int*)(((char*)a1)+1); //не выровнен, обращение ведет к hardware exception
try
{
    *p = 20;
    a1[0] = 2;
}
catch (...)
{
    a1[0] = 1;
}
//что должно быть в a1?


Первое. Компилятор имеет право вынести разыменование в try (оптимизация типа). Потому что оно не возбуждает исключений — и тут бац! поведение программы изменилось. И вообще убрать try-блок. И тогда что должно быть в a1? Вот как раз на инварианты языка компилятор и полагается, делая оптимизацию. Проявите фантазию, за примерами далеко ходить не надо.
Не стыдно попасть в дерьмо, стыдно в нём остаться!
Re[9]: Обработка исключений под linux
От: kov_serg Россия  
Дата: 15.08.06 16:07
Оценка:
Здравствуйте, demi, Вы писали:

...
_>>Но я не понимаю почему деление на 0 это асинхронное событие? Да и PageFault тоже обрабатывается тем же процессором что и вызвал _>его. Неважно что он в i386 сначала на Ring0 попадает. Для кода пользователя это должно выглядеть как-будто оно случилось там. ( _>push flags;call intterrupt_handler )
D>Почему асинхронное? Да потому, что его инициирует и ловит (ну со всеми оговорками) система. PageFault тот же. Вы его средствами языка можете создать? Наверно, нет.
int i=0,j=1/i;

Вот и вызвал. А если у меня процессор например ARM и там нет деления. Оно реализуется небольшой подпрограммой. Деление на 0 тоже ансихронное? Я хочу сказать что подобную ситуацию асинхронной называть странновато. В том смысле что она обрабатывается тем же процессором. Тоесть в процессе обработки прерывания стек моей программы не портится, регистры тоже при возврате из прерывания целые. Вот если бы
for(int i=0;i<10;i++) massiv[i]=i;

поделил на 0 вот тут бы я удивился. Или где нибуть поделил на 0, а потом через несколько инструкций выскочило деление на 0. Вот это было бы асинхронно. А тут же сигнал возникает сразу без задержек. Или при деление на 0 моя программа шла дальше, а я в это время обрабатывал сигнал деления на 0. Но в процессе обработки всё стоит ждёт пока сигнал будет обработан, и вполне способна продолжать выполнение с того же места.

D>Создайте ооогромный массив, бегите по нему. Да случится page-fault поганый. Он отрботается ситемой, страница будет подгружена — но не вы его кидали, и не в состоянии это контролировать (когда кидать).

Если система поддерживает виртуальную память это её проблеммы. Программа пользователя не должна этого замечать. Так ведь оно и есть. Вы же не можити сесть на обработку сигнала и при каждой подгрузке страниы сообщать об этом.

D>То же самое с FPU. Маскируем прерывания или нет. Как вы сами его возбудите? Тут дело не в том, кто добивается эксепшена (это всегда вы, ваш код), а кто его ВЫРАБАТЫВЕТ.

Вырабатывает его всегда процессор, даже если и сопроцессор, то тоже обработка вдётся основным процессорм. А вот асинхронные NMI,IRQ они приходят из вне.

D>throw — синхронное, потому что его вырабатывает не ОС и железо, а криворукие программисты.

Ну чем прерывания процессора хуже! Их тоже могут вызывать криворуки програмисты.

D>Деление на ноль — его тоже находит железо.

Мне ведь всё равно кто его вырабатывает. Мне его обрабатывать надо.

D>А если бы это было так:

D>
D>int idiv(int a, int b)
D>{
D>    if (b == 0)
D>        throw "division by zero";
D>    return a/b;
D>}
D>

D>То это было бы синхронно. Улавливаете в чем фишка?
Нет не улавливаю. Я немогу понять следующее при обработке сигнала я могу вернуться в тоже место. Но немогу вернутся в другое и вызвать там throw.
int idiv(int a,int b) {
  try {
    return a/b;
  } catch(...) {
    throw "ups"; 
  }
}
void _div_exc() { throw "div by zero"; }
void cpu_interrupt int_on_div() {
  change_return_address(_div_exc);
}

Но это полько в linux.gcc потому что то что генерит gcc как-то р@ком привязано к стеку и к месту возникновения исключения. Но это всего лишь особенность gcc.

_>>И какие собственно инварианты тут разрушаются я тоже не вижу! Приведите пример где такие инварианты не сохраняются.

D>К примеру. У нас есть машина, которая в отличие от Wintel архитектуры не так мягко отрабатывает исключение, связанное с выравниванием. Пусть это жесткий эксепшн. И пусть компилятор творит что-то как MSVC — catch(...) его ловит.
D>
D>int array[8] = {0}; //гарантированно выровнен
D>int* p = (int*)(((char*)a1)+1); //не выровнен, обращение ведет к hardware exception
D>try
D>{
D>    *p = 20;
D>    a1[0] = 2;
D>}
D>catch (...)
D>{
D>    a1[0] = 1;
D>}
D>//что должно быть в a1?
D>

a1[0] должно равнятся 2. В независимости от выравнивания.
Извенити в C/C++ на выравнивание никаких исключений возникать недолжно. Даже если они возникают у процессора. Это опять же забота компилятора. Он должен обходить такие ситуации.

D>Первое. Компилятор имеет право вынести разыменование в try (оптимизация типа). Потому что оно не возбуждает исключений — и тут бац! поведение программы изменилось. И вообще убрать try-блок. И тогда что должно быть в a1? Вот как раз на инварианты языка компилятор и полагается, делая оптимизацию. Проявите фантазию, за примерами далеко ходить не надо.

И вообще прерывание по выравниванию это не проблемма пользовательского кода это головная боль тех кто пишет компиляторы.
Вот если я пишу на assembler-е то это моя головная боль.
Re[10]: Обработка исключений под linux
От: demi США  
Дата: 15.08.06 16:58
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Здравствуйте, demi, Вы писали:

_>
_>for(int i=0;i<10;i++) massiv[i]=i;
_>

_>поделил на 0 вот тут бы я удивился. Или где нибуть поделил на 0, а потом через несколько инструкций выскочило деление на 0.
Замечательно! Я вас поздравляю! Вы самый счастливый человек, потому что именно вы кидаете исключения деления на 0. Или по крайней мере знаете, где это происходит. У вас нет багов, вы всегда знаете, где и когда исключение от системы произойдет. Фигня.

D>>Создайте ооогромный массив, бегите по нему. Да случится page-fault Программа пользователя не должна этого замечать.

И не замечает. А вот сесть могу. И еще как могу! Почитайте MSDN, про VirtualAlloc. Соглашусь, что средствами языка я это не сделаю, но когда мы говорим о page fault, мы не ограничены средствами языка — нет понятия page fault в C++.

_>Вырабатывает его всегда процессор, даже если и сопроцессор, то тоже обработка вдётся основным процессорм. А вот асинхронные NMI,IRQ они приходят из вне.

А типа процессор это не железка? Ему просто не нужен механизм IRQ. Зачем из Москвы в Питер ехать через Владик, когда можно напрямик? Проц тут же вызывает обработчики в IDT/cr0/cr1/c2/cr3 или еще где. Зачем пропускать сигнал через чипсет, южный, блин, тормозной мост, когда все здесь!!!

D>>throw — синхронное, потому что его вырабатывает не ОС и железо, а криворукие программисты.

_>Мне ведь всё равно кто его вырабатывает. Мне его обрабатывать надо.
Кто вам мешает! Вы же не ковыряетесь в зубах гвоздем и не прибиваете полку на зубочистки? Вот и здесь — try{}catch(...){} предназначен для обработки синхронных исключений, а никак не для асинхронных. Багофичи MS это зло. Это не только теория, усвоить раз и навсегда что такое синхронное исключение, что нет. Вы требуете от catch (...) того что он НЕ ОБЯЗАН делать. А именно, перехватывать системно-зависимые исключения. Еще раз скажу. catch(...) гарантирует разворот стека. При системном эксепшене это не всегда возможно! А вы говорите где нарушение инварианта — вот оно. Как семантику соблюсти???

_>Нет не улавливаю. Я немогу понять следующее при обработке сигнала я могу вернуться в тоже место. Но немогу вернутся в другое и вызвать там throw.

Ну уж вы, человек очевидно умный но такое загнете, что я в ауте. Вот что вы хотите. Дайте мне механизм throw-catch и семантикой stack unwind, да еще чтоб он работал, когда будет SEH (win32 терминология), да еще и скакнуть куда угодно! Когда вы в обработчике системного сообщения, до вас на стеке системные функции. И их надо корректно завершить. То есть return в них обратно. Как вы представляете реализацию такого механизма? Как скакнуть в другое место, с учетом того, что есть конструкторы деструкторы и прочий хлам? Только call функции! А это не то что надо. Ну вот как??? делайте longjmp на свой страх и риск. Но выбрасывать ниже по стеку систему — это криминал. (PS. в случае ms багофич системных функций под вами нет, конечно, они уже отработали).

_>Извенити в C/C++ на выравнивание никаких исключений возникать недолжно. Даже если они возникают у процессора. Это опять же забота компилятора. Он должен обходить такие ситуации.

Правда??? Есть прототип, который реально размещен в dll, то есть компилер не может узнать, что он делает:
int* GetIntPointer();
Как прикажете обойти ситуацию КОМПИЛЯТОРУ обойти ситуацию, если он не в состоянии предположить хорошее или "плохое" значение ему вернули? Да, компилятор обязан размещеть на стеке, делать массивы, выравнивать и т.д. так, что ексепшенов нет. Но в случае адресной арифметики что ему делать??? Вот именно из-за адресной арифметики много проблем.

_>И вообще прерывание по выравниванию это не проблемма пользовательского кода это головная боль тех кто пишет компиляторы.

Я повторяю, компилятор ответственнен за многое. Саттер надеюсь в авторитете? Так вот почему он пишет, что (про фаст PIMPL когда распинается) что нельзя делать вместо new так: Помните?

class A
{
    char _obj[/*размер наших данных*/];
};


А? Саттер говорит, что будут проблемы с выравниваем. Саттер пишет компиляторы, и ему ли не знать, за что он отвественнен (компилятор). А вы тут говорите, проблемы компилятора.
Не стыдно попасть в дерьмо, стыдно в нём остаться!
Re[11]: Обработка исключений под linux
От: kov_serg Россия  
Дата: 16.08.06 07:16
Оценка: :)
Здравствуйте, demi, Вы писали:


_>>Здравствуйте, demi, Вы писали:

_>>
_>>for(int i=0;i<10;i++) massiv[i]=i;
_>>

_>>поделил на 0 вот тут бы я удивился. Или где нибуть поделил на 0, а потом через несколько инструкций выскочило деление на 0.
D>Замечательно! Я вас поздравляю! Вы самый счастливый человек, потому что именно вы кидаете исключения деления на 0. Или по крайней мере знаете, где это происходит. У вас нет багов, вы всегда знаете, где и когда исключение от системы произойдет. Фигня.
Посмотри обязательно: http://www.karasik.eu.org/dao.html

D>>>Создайте ооогромный массив, бегите по нему. Да случится page-fault Программа пользователя не должна этого замечать.

D>И не замечает. А вот сесть могу. И еще как могу! Почитайте MSDN, про VirtualAlloc. Соглашусь, что средствами языка я это не сделаю, но когда мы говорим о page fault, мы не ограничены средствами языка — нет понятия page fault в C++.
Даже имея функцию VirtualAlloc вы не в состоянии определить когда система загружает и выгружает подкачиваемую память. Это можно сделать только на уровне ядра.

_>>Вырабатывает его всегда процессор, даже если и сопроцессор, то тоже обработка вдётся основным процессорм. А вот асинхронные NMI,IRQ они приходят из вне.

D>А типа процессор это не железка? ...
Программу можно и на эмуляторе пускать. Так что тут предлагаю не дискутировать, что железка а что нет. Это к философам что первично а что вторично. Сама программа определить это не спобна

D>>>throw — синхронное, потому что его вырабатывает не ОС и железо, а криворукие программисты.

Блин я всёравно не понимаю
  if (a) b();

Разве вызов b() асинхронный?
Чем деление на 0 или доступ к запрещенной области хуже. Влез — получи

А вот асинхронный код
void event_handler(params) {
  // process event
}
void thread1() {
  ...
  if (a1) post_event(params1);
  ...
}
....
void threadN() {
  ...
  if (aN) post_event(paramsN);
  ...
}

Или я не прав. А то что делает процессор intel в защищенном режиме — это же ведь проблеммы OS.

_>>Нет не улавливаю. Я немогу понять следующее при обработке сигнала я могу вернуться в тоже место. Но немогу вернутся в другое и вызвать там throw.

D>... Дайте мне механизм throw-catch и семантикой stack unwind, да еще чтоб он работал, когда будет SEH (win32 терминология), да еще и скакнуть куда угодно! Когда вы в обработчике системного сообщения, до вас на стеке системные функции.
Как показывает практика. Обработка такого исключения ведётся уже после того ка система на всё это посмотрела и вернула управление в код пользователя.
Обработка исключения в отдельном системном стеке было только у DOSextender-ов. Да и при обработке сигналов, если нужен отдельный стек это указывается.

D> И их надо корректно завершить. То есть return в них обратно. Как вы представляете реализацию такого механизма? Как скакнуть в другое место, с учетом того, что есть конструкторы деструкторы и прочий хлам? Только call функции! А это не то что надо.

Чем эта реализация не нравится: http://qxov.narod.ru/articles/seh/seh.html (см: SCOPETABLE)

Меня выравнивание ВАЩЕ не интересует. А компилятор на стадии компиляции може предупреждения выводить если не способен генерить код который с этим справится. Конешно реализации компиляторов бывают разные. И их разработчики могут оправдываться как угодно. МНЕ НАДО КОРРЕКТНО ОБРАБАТЫВАТЬ ДЕЛЕНИЕ НА 0, ПЕРЕПОЛНЕНИЯ И ПАДЕНИЯ НА NULL УКАЗАТЕЛЯХ. И всё. Компилятр vc ето способен делать. GCC той версии которая у меня есть пока не удалось заставить. Сегодня буду пробывать intel c++ compiler
Re: Обработка исключений под linux
От: MaximE Великобритания  
Дата: 16.08.06 09:47
Оценка:
kov_serg wrote:

> Возник вопрос досих пор немогу найти решения.

> Что нужно сказать gcc что бы следующий код заработал
>
> try {
> int *p=NULL;
> struct A { ~A() { printf("a.dtor\n"); } } a;
> *p=*p;
> } catch(...) {
> printf("segmentation fault\n");
> }
>
>
>
> При возникновении исключения типа access violation или деление на 0
> в блок catch(...) программа не попадает, а сразу завершается. При
> яем a.dtor не вызывается.
>
> Под windows в VC это решалось ключем компилятора /EHa и кодом вида:

[]

> В если linux обрабатывать сигналы то не получается перевести сигнал

> в exception. Главная неясность как при возникновении сигнала SIGSEGV
> сделать так что бы вызвались деструкторы для созданных объектов.

Обработай сигнал и опосля вызови деструкторы. Но не в обработчике
сигнала
. Из обработчика сигнала ты не можешь сделать практически ничего,
только вызвать async signal safe ф-цию из списка
http://www.opengroup.org/onlinepubs/000095399/functions/xsh_chap02_04.html#tag_02_04_03

> Поставил ряд экспериментов с gcc. Суть такая что при возникновении сигнала

> access violation просто вызвать throw и дальше программа должна поити по
> отлаженной схеме пройтись по unwind tables и грохнуть созданные объекты.
> Но как оказалось gcc очень странно генерить кадры исключений. Вообще
> непонятно как. Но повсей видимости привязывается к месту возникновения
> исключения.

Вывод верный. gcc генерит код + таблицы, которые по instruction counter находят
exception handler. Поэтому, в отличие от m$vc, вход в / выход из try {}
бесплатен, 0 ассемблерных инструкций.

--
Maxim Yegorushkin

No Microsoft product was used in any way to write or send this text.
If you use a Microsoft product to read it, you're doing so at your own risk
Posted via RSDN NNTP Server 2.0
Re[2]: Обработка исключений под linux
От: kov_serg Россия  
Дата: 16.08.06 16:32
Оценка:
Здравствуйте, MaximE, Вы писали:

...
ME>Обработай сигнал и опосля вызови деструкторы. Но не в обработчике
ME>сигнала
. Из обработчика сигнала ты не можешь сделать практически ничего,
ME>только вызвать async signal safe ф-цию из списка
ME>http://www.opengroup.org/onlinepubs/000095399/functions/xsh_chap02_04.html#tag_02_04_03
Так я в коде так и сделал вернулься из обработчика сигнала в то место где случилось
авария и сделал вид что в этом месте вызвал подпрограмму. Которая уже вызывает throw. Но это не прокатило

ME>... gcc генерит код + таблицы, которые по instruction counter находят

ME>exception handler. Поэтому, в отличие от m$vc, вход в / выход из try {}
ME>бесплатен, 0 ассемблерных инструкций.
А можно по подробней как он их генерит. Если бы я знал то мог найти ближайшую точку и
передать туда управление
Re: Обработка исключений под linux
От: Аноним  
Дата: 17.08.06 08:26
Оценка: :)
Уже писали, но повторю:
Деление на ноль и GPF это не исключения — их никто не кидал при помощи throw, поэтому поймать их при помощи catch() невозможно.

Не уверен, что и при помощи сигналов что-то можно сделать — не все сигналы можно перехватить; возможно, для некоторых сигналов установлены предопределённые действия по выходу из обработчика, которые нельзя отменить (я бы сделал так, ибо нефига).

Что полезного можно сделать, так это запускать две программы и когда одна падает, вторая пусть печатает, что, извините, первая упала.
Re[12]: Обработка исключений под linux
От: Аноним  
Дата: 17.08.06 10:40
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Меня выравнивание ВАЩЕ не интересует. А компилятор на стадии компиляции може предупреждения выводить если не способен генерить код который с этим справится. Конешно реализации компиляторов бывают разные. И их разработчики могут оправдываться как угодно. МНЕ НАДО КОРРЕКТНО ОБРАБАТЫВАТЬ ДЕЛЕНИЕ НА 0, ПЕРЕПОЛНЕНИЯ И ПАДЕНИЯ НА NULL УКАЗАТЕЛЯХ. И всё. Компилятр vc ето способен делать. GCC той версии которая у меня есть пока не удалось заставить. Сегодня буду пробывать intel c++ compiler


На что спорим что он тоже не умеет?
Re[3]: Обработка исключений под linux
От: MaximE Великобритания  
Дата: 17.08.06 12:35
Оценка:
kov_serg wrote:

[]

> ME>... gcc генерит код + таблицы, которые по instruction counter находят

> ME>exception handler. Поэтому, в отличие от m$vc, вход в / выход из try {}
> ME>бесплатен, 0 ассемблерных инструкций.
> А можно по подробней как он их генерит. Если бы я знал то мог найти
> ближайшую точку и передать туда управление

Начни с .pdf отсюда http://netlab.ru.is/exception/LinuxCXX.shtml

--
Maxim Yegorushkin

No Microsoft product was used in any way to write or send this text.
If you use a Microsoft product to read it, you're doing so at your own risk
Posted via RSDN NNTP Server 2.0
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.