Skip to content

Commit

Permalink
Rebasing pains fixed
Browse files Browse the repository at this point in the history
  • Loading branch information
BioTurboNick committed Jul 9, 2023
1 parent 38ed30d commit b2e6301
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 56 deletions.
151 changes: 97 additions & 54 deletions base/errorshow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,91 @@ function find_external_frames(trace::Vector)
return is
end

is_ide_support(path) = false # replacable by IDE environment
is_repl(path) = startswith(path, r"(.[/\\])?REPL")
is_julia_dev(path) = contains(path, r"[/\\].julia[/\\]dev[/\\]")
is_julia(path) =
(startswith(path, r".[/\\]") && !is_repl(path)) ||
(contains(path, r"[/\\].julia[/\\]") && !is_julia_dev(path)) ||
contains(path, r"[/\\]julia[/\\]stdlib[/\\]")
is_broadcast(path) = startswith(path, r".[/\\]broadcast.jl")

# Process of identifying a visible frame:
# 1. Identify modules that should be included:
# - Include: All modules observed in backtrace
# - Exclude: Modules in Julia Base files, StdLibs, added packages, or registered by an IDE.
# - Include: Modules in ENV["JULIA_DEBUG"]
# - Exclude: Modules in ENV["JULIA_DEBUG"] that lead with `!`
# 2. Identify all frames in included modules
# 3. Include frames that have a file name matching ENV["JULIA_DEBUG"]
# 4. Exclude frames that have a file name matching ENV["JULIA_DEBUG"] that lead with `!`
# 5. This set of frames is considered user code.
# 6. Include the first frame above each contiguous set of user code frames to show what the user code called into.
# 7. To support broadcasting, identify any visible `materialize` frames, and include the first frame after
# the broadcast functions, to show what function is being broadcast.
# 8. Remove the topmost frame if it's a REPL toplevel.
# 9. Remove a broadcast materialize frame if it's the topmost frame.
function find_visible_frames(trace::Vector)
user_frames_i = findall(trace) do frame
file = String(frame[1].file)
!is_julia(file) && !is_ide_support(file)
end

# construct set of visible modules
all_modules = convert(Vector{Module}, filter!(!isnothing, unique(t[1] |> parentmodule for t trace)))
user_modules = convert(Vector{Module}, filter!(!isnothing, unique(t[1] |> parentmodule for t @view trace[user_frames_i])))
Main user_modules || push!(user_modules, Main)

debug_entries = split(get(ENV, "JULIA_DEBUG", ""), ",")
debug_include = filter(x -> !startswith(x, "!"), debug_entries)
debug_exclude = lstrip.(filter!(x -> startswith(x, "!"), debug_entries), '!')

debug_include_modules = filter(m -> string(m) debug_include, all_modules)
debug_exclude_modules = filter(m -> string(m) debug_exclude, all_modules)
setdiff!(union!(user_modules, debug_include_modules), debug_exclude_modules)

# construct set of visible frames
visible_frames_i = findall(trace) do frame
file = String(frame[1].file)
filenamebase = file |> basename |> splitext |> first
mod = parentmodule(frame[1])
return (mod user_modules || filenamebase debug_include) &&
!(filenamebase debug_exclude) ||
is_top_level_frame(frame[1]) && is_repl(file) ||
!is_julia(file) && !is_ide_support(file)
end

# add one additional frame above each contiguous set of user code frames, removing 0.
filter!(>(0), sort!(union!(visible_frames_i, visible_frames_i .- 1)))

# remove Main frames that originate from internal code (e.g. BenchmarkTools)
filter!(i -> parentmodule(trace[i][1]) != Main || !is_julia(string(trace[i][1].file)), visible_frames_i)

# for each appearance of an already-visible `materialize` broadcast frame, include
# the next immediate hidden frame after the last `broadcast` frame
broadcasti = []
for i visible_frames_i
trace[i][1].func == :materialize || continue
push!(broadcasti, findlast(trace[1:i - 1]) do frame
!is_broadcast(String(frame[1].file))
end)
end
sort!(union!(visible_frames_i, filter!(!isnothing, broadcasti)))

if length(visible_frames_i) > 0 && visible_frames_i[end] == length(trace)
# remove REPL-based top-level
# note: file field for top-level is different from the rest, doesn't include ./
startswith(String(trace[end][1].file), "REPL") && pop!(visible_frames_i)
end

if length(visible_frames_i) == 1 && trace[only(visible_frames_i)][1].func == :materialize
# remove a materialize frame if it is the only visible frame
pop!(visible_frames_i)
end

return visible_frames_i
end

function show_compact_backtrace(io::IO, trace::Vector; print_linebreaks::Bool)
#= Show the lowest stackframe and display a message telling user how to
retrieve the full trace =#
Expand Down Expand Up @@ -709,85 +794,42 @@ function show_compact_backtrace(io::IO, trace::Vector; print_linebreaks::Bool)
length(modules) > 0 && print(io, ", ")
printstyled(io, "Unknown", color = :light_black)
end

