Spec-Zone.ru › Julia 0.5

Стек-трейсы

Модуль StackTraces предоставляет простые стек-трейсы, которые легко читать и использовать программно.

Просмотр стек-трейса

Основная функция для получения стек-трейса — stacktrace():

julia> stacktrace()
3-element Array{StackFrame,1}:
 eval at boot.jl:265
 [inlined code from REPL.jl:3] eval_user_input at REPL.jl:62
 [inlined code from REPL.jl:92] anonymous at task.jl:63

Вызов stacktrace() возвращает вектор StackFrame элементов. Для удобства используется псевдоним StackTrace вместо Vector{StackFrame}. (Примеры с ... показывают, что вывод может различаться в зависимости от способа выполнения кода.)

julia> example() = stacktrace()
example (generic function with 1 method)

julia> example()
6-element Array{StackFrame,1}:
  in example() at none:1
  in eval(::Module, ::Any) at boot.jl:234
  in eval_user_input(::Any, ::Bool) at client.jl:117
  in eval(::Module, ::Any) at boot.jl:234
  in eval_user_input(::Any, ::Bool) at client.jl:117
  in _start() at client.jl:355

julia> @noinline child() = stacktrace()
child (generic function with 1 method)

julia> @noinline parent() = child()
parent (generic function with 1 method)

julia> grandparent() = parent()
grandparent (generic function with 1 method)

julia> grandparent()
8-element Array{StackFrame,1}:
  in child() at none:1
  in parent() at none:1
  in grandparent() at none:1
  ...

Обратите внимание, что при вызове stacktrace() вы обычно увидите фрейм с eval(...) at boot.jl. При вызове stacktrace() из REPL у вас также будет несколько дополнительных фреймов стека из REPL.jl, обычно выглядящих примерно так:

julia> example() = stacktrace()
example (generic function with 1 method)

