Re[13]: Обработка исключений под linux
От: kov_serg Россия  
Дата: 17.08.06 15:49
Оценка:
Здравствуйте, Аноним, Вы писали:

А>На что спорим что он тоже не умеет?


На что спорим?
Я уже готов поспорить
Re[2]: Обработка исключений под linux
От: kov_serg Россия  
Дата: 19.08.06 13:56
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Уже писали, но повторю:

А>Деление на ноль и GPF это не исключения — их никто не кидал при помощи throw, поэтому поймать их при помощи catch() невозможно.

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

Поставил Intel C++ Compiler.v9.1.039 . В нём спокойно можно делать throw из обработчика сигналов . И нет проблем. Всё с чем я немог смерится в gcc тут отсутствует. И работает вполне предсказуемо.

А>Что полезного можно сделать, так это запускать две программы и когда одна падает, вторая пусть печатает, что, извините, первая упала.

Сам такой прогой пользуйся. Придставь делал в ней что-то, а потом при попытке сохранить данные или так без повода, так внезапно, она падает. А вторая прога тебе и говорит: "Ужос дорогой пользователь, все ваши данные не только потеряны но и испорчены. Но могу вас утешить: моей вины в этом нет"
KDeveloper работает по такой схеме. При каждом падении KDeveloper удивляюсь какие нехорошие слова я знаю
Re[3]: Обработка исключений под linux
От: MaximE Великобритания  
Дата: 19.08.06 16:38
Оценка:
kov_serg wrote:


> А>Не уверен, что и при помощи сигналов что-то можно сделать — не все

> сигналы можно перехватить; возможно, для некоторых сигналов установлены
> предопределённые действия по выходу из обработчика, которые нельзя
> отменить (я бы сделал так, ибо нефига).
> Поставил Intel C++ Compiler.v9.1.039 . В нём спокойно можно делать throw
> из обработчика сигналов . И нет проблем. Всё с чем я немог смерится в
> gcc тут отсутствует. И работает вполне предсказуемо.

Интересно, предсказуемо ли поведение в следущем сценарии.

В плагине твоего приложения иногда происходит деление на 0, ты бросаешь из
обработчика сигнала исключение. Код плагина может не использовать RAII или быть
написан на C, другими словами раскрутка стэка в коде плагина может не освободить
ресурсов, или оставить контекст плагина в рассогласованом состоянии.

--
Maxim Yegorushkin
Posted via RSDN NNTP Server 2.0
Re[4]: Обработка исключений под linux
От: kov_serg Россия  
Дата: 21.08.06 06:28
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>kov_serg wrote:



>> А>Не уверен, что и при помощи сигналов что-то можно сделать — не все

>> А>сигналы можно перехватить; возможно, для некоторых сигналов установлены
>> А>предопределённые действия по выходу из обработчика, которые нельзя
>> А>отменить (я бы сделал так, ибо нефига).
>> Поставил Intel C++ Compiler.v9.1.039 . В нём спокойно можно делать throw
>> из обработчика сигналов . И нет проблем. Всё с чем я немог смерится в
>> gcc тут отсутствует. И работает вполне предсказуемо.

ME>Интересно, предсказуемо ли поведение в следущем сценарии.


ME>В плагине твоего приложения иногда происходит деление на 0, ты бросаешь из

ME>обработчика сигнала исключение. Код плагина может не использовать RAII или быть
ME>написан на C, другими словами раскрутка стэка в коде плагина может не освободить
ME>ресурсов, или оставить контекст плагина в рассогласованом состоянии.

Тут всё просто мы зарание предупреждаем разработчиков плагинов что бы они сами
отлавливали свои исключения и освобождали память. И рекомендуем им intel c++ compiler.
Даём примеры и SDK. Но плагин же может выделять память и не освобождать её и при этом
не вызывать исключений. И это будет только на совести тех кто писал плагин.
Re[3]: Обработка исключений под linux
От: MaximE Великобритания  
Дата: 21.08.06 11:45
Оценка:
kov_serg wrote:

> А>Уже писали, но повторю:

