LINUX.ORG.RU

gcc, SIGSEGV и try/catch теперь друзья :)


0

0

Я нарыл способ преобразовывать сигналы типа SIGSEGV в исключения языка C++, которые можно ловить по try/catch, так как это происходит, например, в виндовых компиляторах.

http://www.visualdata.ru/blog/109-segv-signal.html

Сразу прошу не разводить флуд, мол не надо ловить - правильно падать. Прошу потестить подход на различных платформах, версиях компилятора gcc.

Еще хочу сделать на базе этого кроссплатформенную либу под LGPL, облегчающую эту обработку. Не могу придумать название - libsigsegv, libseh уже есть, пока в голову приходит только libsegv или libsegvcatch :)

PS. Уже проверил на opensuse 11.1, на gentoo компилятор gcc-4.3, на MCBC, компилятор gcc-3.3, платформа i386.

Ответ на: комментарий от mv

Это пример. Я же написал что, планирую сделать либу которая будет портабельна. А внутри будет разводится дефайнами. под разные архитектуры. Есть пример портабельного проекта (не моё) linux/windows-32/64бит, использующего этот принцип.

navrocky
() автор топика
Ответ на: комментарий от navrocky

По части обработки сигналов посмотри на libsigsegv. Там много для чего заявлена поддержка, но на деле далеко не везде не работает.

mv ★★★★★
()
Ответ на: комментарий от tailgunner

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

navrocky
() автор топика
Ответ на: комментарий от mv

Как раз хотел посмотреть/пощупать. Слишком у них инфа скупая, ничего не понятно. Можно ли в ихнем обработчике выкидывать исключение? и потом ловить это через try/catch?

navrocky
() автор топика
Ответ на: комментарий от anonymous2

это рассматривать как троллинг? :)

navrocky
() автор топика
Ответ на: комментарий от navrocky

> И вот находится человек который говорит что это уже все знают как десять лет...

Про 10 лет я не говорил :)

где вы раньше были батенька?

Здесь же. И вроде как минимум раз я отвечал на такой вопрос ;)

tailgunner ★★★★★
()

тормозные, очень error prone, нету finally(не надо только про RAII, связывание семантики unwind-protect с деструктором какого-либо класса это убого и ограничено - что если нам надо что-то, непривязанное к какому-либо конкретному объекту сделать в завершении блока? А что если нам надо "прибить" объект в середине блока? Или, например надо что-либо для объектов из разных уровней вложенности сделать? Упс. А если нам надо "подчистить" по-разному в зависимости от состояния нескольких объектов и параметров? деструкторов-мультиметодов в C++ нету, извините)

Love5an
()
Ответ на: комментарий от Love5an

тормозные

Ну это обработка критической ситуации. Тут скорость работы, тем более, столь мелкие и несущественные тормоза, уже не имеют сколь-нибудь ощутимого значения. Если программа умудрилась сгенерить SIGSEGV/GPF то она должна быть благодарна за сам факт того, что ей вообще позволили отползти и хоть как-то сохранить при этом лицо. Если конечно получится.

очень error prone,

Это уже к конкретной реализации.

нету finally(не надо только про RAII, связывание семантики unwind-protect с деструктором какого-либо класса это убого и ограничено -

Если честно, с __finally встретился в первый раз в жизни. AFAIU это http://msdn.microsoft.com/en-us/library/9xtt5hxz(VS.80).aspx ?

The compound statement after the __try clause is the guarded section. The compound statement after the __finally clause is the termination handler. The handler specifies a set of actions that execute when the guarded section is exited, regardless of whether the guarded section is exited by an exception (abnormal termination), or by standard fall through (normal termination).

Control reaches a __try statement by simple sequential execution (fall through). When control enters the __try, its associated handler becomes active. If the flow of control reaches the end of the try block, execution proceeds as follows:

1. The termination handler is invoked. 2. When the termination handler completes, execution continues after the __finally statement. Regardless of how the guarded section ends (for example, via a goto out of the guarded body or a return statement), the termination handler is executed before the flow of control moves out of the guarded section.

A __finally statement does not block searching for an appropriate exception handler.

ОК, попробуем прикинуть.

что если нам надо что-то, непривязанное к какому-либо конкретному объекту сделать в завершении блока?

__try {
    run
}
__finally {
    cleanup
}

Чем это принципиально отличается от приведенного ниже?

try {
    run
} catch (...) {
}
cleanup

Ну хорошо, есть конечно goto и return. С первым, особенно в C++, все понятно. Со вторым.. Ну приятно конечно иметь такую фичу, спорить не буду. Но она IMHO будет скорее развращать и без неё тоже все прекрасно пишется.

А что если нам надо «прибить» объект в середине блока? Или, например надо что-либо для объектов из разных уровней вложенности сделать? Упс.

Примерчик бы не помешал. Мне как-то сложно придумать use case причем чтобы он упирался в __finally. Может, потому, что я его естественным образом не использую.

А если нам надо «подчистить» по-разному в зависимости от состояния нескольких объектов и параметров? деструкторов-мультиметодов в C++ нету, извините)

