diff --git a/Project.toml b/Project.toml index 6adaa4ba..b794a386 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Cthulhu" uuid = "f68482b8-f384-11e8-15f7-abe071a5a75f" authors = ["Valentin Churavy "] -version = "2.8.2" +version = "2.8.3" [deps] CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2" diff --git a/src/Cthulhu.jl b/src/Cthulhu.jl index 3cc45c48..94027c57 100644 --- a/src/Cthulhu.jl +++ b/src/Cthulhu.jl @@ -791,6 +791,7 @@ FoldingTrees.writeoption(buf::IO, data::Data, charsused::Int) = FoldingTrees.wri function ascend(term, mi; interp::AbstractInterpreter=NativeInterpreter(), kwargs...) root = treelist(mi) + root === nothing && return menu = TreeMenu(root) choice = menu.current while choice !== nothing diff --git a/src/backedges.jl b/src/backedges.jl index 0a089a64..ae0ebe89 100644 --- a/src/backedges.jl +++ b/src/backedges.jl @@ -66,7 +66,7 @@ const _emptybackedges = MethodInstance[] struct IPFrames sfs::Vector{StackTraces.StackFrame} end -function buildframes(bt) +function buildframes(bt::Vector{Union{Ptr{Nothing}, Base.InterpreterIP}}) ipframes = IPFrames[] for ip in bt sfs = Base.StackTraces.lookup(ip) @@ -78,6 +78,15 @@ function buildframes(bt) end return ipframes end +function buildframes(st::Vector{StackTraces.StackFrame}) + ipframes = IPFrames[] + for sf in st + mi = sf.linfo + isa(mi, Core.MethodInstance) || continue + push!(ipframes, IPFrames([sf])) + end + return ipframes +end # Extension API backedges(mi::MethodInstance) = isdefined(mi, :backedges) ? mi.backedges : _emptybackedges @@ -88,6 +97,7 @@ nextnode(mi, edge) = edge instance(sfs::Vector{StackTraces.StackFrame}) = isempty(sfs) ? CC.Timings.ROOTmi : sfs[end].linfo::MethodInstance # we checked this type condition within `buildframes` method(sfs::Vector{StackTraces.StackFrame}) = method(instance(sfs)) +backedges(sframes::Vector{StackTraces.StackFrame}) = (ret = sframes[2:end]; isempty(ret) ? () : (ret,)) instance(ipframes::Vector{IPFrames}) = isempty(ipframes) ? CC.Timings.ROOTmi : instance(ipframes[1].sfs) backedges(ipframes::Vector{IPFrames}) = (ret = ipframes[2:end]; isempty(ret) ? () : (ret,)) @@ -131,3 +141,12 @@ end treelist!(::Node, ::IO, ::Nothing, ::AbstractString, ::Base.IdSet) = nothing treelist(bt::Vector{Union{Ptr{Nothing}, Base.InterpreterIP}}) = treelist(buildframes(bt)) +treelist(st::Vector{StackTraces.StackFrame}) = treelist(buildframes(st)) +function treelist(exstk::Base.ExceptionStack) + if length(exstk.stack) == 1 + return treelist(exstk.stack[1].backtrace) + end + # Don't error to avoid trashing `Main.err` + @error "exception stack contains $(length(exstk.stack)) exceptions, pick one with `ascend(err.stack[i].backtrace)`" + return nothing +end diff --git a/src/callsite.jl b/src/callsite.jl index 20509779..47120c35 100644 --- a/src/callsite.jl +++ b/src/callsite.jl @@ -540,5 +540,6 @@ function maybe_callsite(info::MultiCallInfo, callee::MethodInstance) return false end maybe_callsite(info::PureCallInfo, mi::MethodInstance) = mi.specTypes <: Tuple{mapany(Core.Typeof ∘ unwrapconst, info.argtypes)...} +maybe_callsite(info::RTCallInfo, mi::MethodInstance) = false unwrapconst(@nospecialize(arg)) = arg isa Core.Const ? arg.val : arg diff --git a/test/test_terminal.jl b/test/test_terminal.jl index afc2a048..d9ee8a1f 100644 --- a/test/test_terminal.jl +++ b/test/test_terminal.jl @@ -297,6 +297,51 @@ end write(in, 'q') wait(t) end + # With backtraces + bt = try + sum([]) + catch e + catch_backtrace() + end + fake_terminal() do term, in, out, _ + t = @async begin + @with_try_stderr out ascend(term, bt) + end + str = readuntil(out, 'v'; keep=true) + @test occursin(r"zero.*Type{Any}", str) + write(in, 'q') + wait(t) + end + # With stacktraces + st = try + sum([]) + catch e + stacktrace(catch_backtrace()) + end + fake_terminal() do term, in, out, _ + t = @async begin + @with_try_stderr out ascend(term, st) + end + str = readuntil(out, 'v'; keep=true) + @test occursin(r"zero.*Type{Any}", str) + write(in, 'q') + wait(t) + end + # With ExceptionStack (e.g., REPL's global `err` variable) + exstk = try + sum([]) + catch e + Base.ExceptionStack([(exception=e, backtrace=stacktrace(catch_backtrace()))]) + end + fake_terminal() do term, in, out, _ + t = @async begin + @with_try_stderr out ascend(term, exstk) + end + str = readuntil(out, 'v'; keep=true) + @test occursin(r"zero.*Type{Any}", str) + write(in, 'q') + wait(t) + end finally # Restore the previous settings for fn in fieldnames(Cthulhu.CthulhuConfig)