> А>Деление на ноль и GPF это не исключения — их никто не кидал при помощи
> throw, поэтому поймать их при помощи catch() невозможно.
>
> А>Не уверен, что и при помощи сигналов что-то можно сделать — не все
> сигналы можно перехватить; возможно, для некоторых сигналов установлены
> предопределённые действия по выходу из обработчика, которые нельзя
> отменить (я бы сделал так, ибо нефига).
> Поставил Intel C++ Compiler.v9.1.039 . В нём спокойно можно делать throw
> из обработчика сигналов . И нет проблем. Всё с чем я немог смерится в
> gcc тут отсутствует. И работает вполне предсказуемо.

Это не работает по следующим причинам:

1) Обработчик сигнала вызывется kernel'ом в отдельном стеке, т.е. не в
том стеке, где твои объекты. Чтобы это проверить, выведи адрес переменной на
стеке (не из signal handler), брось исключение из signal handler и выведи адрес
той же переменной из catch().
2) Если компилятор Intel не поддерживает асинхронных исключений (что вероятно
для linux версии), раскрутка стэка не будет работать правильно. Но в свете п.1
это уже не имеет никакого значения.

--
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[4]: Обработка исключений под linux
От: kov_serg Россия  
Дата: 21.08.06 12:09
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>kov_serg wrote:


>> А>Уже писали, но повторю:

>> А>Деление на ноль и GPF это не исключения — их никто не кидал при помощи
>> throw, поэтому поймать их при помощи catch() невозможно.
>>
>> А>Не уверен, что и при помощи сигналов что-то можно сделать — не все
>> сигналы можно перехватить; возможно, для некоторых сигналов установлены
>> предопределённые действия по выходу из обработчика, которые нельзя
>> отменить (я бы сделал так, ибо нефига).
>> Поставил Intel C++ Compiler.v9.1.039 . В нём спокойно можно делать throw
>> из обработчика сигналов . И нет проблем. Всё с чем я немог смерится в
>> gcc тут отсутствует. И работает вполне предсказуемо.

ME>Это не работает по следующим причинам:

Вожете говорить что угодно но оно работает!

ME>1) Обработчик сигнала вызывется kernel'ом в отдельном стеке, т.е. не в

ME>том стеке, где твои объекты. Чтобы это проверить, выведи адрес переменной на
ME>стеке (не из signal handler), брось исключение из signal handler и выведи адрес
ME>той же переменной из catch().
Я поставил эксперменты которые показали что обработчики сигналов div0 gpf обрабатываются
именно в том стеке где и было исключения. Обработка исключений у icc и у gcc ведётся
по схожей схеме. Только он делает правильный вход в обработчик сигналов в отличие от gcc.
И вообще если почитаете man-ы отдельный стек будет если при установке обработчика это
специально указать дополнительным параметром.

ME>2) Если компилятор Intel не поддерживает асинхронных исключений (что вероятно

ME>для linux версии), раскрутка стэка не будет работать правильно. Но в свете п.1
ME>это уже не имеет никакого значения.
Повторяю схема обработки исключенй уних практически 1 в один.
Re[5]: Обработка исключений под linux
От: MaximE Великобритания  
Дата: 21.08.06 12:26
Оценка:
kov_serg wrote:

> ME>kov_serg wrote:

>
>> > А>Уже писали, но повторю:
>> > А>Деление на ноль и GPF это не исключения — их никто не кидал при помощи
>> > throw, поэтому поймать их при помощи catch() невозможно.
>> >
>> > А>Не уверен, что и при помощи сигналов что-то можно сделать — не все
>> > сигналы можно перехватить; возможно, для некоторых сигналов установлены
>> > предопределённые действия по выходу из обработчика, которые нельзя
>> > отменить (я бы сделал так, ибо нефига).
>> > Поставил Intel C++ Compiler.v9.1.039 . В нём спокойно можно делать throw
>> > из обработчика сигналов . И нет проблем. Всё с чем я немог смерится в
>> > gcc тут отсутствует. И работает вполне предсказуемо.
>
> ME>Это не работает по следующим причинам:
> Вожете говорить что угодно но оно работает!



Попробуй следущий тест, если он выведет 0, значит действительно работает:

#include <stdio.h>
#include <signal.h>
#include <stdexcept>

void handler(int) { throw std::runtime_error("handler"); }

int count;

int f(int n)
{
     try
     {
         n = 100 / n;
         ++count;
     }
     catch(std::exception&)
     {
     }
     return n;
}

