Engee 文档

堆栈跟踪

"StackTraces"模块提供简单的堆栈跟踪,这些跟踪具有用户友好的格式,并且易于以编程方式使用。

查看堆栈跟踪

用于获取堆栈跟踪的主要函数是 'stacktrace'

6-element Array{Base.StackTraces.StackFrame,1}:
 top-level scope
 eval at boot.jl:317 [inlined]
 eval(::Module, ::Expr) at REPL.jl:5
 eval_user_input(::Any, ::REPL.REPLBackend) at REPL.jl:85
 macro expansion at REPL.jl:116 [inlined]
 (::getfield(REPL, Symbol("##28#29")){REPL.REPLBackend})() в event.jl:92

调用函数时 stacktrace()'返回类型的向量 'StackTraces.StackFrame'。 为简单起见,而不是'+Vector{StackFrame}+'你可以使用别名 'StackTraces.StackTrace'。 (带有+[的例子。..]+'表示输出可能因代码的执行方式而异。)

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

julia> example()
7-element Array{Base.StackTraces.StackFrame,1}:
 example() at REPL[1]:1
 top-level scope
 eval at boot.jl:317 [inlined]
[...]

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()
9-element Array{Base.StackTraces.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)

朱莉娅>例子()
7元素数组{Base.StackTraces.StackFrame,1}:
 例()在REPL[1]:1
 顶级范围
 启动时评估。jl:317[内联]
 EVAL(::Module,::Expr)at REPL.jl:5
 eval_user_input(::Any,::REPL.REPLBackend)在REPL。jl:85
 在REPL进行宏观扩展。jl:116[内联]
 (::getfield(REPL,符号("##28#29")){REPL.REPLBackend})()在事件中。jl:92

提取有用信息

每种类型 'StackTraces.StackFrame'包含函数名、文件名、行号、lambda信息、指示是否嵌入帧的标志、指示是否为C函数的标志(默认情况下,c函数不会出现在堆栈跟踪中)以及函数返回的指针的整 '回溯'

julia> frame = stacktrace()[3]
eval(::Module, ::Expr) at REPL.jl:5

julia> frame.func
:eval

julia> frame.file
Symbol("~/julia/usr/share/julia/stdlib/v0.7/REPL/src/REPL.jl")

julia> frame.line
5

julia> frame.linfo
MethodInstance for eval(::Module, ::Expr)

julia> frame.inlined
false

julia> frame.from_c
false

julia> frame.pointer
0x00007f92d6293171

可以以编程方式获取堆栈跟踪信息,用于日志记录,错误处理等。

错误处理

虽然轻松访问有关调用堆栈当前状态的信息在许多地方都很有用,但其最明显的应用是错误处理和调试。

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()
7-element Array{Base.StackTraces.StackFrame,1}:
 example() at REPL[2]:4
 top-level scope
 eval at boot.jl:317 [inlined]
[...]

您可以看到在上面的示例中,第一个堆栈帧指向第4行,其中调用了函数。 'stacktrace',而不是第2行,其中调用_bad_function_,并且`bad_function’函数的框架完全缺失。 这是可以理解的,因为功能 'stacktrace'从_hook_(catch)的上下文中调用。 虽然在这个例子中很容易找到错误的实际来源,但在复杂的情况下,跟踪错误来源成为一项非平凡的任务。

这可以通过将结果传递给函数来修复 `catch_backtrace'函数 'stacktrace'。 而不是返回有关当前上下文的调用堆栈的信息,函数 `catch_backtrace'返回最近异常上下文的堆栈信息。

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

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

julia> example()
8-element Array{Base.StackTraces.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)
               stacktrace(catch_backtrace())
           end
       end
grandparent (generic function with 1 method)

julia> grandparent()
ERROR: Whoops!
10-element Array{Base.StackTraces.StackFrame,1}:
 error at error.jl:33 [inlined]
 child() at REPL[1]:1
 parent() at REPL[2]:1
 grandparent() at REPL[3]:3
[...]

异常堆栈和 '当前_exceptions'

兼容性:Julia1.1

异常堆栈需要Julia至少1.1的版本。

