Трассировки стека - Stack trace
В вычислении , А трассировки стека (также называемый стек трассировку или стек отслеживающий ) представляет собой отчет из активных кадров стека в определенный момент времени во время выполнения программы . Когда программа запускается, память часто динамически выделяется в двух местах; стека и кучи . Память постоянно выделяется в стеке, но не в куче, что отражает их имена. Стек также относится к программной конструкции, поэтому для отличия этот стек называется стеком вызовов функций программы . Технически, как только блок памяти был выделен в стеке, его нелегко удалить, поскольку могут быть другие блоки памяти, которые были выделены до него. Каждый раз, когда функция вызывается в программе, блок памяти, называемый записью активации, выделяется поверх стека вызовов. Обычно запись активации хранит аргументы функции и локальные переменные. Что именно он содержит и как он расположен, определяется соглашением о вызовах .
Программисты обычно используют трассировку стека во время интерактивной и посмертной отладки . Конечные пользователи могут видеть трассировку стека, отображаемую как часть сообщения об ошибке , о котором пользователь может затем сообщить программисту.
Трассировка стека позволяет отслеживать последовательность вызываемых вложенных функций - до точки, где создается трассировка стека. В посмертном сценарии это распространяется на функцию, в которой произошел сбой (но не обязательно). Вызовы одного уровня не отображаются в трассировке стека.
Языковая поддержка
Многие языки программирования, включая Java и C # , имеют встроенную поддержку для получения текущей трассировки стека с помощью системных вызовов. C ++ не имеет встроенной поддержки для этого, но пользователи C ++ могут извлекать трассировки стека с помощью (например) библиотеки stacktrace . В JavaScript , исключение держать свойство , которое содержит стек от того места , где он был брошен.
stack
Python
В качестве примера следующая программа 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()
используя следующие входные данные:
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, и C ++ не имеют встроенной поддержки для получения трассировки стека, но библиотеки, такие как glibc и boost, предоставляют эту функцию. В этих языках некоторые оптимизации компилятора могут мешать информации стека вызовов, которую можно восстановить во время выполнения. Например, встраивание может привести к отсутствию фреймов стека, оптимизация хвостовых вызовов может заменить один фрейм стека другим, а исключение указателя фрейма может помешать инструментам анализа стека вызовов правильно интерпретировать содержимое стека вызовов.
Например, backtrace()
функция glibc возвращает вывод с программной функцией и адресом памяти.
./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]
Ржавчина
В Rust есть два типа ошибок. Функции, использующие макрос паники , «невозможно восстановить», и текущий поток станет отравленным из-за раскручивания стека. Функции, возвращающие a std::result::Result
, «восстанавливаемы» и могут обрабатываться изящно. Однако исправимые ошибки не могут генерировать трассировку стека, поскольку они добавляются вручную, а не являются результатом ошибки времени выполнения.
По состоянию на июнь 2021 года в Rust появилась экспериментальная поддержка трассировки стека при неисправимых ошибках. Rust поддерживает печать в stderr при возникновении паники в потоке, но ее необходимо включить, задав RUST_BACKTRACE
переменную окружения .
Когда эта функция включена, такая обратная трассировка выглядит примерно так, как показано ниже, с самым последним вызовом в первую очередь.
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
...