использование catch (...) (многоточие) для посмертного анализа

кто-то в другом вопросе предложил использовать catch(...) чтобы захватить все в противном случае необработанные - неожиданные/непрошеные исключения, окружив весь main() С try{}catch(...){} блок.

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

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

кроме того, какие предостережения связаны с ним. В частности:

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

6 ответов


Да это хорошая идея.

Если вы позволяете исключению escape main, это реализация, определенная погодой, стек разматывается до завершения работы приложения. Поэтому, на мой взгляд, важно, чтобы вы поймали все исключения в main.

тогда возникает вопрос, что с ними делать.
Некоторые ОС (см. MS и SE) предоставляют дополнительные средства отладки, поэтому полезно просто повторно бросить исключение после его захвата (потому что стек был размотан сейчас в любом случае.)

int main()
{
    try
    {
        /// All real code
    }
    // I see little point in catching other exceptions at this point 
    // (apart from better logging maybe). If the exception could have been caught
    // and fixed you should have done it before here.

    catch(std::exception const& e)
    {
         // Log e.what() Slightly better error message than ...
         throw;
    }
    catch(...)   // Catch all exceptions. Force the stack to unwind correctly.
    {
        // You may want to log something it seems polite.
        throw;  // Re-throw the exception so OS gives you a debug opportunity.
    }
}
  • будет ли это хорошо играть с потоками, которые прорастают позже?

он не должен влиять на потоки. Обычно вам нужно вручную присоединиться к любым дочерним потокам, чтобы убедиться, что они вышли. Точные сведения о том, что происходит с дочерними потоками, когда основные выходы не определены (поэтому прочитайте документацию), но обычно все дочерние потоки умирают мгновенно (неприятная и ужасная смерть, которая не связана с размоткой их стеки).

Если вы говорите об исключениях в дочерних потоках. Опять же, это не очень хорошо определено (поэтому прочитайте документацию), но если поток выходит через исключение (т. е. функция, используемая для запуска потока, выходит из-за исключения, а не возврата), то это обычно приводит к завершению приложения (то же самое влияет, как выше). Поэтому всегда лучше остановиться все исключения из выхода из потока.

  • он не сломается обработка segfaults (захвачена в другом месте как сигнал)

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

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

не должно влиять на другие обработчики.


насколько я помню, catch(...) на Win32 ловит также SEH исключения, и вы не хочется для этого. Если вы получаете исключение SEH, это потому, что произошло что-то очень страшное (в основном нарушения доступа), поэтому вы больше не можете доверять своей среде. Почти все, что вы могли бы сделать, может потерпеть неудачу с другим исключением SEH, поэтому даже не стоит пытаться. Кроме того, некоторые исключения SEH предназначены для захвата системой; подробнее об этом здесь.

Итак, мой рекомендуется использовать базовый класс исключений (например,std::exception) для всех ваших исключений и поймать только этот тип в "catchall"; ваш код не может быть готов к работе с другими исключениями, поскольку они неизвестны по определению.


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

по поводу ваших вопросов:

  • Я считаю, что глобальный блок catch не перехватывает исключения в другом потоке. Каждый поток имеет свое собственное пространство стека.
  • Я не уверен в этом.
  • вложенные попробовать...блоки catch не затрагиваются и будут выполняться как обычно. - исключение передается вверх по стеку, пока не найдет блок try.

можно попробовать решение, которое я использую Если вы делаете приложение .net. Это захватывает все необработанные исключения. Я обычно включаю только код (с #ifndef DEBUG) для производственного кода, когда я не использую отладчик.

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


уловка-все не будет ужасно полезно, Так как нет информации типа/объекта, который вы можете запросить. Однако, если вы можете убедиться все исключения, вызванные вашим приложением, являются производными от одного базового объекта, вы можете использовать блок catch для базового исключения. Но тогда это не будет ловушкой.


и как его восстановить (как получить доступ к и распознать, в чем подвох. звонил с)

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

try {
   ...
} catch (const SomeCustomException& e) {
   ...
} catch (const std::bad_alloc& e) {
   ...
} catch (const std::runtime_error& e) {
   // Show some diagnosic for generic runtime errors...
} catch (const std::exception& e) {
   // Show some diagnosic for any other unhandled std::exceptions...
} catch (...) {
   // Fallback for unknown errors.
   // Possibly rethrow or omit this if you think the OS can do something with it.
}

обратите внимание, что если вы обнаружите, что делаете это в нескольких местах и хотите консолидировать код (возможно, несколько main функции для отдельных программы), вы можете написать функцию:

void MyExceptionHandler() {
   try {
      throw; // Rethrow the last exception.
   } catch (const SomeCustomException& e) {
      ...
   }
   ...
}

int main(int argc, char** argv) {
   try {
      ...
   } catch (...) {
      MyExceptionHandler();
   }
}