Ну а __finally вам сам по себе чем то поможет в подчистке и сам определит и разведет application specific состояния? Все равно руками разгребать.

bibi
()
Ответ на: комментарий от bibi

> Чем это принципиально отличается от приведенного ниже?

Тем, что после cleanup в __finally может юыть перезапущено исключение.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Тем, что после cleanup в __finally может юыть перезапущено исключение.

Ну оно как бы и в обработчике обычного исключения может быть перезапущено, не проблема. AFAIU радость __finally в том, что:

If an exception occurs in the __try block, the operating system must find a handler for the exception or the program will fail. If a handler is found, any and all __finally blocks are executed and execution resumes in the handler.

For example, suppose a series of function calls links function A to function D, as shown in the following figure. Each function has one termination handler. If an exception is raised in function D and handled in A, the termination handlers are called in this order as the system unwinds the stack: D, C, B.

Т.е. 'деструктор', привязанный не к объекту но к блоку исполнения. Охотно верю, что в некоторых случаях такой функционал языка может быть полезен и удобен. Правда, ничего кроме смешанного C/C++ кода т.е. 'exception unsafe' с ходу в голову не приходит. Я не буду громко кричать 'костыль! костыль!' т.к. в бою как в бою и ничего тут не поделаешь, если такой код, но... Если делать как дохтор прописал можно прекрасно и гарантированно обходиться и без __finally :)

Anyway, __finally - вещь в себе и её и так нет в g++ и это сугубо MSVC Specific фича. Так что тот факт, что предложенное решение не поддерживает __finally IMHO ммм.. не совсем агрумент в g++ :)

bibi
()
Ответ на: комментарий от bibi

>> Тем, что после cleanup в __finally может юыть перезапущено исключение.

Ну оно как бы и в обработчике обычного исключения может быть перезапущено, не проблема

Ты хоть сам смотрел, что написал? Твой cleanup вне обработчика, исключение потеряно.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Ты хоть сам смотрел, что написал? Твой cleanup вне обработчика, исключение потеряно.

Конечно же смотрел и оно намеренно вне обработчика.

Оригинальный вопрос: что если нам надо что-то, непривязанное к какому-либо конкретному объекту сделать в завершении блока?

AFAIU решение с __finally:

__try {
    run
} __finally {
    cleanup
}

...и без:

try {
    run
} catch (...) {
}
cleanup

Подчистка вызывается? Вызывается. По завершению блока? По завершению блока. Всегда вызывается? Всегда. Вывод: должно работать. Если же хочется ещё и исключение поймать дабы было таковое - пожалуйста, ловим в catch() и обрабатываем.

С другой стороны, по ссылке с MSDN я так и не понял - можно ли делать цепочку __finally для различных исключений как с обычным catch? И, кстати, как будет работать такой __finally:

__try {
    run
} __finally (xxx) {
    cleanup
}

...в случае, когда исключения не произошло? Содержимое xxx не несёт смысловой нагрузки. Что будет, если к нему обратиться? Как вообще понять, почему мы попали в __finally - из-за возникновения исключения или же потому, что просто завершился блок?

bibi
()
Ответ на: комментарий от bibi

> Подчистка вызывается? Вызывается. По завершению блока? По завершению блока. Всегда вызывается? Всегда. Вывод: должно работать. Если же хочется ещё и исключение поймать дабы было таковое - пожалуйста, ловим в catch() и обрабатываем.

Мде. Мне его не поймать хочется, а перезапустить. Покажешь способ, которым можно перезапустить неизвестное исключение вне catch(...){} ?

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Мде. Мне его не поймать хочется, а перезапустить. Покажешь способ, которым можно перезапустить неизвестное исключение вне catch(...){} ?

Нет, конечно же не покажу. Я понял. Ты к тому, что по возникновению исключения оно пролетает насквозь до найденного обработчика, отрабатывая при этом по ходу цепочку __finally? Ну хорошо, допустим. Фича интересная и я бы может быть тоже не отказался видеть её в g++. Впрочем, тогда уж и в стандарте.

bibi
()
Ответ на: комментарий от Werehuman

