-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
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
What's needed to include ClearStacktrace.jl in Base? #36026
Comments
This would be really nice to have. Or at least maybe a trimmed down version which improves printing of stacktraces by default in Base. |
I'm all in favor. The main thing I don't get is the colors in the signatures. They seem...random? I guess the different colors are there to make it easier to pick out different elements, but it's a bit weird. How about color == nesting depth? I think the main weirdness is in e.g. |
There's another thing I'd really like to see: julia> foo(x::T, y::T) where T = error("whoops")
foo (generic function with 1 method)
julia> function bar()
a = rand(3, 5)
b = view(a, :, 2:3)
c = reinterpret(Float32, b)
foo(c, c)
end
bar (generic function with 1 method)
julia> bar()
ERROR: whoops
Stacktrace:
[1] error(::String) at ./error.jl:33
[2] foo(::Base.ReinterpretArray{Float32,2,Float64,SubArray{Float64,2,Array{Float64,2},Tuple{Base.Slice{Base.OneTo{Int64}},UnitRange{Int64}},true}}, ::Base.ReinterpretArray{Float32,2,Float64,SubArray{Float64,2,Array{Float64,2},Tuple{Base.Slice{Base.OneTo{Int64}},UnitRange{Int64}},true}}) at ./REPL[1]:1
[3] bar() at ./REPL[2]:5
[4] top-level scope at REPL[3]:1 but since julia> m = first(methods(foo))
foo(x::T, y::T) where T in Main at REPL[1]:1
julia> m.sig
Tuple{typeof(foo),T,T} where T it seems that we should be able to print entry 2 as something like
|
A small comment, but to me it is currently maybe a bit too much color "salad". The stackframe counter being blue, all the different colors in the signature etc. For Base colors so far, we've only used the 16 system colors (8 + 8 bright ones) and that makes fancy coloring a bit harder. |
Good suggestions overall. Maybe a couple comments why I made the color choices so far: The numbers are blue because they are important, so shouldn't be grayed out, but making them white felt to me like they were fighting for attention with the function names. The modules use the primary colors in a cycle, I think this is very useful because it allows to quickly filter the trace for relevant modules. This is something that is very to do with the current stack trace format. The idea is that people should be forced to read as little as possible to find what they’re looking for. (I mostly did eye movement and attention research in my academic life so far, so those aspects are important to me ;) ) The colors for the signatures are very debatable. The ones on the screenshot above were chosen to be only slightly different from dark gray. At the same time, I wanted the different semantic components to be discriminable. That’s why all types and type parameters (curly braces and commas) cause a color switch. There are three colors which means there won’t be two neighbors with the same color. I found that it’s quite difficult to make out single words in huge type signatures if they are all just white. The slightly different shades help a lot but also make everything a bit more noisy, visually. As the 16 color set doesn’t have colors that are only marginally different from dark gray, it’s probably not something that can be included in Base. In a recent update, I replaced the colors with three slightly different shades of gray from the ANSI set, but even that will not be supported everywhere I guess. |
Oh also the stack frame color and the module colors as well as the dark gray and white are all system colors. It’s just my particular VSCode theme that renders them like you see here. |
This would be great to have. When scanning argument types, often it's pretty hard to figure out where one stops and the next starts. Colouring the top level differently might be very helpful here, and perhaps even numbering the arguments: [3] (_1::DataFrames.var"#fun##309#"{...lighter...}, _2::NamedTuple{...lighter...}, _3::Int64}) |
We should be able to show the argument names, like we do in the printing of |
I like it. Will be even better with argument names. It's too bad that the order "function - module - arguments" works so well for the layout but doesn't really make sense semantically. I don't have a solution though; clearly it's best for the function to come first. Maybe the module could go on the next line before the file location? It is part of the location after all. Then maybe we wouldn't need the header either. I think we could also put the numbers in normal text, and the function names in bold and/or white. |
Could the filename at the end of the codepath (and possibly the line number) get their own lighter color too? |
I'll try out a couple things with these suggestions.
It technically can of course ;) It's always a tradeoff between better visibility and attention-grabbing noise. |
Why not write the modules first, since they are part of the function's full name? Perhaps with colour it may be clear enough just to write |
Really crazy thought: if printing the argument types would take up more than one line, print the signature in folded form and then use
Related: #35915 (which I plan to use to create a folded-tree menu, https://github.com/JuliaCollections/FoldingTrees.jl). |
How does it look with codepath rearranged in this order: LineNumber Filename Path |
Yeah I kind of like the idea, I think I started out without color and that didn't work so well, but like this it's actually quite readable. I'll try without columns next, although columns are always nice because they guide your eyes. |
Not sure about that; the function name is a vastly more important piece of information than the module. For me at least, the most important info is definitely the function name, file name, and line number. Also for scripts the left column would be lots of |
I think when I’m reading stacktraces, I’m mostly looking for the last call in my code before it goes to Base/package code, since usually the bug is in my code. So in that case the module is pretty important and the function call isn’t what I’m scanning first. |
How about this:
Module names would still line up so they would be easy to scan. |
The first time I saw that I'd probably go nuts trying to figure out what the colors meant 😂 If there are going to be per-module colors, seems better to just apply the color to the module name. |
I like the module names, they get a bit lost in this latest picture. If they are at the bottom, perhaps making them the same bright colour would connect things? (And make clear where you cross a module boundary.) And, perhaps, if the module name appears in the path, it could simply be highlighted there instead -- that would also move the colourful name off the left edge where the functions are. |
Often the exact package is not so much of interest as the nature of the code: is it core julia, a library I'm using, the library I'm developing, or a script? We could have four colors: core/base/dev/stdlib, packages, packages currently |
For me, the colors are more about boundary changes, where code from one module begins and the other ends. That's why I probably wouldn't assign inherent meaning to them. On the other hand, that might be confusing. I've tried to simplify more, and removed columns again because without the module column they are not so useful anymore. Then first I colored only line number by modules, but this still left the file name not so visible, which many comments said is important. So I colored that in the module color as well. The module name itself is not colored because it's too close to the function name and that's too noisy. Here is the version with both numbers and filenames colored: |
Can you also try coloring the full file path? |
Code used to produce the sample above in case anyone wants to play with it:
|
The problem with this is that this doesn't leave the links clickable in Atom / VSCode, etc. This is something I use very often and I believe others do as well. In fact, I even explicitly expand the base paths to make them clickable. This does of course print more. But I think it increases utility. I know there's some way to jump to stack trace entries by number in the REPL with some shortcut, but this is far less intuitive than just clicking on a link in my opinion. |
Agree: let’s just do that last version. |
Thanks for doing all the work here, @jkrumbiegel . Great to have a version we can try out... And fork: I guess I think it's a bit concerning that |
Note in the current stacktrace printing frames 8-11 in that example are not shown (they are part of the REPL and would be in every REPL stacktrace). |
Indeed, this has improved which is great. But it still goes from 8 lines (for the stack trace alone) to 20 (ClearStacktrace 0.2). There's a bit of a trade-off between how pretty it is & not losing your place. The paths are also printed much more compactly in Base, |
The base paths are expanded on purpose to make them clickable. You can disable that in ClearStacktrace. I could also make the newlines optional for people who don’t like them. Could just be an environment variable. |
And I guess I must have missed copying the part of the function that clips off the last couple of frames that never change |
Probably a bug from splitting and rejoining the path |
No, I mostly removed it because I wouldn’t have it in base :) Load time was just a guess |
I guess there might be too many blank lines if we have many frames of stacktrace as shown in #36026 (comment) ? |
Not to derail the discussion (the last version is great and a big improvement) but on the topic of too many newlines, the problem is that when working at the terminal it seems a lot better to me to print the stacktrace "inverted" (as I originally proposed in #18228) as:
The most important information is the error message itself and the frames towards the top of the stack (closer to the error) and printing it in this order that will always be visible without scrolling. Right now I frequently have to scroll up to even see the error message and not just the tail of a stacktrace. However, when reading a stacktrace on a webstite that has been copy-pasted from the terminal you want the opposite order because you scroll from top to bottom as opposed to bottom to top as you do in a terminal... Kinda tricky to get a good design for both these cases. |
I actually had code in Also, I agree that given the scrolling direction it could make sense to invert the whole thing by default. |
Speaking of gdb, they have a reasonable solution to very long traces with their pager: "press enter for more frames". |
By the way, I Iove the latest visual design in #36026 (comment) and would be extremely happy if we merge that. Changing the frame order or adding interactive features seem like separate issues. Regarding file names, I hope that we can eventually use terminal hyperlink OSC sequences (yes hyperlinks in terminals are somewhat widely supported!) and have a way for the user's editor to pick that up. |
Speaking of finding the "innermost frame", I use enough languages in the course of my work that I can never remember if I should be looking at the top or the bottom of a stacktrace for my code in any particular language. So I end up scanning through looking at the filename until I see one I recognize. The underlining shown here would help, so this is a clear improvement. But I still wonder if there is a goodway to call out if I should be looking at the top or the bottom. In principle printing |
I've made a PR to Base here #36134 |
Is there a way to omit type parameters? The problem we see a lot of the time is that the amount of type parameters in DiffEq, ForwardDiff, etc. can make things... daunting. If by default it just said |
If they correspond to some existing type alias, they'll just go away automatically now (#36107). Otherwise, they indicate possible compilation performance bottlenecks—so possibly worth investigating? |
This is done now. |
For some people, but most people just get confused when it prints out a type that would take 3 pages of printed paper, so it should probably be something that's opt-in. I'll open up another issue to discuss that. |
Continued in #36517 |
The title says it all. I think I figured out a good format to present stack traces, and I think all users could benefit from it. It's currently in ClearStacktrace.jl and depends on
Crayons.jl
for the colors.I want to know what I can do to make this PR-ready. I don't know how Julia Base handles REPL colors, which ones I'm allowed to use, how I access them without
Crayons
etc. I also don't know if there might be system-specific intricacies of stack traces that I have overlooked, testing this only a Windows and a Mac machine.The basic idea is to have three columns, function, module and signature. Function and module together should basically always fit in a REPL horizontally, while signatures can be very long and are therefore allowed to overflow into new lines, while staying intact when resizing the REPL (compared to more complex but brittle table layouting). The code paths get a new line each, and are also allowed to overflow if they are too long. This keeps clickable links in Atom / VSCode and the like intact.
Just for reference, here is the before and after comparison from the README:


Before:
After:
The text was updated successfully, but these errors were encountered: