From ebc743a99ec681a22e3cd0ff3676fc2399f79601 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 12 Dec 2023 00:54:06 +0000 Subject: [PATCH] changes needed to adapt to compressed line table format in Julia For https://github.com/JuliaLang/julia/pull/52415 --- src/construct.jl | 2 +- src/interpret.jl | 15 +++++++- src/types.jl | 17 ++++++++- src/utils.jl | 96 +++++++++++++++++++++++++++++++++++------------- test/utils.jl | 3 +- 5 files changed, 101 insertions(+), 32 deletions(-) diff --git a/src/construct.jl b/src/construct.jl index 4615054d..71352f8e 100644 --- a/src/construct.jl +++ b/src/construct.jl @@ -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) diff --git a/src/interpret.jl b/src/interpret.jl index e3613bf5..894921d2 100644 --- a/src/interpret.jl +++ b/src/interpret.jl @@ -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 diff --git a/src/types.jl b/src/types.jl index 04539fa9..041a5b84 100644 --- a/src/types.jl +++ b/src/types.jl @@ -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 @@ -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 diff --git a/src/utils.jl b/src/utils.jl index 7923d5f8..c26dd28a 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -258,7 +258,11 @@ 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 @@ -266,20 +270,63 @@ function linetable(arg) 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 @@ -287,9 +334,12 @@ function codelocs(arg) 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 @@ -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 @@ -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 @@ -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 @@ -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, @@ -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 @@ -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) @@ -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) diff --git a/test/utils.jl b/test/utils.jl index 1a3f058e..f1cf69a7 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -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