Как раз-таки и НЕ. Отхватишь завершение работы. Это первое что я попытался сделать.

navrocky
() автор топика
Ответ на: комментарий от true_admin

Да лор - это вообще врач в поликлинике, как ты ему можешь доверять в вопросах программирования ?!?!? А?

navrocky
() автор топика
Ответ на: комментарий от navrocky

Да лор - это вообще врач в поликлинике, как ты ему можешь доверять в вопросах программирования ?!?!? А?

Лялялял... Возьми да замерь. Делов на 10 минут. Без всяких врачей сам все увидишь.

bibi
()
Ответ на: комментарий от tailgunner

> Здесь же. И вроде как минимум раз я отвечал на такой вопрос ;)

К сожалению не нашел ваш ответ. Здесь когда-то спрашивал, наверное год назад...

navrocky
() автор топика
Ответ на: комментарий от bibi

А на кой замерять время исполнения исключения? Это ведь исключительная ситуация %) Пусть оно хоть минуту исполняется.. главное чтобы обработалось.

navrocky
() автор топика
Ответ на: комментарий от bibi

> __finally? Ну хорошо, допустим. Фича интересная и я бы может быть тоже не отказался видеть её в g++. Впрочем, тогда уж и в стандарте.

Я вот тоже хочу эту фичу уже давно, много находил поделок её эмулирующих но не до конца.

navrocky
() автор топика
Ответ на: комментарий от bibi

> Лялялял... Возьми да замерь. Делов на 10 минут. Без всяких врачей сам все увидишь.

http://www.linux.org.ru/view-message.jsp?msgid=3856841

с другой стороны, м.б. там было бы более уместно передавать анонимную функцию внутрь той функции, которая кидает исключения, хотя х.з.

и еще, практический подход к программированию на языке и вопрос дизайна языка — несколько разные вещи

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

а меня там больше волновал именно вопрос дизайна языка

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

Нет, ну это же изврат возвращать значение из функции исключением.... Как такое вообще можно замерять? Пусть меня поправят. Или кто-то так подходит к конструированию кода? o_O

navrocky
() автор топика
Ответ на: комментарий от www_linux_org_ru

В CL, вообще, достаточно сложный механизм событий(conditions), без раскрутки стека, с рестартами, соответственно и т.п. Но есть аналог примитивным throw и catch, называется тоже, как ни странно, throw и catch, и работает быстрее плюсовских исключений(в sbcl, по крайней мере)

вот код для сравнения:

(declaim (optimize (speed 3) (space 0) (safety 0) (debug 0)))

