Tuesday, February 07, 2006

Capturing a debugger backtrace

In my web application, I want to capture backtraces automatically and continue with the top-level request-handler-response loop. Here's how I did it:
(with-output-to-string (s)
 (block nil
    (handler-bind ((t #'(lambda (condition)
                                (sys::print-backtrace :out s)
                                (fresh-line s)
                                (sys::pretty-print-condition condition s)
       (error "foo"))))
=> ".....[giant backtrace omitted]....."
Pretty ugly. I need to do some work to get clisp to print only the relevant frames (ie none of the driver, and none of the frames before the handler-bind, which I don't really care about). Pascal Bourguignon suggests a more elegant abstraction, which I have adopted for my own purposes (having the condition object responsible for the mess seemed useful).
(defmacro collecting-backtrace (&body body)
  (let ((s (gensym)) (b (gensym)))
    `(block ,b
           ((t (lambda (condition)
                 (return-from ,b 
                   (values nil condition
                           #+clisp (with-output-to-string (,s)
                                     (sys::print-backtrace :out ,s)
                                     (fresh-line ,s)
                                     (sys::pretty-print-condition condition ,s))
                           (format nil
                             "I don't know how to generate a backtrace on ~A"
         (values (multiple-value-list (progn ,@body)) nil)))))
Much cleaner. I wonder whether the t in the handler-bind should be customizable... Anyways, still haven't looked into limiting the verbosity and depth of the stack trace.


Post a Comment

<< Home