Трассировка стека
В вычислительной технике трассировка стека (также называемая трассировкой стека) [1] или обратная трассировка стека [2] ) — это отчет об активных кадрах стека в определенный момент времени во время выполнения программы . При запуске программы память часто динамически распределяется в двух местах: в стеке и в куче . Память постоянно выделяется в стеке, а не в куче, что отражает их имена. Стек также относится к программной конструкции, поэтому, чтобы отличить его, этот стек называется стеком вызовов функций программы . Технически, как только блок памяти был выделен в стеке, его нельзя легко удалить, поскольку до него могут быть выделены другие блоки памяти. блок памяти, называемый записью активации Каждый раз, когда в программе вызывается функция, поверх стека вызовов выделяется . Обычно запись активации хранит аргументы функции и локальные переменные. Что именно он содержит и как оно расположено, определяется соглашением о вызовах .
Программисты обычно используют трассировку стека во время интерактивной и посмертной отладки . Конечные пользователи могут видеть трассировку стека, отображаемую как часть сообщения об ошибке , о которой пользователь затем может сообщить программисту.
Трассировка стека позволяет отслеживать последовательность вызываемых вложенных функций до момента создания трассировки стека. В посмертном сценарии это распространяется на функцию, в которой произошел сбой (но не обязательно он был вызван). Одноуровневые вызовы не отображаются в трассировке стека.
Языковая поддержка
[ редактировать ]Многие языки программирования, включая Java [3] и С# , [4] имеют встроенную поддержку получения текущей трассировки стека с помощью системных вызовов. До std::stacktrace
был добавлен в стандартную библиотеку как контейнер для std::stacktrace_entry
В версиях до C++23 для этого не было встроенной поддержки, но пользователи C++ могут получать трассировки стека, например, с помощью stacktrace библиотеки . В JavaScript исключения содержат stack
свойство, содержащее стек из того места, куда он был брошен.
Питон
[ редактировать ]Например, следующая программа Python содержит ошибку.
def a():
i = 0
j = b(i)
return j
def b(z):
k = 5
if z == 0:
c()
return k + z
def c():
error()
a()
Запуск программы под стандартным интерпретатором Python приводит к следующему сообщению об ошибке.
Traceback (most recent call last):
File "tb.py", line 15, in <module>
a()
File "tb.py", line 3, in a
j = b(i)
File "tb.py", line 9, in b
c()
File "tb.py", line 13, in c
error()
NameError: name 'error' is not defined
Трассировка стека показывает, где возникает ошибка, а именно в c
функция. Это также показывает, что c
функция была вызвана b
, который был вызван a
, который, в свою очередь, вызывался кодом в строке 15 (последней строке) программы. Записи активации для каждой из этих трех функций будут расположены в стеке таким образом, чтобы a
функция будет занимать нижнюю часть стека, а c
функция будет занимать вершину стека.
Ява
[ редактировать ]В Java трассировку стека можно сбросить вручную с помощью Thread.dumpStack()
[5] Возьмите следующий ввод:
public class Main {
public static void main(String args[]) {
demo();
}
static void demo() {
demo1();
}
static void demo1() {
demo2();
}
static void demo2() {
demo3();
}
static void demo3() {
Thread.dumpStack();
}
}
В исключении функции перечислены в порядке убывания, поэтому самый внутренний вызов идет первым.
java.lang.Exception: Stack trace
at java.lang.Thread.dumpStack(Thread.java:1336)
at Main.demo3(Main.java:15)
at Main.demo2(Main.java:12)
at Main.demo1(Main.java:9)
at Main.demo(Main.java:6)
at Main.main(Main.java:3)
С и С++
[ редактировать ]И C , и C++ (до C++23 ) не имеют встроенной поддержки получения трассировки стека, но такие библиотеки, как glibc и boost, предоставляют эту функциональность. [6] [7] В этих языках некоторые оптимизации компилятора могут влиять на информацию стека вызовов, которую можно восстановить во время выполнения. Например, встраивание может привести к отсутствию кадров стека, оптимизация хвостового вызова может заменить один кадр стека другим, а удаление указателя кадра может помешать инструментам анализа стека вызовов правильно интерпретировать содержимое стека вызовов. [6]
Например, glibc backtrace()
Функция возвращает вывод с функцией программы и адресом памяти.
./a.out() [0x40067f]
./a.out() [0x4006fe]
./a.out() [0x40070a]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7f7e60738f45]
./a.out() [0x400599]
Начиная с C++23 , трассировки стека можно сбрасывать вручную, печатая значение, возвращаемое статической функцией-членом. std::stacktrace::current()
: [8]
std::cout << std::stacktrace::current() << '\n';
Ржавчина
[ редактировать ]В Rust есть два типа ошибок. паники Функции, использующие макрос , «невосстановимы», и текущий поток станет отравленным из-за разматывания стека. Функции, которые возвращают std::result::Result
являются «восстанавливаемыми» и с ними можно обращаться корректно. [9] Однако исправимые ошибки не могут генерировать трассировку стека, поскольку они добавляются вручную, а не являются результатом ошибки времени выполнения.
По состоянию на июнь 2021 года в Rust имеется экспериментальная поддержка трассировки стека при неисправимых ошибках. Rust поддерживает вывод на поток stderr , когда поток паникует, но это необходимо включить, установив параметр RUST_BACKTRACE
переменная среды . [10]
Когда эта функция включена, такие обратные трассировки выглядят так, как показано ниже: сначала самый последний вызов.
thread 'main' panicked at 'execute_to_panic', main.rs:3
stack backtrace:
0: std::sys::imp::backtrace::tracing::imp::unwind_backtrace
1: std::panicking::default_hook::{{closure}}
2: std::panicking::default_hook
3: std::panicking::rust_panic_with_hook
4: std::panicking::begin_panic
5: futures::task_impl::with
6: futures::task_impl::park
...
См. также
[ редактировать ]Ссылки
[ редактировать ]- ^ «Руководство по libc: обратные трассировки» . gnu.org . Проверено 8 июля 2014 г.
- ^ «traceback — Распечатать или получить обратную трассировку стека» . python.org . Проверено 8 июля 2014 г.
- ^ «Поток (Java SE 16 и JDK 16)» . Стандартная версия платформы Java и пакет разработки Java версии 16. Спецификация API . 04.03.2021 . Проверено 4 июля 2021 г.
- ^ «Свойство Environment.StackTrace (система)» . Документы Майкрософт . 07.05.2021 . Проверено 4 июля 2021 г.
- ^ «Поток (платформа Java SE 8)» . docs.oracle.com . Проверено 15 июня 2021 г.
- ^ Jump up to: а б «Обратные трассировки (Библиотека GNU C)» . www.gnu.org . Проверено 15 июня 2021 г.
- ^ «Начало работы — 1.76.0» . www.boost.org . Проверено 15 июня 2021 г.
- ^ «Рабочий проект стандарта языка программирования C++» (PDF) . open-std.org . ИСО/МЭК. 2021-10-23. п. 766.
- ^ "размотка рустономикона - Rust" . doc.rust-lang.org .
- ^ "std::backtrace — Rust" . doc.rust-lang.org . Проверено 15 июня 2021 г.