int main(int ac, char** av)
{
     struct sigaction sa;
     sa.sa_handler = handler;
     sa.sa_flags = 0;
     sigaction(SIGFPE, &sa, 0);

     f(ac - 1);
     printf("%d\n", count);
}


--
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[5]: Обработка исключений под linux
От: MaximE Великобритания  
Дата: 21.08.06 13:48
Оценка:
kov_serg wrote:

[]

> ME>1) Обработчик сигнала вызывется kernel'ом в *отдельном стеке*, т.е. не в

> ME>том стеке, где твои объекты. Чтобы это проверить, выведи адрес
> переменной на
> ME>стеке (не из signal handler), брось исключение из signal handler и
> выведи адрес
> ME>той же переменной из catch().

> Я поставил эксперменты которые показали что обработчики сигналов div0

> gpf обрабатываются именно в том стеке где и было исключения. Обработка исключений у icc и у
> gcc ведётся по схожей схеме.

Это не зависит от компилятора.

Understanding the Linux kernel, 2nd

10.3.3 Catching the Signal
...
Executing a signal handler is a rather complex task because of the need to
juggle stacks carefully while switching between User Mode and Kernel Mode. We
explain exactly what is entailed here.
Signal handlers are functions defined by User Mode processes and included in the
User Mode code segment. The handle_signal( ) function runs in Kernel Mode while
signal handlers run in User Mode; this means that the current process must first
execute the signal handler in User Mode before being allowed to resume its
"normal" execution. Moreover, when the kernel attempts to resume the normal
execution of the process, the Kernel Mode stack no longer contains the hardware
context of the interrupted program because the Kernel Mode stack is emptied at
every transition from User Mode to Kernel Mode.
An additional complication is that signal handlers may invoke system calls. In
this case, after the service routine executes, control must be returned to the
signal handler instead of to the code of the interrupted program.
The solution adopted in Linux consists of copying the hardware context saved in
the Kernel Mode stack onto the User Mode stack of the current process. The User
Mode stack is also modified in such a way that, when the signal handler
terminates, the sigreturn( ) system call is automatically invoked to copy the
hardware context back on the Kernel Mode stack and restore the original content
of the User Mode stack.
Figure 10-2 illustrates the flow of execution of the functions involved in
catching a signal. A nonblocked signal is sent to a process. When an interrupt
or exception occurs, the process switches into Kernel Mode. Right before
returning to User Mode, the kernel executes the do_signal( ) function, which in
turn handles the signal (by invoking handle_signal( )) and sets up the User Mode
stack (by invoking setup_frame( ) or setup_rt_frame( )). When the process
switches again to User Mode, it starts executing the signal handler because the
handler's starting address was forced into the program counter. When that
function terminates, the return code placed on the User Mode stack by the
setup_frame( ) or setup_rt_frame( ) function is executed. This code invokes the
sigreturn( ) system call, whose service routine copies the hardware context of
the normal program in the Kernel Mode stack and restores the User Mode stack
back to its original state (by invoking restore_sigcontext( )). When the system
call terminates, the normal program can thus resume its execution.
...
10.3.3.1 Setting up the frame
To properly set the User Mode stack of the process, the handle_signal( )
function invokes either setup_frame( ) (for signals that do not require a
siginfo_t table; see Section 10.4 later in this chapter) or setup_rt_frame( )
(for signals that do require a siginfo_t table). To choose among these two
functions, the kernel checks the value of the SA_SIGINFO flag in the sa_flags
field of the sigaction table associated with the signal.
The setup_frame( ) function receives four parameters, which have the following
meanings:
sig — Signal number
ka — Address of the k_sigaction table associated with the signal
oldset — Address of a bit mask array of blocked signals
regs — Address in the Kernel Mode stack area where the User Mode register
contents are saved
The setup_frame( ) function pushes onto the User Mode stack a data structure
called a frame, which contains the information needed to handle the signal and
to ensure the correct return to the sys_sigreturn( ) function. A frame is a
sigframe table that includes the following fields (see Figure 10-3):
pretcode — Return address of the signal handler function; it points to the
retcode field (later in this list) in the same table.
sig — The signal number; this is the parameter required by the signal handler.
sc — Structure of type sigcontext containing the hardware context of the User
Mode process right before switching to Kernel Mode (this information is copied
from the Kernel Mode stack of current). It also contains a bit array that
specifies the blocked regular signals of the process.
fpstate — Structure of type _fpstate that may be used to store the floating
point registers of the User Mode process (see Section 3.3.4).
extramask — Bit array that specifies the blocked real-time signals.
retcode — Eight-byte code issuing a sigreturn( ) system call; this code is
executed when returning from the signal handler.
Figure 10-3. Frame on the User Mode stack

