Как можно перебирать кадры стека вручную в C?

При обработке сигналов в приложениях я могу правильно видеть обратную трассировку в отладчике. Но системный вызов backtrace не отображает кадры стека правильно. Есть ли разница в том, как gdb хранит кадры стека и как системный вызов backtrace сбрасывает их?

3 ответа

Решение

Вы не можете переносить итерацию по кадрам стека в C99 или C11.

Во-первых, потому что в стандарте C нет гарантии на какой-либо стек вызовов. (можно представить, что какой-то компилятор C выполняет весь анализ программы и избегает стека, если он бесполезен, например, если рекурсия не может произойти; такого компилятора C я не знаю). Посмотрите, например, этот вопрос C FAQ для странных реализаций Си.

Затем, потому что компилятор может иногда делать некоторые оптимизации, например, встроенные вызовы (даже для функций, не отмеченных inline в частности, при запросе оптимизации времени соединения с -flto перешел к gcc) или иногда испускают хвостовые вызовыGCC может делать оба). Вы можете отключить оптимизацию, но тогда вы потеряете много производительности. Оптимизирующий компилятор помещает некоторые переменные только в регистры и повторно использует некоторые стековые слоты для нескольких переменных.

Наконец, на некоторых архитектурах (например, 32-битных x86), некоторый код (в частности, некоторый библиотечный код, например, внутри libc) может быть скомпилирован с -fomit-frame-pointer и тогда нет способа получить информацию о кадре без нее.

Вы могли бы использовать libbacktrace Ян Тейлор внутри GCC; Вы также можете использовать функцию backtrace(3) из GNU glibc; Вы могли бы даже использовать встроенные адреса возврата, доступные при компиляции с GCC. Но все эти инструменты могут не работать с оптимизированным кодом.

В практическом плане, если вам действительно нужна некоторая обратная трассировка, либо реализуйте это самостоятельно (я сделал это в моей устаревшей системе MELT, чей код C++ генерируется, упаковав локальные файлы в какой-то локальный struct), или избегайте чрезмерной оптимизации, например, путем компиляции с gcc -g -O1 только.

Заметить, что backtrace это не системный вызов (перечисленный в syscalls (2)), а glibc -специфическая библиотечная функция.

Читайте также очень внимательно сигнал (7) и sigreturn (2). Существует очень немного (безопасных для асинхронного сигнала) функций, которые можно надежно вызывать (прямо или косвенно) из обработчиков сигналов, и backtrace (или же printf) не среди них. На практике портативный обработчик сигнала должен часто просто устанавливать некоторые volatile sigatomic_t флаг, который вы должны проверить в другом месте - или вызовите siglongjmp (3).

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

Просто возьмите адрес локальной переменной в функции. Рассчитать размер стека. добавьте его из адреса локальной переменной и выведите его между адресом переменной дополнения и переменной местоположения;.

Вы напечатали свой стек:)

Отладчик использует дополнительный набор дополнительных данных, помещаемых компилятором в двоичный файл gcc, когда вы используете -g вариант. Эти данные не используются обратным вызовом, а используется только основная информация о компоновщике. Это означает, например, что любые статические данные не могут быть просмотрены с помощью backtrace, но с помощью gdb, это также приводит к различным оптимизациям, нарушающим backtrace, которые gdb обходит через явное знание.

Помните, что gdb специфичен для определенного языка и компилятора, тогда как обратная трассировка гораздо более переносима.

Смотрите справочную страницу backtrace http://linux.die.net/man/3/backtrace:

Пропуск указателей кадра (как подразумевается любым из ненулевых уровней оптимизации gcc(1)) может привести к нарушению этих предположений.

Если бы вызов backtrace хотел использовать эту информацию, он бы заставил вас всегда компилировать с символами отладки и имел бы гораздо большие накладные расходы и многие другие проблемы.

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