SourceLevelDebugger

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.

Example

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))))

Use ‘M-x foo’. This prints 12 in the echo area.

Now instrument ‘bar’ by moving the cursor into its code and hitting `C-u C-M-x’. The echo area shows Edebug: bar.

Use ‘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 ‘q’.

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 ‘M-x edebug-display-freq-count’.

Inserting a direct call to edebug

    (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 ‘g’ for ‘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 ‘stop-here’ macro.

Test coverage

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 ‘C-u C-M-x’

  (defun fact (n)
    (if (<= n 1)
        1
      (* n (fact (1- n)))))

Then evaluate an expression that uses the function.

  (fact 6)

Enter ‘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.

The simple 'Watch window' functionality

In Edebug one can get the evaluation list where one could type statements, inspect variables etc, by using ‘E’ key. With this one could easily simulate Watch window functionality from other IDEs, i.e. add variables/statements there. There is (as always) a way to automate it: one could position a cursor on the variable or select a statement and hit the ‘A’ key to get the statement in the Edebug eval list buffer with the code below.

  ;; we are using eldoc-current-symbol from eldoc
  (require 'eldoc)
  ;; define a key
  (define-key edebug-mode-map "A" 'my-elisp-add-to-watch) 
  ;; the implementation
  (defun my-elisp-add-to-watch (&optional region-start region-end)
    "Add the current variable to the *EDebug* window"
    (interactive "r")
    (let ((statement
             (if (and region-start region-end (use-region-p))
               (buffer-substring region-start region-end)
             (symbol-name (eldoc-current-symbol)))))
      ;; open eval buffer
      (edebug-visit-eval-list)
      ;; jump to the end of it and add a newline
      (goto-char (point-max))
      (newline)
      ;; insert the variable
      (insert statement)
      ;; update the list
      (edebug-update-eval-list)
      ;; jump back to where we were
      (edebug-where)))

See also DebuggerMode, EmacsLispTracing.


CategoryDebug