Суть такова, до какого-то обновления (вот не помню, какую версию gcc я использовал) при возникновении исключения внутри потока, созданного через std::async, программа падала (я его нигде не перехватывал), причём срабатывал обработчик какого-то сигнала , который выводил трассу стека (при старте приложения вызывалась такая функция https://llvm.org/doxygen/namespacellvm_1_1sys.html#ab062fd190912d9ca714311df7cbe51d5).
После обновления то ли компилятора, то ли фреймворка (сорян за сумбур, уже не могу откатиться и перепроверить) исключения в другом потоке стали молча глотаться. Причём я пробовал их перехватить и перебросить (например вызвать .get у фьючи, которую вернул std::async), но проблема в том, что при этом я теряю трассу: выводится позиция не того места, где на самом деле возникло исключение, а позиция того места, где я его перебросил.
Как сделать чтоб "было как раньше"? Чтоб исключение в другом потоке сразу вызывало падение с правильной трассой.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, T4r4sB, Вы писали:
TB>Суть такова, до какого-то обновления (вот не помню, какую версию gcc я использовал) при возникновении исключения внутри потока, созданного через std::async, программа падала (я его нигде не перехватывал), причём срабатывал обработчик какого-то сигнала , который выводил трассу стека (при старте приложения вызывалась такая функция https://llvm.org/doxygen/namespacellvm_1_1sys.html#ab062fd190912d9ca714311df7cbe51d5). TB>После обновления то ли компилятора, то ли фреймворка (сорян за сумбур, уже не могу откатиться и перепроверить) исключения в другом потоке стали молча глотаться. Причём я пробовал их перехватить и перебросить (например вызвать .get у фьючи, которую вернул std::async), но проблема в том, что при этом я теряю трассу: выводится позиция не того места, где на самом деле возникло исключение, а позиция того места, где я его перебросил. TB>Как сделать чтоб "было как раньше"? Чтоб исключение в другом потоке сразу вызывало падение с правильной трассой.
тяжело установить все детали. Даже тяжело представить как стек мог сохраниться. В отдельном потоке исключение должен был перехватить обработчик от std::async и сохранить его (то есть на этом этапе мы должны были потерять всю информацию о стеке). При пробросе исключения в главном потоке вызывается terminate, но опять же информация о стеке должна была исчезнуть. Можно предположить, что компилятор провёл какую-то хитрую оптимизацию, понял что исключение в главном потоке всё равно никто ловить не будет и вызвал terminate в дочернем. И вот тогда всё идёт на откуп опять же компилятора:
If no matching handler is found, std::terminate is invoked; whether or not the stack is unwound before this invocation of std::terminate is implementation-defined.
Может оставить как есть, а может размотать стек и потереть таблицу переходов. Что частично может объяснить изменения поведения после обновления компилятора.
В любом случае закладываться на такие вещи себе дороже.
выглядит так что ваш новый фреймворк замаскировал сигналы на всех потоках и обрабатывает их через sigwait на каком то определенном потоке TB>Как сделать чтоб "было как раньше"? Чтоб исключение в другом потоке сразу вызывало падение с правильной трассой.
ну если не задумываться о происходящем, то sigprocmask
Как много веселых ребят, и все делают велосипед...
Здравствуйте, ononim, Вы писали:
O>выглядит так что ваш новый фреймворк замаскировал сигналы на всех потоках и обрабатывает их через sigwait на каком то определенном потоке
По идее std::async так и делает — глотает исключения.
Так что я теперь не понимаю почему раньше оно нормально падало
TB>>Как сделать чтоб "было как раньше"? Чтоб исключение в другом потоке сразу вызывало падение с правильной трассой. O>ну если не задумываться о происходящем, то sigprocmask
И как именно его надо использовать? Как оно поможет если в библиотечной реализации std::async сделан перехват исключений, например?
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, sergii.p, Вы писали:
SP>тяжело установить все детали. Даже тяжело представить как стек мог сохраниться.
Раньше стек выводится в перехватчике сигнала. Исключение вызывало сигнал, всё было хорошо
SP>При пробросе исключения в главном потоке вызывается terminate, но опять же информация о стеке должна была исчезнуть
Я тогда не понимаю вообще прикол исключений.
Вот где-то у меня хз где std::unordered_map::at кинул исключение.
И как мне понять, где именно? Как только я его катчу, инфа теряется.
Мне весь проект каждую функцию обмазать try-catch и навешивать на объект исключения инфу о том, где это произошло?
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
TB>И как мне понять, где именно? Как только я его катчу, инфа теряется. TB>Мне весь проект каждую функцию обмазать try-catch и навешивать на объект исключения инфу о том, где это произошло?
не каждую функцию, а функцию каждого потока оберни в try catch(чтотам у тебя за exception)
Как много веселых ребят, и все делают велосипед...
Здравствуйте, T4r4sB, Вы писали:
TB>Суть такова, до какого-то обновления (вот не помню, какую версию gcc я использовал) при возникновении исключения внутри потока, созданного через std::async, программа падала (я его нигде не перехватывал), причём срабатывал обработчик какого-то сигнала , который выводил трассу стека (при старте приложения вызывалась такая функция https://llvm.org/doxygen/namespacellvm_1_1sys.html#ab062fd190912d9ca714311df7cbe51d5). TB>После обновления то ли компилятора, то ли фреймворка (сорян за сумбур, уже не могу откатиться и перепроверить) исключения в другом потоке стали молча глотаться.
Ни падение программы, ни проглатывание исключения не является валидным поведением для std::async.
На самом деле исключение, вылетевшее из вызываемой функции, сохраняется в future (и его можно потом перевыбросить при вызове get-метода).
Скорее всего, ты что-то путаешь, и либо раньше использовал другие примитивы (например, явно сам запускал thread и выставлял promise), либо твоя программа падала не из-за исключения, а, скажем, из-за связанного SIGSEGV. В обоих случаях обработчики terminate, сигналов или отдадчики вполне могут выдавать stacktrace.
TB>Как сделать чтоб "было как раньше"? Чтоб исключение в другом потоке сразу вызывало падение с правильной трассой.
Вспоминай, что использовал до этого :)
Потому что вызов std::async служит, чтобы выполнить функцию и получить её исключение и результат. Если это не подходит, то нужно не использовать async.
TB> Причём я пробовал их перехватить и перебросить (например вызвать .get у фьючи, которую вернул std::async), но проблема в том, что при этом я теряю трассу: выводится позиция не того места, где на самом деле возникло исключение, а позиция того места, где я его перебросил.
Тут тоже не понятно, на что ты смотришь. Одно дело, если ты видел statcktrace из terminate после неудачной раскрутки, а другое дело, если ты хочешь в объекте исключения хранить ассоциированный с ним путь и инспектировать его в программе...
Технически возможно собирать полный путь раскрутки и сохранять его в объекте исключения. Но дорого. Например, есть неприятные случаи, связанные тем, что сохранённое в exception_ptr исключение может быть перевыброшено несколько раз, и в памяти стектрейсы представляют собой дерево, а не список. И желательно не тормозить.
Впрочем, это решаемые проблемы, и хотя предложение P2370 Stacktrace from exception стагнирует, у него есть пара реализаций, которые в большинстве случаев включают запись пути через границы exception_ptr/future.
Здравствуйте, watchmaker, Вы писали:
W>Скорее всего, ты что-то путаешь, и либо раньше использовал другие примитивы (например, явно сам запускал thread и выставлял promise),
Мб дело в том, что я не напрямую зову std::async, а через фреймворк, и мб в предыдущей версии фреймворка поток создавался как-то по-другому
W>либо твоя программа падала не из-за исключения, а, скажем, из-за связанного SIGSEGV
Она именно на явное
throw std::runtime_error("MyError")
реагировала падением с полной выдачей трассы
W>и его можно потом перевыбросить при вызове get-метода
И потерять трассу
W>которые в большинстве случаев включают запись пути через границы exception_ptr/future.
Мне пока не удалось. Потому что уже когда я делаю катч — я уже теряю трассу
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
TB>И потерять трассу W>>которые в большинстве случаев включают запись пути через границы exception_ptr/future. TB>Мне пока не удалось. Потому что уже когда я делаю катч — я уже теряю трассу
Вместо std::runtime_error сделай свой класс исключений, который в своем конструкторе будет делать backtrace и запоминать его в себе.
Чтоб меньше все менять — отнаследуй его от std::runtime_error
Как много веселых ребят, и все делают велосипед...
Здравствуйте, ononim, Вы писали:
O>Вместо std::runtime_error сделай свой класс исключений, который в своем конструкторе будет делать backtrace и запоминать его в себе. O>Чтоб меньше все менять — отнаследуй его от std::runtime_error
Ок, для своих исключений это должно прокатить.
А что делать с std::out_of_range, и прочими вещами, кидаемыми библиотеками?
Ну либо какой флаг указать при сборке, чтоб at не кидал исключение, а валил программу?
Кстати, что вызывается в at в режиме -fno-exceptions?
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Там ссылка на codesourcery какая-то нерабочая
А как хукать? Просто объявить такую функцию нельзя, линкер ругается. Че за ерунда, для new разрешили глобальное переопределение а для исключений нет.
Компилировать отдельную либу и говорить пользователям "запускать через прелод" тоже не вариант
Вот это свинью подложили конечно в std async тем что без спросу перехватывают исключения. Спасибо, чо
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
TB>А как хукать? Просто объявить такую функцию нельзя, линкер ругается. Че за ерунда, для new разрешили глобальное переопределение а для исключений нет.
Ну както так:
Работает в линуксе, а для винды нужна будет другая магия.
А ну и еще важно чтобы эта __cxa_throw была в файле исполняемого процесса, если будет в библиотеке (.so) то чудо не случится.
Как много веселых ребят, и все делают велосипед...
Здравствуйте, so5team, Вы писали:
S>Здравствуйте, T4r4sB, Вы писали:
TB>>Вот это свинью подложили конечно в std async тем что без спросу перехватывают исключения.
S>Если вам нужно, чтобы исключение в std::async валило все приложение, то можно попробовать через std::async запускать noexcept-функцию.
И тогда оно падает, но трасса идет ровно до той функции которую я пометил как noexcept. То есть где там в ее дебрях у меня действительно кинулось исключение — не понять
Весь проект обмазывать noexcept?
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, ononim, Вы писали:
TB>>А как хукать? Просто объявить такую функцию нельзя, линкер ругается. Че за ерунда, для new разрешили глобальное переопределение а для исключений нет. O>Ну както так:
Линкер не дает, говорит что __cxa_throw уже определена в libstd++.a(eh_throw.o)
Кажется проще треды по-своему создавать, без навязанной услуги перехвата исключений
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, T4r4sB, Вы писали:
S>>Если вам нужно, чтобы исключение в std::async валило все приложение, то можно попробовать через std::async запускать noexcept-функцию.
TB>И тогда оно падает, но трасса идет ровно до той функции которую я пометил как noexcept. То есть где там в ее дебрях у меня действительно кинулось исключение — не понять TB>Весь проект обмазывать noexcept?
У меня есть ощущение, что вы нам чего-то важного недоговариваете, поскольку я еще не встречался с runtime-ом для C++, который бы при крахе из-за вылетевшего из треда исключения (или при вызове std::terminate) выдавал бы стек-трейс.
Возможно, вы пользуетесь какой-то сторонней либой, которая берет на себя выдачу подобных стек-трейсов. Если это так, то вопросы нужно задавать по этой либе.
TB>>>А как хукать? Просто объявить такую функцию нельзя, линкер ругается. Че за ерунда, для new разрешили глобальное переопределение а для исключений нет. O>>Ну както так: TB>Линкер не дает, говорит что __cxa_throw уже определена в libstd++.a(eh_throw.o)
это при статической линковке, с динамической все ок
Как много веселых ребят, и все делают велосипед...