The setup_frame( ) function starts by invoking get_sigframe( ) to compute the
first memory location of the frame. That memory location is usually[4] in the
User Mode stack, so the function returns the value:
[4] Linux allows processes to specify an alternate stack for their signal
handlers by invoking the sigaltstack( ) system call; this feature is also
requested by the X/Open standard. When an alternate stack is present, the
get_sigframe( ) function returns an address inside that stack. We don't discuss
this feature further, since it is conceptually similar to regular signal handling.
(regs->esp — sizeof(struct sigframe)) & 0xfffffff8

Since stacks grow toward lower addresses, the initial address of the frame is
obtained by subtracting its size from the address of the current stack top and
aligning the result to a multiple of 8.

The returned address is then verified by means of the access_ok macro; if it is
valid, the function repeatedly invokes _ _put_user( ) to fill all the fields of
the frame. Once this is done, it modifies the regs area of the Kernel Mode
stack, thus ensuring that control is transferred to the signal handler when
current resumes its execution in User Mode:

regs->esp = (unsigned long) frame;
regs->eip = (unsigned long) ka->sa.sa_handler;

The setup_frame( ) function terminates by resetting the segmentation registers
saved on the Kernel Mode stack to their default value. Now the information
needed by the signal handler is on the top of the User Mode stack.
The setup_rt_frame( ) function is very similar to setup_frame( ), but it puts on
the User Mode stack an extended frame (stored in the rt_sigframe data structure)
that also includes the content of the siginfo_t table associated with the signal.


> Только он делает правильный вход в обработчик сигналов

> в отличие от gcc.

Как отличить правильный вход от неправильного?

> И вообще если почитаете man-ы отдельный стек будет если при установке

> обработчика это специально указать дополнительным параметром.

Читай выше, что просходит со стэком.

--
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[6]: Обработка исключений под linux
От: kov_serg Россия  
Дата: 21.08.06 15:11
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>Попробуй следущий тест, если он выведет 0, значит действительно работает:


ME>
ME>#include <stdio.h>
ME>#include <signal.h>
ME>#include <stdexcept>

ME>void handler(int) { throw std::runtime_error("handler"); }

ME>int count;

ME>int f(int n)
ME>{
ME>     try
ME>     {
ME>         n = 100 / n;
ME>         ++count;
ME>     }
ME>     catch(std::exception&)
ME>     {
ME>     }
ME>     return n;
ME>}

ME>int main(int ac, char** av)
ME>{
ME>     struct sigaction sa;
ME>     sa.sa_handler = handler;
ME>     sa.sa_flags = 0;
ME>     sigaction(SIGFPE, &sa, 0);

ME>     f(ac - 1);
ME>     printf("%d\n", count);
ME>}
ME>

Признаю свою ошибку. Дествительно не работает. Но результаты весьма странные:
#include <stdio.h>
#include <signal.h>
#include <stdexcept>

void handler(int) { throw std::runtime_error("handler"); }

int count=0;

int f(int n)
{
     try
     {
         n = 100 / n;
         ++count;
     }
     catch(std::exception&)
     {
     }
     return n;
}

int main(int ac, char** av)
{
     struct sigaction sa;
     sa.sa_handler = handler;
     sa.sa_flags = 0;
     sigaction(SIGFPE, &sa, 0);

     f(ac - 1);
     printf("%d\n", count);
}

/*
./icc -static test2.cpp
./a.out
Segmentation fault
./a.out one
1
*/

Очень насторажил "Segmentation fault"

Дальше больше:
#include <stdio.h>
#include <signal.h>

void stk(char* place) {
  static   int base=0;
  volatile int z=0;
  if (!base) base=(int)&z;
  int sp=(int)&z;
  printf("%s.stack: %d (la=0x%08X)\n",place,sp-base,sp);
}

