gdb调试技巧
显示Julia变量
内 gdb,gdb,任何 jl_value_t* 对象 反对 可以使用显示
(gdb) call jl_(obj)
对象将显示在 朱莉娅 会话,而不是在gdb会话中。 这是一个有用的方法来发现被Julia的C代码操纵的对象的类型和值。
同样,如果你正在调试Julia的一些内部(例如, 编译器。jl),可以打印 反对 使用
ccall(:jl_, Cvoid, (Any,), obj)
这是一个很好的方法来规避由julia的输出流初始化顺序引起的问题。
Julia的flisp解释器使用 价值_t 对象;这些可以用 调用fl_print(fl_ctx,ios_stdout,obj).
用于检查的有用Julia变量
虽然许多变量(如单例)的地址对于许多故障的打印非常有用,但还有许多其他变量(请参阅 朱莉娅。h 对于一个完整的列表),甚至更有用。
*(当在 jl_应用程序) mfunc 和 jl_uncompress_ast(mfunc->def,mfunc->code) ::为了弄清楚一些关于调用堆栈
* jl_列尼诺 和 jl_filename ::用于确定测试中的哪一行开始调试(或确定文件被解析了多远)
* $1 ::不是真正的变量,但仍然是引用最后一个gdb命令结果的有用简写(例如 印刷业)
* jl_选项 ::有时很有用,因为它列出了成功解析的所有命令行选项
* jl_uv_stderr ::因为谁不喜欢能够与stdio互动
用于检查这些变量的有用Julia函数
* jl_print_task_backtraces(0) ::类似于gdb的 线程应用所有bt 或lldb的 所有线程回溯. 运行所有线程,同时打印所有现有任务的回溯。
* jl_gdblookup($pc) ::用于查找当前函数和行。
* jl_gdblookupinfo($pc) ::用于查找当前方法实例对象。
* jl_gdbdumpcode(mi) ::倾销所有 code_typed/code_llvm/code_asm 当REPL不能正常工作时。
* jlbacktrace() ::用于将当前的Julia backtrace堆栈转储到stderr。 只有在之后才能使用 记录_backtrace() 已经被召唤了。
* jl_dump_llvm_value(值*) ::用于调用 值->转储() 在gdb中,它本身不起作用。 例如, f->linfo->functionObject, f->linfo->specFunctionObject,而 to_function(f->linfo).
* jl_dump_llvm_module(模块*) ::用于调用 模块->转储() 在gdb中,它本身不起作用。
* 类型->转储() ::仅适用于lldb。 注意:添加类似的东西 ;1 防止lldb在输出上打印其提示
* jl_eval_string("expr") ::用于调用副作用来修改当前状态或查找符号
* jl_typeof(jl_value_t*) ::用于提取Julia值的类型标签(在gdb中,调用 宏定义jl_typeof jl_typeof 首先,或者选择一些简短的东西,比如 泰 对于第一个arg定义一个简写)
插入断点以从gdb进行检查
在你的 gdb,gdb 会话,在 jl_breakpoint 像这样:
(gdb) break jl_breakpoint
然后在你的Julia代码中,插入一个调用 jl_breakpoint 通过添加
ccall(:jl_breakpoint, Cvoid, (Any,), obj)
哪里 反对 可以是您希望在断点中可访问的任何变量或元组。
备份到 jl_应用 框架,您可以从中显示函数的参数,例如,
(gdb) call jl_(args[0])
另一个有用的框架是 to_function(jl_method_instance_t*li,bool cstyle). 该 jl_method_instance_t* 参数是一个结构体,引用发送到编译器的最终AST。 但是,此时的AST通常会被压缩;要查看AST,请调用 jl_uncompress_ast 然后将结果传递给 jl_:
#2 0x00007ffff7928bf7 in to_function (li=0x2812060, cstyle=false) at codegen.cpp:584 584 abort(); (gdb) p jl_(jl_uncompress_ast(li, li->ast))
处理信号
Julia需要一些信号才能正常工作。 分析器使用 SIGUSR2 用于采样和垃圾收集器使用 [医]乙状结肠 用于线程同步。 如果您正在调试一些使用探查器或多个线程的代码,则可能希望让调试器忽略这些信号,因为它们可以在正常操作期间经常触发。 在GDB中执行此操作的命令是(替换 [医]乙状结肠 与 SIGUSR2 或其他你想忽略的信号):
(gdb) handle SIGSEGV noprint nostop pass
对应的LLDB命令是(进程启动后):
(lldb) pro hand -p true -s false -n false SIGSEGV
如果您正在使用线程代码调试segfault,则可以在以下位置设置断点 jl_critical_error (西格迪*汉德勒 也应该在Linux和BSD上工作),以便只捕获实际的segfault而不是GC同步点。
Julia构建过程中的调试(bootstrap)
期间发生的错误 使 需要特殊处理。 茱莉亚分两个阶段建造 系统0 和 系统。纪. 要查看故障时正在运行的命令,请使用 使详细=1.
在撰写本文时,您可以在 系统0 阶段从 基地 使用目录:
julia/base$ gdb --args ../usr/bin/julia-debug -C native --build ../usr/lib/julia/sys0 sysimg.jl
您可能需要删除其中的所有文件 usr/lib/julia/ 让这个工作起来。
您可以调试 系统。纪 阶段使用:
julia/base$ gdb --args ../usr/bin/julia-debug -C native --build ../usr/lib/julia/sys -J ../usr/lib/julia/sys0.ji sysimg.jl
默认情况下,任何错误都会导致Julia退出,即使在gdb下也是如此。 要捕获"在行为中"的错误,请在 jl_error (对于特定类型的故障,还有其他几个有用的点,包括: jl_too_few_args, jl_too_many_args,而 jl_成长).
一旦捕获了错误,一个有用的技术是遍历堆栈并通过检查相关的调用来检查函数 jl_应用. 以现实世界为例:
Breakpoint 1, jl_throw (e=0x7ffdf42de400) at task.c:802
802 {
(gdb) p jl_(e)
ErrorException("auto_unbox: unable to determine argument type")
$2 = void
(gdb) bt 10
#0 jl_throw (e=0x7ffdf42de400) at task.c:802
#1 0x00007ffff65412fe in jl_error (str=0x7ffde56be000 <_j_str267> "auto_unbox:
unable to determine argument type")
at builtins.c:39
#2 0x00007ffde56bd01a in julia_convert_16886 ()
#3 0x00007ffff6541154 in jl_apply (f=0x7ffdf367f630, args=0x7fffffffc2b0, nargs=2) at julia.h:1281
...
最新的 jl_应用 是在第3帧,所以我们可以回到那里,看看ast的功能 julia_convert_16886. 这是一些方法的唯一名称 转换/转换. f 在这个框架是一个 jl_功能_t*,所以我们可以看看类型签名,如果有的话,从 眼镜型 领域:
(gdb) f 3
#3 0x00007ffff6541154 in jl_apply (f=0x7ffdf367f630, args=0x7fffffffc2b0, nargs=2) at julia.h:1281
1281 return f->fptr((jl_value_t*)f, args, nargs);
(gdb) p f->linfo->specTypes
$4 = (jl_tupletype_t *) 0x7ffdf39b1030
(gdb) p jl_( f->linfo->specTypes )
Tuple{Type{Float32}, Float64} # <-- type signature for julia_convert_16886
然后,我们可以看看这个函数的AST:
(gdb) p jl_( jl_uncompress_ast(f->linfo, f->linfo->ast) )
Expr(:lambda, Array{Any, 1}[:#s29, :x], Array{Any, 1}[Array{Any, 1}[], Array{Any, 1}[Array{Any, 1}[:#s29, :Any, 0], Array{Any, 1}[:x, :Any, 0]], Array{Any, 1}[], 0], Expr(:body,
Expr(:line, 90, :float.jl)::Any,
Expr(:return, Expr(:call, :box, :Float32, Expr(:call, :fptrunc, :Float32, :x)::Any)::Any)::Any)::Any)::Any
最后,也许最有用的是,我们可以强制重新编译函数,以便逐步完成codegen过程。 为此,请清除缓存 函数对象 从 jl_lamdbda_info_t*:
(gdb) p f->linfo->functionObject $8 = (void *) 0x1289d070 (gdb) set f->linfo->functionObject = NULL
然后,在有用的地方设置断点(例如 emit_函数, emit_expr, emit_call 等。),并运行codegen:
(gdb) p jl_compile(f) ... # your breakpoint here