-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Integrate StackTraces.jl into Base #14469
Conversation
julia> using StackTraces | ||
|
||
julia> stacktrace() | ||
2-element Array{StackTraces.StackFrame,1}: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some or all of these examples should maybe be doctests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm having a fair amount of trouble coming up with a way to make good doctests for most of these. Because the doctests aren't run from the REPL, every single stack trace generated by the doctests will be different from stack traces generated by the user. For example:
File "manual/stacktraces.rst", line 172, in default
Failed example:
grandparent()
Expected:
ERROR: Whoops!
StackTrace with 5 StackFrames:
child at none:1
parent at none:1
grandparent at none:3
eval_user_input at REPL.jl:62
[inlined code from REPL.jl:92] anonymous at task.jl:63
Got:
ERROR: Whoops!
StackTrace with 8 StackFrames:
child at none:1
parent at none:1
grandparent at none:3
eval at boot.jl:265
[inlined code from sysimg.jl:14] eval_user_input at client.jl:96
eval at boot.jl:265
[inlined code from sysimg.jl:14] eval_user_input at client.jl:96
_start at client.jl:338
I can change the stack trace examples in the documentation to match what will happen when make -C doc doctest
is run, but I worry that this would make the documentation itself less useful to the user.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there's supposed to be some filtering out of those last few lines, but it may have been broken since the big inlined-line-numbers change a few months back.
Thanks for the feedback. Will make those adjustments. Looks like I also need to figure out why 32-bit Windows is behaving differently, too... |
Generally rebases are also cleaner to review than merge commits, and help avoid tangling the history for future bisects or blames. |
I've done some looking into why the Windows 32-bit build is failing and it appears that |
You'd have to build and upload an llvm binary, then modify |
Thanks @tkelman. I'm not sure that the problem will just go away with a newer LLVM but it is definitely worth looking into. |
Got most of the doctests working using the ellipsis, but @omus and I ran into what seems to be some inconsistencies in the way While In this example I would expect julia> @noinline bad_function() = nonexistent_var
bad_function (generic function with 1 method)
julia> @noinline function good_function()
try
x = bad_function()
catch
return stacktrace()
end
end
good_function (generic function with 1 method)
julia> good_function()
4-element Array{Base.StackTraces.StackFrame,1}:
Base.StackTraces.StackFrame(:good_function,:none,3,symbol(""),-1,false,13140383748)
Base.StackTraces.StackFrame(:eval,symbol("./boot.jl"),265,symbol(""),-1,false,4494236952)
Base.StackTraces.StackFrame(:eval_user_input,symbol("REPL.jl"),3,symbol("REPL.jl"),62,false,13140281135)
Base.StackTraces.StackFrame(:anonymous,symbol("REPL.jl"),92,symbol("task.jl"),63,false,13140230754) In the next example, adding a second line to the try block seems to increment the line number returned in the stack trace as well, causing it to point at line 4 (a line that should never be executed): julia> @noinline function good_function()
try
x = bad_function()
x += 1
catch
return stacktrace()
end
end
good_function (generic function with 1 method)
julia> good_function()
4-element Array{Base.StackTraces.StackFrame,1}:
Base.StackTraces.StackFrame(:good_function,:none,4,symbol(""),-1,false,13140385322)
Base.StackTraces.StackFrame(:eval,symbol("./boot.jl"),265,symbol(""),-1,false,4494236952)
Base.StackTraces.StackFrame(:eval_user_input,symbol("REPL.jl"),3,symbol("REPL.jl"),62,false,13140281135)
Base.StackTraces.StackFrame(:anonymous,symbol("REPL.jl"),92,symbol("task.jl"),63,false,13140230754) Finally, adding another line to the catch block results in julia> @noinline function good_function()
try
x = bad_function()
x += 1
catch
println("Something.")
return stacktrace()
end
end
good_function (generic function with 1 method)
julia> good_function()
Something.
4-element Array{Base.StackTraces.StackFrame,1}:
Base.StackTraces.StackFrame(:good_function,:none,7,symbol(""),-1,false,13140386475)
Base.StackTraces.StackFrame(:eval,symbol("./boot.jl"),265,symbol(""),-1,false,4494236952)
Base.StackTraces.StackFrame(:eval_user_input,symbol("REPL.jl"),3,symbol("REPL.jl"),62,false,13140281135)
Base.StackTraces.StackFrame(:anonymous,symbol("REPL.jl"),92,symbol("task.jl"),63,false,13140230754) In all of these examples, I was hoping that you might be able to shed some light on how these line numbers are generated, @tkelman. It seems like some under-the-hood optimizations are probably interfering with correct line numbers being reported by |
Maybe I'm missing something obvious, but why do you have |
When the original StackTraces.jl module written, there was a separate |
be2dfff
to
f8c7165
Compare
line_numbers = @__LINE__ .- [15, 10, 5] | ||
|
||
# Test try...catch with stacktrace | ||
@test try_stacktrace()[1] == StackFrame(:try_stacktrace, @__FILE__, line_numbers[2]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that only one issue remains unaddressed. As mentioned earlier, there are some inconsistencies with the way backtrace
reports line numbers, which cause the test case on line 72 (and the corresponding doctest) to fail. Despite the @noinline
s, some sort of optimization seems to be occurring which causes a line number in the try
block to be reported for code run in the catch
(or perhaps something else entirely is going on; this is just a guess).
The test case could be modified to succeed by replacing lines 54 through 69 with something like:
@noinline good_function() = 15 / pi
@noinline bad_function() = throw(UndefVarError(:nonexistent))
@noinline function try_stacktrace()
try
good_function()
bad_function()
catch
good_function()
return stacktrace()
end
end
@noinline function try_catch()
try
good_function()
bad_function()
catch
good_function()
return catch_stacktrace()
end
end
line_numbers = @__LINE__ .- [19, 12, 6]
For reasons that I do not fully understand (see my earlier comment) this causes the line numbers to be reported correctly by backtrace
—but modifying the test case in this way seems like sidestepping the issue, and the tests ought to pass as they are.
9275963
to
9700d61
Compare
The Travis/AppVeyor failures could be addressed by any of the following:
My preference would be option 1, followed by option 2. But because my understanding of the underlying C code is limited (to be generous), I'm not planning to attempt a fix the line number issue myself. |
The last issue definitely seems related to #13947. In this case the backtrace isn't missing just the line numbers are being reported incorrectly. It appears that the line that does get reported is the line of the last expression in the try. # file example.jl
function example()
try
error()
false # Line 5
# Ignored
catch
display(stacktrace()) # Line 8
end
end
example() $ ./julia example.jl
5-element Array{StackFrame,1}:
example at st.jl:5
include at boot.jl:262
include_from_node1 at loading.jl:401
process_options at client.jl:249
_start at client.jl:301 You can correct the line number by introducing an expression prior to running stacktrace: # file example_works.jl
function example()
try
error()
false # Line 5
# Ignored
catch
true
display(stacktrace()) # Line 9
end
end
example() $ ./julia example_works.jl
5-element Array{StackFrame,1}:
example at example_works.jl:9
include at boot.jl:262
include_from_node1 at loading.jl:401
process_options at client.jl:249
_start at client.jl:301 |
Added doctests to the manual. Removed show_stacktrace in favour of show(x::StackFrame). Removed format_stacktrace and format_stackframe. Additional test cases (which currently fail due to difficult to track down line number issues)
Reintroduced UNKNOWN constant stack frame from profile.jl. Added test case for looking up an invalid stack frame. Selectively removed some tests that consistently fail in Appveyor's 32-bit Windows LVM.
Currently a call to `stacktrace()` at the beginning of a catch will result in incorrect line numbers being returned. This is a problem with `backtrace()`.
subsumed by #14815 |
StackTraces.jl is an external package that provides an immutable StackFrame type that simplifies interacting with a stack trace. This pull request would integrate this module into Base, as per #14212. Some refactoring has been done to avoid duplicating some functionality from the Profile module.