*debugger-hook*
∗debugger-hook∗ Variable
Value Type:
a designator for a function of two arguments (a condition and the value of *debugger-hook* at the time the debugger was entered), or nil.
Initial Value:
nil.
Description:
When the value of *debugger-hook* is non-nil, it is called prior to normal entry into the debugger, either due to a call to invoke-debugger or due to automatic entry into the debugger from a call to error or cerror with a condition that is not handled. The function may either handle the condition (transfer control) or return normally (allowing the standard debugger to run). To minimize recursive errors while debugging, *debugger-hook* is bound to nil by invoke-debugger prior to calling the function.
Examples:
(defun one-of (choices &optional (prompt "Choice"))
(let ((n (length choices)) (i))
(do ((c choices (cdr c)) (i 1 (+ i 1)))
((null c))
(format t "~&[~D] ~A~%" i (car c)))
(do () ((typep i ‘(integer 1 ,n)))
(format t "~&~A: " prompt)
(setq i (read))
(fresh-line))
(nth (- i 1) choices)))
(defun my-debugger (condition me-or-my-encapsulation)
(format t "~&Fooey: ~A" condition)
(let ((restart (one-of (compute-restarts))))
(if (not restart) (error "My debugger got an error."))
(let ((\*debugger-hook\* me-or-my-encapsulation))
(invoke-restart-interactively restart))))
(let ((\*debugger-hook\* #’my-debugger))
(+ 3 ’a))
▷ Fooey: The argument to +, A, is not a number.
▷ [1] Supply a replacement for A.
▷ [2] Return to Cloe Toplevel.
▷ Choice: 1
▷ Form to evaluate and use: (+ 5 ’b)
▷ Fooey: The argument to +, B, is not a number.
▷ [1] Supply a replacement for B.
▷ [2] Supply a replacement for A.
▷ [3] Return to Cloe Toplevel.
▷ Choice: 1
▷ Form to evaluate and use: 1
→ 9
Affected By:
invoke-debuggerNotes:
When evaluating code typed in by the user interactively, it is sometimes useful to have the hook function bind *debugger-hook* to the function that was its second argument so that recursive errors can be handled using the same interactive facility.
Expanded Reference: *debugger-hook*
Default Value
*debugger-hook* is nil by default, meaning the standard debugger is used.
*debugger-hook*
=> NIL
Installing a Custom Debugger Hook
When *debugger-hook* is non-nil, it should be a function of two arguments: the condition and the previous value of *debugger-hook*. It is called by invoke-debugger before entering the standard debugger.
;; A hook that prints the condition and aborts instead of entering the debugger
(let ((*debugger-hook*
(lambda (condition old-hook)
(declare (ignore old-hook))
(format t "Hook caught: ~A~%" condition)
(abort))))
;; In a real session, (error "test") would invoke this hook.
;; The hook must either handle the condition (transfer control)
;; or return normally to enter the standard debugger.
*debugger-hook*)
Automatic NIL Binding
When invoke-debugger calls the hook function, it binds *debugger-hook* to nil first. This prevents infinite recursion if the hook itself signals an error.
;; Inside the hook, *debugger-hook* is nil:
;; (let ((*debugger-hook*
;; (lambda (condition old-hook)
;; (declare (ignore condition old-hook))
;; ;; Here, *debugger-hook* is NIL
;; (format t "hook is nil: ~A~%" *debugger-hook*)
;; (abort))))
;; (error "test"))
Re-binding the Hook for Recursive Errors
The hook's second argument is the previous value. You can rebind *debugger-hook* to it if you want recursive errors to use the same hook.
;; (let ((*debugger-hook*
;; (lambda (condition old-hook)
;; (format t "Handling: ~A~%" condition)
;; (let ((*debugger-hook* old-hook))
;; ;; Now recursive errors also go through the hook
;; (abort)))))
;; (error "test"))