diff --git a/base/compiler/ssair/show.jl b/base/compiler/ssair/show.jl index 74a3e60fbf927..9e776c2c4de57 100644 --- a/base/compiler/ssair/show.jl +++ b/base/compiler/ssair/show.jl @@ -360,36 +360,35 @@ end # utility function for converting a debuginfo object a particular pc to list of LineInfoNodes representing the inlining info at that pc for function `def` # which is either `nothing` (macro-expand), a module (top-level), a Method (unspecialized code) or a MethodInstance (specialized code) -function buildLineInfoNode(debuginfo, @nospecialize(def), pc::Int) - DI = LineInfoNode[] - pc == 0 && return DI - let codeloc = getdebugidx(debuginfo, pc) +# Returns `false` if the line info should not be updated with this info because this +# statement has no effect on the line numbers. The `scopes` will still be populated however +# with as much information as was available about the inlining at that statement. +function append_scopes!(scopes::Vector{LineInfoNode}, pc::Int, debuginfo, @nospecialize(def)) + doupdate = true + while true + debuginfo.def isa Symbol || (def = debuginfo.def) + codeloc = getdebugidx(debuginfo, pc) line::Int = codeloc[1] - line < 0 && return DI # broken or disabled debug info? - if line == 0 && codeloc[2] == 0 - return DI # no update - end - end - function append_scopes!(scopes::Vector{LineInfoNode}, pc::Int, debuginfo, @nospecialize(def)) - while true - debuginfo.def isa Symbol || (def = debuginfo.def) - codeloc = getdebugidx(debuginfo, pc) - line::Int = codeloc[1] - line < 0 && return # broken or disabled debug info? - if debuginfo.linetable === nothing || line == 0 - push!(scopes, LineInfoNode(def, debuginfo_file1(debuginfo), Int32(line))) - else - append_scopes!(scopes, line, debuginfo.linetable::Core.DebugInfo, def) - end - def = :var"macro expansion" - inl_to::Int = codeloc[2] - inl_to == 0 && break - debuginfo = debuginfo.edges[inl_to] - pc::Int = codeloc[3] - pc == 0 && break # TODO: use toplevel line? + inl_to::Int = codeloc[2] + if debuginfo.linetable === nothing || pc <= 0 || line < 0 + line < 0 && (line = 0) # broken debug info + doupdate &= line != 0 || inl_to != 0 # disabled debug info--no update + push!(scopes, LineInfoNode(def, debuginfo_file1(debuginfo), Int32(line))) + else + doupdate = append_scopes!(scopes, line, debuginfo.linetable::Core.DebugInfo, def) && doupdate end + inl_to == 0 && return doupdate + def = :var"macro expansion" + debuginfo = debuginfo.edges[inl_to] + pc::Int = codeloc[3] end - append_scopes!(DI, pc, debuginfo, def) +end + +# utility wrapper around `append_scopes!` that returns an empty list instead of false +# when there is no applicable line update +function buildLineInfoNode(debuginfo, @nospecialize(def), pc::Int) + DI = LineInfoNode[] + append_scopes!(DI, pc, debuginfo, def) || empty!(DI) return DI end diff --git a/base/stacktraces.jl b/base/stacktraces.jl index 355910d0b2250..84d280973c28a 100644 --- a/base/stacktraces.jl +++ b/base/stacktraces.jl @@ -8,7 +8,7 @@ module StackTraces import Base: hash, ==, show import Core: CodeInfo, MethodInstance -using Base.IRShow: debuginfo_file1, normalize_method_name # or import buildLineInfoNode? +using Base.IRShow: normalize_method_name, append_scopes!, LineInfoNode export StackTrace, StackFrame, stacktrace @@ -21,10 +21,10 @@ Stack information representing execution context, with the following fields: The name of the function containing the execution context. -- `linfo::Union{Core.MethodInstance, Method, Module, Core.CodeInfo, Nothing}` +- `linfo::Union{Method, Core.MethodInstance, Core.CodeInfo, Nothing}` - The MethodInstance or CodeInfo containing the execution context (if it could be found), \ - or Module (for macro expansions)" + The Method, MethodInstance, or CodeInfo containing the execution context (if it could be found), \ + or nothing (for example, if the inlining was a result of macro expansion). - `file::Symbol` @@ -55,8 +55,8 @@ struct StackFrame # this type should be kept platform-agnostic so that profiles "the line number in the file containing the execution context" line::Int "the MethodInstance or CodeInfo containing the execution context (if it could be found), \ - or Module (for macro expansions)" - linfo::Union{MethodInstance, Method, Module, CodeInfo, Nothing} + or nothing (for example, if the inlining was a result of macro expansion)." + linfo::Union{MethodInstance, Method, CodeInfo, Nothing} "true if the code is from C" from_c::Bool "true if the code is from an inlined frame" @@ -141,35 +141,25 @@ function lookup(ip::Union{Base.InterpreterIP,Core.Compiler.InterpreterIP}) file = empty_sym line = Int32(0) end + def = (code isa MethodInstance ? code : StackTraces) # Module just used as a token for top-level code pc::Int = max(ip.stmt + 1, 0) # n.b. ip.stmt is 0-indexed - debuginfo = codeinfo.debuginfo - codeloc = @ccall jl_uncompress1_codeloc(debuginfo.codelocs::Any, pc::Int)::NTuple{3,Int32} - if (codeloc[1] == 0 && codeloc[2] == 0) || codeloc[1] < 0 + scopes = LineInfoNode[] + append_scopes!(scopes, pc, codeinfo.debuginfo, def) + if isempty(scopes) return [StackFrame(func, file, line, code, false, false, 0)] end - scopes = StackFrame[] inlined = false - def = (code isa MethodInstance ? code : StackTraces) # Module just used as a token - function append_scopes!(scopes::Vector{StackFrame}, pc::Int, debuginfo::Core.DebugInfo, @nospecialize(def), inlined::Bool) - while true - debuginfo.def isa Symbol || (def = debuginfo.def) - codeloc = @ccall jl_uncompress1_codeloc(debuginfo.codelocs::Any, pc::Int)::NTuple{3,Int32} - line = codeloc[1] - if debuginfo.linetable === nothing || pc <= 0 || line < 0 - line < 0 && (line = 0) # broken debug info? - push!(scopes, StackFrame(normalize_method_name(def), debuginfo_file1(debuginfo), line, inlined ? nothing : code, false, inlined, 0)) - else - append_scopes!(scopes, line - 1, debuginfo.linetable::Core.DebugInfo, def, inlined) - end - inlined = true - def = :var"macro expansion" - inl_to::Int = codeloc[2] - inl_to == 0 && break - debuginfo = debuginfo.edges[inl_to] - pc::Int = codeloc[3] + scopes = map(scopes) do lno + if inlined + def = lno.method + def isa Union{Method,MethodInstance} || (def = nothing) + else + def = codeinfo end + sf = StackFrame(normalize_method_name(lno.method), lno.file, lno.line, def, false, inlined, 0) + inlined = true + return sf end - append_scopes!(scopes, pc, debuginfo, def, false) return scopes end diff --git a/doc/src/devdocs/ast.md b/doc/src/devdocs/ast.md index cfd2f2564726e..bdd798bd96a5f 100644 --- a/doc/src/devdocs/ast.md +++ b/doc/src/devdocs/ast.md @@ -811,9 +811,10 @@ end A vector of indices, with 3 values for each statement in the IR plus one for the starting point of the block, that describe the stacktrace from that point: - 1. the integer index into the `linetable.codelocs` field, giving the original location - associated with each statement (including its edges), or zero indicating to use - `linetable.firstline` as the line number. + 1. the integer index into the `linetable.codelocs` field, giving the + original location associated with each statement (including its syntatic edges), + or zero indicating no change to the line number from the previously + executed statement (which is not necessarily syntatic or lexical prior). or the line number itself if the `linetable` field is `nothing` 2. the integer index into edges, giving the DebugInfo inlined there (or zero if there diff --git a/src/codegen.cpp b/src/codegen.cpp index 0b76d027ab8b3..dafb6f06f30f3 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8674,14 +8674,8 @@ static jl_llvm_functions_t SmallVector prev_lineinfo, new_lineinfo; new_lineinfo.push_back(topinfo); auto update_lineinfo = [&] (size_t pc) { - jl_debuginfo_t *debuginfo = src->debuginfo; - struct jl_codeloc_t lineidx = jl_uncompress1_codeloc(debuginfo->codelocs, pc + 1); - if (lineidx.line == 0 && lineidx.to == 0) - return false; // do not change anything - prev_lineinfo.resize(0); - std::swap(prev_lineinfo, new_lineinfo); - std::function append_lineinfo = - [&] (jl_debuginfo_t *debuginfo, jl_value_t *func, size_t to, size_t pc) -> void { + std::function append_lineinfo = + [&] (jl_debuginfo_t *debuginfo, jl_value_t *func, size_t to, size_t pc) -> bool { while (1) { if (!jl_is_symbol(debuginfo->def)) // this is a path func = debuginfo->def; // this is inlined @@ -8689,11 +8683,14 @@ static jl_llvm_functions_t size_t i = lineidx.line; if (pc > 0 && i >= 0 && (jl_value_t*)debuginfo->linetable != jl_nothing) { // indirection node - append_lineinfo(debuginfo->linetable, func, to, i); + if (!append_lineinfo(debuginfo->linetable, func, to, i)) + return false; // no update } else { - if (i < 0) - i = 0; // pc out of range: broken debuginfo? + if (i < 0) // pc out of range: broken debuginfo? + return false; + if (i == 0 && lineidx.to == 0) // no update + return false; // actual node DebugLineTable info; info.edgeid = to; @@ -8739,15 +8736,20 @@ static jl_llvm_functions_t } to = lineidx.to; if (to == 0) - break; + return true; pc = lineidx.pc; debuginfo = (jl_debuginfo_t*)jl_svecref(debuginfo->edges, to - 1); func = NULL; } }; - append_lineinfo(debuginfo, (jl_value_t*)lam, 0, pc + 1); - assert(new_lineinfo.size() > 0); - return true; + prev_lineinfo.resize(0); + std::swap(prev_lineinfo, new_lineinfo); + bool updated = append_lineinfo(src->debuginfo, (jl_value_t*)lam, 0, pc + 1); + if (!updated) + std::swap(prev_lineinfo, new_lineinfo); + else + assert(new_lineinfo.size() > 0); + return updated; }; SmallVector aliasscopes; @@ -8903,7 +8905,7 @@ static jl_llvm_functions_t bool is_tracked = in_tracked_path(file); if (do_coverage(is_user_code, is_tracked)) { for (size_t pc = 0; 1; pc++) { - struct jl_codeloc_t lineidx = jl_uncompress1_codeloc(debuginfo->codelocs, pc + 1); + struct jl_codeloc_t lineidx = jl_uncompress1_codeloc(debuginfo->codelocs, pc); if (lineidx.line == -1) break; jl_debuginfo_t *linetable = debuginfo->linetable; diff --git a/src/stackwalk.c b/src/stackwalk.c index 1655c508a8545..e45af4bb6634e 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -718,8 +718,8 @@ static void jl_print_debugloc(jl_debuginfo_t *debuginfo, jl_value_t *func, size_ jl_print_debugloc(debuginfo->linetable, func, ip2, 0); } else { - if (ip2 < 0) - ip2 = 0; // broken debug info? + if (ip2 < 0) // set broken debug info to ignored + ip2 = 0; const char *func_name = jl_debuginfo_name(func); const char *file = jl_debuginfo_file(debuginfo); jl_safe_print_codeloc(func_name, file, ip2, inlined);