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"?

Tobias
  • 33,167
  • 1
  • 37
  • 77
american-ninja-warrior
  • 3,903
  • 2
  • 24
  • 44
  • 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 07 '19 at 14:23
  • @JeanPierre: Please consider adding that info as an answer. – Drew Nov 07 '19 at 15:31
  • The previous title could not be understood without context. I hope you accept the new one. – Tobias Nov 11 '19 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 '19 at 11:18
  • I think it would be helpful to me if all functions of package robe.el was instrumented. – american-ninja-warrior Nov 11 '19 at 18:16

3 Answers3

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.

Drew
  • 77,472
  • 10
  • 114
  • 243
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.

Tobias
  • 33,167
  • 1
  • 37
  • 77
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’.

JeanPierre
  • 7,465
  • 1
  • 20
  • 39