void test() {
  stk("test()");
  try {
    struct A { ~A() { printf("A.dtor\n"); } } a;
    printf("throwing AV\n");
    *(int*)0=0;
    //volatile int i=0,j=1/i; printf("j=%d\n",j);
    printf("no errors :) strange!\n");
  } catch(int x) {
    printf("catch int=%d\n",x);
  }
}

void sig_handler_install();
void sig_handler(int num) { 
  printf("sig_handler %d\n",num);
  stk("sig_handler()");
  throw 1001; 
}
void sig_handler_install() {
  struct sigaction sa;
  sa.sa_handler = sig_handler;
  sa.sa_flags   = 0;
  printf("install signal handler\n");
  sigaction(SIGFPE,  &sa, 0);
  sigaction(SIGSEGV, &sa, 0);
}

int main(int,char**) {
  stk("main()");
  sig_handler_install();
  printf("-----test1------\n"); test();
  printf("-----test2------\n"); sig_handler_install(); test();
  printf("-----done-------\n");
}

/*

page fault:
./icc -static test.cpp
./a.out
main().stack: 0 (la=0xBFD9EAD4)
install signal handler
-----test1------
test().stack: -60 (la=0xBFD9EA98)
throwing AV
sig_handler 11
sig_handler().stack: -800 (la=0xBFD9E7B4)
A.dtor
catch int=1001
-----test2------
install signal handler
test().stack: -60 (la=0xBFD9EA98)
throwing AV
Segmentation fault

divide by zero:
./icc -static test.cpp
./a.out
main().stack: 0 (la=0xBFCBF244)
install signal handler
-----test1------
test().stack: -68 (la=0xBFCBF200)
throwing AV
sig_handler 8
sig_handler().stack: -808 (la=0xBFCBEF1C)
terminate called after throwing an instance of 'int'
Aborted

*/


замечание: обратим внимание на стек не далеко отлетает ~800 байт (в windows 980)
ни как не в отдельное адресное пространство.

Самое весёлое что для AV первое исключение обработалось как надо.
Кода я это увидел второй вызов test() я удивился. Добавил еще один
sig_handler_install(); Еще раз запустил результат не изменился
"Я ПЛАКАТЬ"
Попробывал деление на 0. Офигел еще раз


Умееш же MaximE испортить настроение!
Теперь придётся второй вариант использовать. С ручным построением точек возврата
И какого это линукс не дружит с исключениями Руки бы поотрывал тому кто придумал в gcc
так исключения обрабатывать
Re[7]: Обработка исключений под linux
От: MaximE Великобритания  
Дата: 21.08.06 15:44
Оценка:
kov_serg wrote:

> ME>Попробуй следущий тест, если он выведет 0, значит действительно работает:


> Признаю свою ошибку. Дествительно не работает.


[]

> Дальше больше:


[]

> замечание: обратим внимание на стек не далеко отлетает ~800 байт (в

> windows 980) ни как не в отдельное адресное пространство.

Так и должно быть. Kernel создает stack frame для signal handler на том же самом
стеке, но это другой, новый frame, не frame той ф-ции, которая сделала fault.
См. цитату из умной книжки в другом постинге.

[]

> И какого это линукс не дружит с исключениями Руки бы поотрывал тому кто

> придумал в gcc так исключения обрабатывать.

gcc тут не причем. Это так устроена обработка сигналов в OS.

--
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[7]: Обработка исключений под linux
От: mr_jek  
Дата: 21.08.06 16:33
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>И какого это линукс не дружит с исключениями Руки бы поотрывал тому кто придумал в gcc

_>так исключения обрабатывать

А причем здесь gcc, вы же с помощью icc все собрали, или я что-то пропустил?
Re[8]: Обработка исключений под linux
От: MaximE Великобритания  
Дата: 21.08.06 17:02
Оценка:
mr_jek wrote:

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

>
> _>И какого это линукс не дружит с исключениями Руки бы поотрывал тому
> кто придумал в gcc
> _>так исключения обрабатывать
>
> А причем здесь gcc, вы же с помощью icc все собрали, или я что-то пропустил?

Не имеет значения каким компилятором собирать, т.к. не в компиляторе дело.

--
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[9]: Обработка исключений под linux
От: kov_serg Россия  
Дата: 29.08.06 12:11
Оценка: :)
Здравствуйте, MaximE, Вы писали:

...
ME>Не имеет значения каким компилятором собирать, т.к. не в компиляторе дело.

