stacktraces: follow-up to "Enable method signature printing for inlined frames"#61699
Conversation
aviatesk
left a comment
There was a problem hiding this comment.
Thanks, the point 2 is fixing a real bug, not an intentional design in #53925. I missed the case where the outer debuginfo has a linetable indirection.
The existing inlined_test just happens to pass by coincidence as every IR PC of f points at to=1, so the column lands on the right edge regardless.
I crafted this reproducer of the bug that surfaces when the caller has several non-inlined statements before an inlined call:
@inline f_inner3(x) = x > 0 ? x : error("neg: $x")
function f_parent3(a, b, c, d)
s = 0
@noinline begin # keep `+` as invokes so codelocs has `to=0` entries
s += a; s += b; s += c; s += d
end
return f_inner3(s)
end
let st = try f_parent3(1, 2, 3, -10) catch; stacktrace(catch_backtrace()) end
sf = only(filter(sf -> sf.func === :f_inner3 && sf.inlined, st))
@test sf.linfo isa Core.MethodInstance
@test sf.linfo.def === which(f_inner3, (Int,))
endThis fails on master, but passes with your patch.
Point 1 also LGTM, so ready to come out of draft from my side.
| if (line != -1) { | ||
| if (pc > 0) | ||
| jl_safe_fprintf(s, "%s at %s:%d:%d%s\n", func_name, file_name, line, pc, inlined_str); | ||
| jl_safe_fprintf(s, "%s at %s:%d (pc: %d)%s\n", func_name, file_name, line, pc, inlined_str); |
There was a problem hiding this comment.
I think our frame construction fills in .pc even for C frames, so that may also need follow-up.
topolarity
left a comment
There was a problem hiding this comment.
Great find. Probably means that we need better testing infra. for the various DebugInfo variants long-term.
framepc may be a slightly better name than origpc
Thanks for the fix-up @mlechu !
|
Failure (OOM in LLVM on 32-bit windows) looks real; the gnuprofiling build shows section |
|
I suspect the cause is storing way more unique locations given that optimized IR usually has more unique indices. For my current use case (byte provenance) it doesn't matter if we use the innermost-linetable indices, but I'm not sure if that's OK for the purposes of the original change. |
Change / approach is conceptually right, but size regression needs to be resolved before merging
de72d43 to
95ac8f5
Compare
…tion (#61817) This change adds a basic C API for reading `jl_debuginfo_t`, both in its current form and with byte-precise provenance to be generated by JuliaLowering. ``` (debuginfo, pc) -> (firstbyte, lastbyte) (debuginfo, byte) -> (line, column) (debuginfo, pc) -> (firstline, firstcolumn) debuginfo -> first_line_of_entire_codeinfo ``` Note that the byte-precise provenance is not yet produced (and I've marked those branches unreachable). I'm making this change first so I can use them to try and resolve issues in #61699 and #53925. (Namely, I'm going to try and compute inlined frames from julia debuginfo rather than DWARF.) ## Implementation of byte-precise info storage The API shouldn't depend on how we do this, but I'll describe it here for reference. The simplicity of our current `Core.DebugInfo` (ref. #52415) is thanks to simple line numbers looking the same as indices into the next debuginfo, so they can be stored in the same way. Unfortunately, byte-precise information breaks this: from an index, we want to be able to know two byte positions, what lines they fall on, and how far from the beginning of those lines they are. I intend to store this info by putting a string in `linetable` like we do for `di.codelocs`. The compression format is described in julia.h, but only a couple points are relevant here: - Like `di.codelocs` today, I made a reasonable attempt to compress it (so we don't blow up the sysimg) without trying to be too clever. - Position lookup for a single IR statement can still be done without decompressing everything. I've already implemented the compression/decompression code (in JuliaLowering), but it's not part of this PR. This format might be torn out when we start trying to store AST provenance. --------- Co-authored-by: Cody Tapscott <topolarity@tapscott.me>
…tion (JuliaLang#61817) This change adds a basic C API for reading `jl_debuginfo_t`, both in its current form and with byte-precise provenance to be generated by JuliaLowering. ``` (debuginfo, pc) -> (firstbyte, lastbyte) (debuginfo, byte) -> (line, column) (debuginfo, pc) -> (firstline, firstcolumn) debuginfo -> first_line_of_entire_codeinfo ``` Note that the byte-precise provenance is not yet produced (and I've marked those branches unreachable). I'm making this change first so I can use them to try and resolve issues in JuliaLang#61699 and JuliaLang#53925. (Namely, I'm going to try and compute inlined frames from julia debuginfo rather than DWARF.) ## Implementation of byte-precise info storage The API shouldn't depend on how we do this, but I'll describe it here for reference. The simplicity of our current `Core.DebugInfo` (ref. JuliaLang#52415) is thanks to simple line numbers looking the same as indices into the next debuginfo, so they can be stored in the same way. Unfortunately, byte-precise information breaks this: from an index, we want to be able to know two byte positions, what lines they fall on, and how far from the beginning of those lines they are. I intend to store this info by putting a string in `linetable` like we do for `di.codelocs`. The compression format is described in julia.h, but only a couple points are relevant here: - Like `di.codelocs` today, I made a reasonable attempt to compress it (so we don't blow up the sysimg) without trying to be too clever. - Position lookup for a single IR statement can still be done without decompressing everything. I've already implemented the compression/decompression code (in JuliaLowering), but it's not part of this PR. This format might be torn out when we start trying to store AST provenance. --------- Co-authored-by: Cody Tapscott <topolarity@tapscott.me>
cd05077 to
ab6a0c2
Compare
|
Approach and PR message updated; this is ready for re-review. |
…61939) Fix #33457. Nightly: ``` julia> begin # line 2 [y for _ in 1:10] # line 4 end ERROR: UndefVarError: `y` not defined in `Main` Suggestion: check for spelling errors or missing imports. Stacktrace: [1] #2 @ ./none:-1 [inlined] [2] iterate @ ./generator.jl:48 [inlined] [3] collect(itr::Base.Generator{UnitRange{Int64}, var"#2#3"}) @ Base ./array.jl:833 [4] top-level scope @ REPL[1]:3 ``` This change: ``` ERROR: UndefVarError: `y` not defined in `Main` Suggestion: check for spelling errors or missing imports. Stacktrace: [1] #12 @ ./REPL[1]:3 [inlined] [2] iterate @ ./generator.jl:48 [inlined] [3] collect(itr::Base.Generator{UnitRange{Int64}, var"#12#13"}) @ Base ./array.jl:833 [4] top-level scope @ REPL[1]:3 ``` As a sneak peek, this is further improved with #61699: ``` ERROR: UndefVarError: `y` not defined in `Main` Suggestion: check for spelling errors or missing imports. Stacktrace: [1] (::var"#12#13")(::Int64) @ Main REPL[1]:3 [inlined] [2] iterate(::Base.Generator{UnitRange{Int64}, var"#12#13"}) @ Base generator.jl:48 [inlined] [3] collect(itr::Base.Generator{UnitRange{Int64}, var"#12#13"}) @ Base array.jl:833 [4] top-level scope @ REPL[1]:3 ``` I doubt this frame is important (it being a lambda is considered an implementation detail IIRC) but (1) we shouldn't show users a nonsense frame, and (2) this fixes another case where we produce malformed DebugInfo.
topolarity
left a comment
There was a problem hiding this comment.
How exciting this worked out in the end!
I've opened JuliaLang/llvm-project#52 to expand the DWARF column to 32-bit, but I think we can land this as-is
Put PC in most-inlined frame
We want to decode `DebugInfo` rather than using linfo from
`jl_lookup_code_address`. This temporarily matches flisp.
We no longer get the dummy `(::Tuple)() at <missing>:0` in the stacktrace since
we produced the opaque closure by hand with abnormal debuginfo. Seeing no
better alternative, still inspect each of the frames to check we don't
crash, but the absence of the frame means this test probably can't hit the
original crash anyway.
Avoid construction using Core.OpaqueClosure
|
Thanks again for fixing this issue and getting it merged. I really appreciate it! |
Two changes to #53925:
linetableinappend_lineinfo, we ended up withthe wrong
pcin the column field, since we were using the innermostindex rather than the one usable with a CodeInstance's
.debuginfo.Store the optimized
pcin the "innermost" frame, since DILocationexpects inlinee locations to change more quickly than inliners (see
performance discussion below). Thanks Cody and Jameson for this
suggestion.
seeing a
debuginfo,pcpair. See comment; I don't like having boththis code and the old fallback
lookupimplementation, but it seemsless brittle to just re-create the inlined frame stack than to create it
and match it with the linear
framesarray fromjl_lookup_code_address.