julia> example()
5-element Array{StackFrame,1}:
  in example() at REPL[1]:1
  in eval(::Module, ::Any) at boot.jl:234
  in eval_user_input(::Any, ::Base.REPL.REPLBackend) at REPL.jl:62
  in macro expansion at REPL.jl:92 [inlined]
  in (::Base.REPL.##1#2{Base.REPL.REPLBackend})() at event.jl:46

Извлечение полезной информации

Каждый StackFrame содержит имя функции, имя файла, номер строки, информацию о лямбда-выражении, флаг, указывающий, был ли фрейм инлайнирован, флаг, указывающий, является ли это C-функция (по умолчанию C-функции не отображаются в стек-трейсе), и целое число, представляющее указатель, возвращённый backtrace():

julia> top_frame = stacktrace()[1]
 in eval(::Module, ::Any) at boot.jl:234

julia> top_frame.func
:eval

julia> top_frame.file
Symbol("./boot.jl")

julia> top_frame.line
234

julia> top_frame.linfo
Nullable{LambdaInfo}(LambdaInfo for eval(::Module, ::Any))

julia> top_frame.inlined
false

julia> top_frame.from_c
false
julia> top_frame.pointer
13203085684

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

Обработка ошибок

Легкий доступ к информации о текущем состоянии стека вызовов полезен во многих местах, но наиболее очевидное применение — обработка ошибок и отладка.

julia> @noinline bad_function() = undeclared_variable
bad_function (generic function with 1 method)

julia> @noinline example() = try
           bad_function()
       catch
           stacktrace()
       end
example (generic function with 1 method)

julia> example()
6-element Array{StackFrame,1}:
  in example() at none:4
  in eval(::Module, ::Any) at boot.jl:234
  ...

Вы можете заметить, что в приведённом выше примере первый фрейм стека указывает на строку 4, где вызывается stacktrace(), а не на строку 2, где вызывается bad_function, и фрейм bad_function отсутствует. Это объяснимо, так как stacktrace() вызывается из контекста catch. Хотя в данном примере найти фактическую причину ошибки довольно легко, в сложных случаях отслеживание источника ошибки становится нетривиальной задачей.

Это можно исправить, вызвав catch_stacktrace() вместо stacktrace(). Вместо возвращения информации о стеке вызовов для текущего контекста, catch_stacktrace() возвращает информацию о стеке для контекста последней возникшей ошибки:

julia> @noinline bad_function() = undeclared_variable
bad_function (generic function with 1 method)

julia> @noinline example() = try
           bad_function()
       catch
           catch_stacktrace()
       end
example (generic function with 1 method)

julia> example()
7-element Array{StackFrame,1}:
  in bad_function() at none:1
  in example() at none:2
  ...

Обратите внимание, что стек-трейс теперь указывает на соответствующий номер строки и отсутствующий фрейм.

julia> @noinline child() = error("Whoops!")
child (generic function with 1 method)

julia> @noinline parent() = child()
parent (generic function with 1 method)

julia> @noinline function grandparent()
           try
               parent()
           catch err
               println("ERROR: ", err.msg)
               catch_stacktrace()
           end
       end
grandparent (generic function with 1 method)

julia> grandparent()
ERROR: Whoops!
8-element Array{StackFrame,1}:
  in child() at none:1
  in parent() at none:1
  in grandparent() at none:3
  ...

Сравнение с backtrace()

Вызов backtrace() возвращает вектор Ptr{Void}, который затем можно передать в stacktrace() для преобразования:

julia> trace = backtrace()
20-element Array{Ptr{Void},1}:
 Ptr{Void} @0x0000000100a26fc2
 Ptr{Void} @0x00000001029435df
 Ptr{Void} @0x0000000102943635
 Ptr{Void} @0x00000001009e9620
 Ptr{Void} @0x00000001009fe1e8
 Ptr{Void} @0x00000001009fc7b6
 Ptr{Void} @0x00000001009fdae3
 Ptr{Void} @0x00000001009fe0d2
 Ptr{Void} @0x0000000100a1321b
 Ptr{Void} @0x00000001009f64e7
 Ptr{Void} @0x000000010265ac5d
 Ptr{Void} @0x000000010265acc1
 Ptr{Void} @0x00000001009e9620
 Ptr{Void} @0x000000031007744b
 Ptr{Void} @0x0000000310077537
 Ptr{Void} @0x00000001009e9620
 Ptr{Void} @0x000000031006feec
 Ptr{Void} @0x00000003100701b0
 Ptr{Void} @0x00000001009e9635
 Ptr{Void} @0x0000000100a06418

julia> stacktrace(trace)
5-element Array{StackFrame,1}:
  in backtrace() at error.jl:26
  in eval(::Module, ::Any) at boot.jl:231
  in eval_user_input(::Any, ::Base.REPL.REPLBackend) at REPL.jl:62
  in macro expansion at REPL.jl:92 [inlined]
  in (::Base.REPL.##1#2{Base.REPL.REPLBackend})() at event.jl:46

Обратите внимание, что вектор, возвращённый backtrace(), содержал 15 указателей, в то время как вектор, возвращённый stacktrace(), содержит только 4. Это происходит потому, что по умолчанию stacktrace() удаляет все фреймы C-функций из стека. Если вам нужно включить фреймы стека из вызовов C, можно сделать это так:

julia> stacktrace(trace, true)
26-element Array{StackFrame,1}:
  in jl_backtrace_from_here at stackwalk.c:104
  in backtrace() at error.jl:26
  in ip:0x102943635
  in jl_call_method_internal at julia_internal.h:86 [inlined]
  in jl_apply_generic at gf.c:1805
  in do_call at interpreter.c:65
  in eval at interpreter.c:188
  in eval_body at interpreter.c:469
  in jl_interpret_call at interpreter.c:573
  in jl_toplevel_eval_flex at toplevel.c:543
  in jl_toplevel_eval_in_warn at builtins.c:571
  in eval(::Module, ::Any) at boot.jl:231
  in ip:0x10265acc1
  in jl_call_method_internal at julia_internal.h:86 [inlined]
  in jl_apply_generic at gf.c:1805
  in eval_user_input(::Any, ::Base.REPL.REPLBackend) at REPL.jl:62
  in ip:0x310077537
  in jl_call_method_internal at julia_internal.h:86 [inlined]
  in jl_apply_generic at gf.c:1805
  in macro expansion at REPL.jl:92 [inlined]
  in (::Base.REPL.##1#2{Base.REPL.REPLBackend})() at event.jl:46
  in ip:0x3100701b0
  in jl_call_method_internal at julia_internal.h:86 [inlined]
  in jl_apply_generic at gf.c:1795
  in jl_apply at julia.h:1388 [inlined]
  in start_task at task.c:247

Индивидуальные указатели, возвращаемые backtrace(), можно преобразовать в StackFrame элементы, передав их в StackTraces.lookup():

julia> pointer = backtrace()[1];

julia> frame = StackTraces.lookup(pointer)
1-element Array{StackFrame,1}:
  in jl_backtrace_from_here at stackwalk.c:105

julia> println("The top frame is from $(frame[1].func)!")
The top frame is from jl_backtrace_from_here!

© 2009–2016 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors
Licensed under the MIT License.
https://docs.julialang.org/en/release-0.5/manual/stacktraces/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API