Every now and then I see complaints about the stacktraces in SBCL. They contain too little info, or too much info, or are formatted the wrong way, etc. But the backtrace printing isn't really any dark magic, it's just basic Lisp code. If you don't like the default format, just write a new backtrace function that prints something prettier/less cluttered/more informative/etc.

For inspiration, below is one implementation, based on a really quick hack I wrote in answer to a c.l.l post a few weeks ago. In addition to cosmetic changes, it adds a a couple of extra features: printing filenames and line numbers for the frames when possible, and printing the values of local variables when possible. Just call backtrace-with-extra-info in any condition handler where you'd normally call sb-debug:backtrace , or call it from the debugger REPL instead of using the backtrace debugger command.

The code assumes that you've got Swank loaded. For best results, compile your code with (debug 2) or higher.

(defun backtrace-with-extra-info (&key (start 1) (end 20)) (swank-backend::call-with-debugging-environment (lambda () (loop for i from start to (length (swank-backend::compute-backtrace start end)) do (ignore-errors (print-frame i)))))) (defun print-frame (i) (destructuring-bind (&key file position &allow-other-keys) (apply #'append (remove-if #'atom (swank-backend:frame-source-location-for-emacs i))) (let* ((frame (swank-backend::nth-frame i)) (line-number (find-line-position file position frame))) (format t "~2@a: ~s~%~ ~:[~*~;~:[~2:* At ~a (unknown line)~*~%~;~ ~2:* At ~a:~a~%~]~]~ ~:[~*~; Local variables:~%~{ ~a = ~s~%~}~]" i (sb-debug::frame-call (swank-backend::nth-frame i)) file line-number (swank-backend::frame-locals i) (mapcan (lambda (x) ;; Filter out local variables whose variables we ;; don't know (unless (eql (getf x :value) :<not-available>) (list (getf x :name) (getf x :value)))) (swank-backend::frame-locals i)))))) (defun find-line-position (file char-offset frame) ;; It would be nice if SBCL stored line number information in ;; addition to form path information by default Since it doesn't ;; we need to use Swank to map the source path to a character ;; offset, and then map the character offset to a line number (ignore-errors (let* ((location (sb-di::frame-code-location frame)) (debug-source (sb-di::code-location-debug-source location)) (line (with-open-file (stream file) (1+ (loop repeat char-offset count (eql (read-char stream) #\Newline)))))) (format nil "~:[~a (file modified)~;~a~]" (= (file-write-date file) (sb-di::debug-source-created debug-source)) line))))

For example on the following code:

(declaim (optimize debug)) (defun foo (x) (let ((y (+ x 3))) (backtrace) (backtrace-with-extra-info) (+ x y))) (defmethod bar ((n fixnum) (y (eql 1))) (foo (+ y n)))

The old backtrace would look like:

1: (FOO 4) 2: ((SB-PCL::FAST-METHOD BAR (FIXNUM (EQL 1))) #<unused argument> #<unused argument> 3 1) 3: (SB-INT:SIMPLE-EVAL-IN-LEXENV (BAR 3 1) #<NULL-LEXENV>)

And the new backtrace like:

1: FOO At /tmp/testlisp:5 Local variables: X = 4 Y = 7 2: (SB-PCL::FAST-METHOD BAR (FIXNUM (EQL 1))) At /tmp/testlisp:8 Local variables: N = 3 Y = 1 3: SB-INT:SIMPLE-EVAL-IN-LEXENV At /scratch/src/sbcl/src/code/evallisp:93 (file modified) Local variables: ARG-0 = (BAR 3 1) ARG-1 = #<NULL-LEXENV>

An improvement? That's probably in the eye of the beholder, and depends on the codebase and the use cases. For example I can imagine that for large functions showing the values of local variables in the trace would make it way too spammy. But that's besides the point: if the default stacktrace format is making debugging difficult for you, it's not hard to customize it.