Edebug is a source level debugger for EmacsLisp. It instruments code. This means that Emacs adds special instructions to the code when it is evaluated. The main entry point is
‘M-x edebug-defun’ (also on
‘C-u C-M-x’). Use it instead of `C-x C-e’ or
‘C-M-x’ to evaluate a
‘defun’ and instrument it for debugging.
‘edebug-defun’ must be able to read your code. It expects all function definitions to start in column 0. If they do not, hitting
‘C-u C-M-x’ will show you the wrong function name in the echo area.
Paste the following functions into the
‘*scratch*’ buffer (starting at column 0). Hit
‘C-M-x’ with the cursor inside each of the function definitions.
(defun foo () (interactive) (bar)) (defun bar () (let ((a 5) (b 7)) (message "%d" (+ a b))))
‘M-x foo’. This prints
12 in the echo area.
‘bar’ by moving the cursor into its code and hitting `C-u C-M-x’. The echo area shows
‘M-x foo’ again. Emacs steps through the code of
‘bar’, showing you the result of evaluating each sexp in the echo area. Hit
‘SPC’ to step ahead or
‘e’ to evaluate another expression. The arrow shows you which sexp will be evaluated next:
(defun bar () =>(let ((a 5) (b 7)) (message "%d" (+ a b))))
To quit the debugger, type
Edebug can enter any defun whose source can be found with FindFunc by hitting
‘i’ which steps into the next function to be evaluated. This instruments the function and will be debugged every time it is run.
You can disable edebug on a function by evaluating the function again using
‘C-M-x’ without a PrefixArgument.
You can see the code coverage of the defun at point with `C-x X =’ or
(defmacro stop-here (fn) "Call edebug here. FN is assumed to be a symbol of the function you are in." `(if (consp (get ,fn 'edebug)) (edebug))) (defun clear-edebug (fn-sym) "Remove 'edebug property from FN-SYM, a function symbol." (put fn-sym 'edebug nil))
;; An example of use: (defun fact (n) (cond ((= n 0) ) ((= n 1) 1) ((> n 1) (progn (stop-here 'fact) (* n (fact (1- n))))) (t nil)))
If you eval the above and then edebug-defun while the point is on fact, you’ll first stop at fact but then when you enter
‘go’ it will next stop after the (* n… ) which is what I want.
And note you could also put a condition around `stop-here` for example:
((> n 1) (progn (if (= n 3) (stop-here 'fact)) (* n (fact (1- n)))))
What’s hoaky about the above is all the verbiage in the (progn … ) and that you have to enter the function argument ’fact in
‘stop-here’. There may be a way to reduce this in the
Assume you have the following Lisp code.
(let ((x 2)) (let ((x^2 (* x x))) (while (> x^2 0) (setq x x^2 x^2 (* x^2 x^2)))) (let ((2x (+ x x))) (while (> 2x 0) (setq x 2x 2x (+ 2x 2x)))) (let ((x/2 (/ x 2))) (while (and (> x/2 0) (> (+ x x/2) 0)) (setq x (+ x x/2) x/2 (/ x/2 2)))) x)
If you wanted to see how often each expression is used you can use Edebug. First, start Edebug on the top-level expression with
‘C-u C-M-x’. Then hit
‘c’ to completely evaluate the expression until it’s finished followed by `C-x X =’ (
‘edebug-display-freq-count’). It will annotate the source code as follows:
(let ((x 2)) ;#1 (let ((x^2 (* x x))) ;#1 (while (> x^2 0) ;# 1 6 (setq x x^2 x^2 (* x^2 x^2)))) ;# 5 1 (let ((2x (+ x x))) ;#1 (while (> 2x 0) ;# 1 28 (setq x 2x 2x (+ 2x 2x)))) ;# 27 1 (let ((x/2 (/ x 2))) ;#1 (while (and (> x/2 0) (> (+ x x/2) 0)) ;# 1 60 59 60 (setq x (+ x x/2) x/2 (/ x/2 2)))) ;# 59 1 x)
It says all the let-expressions were each visited once. The test in the fist while-loop was done 6 times and its body 5 times. The next while-loop 28 times and so on.
Using a function is similar, first instrument the defun with
(defun fact (n) (if (<= n 1) 1 (* n (fact (1- n)))))
Then evaluate an expression that uses the function.
‘c’ to continue the evaluation until it completes. Move your point back in to the function and use `C-x X =’.
(defun fact (n) (if (<= n 1) ;#6 1 (* n (fact (1- n))))) ;# 5 6
The function was entered 6 times, and the second condition 5 times.