Повторно выбросить исключение с сохранением трассировки

Мне нужно поймать "фатальный" C++ exception, затем очистить журналы и заново выбросить первый, со своим собственным следом.

Однако мое текущее решение отображает (правильно) неправильную трассировку стека.

#include <exception>
#include <iostream>

void fatal(const std::exception & E)
{
    // do - something - extremely - important
    throw E;
}

int foo()
{
    throw std::runtime_error("yet another foo function");
}

int main()
{
    try
    {
        return foo();
    }
    catch (const std::exception & E)
    {
        fatal(E);
    }
    return -1;
}

Программа завершается

  $ cat ./backtrace
  backtrace
  quit

  $ ulimit -c unlimited
  $ ./a.out
  $ gdb -q ./a.out core -x ./backtrace

Результат

Program terminated with signal SIGABRT, Aborted.

..................................................

4 0x00007f496eb53701 in std::terminate() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6

5 0x00007f496eb53919 in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6

6 0x0000000000400d71 in fatal(std::exception const&) ()

7 0x0000000000400e5b in main ()

Я думал, что повторное создание исключения (с помощью const ref) было методом передачи исходной трассировки; Меня интересует обратная трассировка foo(), а не fatal().

Хорошо, я throwed внутри блок catch, отказавшись от области видимости fatal() ... и обратная трассировка такая, как ожидалось

Patrizio Bertoni 18.09.2018 10:32

В стандартном C++ нет ничего, что связывало бы исключение с «трассировкой». Что бы вы ни описывали, это относится к одной реализации. Вам нужно будет прочитать документацию по этой реализации. Между прочим, чтобы повторно вызвать исключение в обработчике исключений, все, что вам нужно сделать, это throw; - нет необходимости явно повторно генерировать исключение, которое было перехвачено. Имейте в виду, что вызывает terminate(), если нет активного исключения для повторного создания.

Peter 18.09.2018 12:25
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
738
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

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

Ответ принят как подходящий

С вашим сценарием:

backtrace
quit

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

Другой подход - использовать команду gdb catch throw плюс немного скриптов. Таким образом, вы можете записать трассировку стека на каждом throw. Вы можете сделать это так:

(gdb) catch throw
Catchpoint 1 (throw)
(gdb) commands
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
> silent
> backtrace
> continue
> end

Это остановится на каждом throw и напечатает обратную трассировку. Однако вы не хотите печатать трассировки стека из фрейма fatal. Для этого вы можете использовать вспомогательную функцию gdb и сделать точку перехвата условной:

(gdb) cond 1 $_any_caller_matches("fatal", 10)

(Цифра «10» - это всего лишь предположение о том, сколько кадров может отделять fatal от внутренностей библиотеки C++, которые обрабатывают метание.)

На этот вопрос уже был дан ответ, но я хочу добавить, что в стандартный C++ 11 можно создавать правильные обратные трассировки:

Используйте std::nested_exception и std::throw_with_nested

В StackOverflow здесь и здесь описано, как вы можете проследить ваши исключения внутри своего кода без необходимости в отладчике или громоздком журналировании, просто написав правильный обработчик исключений, который повторно генерирует вложенные исключения. Однако для этого требуется, чтобы вы обернули все функции, которые вы хотите отслеживать, в блоки try/catch, но это позволяет вам выполнять большую настройку того, что происходит и какая информация печатается. Поскольку вы можете сделать это с любым производным классом исключений, вы можете добавить много информации в такую ​​трассировку!

Вы также можете взглянуть на мой MWE на GitHub или мой библиотека "след", где обратная трассировка будет выглядеть примерно так:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"

Другие вопросы по теме