Sometimes when something goes wrong, what you want is not just a
representation of the exceptional situation, but the context that
brought about that situation. The example in the previous section
passed #:unwind #t
to with-exception-handler
, indicating
that raise-exception
should unwind the stack before invoking the
exception handler. However if you don’t take this approach and instead
let the exception handler be invoked in the context of the
raise-exception
, you can print a backtrace, launch a recursive
debugger, or take other “pre-unwind” actions.
The most basic idea would be to simply print a backtrace:
(define (call-with-backtrace thunk) (with-exception-handler (lambda (exn) (backtrace) (raise-exception exn)) thunk))
Here we use the built-in backtrace
procedure to print the
backtrace.
Display a backtrace of the current stack to the current output port. If highlights is given it should be a list; the elements of this list will be highlighted wherever they appear in the backtrace.
By re-raising the exception, call-with-backtrace
doesn’t actually
handle the error. We could define a version that instead aborts the
computation:
(use-modules (ice-9 control)) (define (call-with-backtrace thunk) (let/ec cancel (with-exception-handler (lambda (exn) (backtrace) (cancel #f)) thunk)))
In this second example, we use an escape continuation to abort the
computation after printing the backtrace, returning #f
instead.
It could be that you want to only print a limited backtrace. In that
case, use start-stack
:
(use-modules (ice-9 control)) (define (call-with-backtrace thunk) (let/ec cancel (start-stack 'stack-with-backtrace (with-exception-handler (lambda (exn) (backtrace) (cancel #f)) thunk))))
There are also more powerful, programmatic ways to walk the stack using
make-stack
and friends; see the API described in Stacks and
Frames.