#опытным
Чтобы полноценно работать со стандартными трейсами, нужно знать несколько вещей:
✅ Чтобы фича заработала с новыми версиями gcc и clang, вам нужно залинковать либу
-lstdc++exp. В туториалах обычно используется линковка с -lstdc++_libbacktrace, которая не работает на свежих версиях компиляторов✅ Собирайте код с дебаг символами. Если хотите видеть трейсы и получать из них полезную информацию об именах функций и файлов, вместо прочтения чистых адресов, то надо собирать в дебаге. Итого полная команда сборки:
g++ -std=c++23 -g -lstdc++exp -O2 main.cpp -o app
✅ Да, вы можете включать оптимизации компилятора и добавить в бинарник дебаг символы одновременно. Оптимизации конечно могу сильно преобразовывать потенциальные трейсы за счет инлайнинга, но без оптимизаций мы не можем.
✅ Дебаг символы обычно занимают довольно внушительный объем и не всегда хочется их тянуть везде вместе с бинарем. В таких случаях можно использовать утилиту
strip и вырезать из бинаря отдельный файл с дебаг символами. При обнаружении проблемы с программой можно в дебаггере подгрузить файл с символами и наслаждаться прелестями человекочитаемой отладки.Ну или можно собирать код с флагом -g1, который включает только самую необходимую отладочную информацию
✅ Мы рассматривали стектрейсы в разрезе исключений, но их конечно же можно использовать и отдельно от них.
int foo(int a, int b) {
if (b == 0) {
LOG_ERROR << "Second operand is zero. Trace:\n" << std::stacktrace::current();
}
return a / b;
}Однако стоит помнить, что получение трейса - не бесплатная штука. Логи-то не бесплатный, а проход по стеку вызовов и сбор информации из дебаг символов - очень затратная штука. Не стоит использовать трейсы в нагруженных частях программы. Только в ошибочных путях программы в некритичном коде.
✅ Сам класс стектрейсов предоставляет много гибкости. Можно отдельно проходиться по каждому фрейму и в принципе контролировать количество захватываемых фреймов:
void inspectTrace(const std::stacktrace& trace) {
for (const auto& frame : trace) {
std::cout << "Function : " << frame.description()
<< '\n'
<< "Source file : " << frame.source_file()
<< '\n'
<< "Line : " << frame.source_line()
<< '\n'
<< "---\n";
}
}
// Capture at most 10 frames except of the last one.
auto trace = std::stacktrace::current(/skip=/1, /max_depth=/10);Трейсы скорее всего рано или поздно заедут в каком-то виде в стандарт, чтобы их можно было использовать со стандартными исключениями и конктейнерами. Поэтому полезно же сейчас понимать, как это работает под капотом и как этим правильно пользоваться.
Know your origin. Stay cool.
#cpp23