Idea for shortening stacktraces

There are already approaches for shortening stack traces in external packages, but I’ve been wanting something a little less based on others’ heuristics and more on my own knowledge about the functions I write.

So the idea is to add a function to the stack trace machinery that can determine for a given method signature whether / how many of the following stack frames can be skipped. Here’s how it works in a mockup I have. Let’s say we’re annoyed by broadcasting machinery noise in the stack trace. This is always the same I think, unless we’re dealing with some overloaded special behavior.

f(args...) = error("Some error")
f.([1,2])

gives:

Now I define my special function for the materialize method that always starts the broadcasting machinery:

function Base.stackframe_shortening(::Type{Tuple{typeof(Base.Broadcast.materialize), Base.Broadcast.Broadcasted}})
    ("broadcast internals", 5)
end

For my mockup, I just counted stack frames and didn’t do any validation logic. But in principle the function could check if the next stack frames have specific properties that allow skipping. Then this results in this shortened stack trace:

I wanted to know if there were already others exploring manual approaches like this, what pros and cons might be, if you think it’s worth pursuing etc. For example I thought this could be useful for the noise that Observables add to Makie’s stack traces.

1 Like

This is along the same lines I was thinking with Making stacktraces shorter (reducing the number of frames) · JuliaLang/julia · Discussion #49044 · GitHub. The discussion raises a diversity of examples, some of which are reputed to be hard to shorten (I haven’t gotten back to that yet…)

In the meantime, check out my package here GitHub - BioTurboNick/AbbreviatedStackTraces.jl

3 Likes

@BioTurboNick Yes I knew AbbreviatedStackTraces of course :slight_smile: I tend to find its outputs a bit too short for my liking, because it operates not on a per-function base but on a boundary between user and package code base. Which is nice in that it highlights user code, but I still want to understand in what way I traverse through other functions, just not the parts that are redundant and therefore don’t add information for me. In the sum example that’s in the readme, I would want to know that we got to the error by mapreducing over an empty collection for which I need mapreduce and mapreduce_empty_iter.

@tim.holy I’ll have a look at that issue, thanks!

1 Like

The upcoming public keyword may be able to help here. I haven’t looked at how that could work yet though.

Though, needing to see an internal function name to know why a failure occurred IMO points to the error being bad.

1 Like

I was inspired. Made a PR to AbbreviatedStackTraces to explore using the public names API: Test of relying on new public api definitions in Julia by BioTurboNick · Pull Request #64 · BioTurboNick/AbbreviatedStackTraces.jl · GitHub

Currently, just throwing back in any frames from public functions.

1 Like