4

When working with bash scripts, when something is not behaving the way you want it to, you can add a

set -x

and the next time you run the script you can see the methods and the sub methods being called in real time.

Is there an equivalent switch in "elisp"?

5
  • I don't know anything like that, but if you want to see the calls for a few specific functions you can use M-x trace-function. See also "Debugging lisp programs" in the elisp manual.
    – JeanPierre
    Nov 7, 2019 at 14:23
  • @JeanPierre: Please consider adding that info as an answer.
    – Drew
    Nov 7, 2019 at 15:31
  • The previous title could not be understood without context. I hope you accept the new one.
    – Tobias
    Nov 11, 2019 at 11:17
  • Could you please explain what you mean by sub methods. Do you mean that all functions called in the function body should also be instrumented for tracing? I am afraid that this clutters up the tracing buffer quite much.
    – Tobias
    Nov 11, 2019 at 11:18
  • I think it would be helpful to me if all functions of package robe.el was instrumented. Nov 11, 2019 at 18:16

3 Answers 3

5

The Elisp debugger doesn't provide a trace, but it does let you investigate (and even affect) the evaluation of Lisp code on the fly.

  • You can enter the debugger, to walk or skip through any function, using M-x debug-on-entry.

  • You can put breakpoints, which enter the debugger, at any place in Lisp source code, just by adding (debug). (See C-h f debug for info about optional args.)

  • You can automatically open the debugger when an error is raised, by setting variable debug-on-error to non-nil.

See the Elisp manual, starting with node Invoking the Debugger. In Emacs you can get to that by C-h i, choosing Elisp, then i debug RET.

There is also Edebug, whose use is a bit different. For that, you "instrument" particular functions whose evaluation in calls you want to investigate. You can read about it here: C-h i then choose Elisp, then g edebug RET. That takes you to node Edebug of the Elisp manual.

2
3

The function edebug-instrument-for-tracing as defined in the following Elisp snippet works similar to edebug-defun. But, instead of instrumenting stuff for edebug it prepares it for tracing into the buffer *edebug-trace*.

(defun edebug-untrace (form)
  "Remove tracing instructions from FORM."
  (if (consp form)
      (if (eq (car form) 'edebug-tracing)
          (edebug-untrace (caddr form))
        (cons (edebug-untrace (car form))
              (edebug-untrace (cdr form))))
    form))

(defcustom edebug-trace-print-level 3
  "`print-level' for `edebug-make-trace-form'."
  :type 'integer
  :group 'edebug)

(defcustom edebug-trace-print-length 5
  "`print-length' for `edebug-make-trace-form'."
  :type 'integer
  :group 'edebug)

(defun edebug-make-trace-form (form)
  "Prepare FORM for tracing."
  `(edebug-tracing ,(let ((print-level edebug-trace-print-level)
                          (print-length edebug-trace-print-length))
                      (prin1-to-string (edebug-untrace form)))
                   ,form))

(defun edebug-make-trace-enter-wrapper (forms)
  "Prepare function with FORMS for tracing."
  (if edebug-def-name
      `(edebug-tracing ,(format "%S%S"
                                edebug-def-name
                                (nreverse edebug-def-args))
                       ,@forms)
    `(progn ,@forms)))

(defun edebug-instrument-for-tracing ()
  "Like `edebug-defun' but instruments for tracing."
  (interactive)
  (cl-letf (((symbol-function 'edebug-make-enter-wrapper)
             (lambda (forms)
               (edebug-make-trace-enter-wrapper forms)))
            ((symbol-function 'edebug-make-before-and-after-form)
             (lambda (before-index form after-index)
               (edebug-make-trace-form form)))
            ((symbol-function 'edebug-make-after-form)
             (lambda (form after-index)
               (edebug-make-trace-form form))))
    (edebug-defun)))

For testing, put point into the following form and run M-x edebug-instrument-for-tracing.

(let ((i 0))
  (while (< i 2)
    (message "i=%d" i)
    (setq i (1+ i))))

You get an *edebug-trace* buffer with the following output. Each form is printed before and after its evaluation. The result of the evaluation of the form is also printed.

{ (let ((i 0)) (while (< i 2) (message "i=%d" i) (setq i ...)))
:{ (while (< i 2) (message "i=%d" i) (setq i (1+ i)))
::{ (< i 2)
:::{ i
:::} i result: 0
::} (< i 2) result: t
::{ (message "i=%d" i)
:::{ i
:::} i result: 0
::} (message "i=%d" i) result: i=0
::{ (setq i (1+ i))
:::{ (1+ i)
::::{ i
::::} i result: 0
:::} (1+ i) result: 1
::} (setq i (1+ i)) result: 1
::{ (< i 2)
:::{ i
:::} i result: 1
::} (< i 2) result: t
::{ (message "i=%d" i)
:::{ i
:::} i result: 1
::} (message "i=%d" i) result: i=1
::{ (setq i (1+ i))
:::{ (1+ i)
::::{ i
::::} i result: 1
:::} (1+ i) result: 2
::} (setq i (1+ i)) result: 2
::{ (< i 2)
:::{ i
:::} i result: 2
::} (< i 2) result: nil
:} (while (< i 2) (message "i=%d" i) (setq i (1+ i))) result: nil
} (let ((i 0)) (while (< i 2) (message "i=%d" i) (setq i ...))) result: nil

Note that this is a kind of simple prototype-implementation. It is not very efficient. It may be that tracing becomes a bit slow on deeply nested functions.

2

I don't know anything like that, but if you want to see the calls (including parameters and return value) for a few specific functions you can use the trace-function command.

Here's its docstring:

(trace-function FUNCTION &optional BUFFER CONTEXT)

Trace calls to function FUNCTION.

With a prefix argument, also prompt for the trace buffer (default ‘trace-buffer’), and a Lisp expression CONTEXT.

Tracing a function causes every call to that function to insert into BUFFER Lisp-style trace messages that display the function’s arguments and return values. It also evaluates CONTEXT, if that is non-nil, and inserts its value too. For example, you can use this to track the current buffer, or position of point.

...

To stop tracing a function, use ‘untrace-function’ or ‘untrace-all’.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.