Skip to content

Commit

Permalink
changes needed to adapt to compressed line table format in Julia
Browse files Browse the repository at this point in the history
  • Loading branch information
vtjnash authored and aviatesk committed Mar 26, 2024
1 parent 31253a0 commit ebc743a
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 32 deletions.
2 changes: 1 addition & 1 deletion src/construct.jl
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ function prepare_framecode(method::Method, @nospecialize(argtypes); enter_genera
if (!isempty(lenv) && (hasarg(isidentical(:llvmcall), code.code) ||
hasarg(isidentical(Base.llvmcall), code.code) ||
hasarg(a->is_global_ref(a, Base, :llvmcall), code.code))) ||
hasarg(isidentical(:iolock_begin), code.code)
hasarg(isidentical(:iolock_begin), code.code)
return Compiled()
end
framecode = FrameCode(method, code; generator=generator)
Expand Down
15 changes: 13 additions & 2 deletions src/interpret.jl
Original file line number Diff line number Diff line change
Expand Up @@ -460,14 +460,25 @@ function coverage_visit_line!(frame::Frame)
pc, code = frame.pc, frame.framecode
code.report_coverage || return
src = code.src
@static if VERSION v"1.12.0-DEV.173"
lineinfo = linetable(src, pc)
file, line = lineinfo.file, lineinfo.line
if line != frame.last_codeloc
file isa Symbol || (file = Symbol(file)::Symbol)
@ccall jl_coverage_visit_line(file::Cstring, sizeof(file)::Csize_t, line::Cint)::Cvoid
frame.last_codeloc = line
end
else # VERSION < v"1.12.0-DEV.173"
codeloc = src.codelocs[pc]
if codeloc != frame.last_codeloc && codeloc != 0
linetable = src.linetable::Vector{Any}
lineinfo = linetable[codeloc]::Core.LineInfoNode
file, line = String(lineinfo.file), lineinfo.line
ccall(:jl_coverage_visit_line, Cvoid, (Cstring, Csize_t, Cint), file, sizeof(file), line)
file, line = lineinfo.file, lineinfo.line
file isa Symbol || (file = Symbol(file)::Symbol)
@ccall jl_coverage_visit_line(file::Cstring, sizeof(file)::Csize_t, line::Cint)::Cvoid
frame.last_codeloc = codeloc
end
end # @static if
end

# For "profiling" where JuliaInterpreter spends its time. See the commented-out block
Expand Down
17 changes: 16 additions & 1 deletion src/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,24 @@ function FrameCode(scope, src::CodeInfo; generator=false, optimize=true)

lt = linetable(src)
unique_files = Set{Symbol}()
@static if VERSION v"1.12.0-DEV.173"
function pushuniquefiles!(unique_files::Set{Symbol}, lt)
for edge in lt.edges
pushuniquefiles!(unique_files, edge)
end
linetable = lt.linetable
if linetable === nothing
push!(unique_files, Base.IRShow.debuginfo_file1(lt))
else
pushuniquefiles!(unique_files, linetable)
end
end
pushuniquefiles!(unique_files, lt)
else # VERSION < v"1.12.0-DEV.173"
for entry in lt
push!(unique_files, entry.file)
end
end # @static if

framecode = FrameCode(scope, src, methodtables, breakpoints, slotnamelists, used, generator, report_coverage, unique_files)
if scope isa Method
Expand Down Expand Up @@ -237,7 +252,7 @@ mutable struct Frame
assignment_counter::Int64
caller::Union{Frame,Nothing}
callee::Union{Frame,Nothing}
last_codeloc::Int32
last_codeloc::Int
end
function Frame(framecode::FrameCode, framedata::FrameData, pc=1, caller=nothing)
if length(junk_frames) > 0
Expand Down
96 changes: 70 additions & 26 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -258,38 +258,88 @@ end

# These getters improve inference since fieldtype(CodeInfo, :linetable)
# and fieldtype(CodeInfo, :codelocs) are both Any
const LineTypes = Union{LineNumberNode,Core.LineInfoNode}
@static if VERSION v"1.12.0-DEV.173"
const LineTypes = Union{LineNumberNode,Base.IRShow.LineInfoNode}
else
const LineTypes = Union{LineNumberNode,Core.LineInfoNode}
end
function linetable(arg)
if isa(arg, Frame)
arg = arg.framecode
end
if isa(arg, FrameCode)
arg = arg.src
end
return (arg::CodeInfo).linetable::Union{Vector{Core.LineInfoNode},Vector{Any}} # issue #264
ci = arg::CodeInfo
@static if VERSION v"1.12.0-DEV.173"
return ci.debuginfo
else # VERSION < v"1.12.0-DEV.173"
return ci.linetable::Union{Vector{Core.LineInfoNode},Vector{Any}} # issue #264
end # @static if
end
_linetable(list::Vector, i::Integer) = list[i]::Union{Expr,LineTypes}
function linetable(arg, i::Integer; macro_caller::Bool=false)::Union{Expr,LineTypes}
lt = linetable(arg)
lineinfo = _linetable(lt, i)
@static if VERSION v"1.12.0-DEV.173"
# TODO: decode the linetable at this frame efficiently by reimplementing this here
# TODO: get the contextual name from the parent, rather than returning "n/a" (which breaks Cthulhu)
return Base.IRShow.buildLineInfoNode(lt, :var"n/a", i)[1] # ignore all inlining / macro expansion / etc :(
else # VERSION < v"1.12.0-DEV.173"
lin = lt[i]::Union{Expr,LineTypes}
if macro_caller
while lineinfo isa Core.LineInfoNode && lineinfo.method === Symbol("macro expansion") && lineinfo.inlined_at != 0
lineinfo = _linetable(lt, lineinfo.inlined_at)
while lin isa Core.LineInfoNode && lin.method === Symbol("macro expansion") && lin.inlined_at != 0
lin = lt[lin.inlined_at]::Union{Expr,LineTypes}
end
end
return lineinfo
return lin
end # @static if
end

@static if VERSION v"1.12.0-DEV.173"

function getlastline(arg)
debuginfo = linetable(arg)
while true
ltnext = debuginfo.linetable
ltnext === nothing && break
debuginfo = ltnext
end
lastline = 0
for k = 0:typemax(Int)
codeloc = Core.Compiler.getdebugidx(debuginfo, k)
line::Int = codeloc[1]
line < 0 && break
lastline = max(lastline, line)
end
return lastline
end
function codelocs(arg, i::Integer)
debuginfo = linetable(arg)
codeloc = Core.Compiler.getdebugidx(debuginfo, i)
line::Int = codeloc[1]
line < 0 && return 0# broken or disabled debug info?
if line == 0 && codeloc[2] == 0
return 0 # no line number update
end
return i
end

else # VERSION < v"1.12.0-DEV.173"

getfirstline(arg) = getline(linetable(arg)[begin])
getlastline(arg) = getline(linetable(arg)[end])
function codelocs(arg)
if isa(arg, Frame)
arg = arg.framecode
end
if isa(arg, FrameCode)
arg = arg.src
end
return (arg::CodeInfo).codelocs::Vector{Int32}
ci = arg::CodeInfo
return ci.codelocs
end
codelocs(arg, i::Integer) = codelocs(arg)[i] # for consistency with linetable (but no extra benefit here)
codelocs(arg, i::Integer) = codelocs(arg)[i]

end # @static if

function lineoffset(framecode::FrameCode)
offset = 0
Expand All @@ -302,9 +352,9 @@ function lineoffset(framecode::FrameCode)
end

function getline(ln::Union{LineTypes,Expr})
_getline(ln::LineTypes) = ln.line
_getline(ln::Expr) = ln.args[1] # assuming ln.head === :line
return Int(_getline(ln))::Int
_getline(ln::LineTypes) = Int(ln.line)
_getline(ln::Expr) = ln.args[1]::Int # assuming ln.head === :line
return _getline(ln)
end
function getfile(ln::Union{LineTypes,Expr})
_getfile(ln::LineTypes) = ln.file::Symbol
Expand Down Expand Up @@ -367,7 +417,7 @@ function codelocation(code::CodeInfo, idx::Int)
idx′ = idx
# look ahead if we are on a meta line
while idx′ < length(code.code)
codeloc = codelocs(code)[idx′]
codeloc = codelocs(code, idx′)
codeloc == 0 || return codeloc
ex = code.code[idx′]
ex === nothing || isexpr(ex, :meta) || break
Expand All @@ -377,7 +427,7 @@ function codelocation(code::CodeInfo, idx::Int)
# if zero, look behind until we find where we last might have had a line
while idx′ > 0
ex = code.code[idx′]
codeloc = codelocs(code)[idx′]
codeloc = codelocs(code, idx′)
codeloc == 0 || return codeloc
idx′ -= 1
end
Expand All @@ -390,13 +440,11 @@ function compute_corrected_linerange(method::Method)
offset = line1 - method.line
@assert !is_generated(method)
src = JuliaInterpreter.get_source(method)
lastline = linetable(src)[end]::LineTypes
return line1:getline(lastline) + offset
lastline = getlastline(src)
return line1:lastline + offset
end

function compute_linerange(framecode)
getline(linetable(framecode, 1)):getline(last(linetable(framecode)))
end
compute_linerange(framecode) = getfirstline(framecode):getlastline(framecode)

function statementnumbers(framecode::FrameCode, line::Integer, file::Symbol)
# Check to see if this framecode really contains that line. Methods that fill in a default positional argument,
Expand Down Expand Up @@ -434,7 +482,6 @@ function statementnumbers(framecode::FrameCode, line::Integer, file::Symbol)
return stmtidxs
end


# If the exact line number does not exist in the line table, take the one that is closest after that line
# restricted to the line range of the current scope.
scope = framecode.scope
Expand Down Expand Up @@ -506,9 +553,7 @@ breakpointchar(framecode, stmtidx) =
function print_framecode(io::IO, framecode::FrameCode; pc=0, range=1:nstatements(framecode), kwargs...)
iscolor = get(io, :color, false)
ndstmt = ndigits(nstatements(framecode))
lt = linetable(framecode)
offset = lineoffset(framecode)
ndline = isempty(lt) ? 0 : ndigits(getline(lt[end]) + offset)
ndline = ndigits(getlastline(framecode) + lineoffset(framecode))
nullline = " "^ndline
src = copy(framecode.src)
replace_coretypes!(src; rev=true)
Expand Down Expand Up @@ -759,12 +804,11 @@ function Base.StackTraces.StackFrame(frame::Frame)
Base.StackFrame(
fname,
Symbol(getfile(frame)),
@something(linenumber(frame), getline(linetable(frame, 1))),
@something(linenumber(frame), getfirstline(frame)),
mi,
false,
false,
C_NULL
)
C_NULL)
end

function Base.show_backtrace(io::IO, frame::Frame)
Expand Down
3 changes: 1 addition & 2 deletions test/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ struct Aborted # for signaling that some statement or test blocks were interr
end

function Aborted(frame::Frame, pc)
src = frame.framecode.src
lineidx = src.codelocs[pc]
lineidx = JuliaInterpreter.codelocs(frame, pc)
lineinfo = JuliaInterpreter.linetable(frame, lineidx; macro_caller=true)
return Aborted(lineinfo)
end
Expand Down

0 comments on commit ebc743a

Please sign in to comment.