Здравствуйте, Аноним, Вы писали:
А>Уже писали, но повторю: А>Деление на ноль и GPF это не исключения — их никто не кидал при помощи throw, поэтому поймать их при помощи catch() невозможно.
А>Не уверен, что и при помощи сигналов что-то можно сделать — не все сигналы можно перехватить; возможно, для некоторых сигналов установлены предопределённые действия по выходу из обработчика, которые нельзя отменить (я бы сделал так, ибо нефига).
Поставил Intel C++ Compiler.v9.1.039 . В нём спокойно можно делать throw из обработчика сигналов . И нет проблем. Всё с чем я немог смерится в gcc тут отсутствует. И работает вполне предсказуемо.
А>Что полезного можно сделать, так это запускать две программы и когда одна падает, вторая пусть печатает, что, извините, первая упала.
Сам такой прогой пользуйся. Придставь делал в ней что-то, а потом при попытке сохранить данные или так без повода, так внезапно, она падает. А вторая прога тебе и говорит: "Ужос дорогой пользователь, все ваши данные не только потеряны но и испорчены. Но могу вас утешить: моей вины в этом нет"
KDeveloper работает по такой схеме. При каждом падении KDeveloper удивляюсь какие нехорошие слова я знаю
> А>Не уверен, что и при помощи сигналов что-то можно сделать — не все > сигналы можно перехватить; возможно, для некоторых сигналов установлены > предопределённые действия по выходу из обработчика, которые нельзя > отменить (я бы сделал так, ибо нефига). > Поставил Intel C++ Compiler.v9.1.039 . В нём спокойно можно делать throw > из обработчика сигналов . И нет проблем. Всё с чем я немог смерится в > gcc тут отсутствует. И работает вполне предсказуемо.
Интересно, предсказуемо ли поведение в следущем сценарии.
В плагине твоего приложения иногда происходит деление на 0, ты бросаешь из
обработчика сигнала исключение. Код плагина может не использовать RAII или быть
написан на C, другими словами раскрутка стэка в коде плагина может не освободить
ресурсов, или оставить контекст плагина в рассогласованом состоянии.
Здравствуйте, 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. Но плагин же может выделять память и не освобождать её и при этом
не вызывать исключений. И это будет только на совести тех кто писал плагин.
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
Здравствуйте, 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 в один.
kov_serg wrote:
> ME>kov_serg wrote: > >> > А>Уже писали, но повторю: >> > А>Деление на ноль и GPF это не исключения — их никто не кидал при помощи >> > throw, поэтому поймать их при помощи catch() невозможно. >> > >> > А>Не уверен, что и при помощи сигналов что-то можно сделать — не все >> > сигналы можно перехватить; возможно, для некоторых сигналов установлены >> > предопределённые действия по выходу из обработчика, которые нельзя >> > отменить (я бы сделал так, ибо нефига). >> > Поставил Intel C++ Compiler.v9.1.039 . В нём спокойно можно делать throw >> > из обработчика сигналов . И нет проблем. Всё с чем я немог смерится в >> > gcc тут отсутствует. И работает вполне предсказуемо. > > ME>Это не работает по следующим причинам: > Вожете говорить что угодно но оно работает!
Попробуй следущий тест, если он выведет 0, значит действительно работает:
[]
> 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:
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
#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
так исключения обрабатывать
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
Здравствуйте, kov_serg, Вы писали:
_>И какого это линукс не дружит с исключениями Руки бы поотрывал тому кто придумал в gcc _>так исключения обрабатывать
А причем здесь gcc, вы же с помощью icc все собрали, или я что-то пропустил?
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
Если использовать такой синтаксис то обработка исключений работает великолепно. Но напрягает после каждого объекта ставить EXC_MARKER, что бы при возникновении исключения его обработка шла именно с последнего EXC_MARKER (quick_save). Если бы заставить компилятор после каждого создания статического объекта вызывать EXC_MARKER, проблема как-то решилась. Или если бы схема обработки исключений была иной, например как в vc то всё было бы проще. Так что именно компилятор виновен!!!
Руки бы им поотрывал!!!
и можно поставить EXC_MARKER скажем в функцию __cyg_profile_func_exit но это приводит к тому что map<> уже нельзя использовать т.к. возникнет рекурсия и всем функциям придётся ставить __attribute__((__no_instrument_function__)).
И в общем случае это всё равно не выход. Т.к. вызываться EXC_MARKER в таком варианте будет чересчур часто.
Короче проблемма с обработкой исключений в linux gcc остаётся
Что делать Я в панике
Здравствуйте, kov_serg, Вы писали:
_>Вот схема (детали реализации чуть ниже) которая работает так как надо под linux:
_>
_>...
_>
_>Если использовать такой синтаксис то обработка исключений работает великолепно. Но напрягает после каждого объекта ставить EXC_MARKER, что бы при возникновении исключения его обработка шла именно с последнего EXC_MARKER (quick_save). Если бы заставить компилятор после каждого создания статического объекта вызывать EXC_MARKER, проблема как-то решилась. Или если бы схема обработки исключений была иной, например как в vc то всё было бы проще. Так что именно компилятор виновен!!! _>Руки бы им поотрывал!!!
А в сторону ключей '-fasynchronous-unwind-tables', '-fnon-call-exceptions' копал? Не помогает?