Limit `catch_backtrace()` to function handling the exception

Consider the following example:

f(x) = 1/sqrt(x)

function myfunc(x)
    try
        @show f(x)
    catch exc
        @error "Exception in f" exception=(exc, catch_backtrace())
    end
end

myfunc(-1)

If I run this in the terminal, it will print

┌ Error: Exception in f
│   exception =
│    DomainError with -1.0:
│    sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).
│    Stacktrace:
│      [1] throw_complex_domainerror(f::Symbol, x::Float64)
│        @ Base.Math ./math.jl:33
│      [2] sqrt
│        @ ./math.jl:677 [inlined]
│      [3] sqrt
│        @ ./math.jl:1491 [inlined]
│      [4] f
│        @ ~/test_backtrace.jl:1 [inlined]
│      [5] macro expansion
│        @ ./show.jl:1128 [inlined]
│      [6] myfunc(x::Int64)
│        @ Main ~/test_backtrace.jl:5
│      [7] top-level scope
│        @ ~/test_backtrace.jl:11
│      [8] include(mod::Module, _path::String)
│        @ Base ./Base.jl:457
│      [9] exec_options(opts::Base.JLOptions)
│        @ Base ./client.jl:307
│     [10] _start()
│        @ Base ./client.jl:522
└ @ Main ~/test_backtrace.jl:7

In a Jupyter notebook, the backtrace is even deeper.

How can I limit / filter catch_backtrace() to the function where I’m handling the exception? In this case, that would be myfunc, so I would like to truncate the backtrace after element [6].

You could also check out AbbreviatedStackTraces.jl which aims give a good overview of the relevant lines.

Of course, it is quite a difficult problem in general to select the most relevant bits. While it is clear in your example, the situation becomes less clear if not all relevant code is in one file.

1 Like

The following seems to work:

f(x) = 1/sqrt(x)


function catch_abbreviated_backtrace()
    bt_caught = catch_backtrace()
    bt_here = backtrace()
    N = length(bt_caught)
    offset = length(bt_here) - N
    while N > 1
        if bt_caught[N] ≢ bt_here[N + offset]
            break
        end
        N = N - 1
    end
    return bt_caught[1:N]
end


function myfunc(x)
    try
        @show f(x)
    catch exc
        @error "Exception in f" exception=(exc, catch_abbreviated_backtrace())
    end
end

myfunc(-1)

The backtrace data structure is a bit non-transparent, but what this tries to do is to simply throw out the lower frames common between catch_backtrace() and backtrace() of the handling function.

1 Like