println(io)
end

# select frames from user-controlled code
is = findall(trace) do frame
file = String(frame[1].file)
!is_base_not_repl(file) &&
!is_registry_pkg(file) &&
!is_stdlib(file) &&
!is_private_not_julia(file) ||
is_dev_pkg(file) ||
(is_top_level_frame(frame[1]) && startswith(file, "REPL"))
end

# get list of visible modules
visible_modules = convert(Vector{Module}, filter!(!isnothing, unique(t[1] |> parentmodule for t @view trace[is])))
Main visible_modules || push!(visible_modules, Main)

# find the highest contiguous internal frames evaluted in the context of a visible module
internali = setdiff!(findall(trace) do frame
parentmodule(frame[1]) visible_modules
end, is)
setdiff!(internali, internali .+ 1)

# include the next immediate hidden frame called into from user-controlled code
filter!(>(0), sort!(union!(is, union!(is .- 1, internali .- 1))))

# for each appearance of an already-visible `materialize` broadcast frame, include
# the next immediate hidden frame after the last `broadcast` frame
broadcasti = []
for i is
trace[i][1].func == :materialize || continue
push!(broadcasti, findlast(trace[1:i - 1]) do frame
!is_broadcast(String(frame[1].file))
end)
end
sort!(union!(is, filter!(!isnothing, broadcasti)))

if length(is) > 0 && is[end] == num_frames
# remove REPL-based top-level
# note: file field for top-level is different from the rest, doesn't include ./
startswith(String(trace[end][1].file), "REPL") && pop!(is)
end

if length(is) == 1 && trace[only(is)][1].func == :materialize
# remove a materialize frame if it is the only visible frame
pop!(is)
end

is = find_visible_frames(trace)

num_vis_frames = length(is)

if num_vis_frames > 0
println(io, "\nStacktrace:")
print(io, "\nStacktrace:")

if is[1] > 1
println(io)
print_omitted_modules(1, is[1] - 1)
end

lasti = first(is)
@views for i is
if i > lasti + 1
println(io)
print_omitted_modules(lasti + 1, i - 1)
end
print_stackframe(io, i, trace[i][1], trace[i][2], ndigits_max, modulecolordict, modulecolorcycler)
println(io)
Base.print_stackframe(io, i, trace[i][1], trace[i][2], ndigits_max, modulecolordict, modulecolorcycler)
if i < num_frames - 1
println(io)
print_linebreaks && println(io)
end
lasti = i
end

# print if frames other than top-level were omitted
if num_frames - 1 > num_vis_frames
if num_frames - 1 > num_vis_frames
if lasti < num_frames - 1
print_omitted_modules(lasti + 1, num_frames - 1)
else
println(io)
print_omitted_modules(lasti + 1, num_frames - 1)
end
println(io)
print(io, "Use `err` to retrieve the full stack trace.")
end
end
Expand Down Expand Up @@ -937,6 +979,7 @@ function print_stackframe(io, i, frame::StackFrame, n::Int, ndigits_max, modulec
if n > 1
printstyled(io, " (repeats $n times)"; color=:light_black)
end
println(io)

# @ Module path / file : line
print_module_path_file(io, modul, file, line; modulecolor, digit_align_width)
Expand Down Expand Up @@ -997,7 +1040,7 @@ function show_backtrace(io::IO, t::Vector)
try invokelatest(update_stackframes_callback[], filtered) catch end
# process_backtrace returns a Vector{Tuple{Frame, Int}}
if get(io, :compacttrace, false)
show_compact_backtrace(io, filtered)
show_compact_backtrace(io, filtered; print_linebreaks = stacktrace_linebreaks())
else
show_full_backtrace(io, filtered; print_linebreaks = stacktrace_linebreaks())
end
Expand Down
4 changes: 2 additions & 2 deletions stdlib/REPL/src/REPL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ function print_response(errio::IO, response, show_value::Bool, have_color::Bool,
if iserr
val = Base.scrub_repl_backtrace(val)
Base.istrivialerror(val) || setglobal!(Base.MainInclude, :err, val)
Base.invokelatest(Base.display_error, errio, val)
Base.invokelatest(Base.display_error, errio, val, true)
else
if val !== nothing && show_value
try
Expand All @@ -318,7 +318,7 @@ function print_response(errio::IO, response, show_value::Bool, have_color::Bool,
try
excs = Base.scrub_repl_backtrace(current_exceptions())
setglobal!(Base.MainInclude, :err, excs)
Base.invokelatest(Base.display_error, errio, excs)
Base.invokelatest(Base.display_error, errio, excs, true)
catch e
# at this point, only print the name of the type as a Symbol to
# minimize the possibility of further errors.
Expand Down

0 comments on commit b2e6301

Please sign in to comment.