在处理一个异常时,可能会发生其他异常。 建议检查所有这些异常以确定问题的根本原因。 Julia运行时通过将发生的每个异常发送到内部异常堆栈来支持此功能。 当代码以通常的方式退出`catch`时,在关联的`try’中发送到堆栈的所有异常都被视为成功处理并从堆栈中删除。

可以使用函数访问当前异常堆栈 'current_exceptions'。 例如,

julia> try
           error("(A) The root cause")
       catch
           try
               error("(B) An exception while handling the exception")
           catch
               for (exc, bt) in current_exceptions()
                   showerror(stdout, exc, bt)
                   println(stdout)
               end
           end
       end
(A) The root cause
Stacktrace:
 [1] error(::String) at error.jl:33
 [2] top-level scope at REPL[7]:2
 [3] eval(::Module, ::Any) at boot.jl:319
 [4] eval_user_input(::Any, ::REPL.REPLBackend) at REPL.jl:85
 [5] macro expansion at REPL.jl:117 [inlined]
 [6] (::getfield(REPL, Symbol("##26#27")){REPL.REPLBackend})() в task.jl:259
(B) An exception while handling the exception
Stacktrace:
 [1] error(::String) at error.jl:33
 [2] top-level scope at REPL[7]:5
 [3] eval(::Module, ::Any) at boot.jl:319
 [4] eval_user_input(::Any, ::REPL.REPLBackend) at REPL.jl:85
 [5] macro expansion at REPL.jl:117 [inlined]
 [6] (::getfield(REPL, Symbol("##26#27")){REPL.REPLBackend})() в task.jl:259

在此示例中,根本原因异常(A)是堆栈上的第一个异常,后跟另一个异常(B)。 在两个catch块都正确退出(即没有额外的异常)后,所有异常都将从堆栈中删除并变得不可用。

异常堆栈存储在发生异常的任务中。 当任务失败且未捕获异常时,'current_exceptions(task)`函数可用于检查该任务的异常堆栈。

与之比较 '后退'

函数调用 backtrace'返回向量'+Union{Ptr{Nothing},基地。Interpretip}+,然后可以传递给函数 `stacktrace'用于转换:

julia> trace = backtrace()
18-element Array{Union{Ptr{Nothing}, Base.InterpreterIP},1}:
 Ptr{Nothing} @0x00007fd8734c6209
 Ptr{Nothing} @0x00007fd87362b342
 Ptr{Nothing} @0x00007fd87362c136
 Ptr{Nothing} @0x00007fd87362c986
 Ptr{Nothing} @0x00007fd87362d089
 Base.InterpreterIP(CodeInfo(:(begin
      Core.SSAValue(0) = backtrace()
      trace = Core.SSAValue(0)
      return Core.SSAValue(0)
  end)), 0x0000000000000000)
 Ptr{Nothing} @0x00007fd87362e4cf
[...]

julia> stacktrace(trace)
6-element Array{Base.StackTraces.StackFrame,1}:
 top-level scope
 eval at boot.jl:317 [inlined]
 eval(::Module, ::Expr) at REPL.jl:5
 eval_user_input(::Any, ::REPL.REPLBackend) at REPL.jl:85
 macro expansion at REPL.jl:116 [inlined]
 (::getfield(REPL, Symbol("##28#29")){REPL.REPLBackend})() в event.jl:92

注意函数返回的向量有 'backtrace',有18个元素,函数返回的向量 'stacktrace',只有6。 这是由于这样的事实,默认情况下,功能 'stacktrace'从堆栈中删除所有较低级别的C语言函数。 您可以从C调用中启用堆栈帧,如下所示:

julia> stacktrace(trace, true)
21-element Array{Base.StackTraces.StackFrame,1}:
 jl_apply_generic at gf.c:2167
 do_call at interpreter.c:324
 eval_value at interpreter.c:416
 eval_body at interpreter.c:559
 jl_interpret_toplevel_thunk_callback at interpreter.c:798
 top-level scope
 jl_interpret_toplevel_thunk at interpreter.c:807
 jl_toplevel_eval_flex at toplevel.c:856
 jl_toplevel_eval_in at builtins.c:624
 eval at boot.jl:317 [inlined]
 eval(::Module, ::Expr) at REPL.jl:5
 jl_apply_generic at gf.c:2167
 eval_user_input(::Any, ::REPL.REPLBackend) at REPL.jl:85
 jl_apply_generic at gf.c:2167
 macro expansion at REPL.jl:116 [inlined]
 (::getfield(REPL, Symbol("##28#29")){REPL.REPLBackend})() в event.jl:92
 jl_fptr_trampoline at gf.c:1838
 jl_apply_generic at gf.c:2167
 jl_apply at julia.h:1540 [inlined]
 start_task at task.c:268
 ip:0xffffffffffffffff

函数返回的单个指针 'backtrace',可以转换为类型 'StackTraces.StackFrame'通过将它们传递给函数 'StackTraces.查找'

julia> pointer = backtrace()[1];

julia> frame = StackTraces.lookup(pointer)
1-element Array{Base.StackTraces.StackFrame,1}:
 jl_apply_generic at gf.c:2167

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