(defun f (x)
  (declare (type fixnum x))
  (unless (zerop x)
    (throw 'error nil)))

(defun g (x)
  (declare (type fixnum x))
  (catch 'error
    (f x)))

(defun main (n x)
  (declare (type fixnum n x))
  (dotimes (i n)
    (declare (type fixnum i))
    (g x)))
#include <stdlib.h>
#include <iostream>

class Error {} error;

void f(int x)
{
        if(!x)
                throw error;
}

void g(int x)
{
        try
        {
                f(x);
        }
        catch(Error)
        {
                return;
        }
}

int main(int argc, char *argv[])
{
        if(argc!=3)
                return -1;
        int n = strtoul(argv[1], NULL, 10),
            x = strtoul(argv[2], NULL, 10);
        while(n--)
                g(x);
        return 0;
}
у меня лисповский отрабатывает за ~330 мс, плюсовый - ~530 (-O3 и т.п., естественно, включены)

Love5an
()
Ответ на: комментарий от navrocky

щас *опять* придется долго объяснять

представь, что f — это функция парсинга лога и возвращает количество байт в данной строке, тогда логично ее неудачу сделать исключение

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от Love5an

> у меня лисповский отрабатывает за ~330 мс, плюсовый - ~530 (-O3 и т.п., естественно, включены)

к коду слишком много замечаний, чтобы его принять всерьез

перепиши мой пример на лиспе и сравни http://www.linux.org.ru/view-message.jsp?msgid=3856841

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от Love5an

> время - за миллион повторений, забыл сказать мерял плюсы из шелла с помощью time, лисп - с помощью макроса time из repl

меряй оба варианта time-ом (стартапом на 3 секундах уже можно пренебречь)

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

> щас *опять* придется долго объяснять

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

А если важна производительность, то коды возврата в руки и вперед!...

navrocky
() автор топика
Ответ на: комментарий от navrocky

> Я знаю как правильно применять исключения. Совершенно верно, их выкидывать по ошибке. Но ошибка - это не штатный режим работы программы, и соответственно на производительность никак не влияет.

0.5% случаев можно назвать достаточно редким и нештатным режимом работы?

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

> 0.5% случаев можно назвать достаточно редким и нештатным режимом работы?

Да. А в чем подвох вопроса?

navrocky
() автор топика
Ответ на: комментарий от www_linux_org_ru

> возвращает количество байт в данной строке, тогда логично ее неудачу сделать исключение

Гкхм-кхм.

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

> Читай ту ветку целиком. Сэкономишь кучу времени и постов в этой.

Прочел. Тот-же бред что и в этой. Тоесть ты клонишь к тому что можно не продолжать? :D

navrocky
() автор топика
Ответ на: комментарий от www_linux_org_ru

Каких замечаний? Вот тот код, переписанный, ладно.

(declaim (optimize (speed 3) (space 0) (safety 0) (debug 0)))

(deftype ufixnum () '(integer 0 #.most-positive-fixnum))

(define-symbol-macro +BITS+ 10)

(declaim (type ufixnum *i0* *i1*)
         (type condition *condition*))
(defvar *i0* 0)
(defvar *i1* 0)
(defvar *condition* (make-condition 'condition))

(defun f (arg)
  (declare (type ufixnum arg))
  (if (zerop (logand (ash arg -16)
                     (1- (ash 1 +BITS+))))
    (throw *condition*
      (incf *i1*))
    (incf *i0*))
  nil)

(defun main (&aux (i 0) (x 1))
  (declare (type ufixnum i x))
  (loop do (incf i)
           (incf x (ash x 2))
           (catch *condition*
             (f x))
        while (/= x 1))
  (format t "i=~a i0=~a i1=~a i0+i1=~a~%"
          i *i0* *i1* (+ *i0* *i1*))
  (format t "  ~a    ~a    ~a       ~a~%"
          #x40000000 (- i *i1*)
          (ash (ash 1 (- 16 +BITS+)) 14)
          #x40000000))
Время получается примерно как в плюсах - 2.42 секунд real, +- 20 мс Ну, учитывая в лиспе 50-100 мс на подгрузку рантайма, плюс оверхед на доступ к динамическим переменным, получается, что в лиспе оно таки быстрее :)

У меня на 32-битной системе, правда, оверфлоу ufixnum происходит(fixnum на 32битном sbcl равно (signed-byte 29)), но это мелочи :)

Love5an
()
Ответ на: комментарий от Love5an

Вообще, исключения в плюсах и подобных языках медленнее еще и от того, что им надо раскрутить стек. И, перезапустить, если что, например.
В CL исключения стек не раскручивают, так что, при достаточно больших вложенностях функций, CL, даже несмотря на оверхед из-за общей динамичности и высокоуровневости, может выигрывать, в теории.

Love5an
()
Ответ на: комментарий от Love5an

> У меня на 32-битной системе, правда, оверфлоу ufixnum происходит(fixnum на 32битном sbcl равно (signed-byte 29)), но это мелочи :)

хм... это может вызвать то, что у тебя выполняется в 4-8 раз меньше итераций, т.к. количество итераций определяется из разрядности

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от Love5an

> Вообще, исключения в плюсах и подобных языках медленнее еще и от того, что им надо раскрутить стек. И, перезапустить, если что, например. В CL исключения стек не раскручивают, так что, при достаточно больших вложенностях функций, CL, даже несмотря на оверхед из-за общей динамичности и высокоуровневости, может выигрывать, в теории.

У меня там раскручивать нечего. К слову, рестарты чем-то отличаются от подачи «во внутрь» анонимной функции, определенной «снаружи»?

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от Love5an

> У меня на 32-битной системе, правда, оверфлоу ufixnum происходит(fixnum на 32битном sbcl равно (signed-byte 29)), но это мелочи :)

у тебя там есть нормальные 32-разрядные беззнаковые?

www_linux_org_ru ★★★★★
()

Хоре трындеть про скорость работы с исключениями, если ты на ноль делишь миллион раз - миллион раз тебе руки оторвать надо.

Все кто спорят - читайте что такое исключения и зачем они нужны.

И наконец, есть какой-либо другой вариант(соизмеримо-простой) сделать так, чтоб сторонний плагин обращающийся по нулевому указателю не завершал извлекающий его код(plasma-plugins под kde4)?

PS: из 7 скачанных плагинов 4 убивают плазму.

jerry_ru
()
Ответ на: комментарий от Love5an

> время - за миллион повторений, забыл сказать мерял плюсы из шелла с помощью time, лисп - с помощью макроса time из repl

У тебя на старт процесса много сжирается, делай 100 млн. циклов.

mv ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.