Вот схема (детали реализации чуть ниже) которая работает так как надо под linux:

#include "exc_emu.h"
struct SimpleObject {
    SimpleObject() { ... }
    virtual ~SimpleObject() { ... }
};
...
void some_func() {
    ...
    TRY {
        ...
        SimpleObject so1; EXC_MARKER;
        SimpleObject so2; EXC_MARKER;
            ... div by zero or a/v ..
        SimpleObject so3; EXC_MARKER;
        ...
        fn( exc_param_marker(SimpleObject()) );
        ...
    }
    CATCH(runtime_error &re) {
        ...
    }
    CATCH(...) {
        ...
    }
    TRY_END
    ...
}
...


Если использовать такой синтаксис то обработка исключений работает великолепно. Но напрягает после каждого объекта ставить EXC_MARKER, что бы при возникновении исключения его обработка шла именно с последнего EXC_MARKER (quick_save). Если бы заставить компилятор после каждого создания статического объекта вызывать EXC_MARKER, проблема как-то решилась. Или если бы схема обработки исключений была иной, например как в vc то всё было бы проще. Так что именно компилятор виновен!!!
Руки бы им поотрывал!!!

Вот детали реализации:
// exc_emu.h
#pragma once
#include "Threads.h"
#include <map>
using namespace std;

struct ef_t { // exception frame
    ef_t *prev; // prev frame
    void *lra;  // last saved safe return address
    int   ebp;  // old ebp value
    ef_t()  { prev=NULL; lra=NULL; ebp=0; } 
};

struct tib_t { // thread information block
    ef_t      *efp; // exception frame pointer
    ef_t       head;// dummy frame for first exception
    threadid_t id;  // thread id
    static tib_t* getTIB(); // return thread information block

    tib_t() {}
    tib_t(threadid_t id) : id(id) {}
    tib_t(const tib_t& src){ efp=src.efp; head=src.head; id=src.id; }

    typedef map<threadid_t,tib_t> tibs_t;
    static tibs_t tibs;       // Thread Information Blocks
    static Mutex  tibs_mutex; // mutex for tibs
};

enum ExcCodes { exc_av=1, exc_div0=2,exc_unk=-1 };
void exc_marker(int exc=0);

struct ExcScope {
    ExcScope()  { exc_enter(&frame); }
    ~ExcScope() { exc_leave();       }
private:
    ef_t frame;
    static void exc_enter(ef_t *fp);
    static void exc_leave();
};

#define TRY        try { ExcScope __try_scope; exc_marker();
#define CATCH(x)   exc_marker(); } catch(x) { exc_marker();
#define EXC_MARKER { exc_marker(0); }
#define TRY_END    }

template<class T> T& exc_param_marker(T& p) { EXC_MARKER; return p; }
struct ExcObject { ExcObject() { EXC_MARKER; } };


// exc_emu.cpp
#include "exc_emu.h"
#include <signal.h>
#include <stdexcept>
using namespace std;

tib_t::tibs_t tib_t::tibs;
Mutex  tib_t::tibs_mutex;

