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.

Sunday, February 05, 2006

Finding nested ASDF components easily

The problem that prompted this excursion is that I wanted to get at a specific file in another ASDF package, and I didn't particularly care to know where the package is installed. Here's how the code started
    (asdf:find-component nil :cl-catalyst)
There's a much easier way to do this kind of canonical component finding:
(reduce #'asdf:find-component 
        '(nil :cl-catalyst "templates" "error"))
Neat!!! It almost reminds me of logical pathnames...

Wednesday, February 01, 2006

Using mod_rewrite to avoid seeing script names

It seemed like a pretty innocuous job... getting rid of the script name in the request http://localhost/~scottw/puma3/puma3.lisp/extra/path. But you wouldn't believe the pain it caused me! I record here the eventual solution (the inspiration is from this post related to removing the index.php from wiki urls).

Suppose you have a script ~/web/script.cgi, which is ordinarily accessible from the web as ~user/script.cgi. You want to rewrite requests like ~user/path into requests like /~user/script.cgi/path. The important piece being that the script sits in the same directory as your "virtual" path filename. Because of this important fact, the following ~/web/.htaccess will not work:

RewriteEngine On
RewriteBase /~user/
RewriteRule ^(.*)$ script.cgi/$1 [L]
Even though you added [L] to your rule, requesting ~user/anything will result in a 500 error. Looking at the apache logs, you'll see mod_rewrite complaining of too many internal rewrites. This is because your request is being rewritten over and over again like this:
anything -> script.cgi/anything -> script.cgi/script.cgi/anything

This is where the magic happens. You only want the rewrite rule to run when the virtual path doesn't exist. Here's the correct and working solution:

RewriteEngine On
RewriteBase /~user/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ script.cgi/$1
Now after the first rewrite, the request filename begins with script.cgi, so the condition isn't true and the rewrite isn't triggered.