Skip to content

Commit

Permalink
Support ascend(err) (#380)
Browse files Browse the repository at this point in the history
Julia now has the global REPL varible `err`, and it's nice to be
able to `ascend(err)`. This seems particularly handy for
debugging MethodErrors as `ascend`/`descend` make the
types visible.
  • Loading branch information
timholy authored Mar 12, 2023
1 parent e7f8ed3 commit fa73928
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "Cthulhu"
uuid = "f68482b8-f384-11e8-15f7-abe071a5a75f"
authors = ["Valentin Churavy <[email protected]>"]
version = "2.8.2"
version = "2.8.3"

[deps]
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
Expand Down
1 change: 1 addition & 0 deletions src/Cthulhu.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 20 additions & 1 deletion src/backedges.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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,))
Expand Down Expand Up @@ -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
1 change: 1 addition & 0 deletions src/callsite.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
45 changes: 45 additions & 0 deletions test/test_terminal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

2 comments on commit fa73928

@timholy
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/79437

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v2.8.3 -m "<description of version>" fa73928b0a650ec864f45c737d1c03cde51c9f97
git push origin v2.8.3

Please sign in to comment.