tib_t* tib_t::getTIB() { // return tib for current thread
    Lock lock(&tib_t::tibs_mutex);
    threadid_t id=Thread::getCurrentThreadID();
    tibs_t::iterator i=tibs.find(id);
    tib_t *tib;
    if (i!=tibs.end()) {
        tib=&(i->second);
    } else {
        tib=&(tibs[id]=tib_t(id));
        tib->efp=&(tib->head);
    }
    return tib;
}
void ExcScope::exc_enter(ef_t *fp) {
    tib_t* tib=tib_t::getTIB();
    fp->prev=tib->efp;
    tib->efp=fp;
    EXC_MARKER;
}
void ExcScope::exc_leave() {
    tib_t* tib=tib_t::getTIB();
    if (!tib->efp->prev) throw runtime_error("no previous exception frame");
    tib->efp=tib->efp->prev;
    EXC_MARKER;
}
void exc_marker(int p) {
    int ra=(&p)[-1]; // ra  - return address
    int bp=(&p)[-2]; // ebp - prev stack frame register
    if (p) {
        if (p==exc_av)   throw runtime_error("access violation");
        if (p==exc_div0) throw runtime_error("div by zero");
        throw runtime_error("unknown error");
    }
    tib_t* tib=tib_t::getTIB();
    tib->efp->lra=(void*)ra;
    tib->efp->ebp=bp;
}
void emu_push(sigcontext *regs,int val) {
    regs->esp-=sizeof(int);
    *(int*)regs->esp=val;
}
void emu_call(sigcontext *regs,int subr,int ret_addr) {
    emu_push(regs,ret_addr);
    regs->eip=subr;
}
void panic() { /*exit(1);*/ throw 0xDEAD; }
void sig_action(int signum, siginfo_t *si, void* unk) {
    sigcontext *regs=(sigcontext*)((int*)unk+5); // !!! подобрано экспериментально Linux<SuSe10,x86>::gcc
    tib_t *tib=tib_t::getTIB();
    void *ra=tib->efp->lra;
    if (ra==NULL) { panic(); }
    int param=exc_unk;
    switch(signum) {
    case SIGSEGV: param=exc_av;   break;
    case SIGFPE:  param=exc_div0; break;
    };
    regs->ebp=tib->efp->ebp;
    emu_push(regs,param);
    emu_call(regs,(int)(void*)exc_marker,(int)ra);
}
int sig_install() {
    struct sigaction sa,old_sa;
    sa.sa_flags=SA_SIGINFO;
    sa.sa_sigaction=&sig_action;
    if (sigaction(SIGSEGV,&sa,&old_sa)==-1)
        throw runtime_error("can't set SEGV signal handler");
    if (sigaction(SIGFPE ,&sa,&old_sa)==-1)
        throw runtime_error("can't set FPE signal handler");
    return 1;
}
int sig_installed=sig_install();


// Threads.h
#pragma once
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>

#define threadid_t long

struct Thread {
    static threadid_t getCurrentThreadID() { return (threadid_t)pthread_self(); }
    //....
};
struct Mutex {
    Mutex()      { pthread_mutex_init(&mutex,NULL); }
    void Enter() { pthread_mutex_lock(&mutex);      }
    void Leave() { pthread_mutex_unlock(&mutex);    }
    ~Mutex()     { pthread_mutex_destroy(&mutex);   }
private:
    pthread_mutex_t mutex;
};
struct Lock {
    Lock(Mutex *mutex) : mutex(mutex) { if (mutex) mutex->Enter(); }
    ~Lock() { if (mutex) { mutex->Leave(); mutex=NULL; } }
private:
    Mutex *mutex;
};
//....


Впринципе gcc поддерживает автоматическое профилирования и сам вставляет функции
extern "C" {
void __cyg_profile_func_enter(void *this_fn, void *call_site) __attribute__((__no_instrument_function__));
void __cyg_profile_func_exit (void *this_fn, void *call_site) __attribute__((__no_instrument_function__));
};

и можно поставить EXC_MARKER скажем в функцию __cyg_profile_func_exit но это приводит к тому что map<> уже нельзя использовать т.к. возникнет рекурсия и всем функциям придётся ставить __attribute__((__no_instrument_function__)).
И в общем случае это всё равно не выход. Т.к. вызываться EXC_MARKER в таком варианте будет чересчур часто.

Короче проблемма с обработкой исключений в linux gcc остаётся
Что делать Я в панике
Re[10]: Обработка исключений под linux
От: Elifant  
Дата: 30.08.06 02:33
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Вот схема (детали реализации чуть ниже) которая работает так как надо под linux:


_>
_>...
_>


_>Если использовать такой синтаксис то обработка исключений работает великолепно. Но напрягает после каждого объекта ставить EXC_MARKER, что бы при возникновении исключения его обработка шла именно с последнего EXC_MARKER (quick_save). Если бы заставить компилятор после каждого создания статического объекта вызывать EXC_MARKER, проблема как-то решилась. Или если бы схема обработки исключений была иной, например как в vc то всё было бы проще. Так что именно компилятор виновен!!!

_>Руки бы им поотрывал!!!

А в сторону ключей '-fasynchronous-unwind-tables', '-fnon-call-exceptions' копал? Не помогает?
Re[11]: Обработка исключений под linux
От: kov_serg Россия  
Дата: 30.08.06 08:04
Оценка:
Здравствуйте, Elifant, Вы писали:

...
E>А в сторону ключей '-fasynchronous-unwind-tables', '-fnon-call-exceptions' копал? Не помогает?

Пробывал не помагает
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.