Clementson's Blog

Bits and pieces (mostly Lisp-related) that I collect from the ether.

June 2005
Sun Mon Tue Wed Thu Fri Sat
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
May  Jul

Some useful CL debugging code

Thursday, June 16, 2005

I have posted about debugging in CL in the past (see here and here). Well, Wade Humeniuck recently posted on c.l.l. some useful code that he developed for debugging in CL. Here it is:

(defvar *debug-output* t) 
(define-condition progn-debug-output (simple-condition) ((form :initarg :form) (multiple-value-list :initarg :multiple-value-list)))
(defun progn-debugger (condition value) (declare (ignorable value)) (typecase condition (progn-debug-output (with-slots (form multiple-value-list) condition (when *debug-output* (cond ((and (= (length multiple-value-list) 1) (eql form (first multiple-value-list))) (format *debug-output* " ~S~%" form)) (t (format *debug-output* " ~S -> ~{~S~^,~}~%" form multiple-value-list)))) (signal condition)))))
(defmacro progn-debug (&body body) (let ((completedp (gensym "completed")) (values (gensym "values"))) `(let ((*debugger-hook* #'progn-debugger) (,completedp nil)) (unwind-protect (progn (when *debug-output* (format *debug-output* "(PROGN-DEBUG~%")) (let ((,values (progn ,@(mapcar (lambda (form) `(handler-case (invoke-debugger (make-condition 'progn-debug-output :form ',form :multiple-value-list (multiple-value-list ,form))) (progn-debug-output (condition) (slot-value condition 'multiple-value-list)))) body)))) (setf ,completedp t) (apply #'values ,values))) (when *debug-output* (unless ,completedp (format *debug-output* " #<Abnormal termination of PROGN-DEBUG>~%")) (format *debug-output* ")~%"))))))

Here's an example session using Wade's code:
CL-USER> (progn-debug
	   (+ 2 3)
	   (format t "this is a test")
	   (defparameter x 10)
	   (values (setf x (/ x 10)) (incf x)))
(PROGN-DEBUG
  (+ 2 3) -> 5
this is a test  (FORMAT T "this is a test") -> NIL
  (DEFPARAMETER X 10) -> X
  (VALUES (SETF X (/ X 10)) (INCF X)) -> 1,2
)
1
2
CL-USER>

Another example of a useful debugging utility is the following code snippet which is based on a macro that Kenny Tilton posted on c.l.l. at one stage (unfortunately, I didn't keep a record of the URL of his post and I have made a few minor mods to his code):
(defmacro debug-prt (tag &rest sexps)
  `(progn
     ,@(progn
	(format t "~&debug-prt> start ~a" tag)
	(mapcar (lambda (s)
		  (format t "~&debug-prt> eval: ~a=~a" s (eval s)))
		sexps))
     (format t "~&debug-prt> end ~a" ,tag)))

Here's an example session using the above code:
CL-USER> (debug-prt :test
		   (+ 2 3)
	           (format t "this is a test")
	           (defparameter x 10)
	           (values (setf x (/ x 10)) (incf x)))
debug-prt> start TEST
debug-prt> eval: (+ 2 3)=5this is a test
debug-prt> eval: (FORMAT T this is a test)=NIL
debug-prt> eval: (DEFPARAMETER X 10)=X
debug-prt> eval: (VALUES (SETF X (/ X 10)) (INCF X))=1
debug-prt> end TEST

NIL CL-USER>

Kenny's macro is nice and concise and provides a mechanism for tagging different blocks of debug code, however, it doesn't handle the display of multiple values that are returned by a lisp form. Both Wade and Kenny's code are useful code snippets to keep in your toolbox and good examples to look at if you're interested in creating your own debugging code!

emacs Copyright © 2005 by Bill Clementson