Spec-Zone.ru › Julia 0.6

Стек-трейсы

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

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

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

julia> stacktrace()
4-element Array{StackFrame,1}:
 eval(::Module, ::Any) at boot.jl:236
 eval_user_input(::Any, ::Base.REPL.REPLBackend) at REPL.jl:66
 macro expansion at REPL.jl:97 [inlined]
 (::Base.REPL.##1#2{Base.REPL.REPLBackend})() at event.jl:73

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

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

julia> example()
5-element Array{StackFrame,1}:
 example() at REPL[1]:1
 eval(::Module, ::Any) at boot.jl:236
[...]

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()
7-element Array{StackFrame,1}:
 child() at REPL[3]:1
 parent() at REPL[4]:1
 grandparent() at REPL[5]: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}:
 example() at REPL[1]:1
 eval(::Module, ::Any) at boot.jl:236
 eval_user_input(::Any, ::Base.REPL.REPLBackend) at REPL.jl:66
 macro expansion at REPL.jl:97 [inlined]
 (::Base.REPL.##1#2{Base.REPL.REPLBackend})() at event.jl:73

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

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

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

julia> top_frame.func
:eval

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

julia> top_frame.line
236

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

julia> top_frame.inlined
false

julia> top_frame.from_c
false
julia> top_frame.pointer
0x00007f390d152a59

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

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

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

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()
5-element Array{StackFrame,1}:
 example() at REPL[2]:4
 eval(::Module, ::Any) at boot.jl:236
[...]

Вы можете заметить, что в приведённом выше примере первый фрейм стека указывает на строку 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()
6-element Array{StackFrame,1}:
 bad_function() at REPL[1]:1
 example() at REPL[2]: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!
7-element Array{StackFrame,1}:
 child() at REPL[1]:1
 parent() at REPL[2]:1
 grandparent() at REPL[3]:3
[...]

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

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

julia> trace = backtrace()
21-element Array{Ptr{Void},1}:
 Ptr{Void} @0x00007f10049d5b2f
 Ptr{Void} @0x00007f0ffeb4d29c
 Ptr{Void} @0x00007f0ffeb4d2a9
 Ptr{Void} @0x00007f1004993fe7
 Ptr{Void} @0x00007f10049a92be
 Ptr{Void} @0x00007f10049a823a
 Ptr{Void} @0x00007f10049a9fb0
 Ptr{Void} @0x00007f10049aa718
 Ptr{Void} @0x00007f10049c0d5e
 Ptr{Void} @0x00007f10049a3286
 Ptr{Void} @0x00007f0ffe9ba3ba
 Ptr{Void} @0x00007f0ffe9ba3d0
 Ptr{Void} @0x00007f1004993fe7
 Ptr{Void} @0x00007f0ded34583d
 Ptr{Void} @0x00007f0ded345a87
 Ptr{Void} @0x00007f1004993fe7
 Ptr{Void} @0x00007f0ded34308f
 Ptr{Void} @0x00007f0ded343320
 Ptr{Void} @0x00007f1004993fe7
 Ptr{Void} @0x00007f10049aeb67
 Ptr{Void} @0x0000000000000000

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

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

julia> stacktrace(trace, true)
27-element Array{StackFrame,1}:
 jl_backtrace_from_here at stackwalk.c:103
 backtrace() at error.jl:46
 backtrace() at sys.so:?
 jl_call_method_internal at julia_internal.h:248 [inlined]
 jl_apply_generic at gf.c:2215
 do_call at interpreter.c:75
 eval at interpreter.c:215
 eval_body at interpreter.c:519
 jl_interpret_toplevel_thunk at interpreter.c:664
 jl_toplevel_eval_flex at toplevel.c:592
 jl_toplevel_eval_in at builtins.c:614
 eval(::Module, ::Any) at boot.jl:236
 eval(::Module, ::Any) at sys.so:?
 jl_call_method_internal at julia_internal.h:248 [inlined]
 jl_apply_generic at gf.c:2215
 eval_user_input(::Any, ::Base.REPL.REPLBackend) at REPL.jl:66
 ip:0x7f1c707f1846
 jl_call_method_internal at julia_internal.h:248 [inlined]
 jl_apply_generic at gf.c:2215
 macro expansion at REPL.jl:97 [inlined]
 (::Base.REPL.##1#2{Base.REPL.REPLBackend})() at event.jl:73
 ip:0x7f1c707ea1ef
 jl_call_method_internal at julia_internal.h:248 [inlined]
 jl_apply_generic at gf.c:2215
 jl_apply at julia.h:1411 [inlined]
 start_task at task.c:261
 ip:0xffffffffffffffff

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

julia> pointer = backtrace()[1];

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

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.6/manual/stacktraces/

Spec-Zone.ru

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