15

Say that emacs throws some error that I don't understand. Or maybe the error says "Symbol's value as variable is void: modes", but there are many occurrences of the symbol modes in my code, so I need some context. Can Emacs be configured to mention the line number of the lisp code so I can know what code is causing the error?

I have tried doing (setq stack-trace-on-error '(buffer-read-only)) and ran the perpetrating code in an effort to get a stack trace. No stack trace either.

I have also tried calling edebug-defun on my function and stepping through it. It is not until I step out of the function that the error is thrown.

(I am really not as interested in the cause of the particular error I am facing currently as I am in developing general debugging skills for elisp. Please advise on how I can gleam a line number, or a sexp, or a stack trace from an error.)

  • Did you already try non-nil debug-on-error? Doesn't that help? – Drew Feb 1 '15 at 6:05
  • Nope. That doesn't seem to do anything. (After I set it to t and then proceed to eval an error-throwing function.) – Jackson Feb 1 '15 at 6:26
  • Probably what happens is that some other code catches the error and just prints the error message. Also check that debug-ignored-errors doesn't list any errors. If you set debug-on-signal to non-nil, and it was the case the other code handled the error, you will be able to get the error before the other code did. – wvxvw Feb 1 '15 at 6:55
  • I am currently in a similar situation and was reading this question. I am wondering about stack-trace-on-error. This variable is not documented in Emacs 25.1. – Matthias Feb 8 '17 at 14:07
15

Emacs provides a good amount of debugging facilities including M-x toggle-debug-on-error, M-x toggle-debug-on-quit, debug on signal (which can be used by sending USR2 to Emacs from outside), debug-on-entry (of a function), debug-on-message (when seeing a specific regexp match of a message) and finally, debug itself as alternative to instrumenting a function with C-u C-M-x.

Both debug and edebug offer enough functionality to inspect the state of Emacs when evaluating the code that you're interested in, hit e and enter an expression.

However, while edebug jumps to the place in the instrumented function and therefore gives you a clue where to look (which is kind of silly since you already know what exactly you've instrumented), debug doesn't do this at all. I've pulled off a smaller hack after discovering that whenever debug evaluates a buffer, it emits the value of point associated with the error; in other words using this information on the buffer can give you a line number in the backtrace!

(with-eval-after-load 'debug
  (defun debugger-setup-buffer (debugger-args)
    "Initialize the `*Backtrace*' buffer for entry to the debugger.
That buffer should be current already."
    (setq buffer-read-only nil)
    (erase-buffer)
    (set-buffer-multibyte t)        ;Why was it nil ?  -stef
    (setq buffer-undo-list t)
    (let ((standard-output (current-buffer))
          (print-escape-newlines t)
          (print-level 8)
          (print-length 50))
      (backtrace))
    (goto-char (point-min))
    (delete-region (point)
                   (progn
                     (search-forward "\n  debug(")
                     (forward-line (if (eq (car debugger-args) 'debug)
                                       2    ; Remove implement-debug-on-entry frame.
                                     1))
                     (point)))
    (insert "Debugger entered")
    ;; lambda is for debug-on-call when a function call is next.
    ;; debug is for debug-on-entry function called.
    (pcase (car debugger-args)
      ((or `lambda `debug)
       (insert "--entering a function:\n"))
      ;; Exiting a function.
      (`exit
       (insert "--returning value: ")
       (setq debugger-value (nth 1 debugger-args))
       (prin1 debugger-value (current-buffer))
       (insert ?\n)
       (delete-char 1)
       (insert ? )
       (beginning-of-line))
      ;; Debugger entered for an error.
      (`error
       (insert "--Lisp error: ")
       (prin1 (nth 1 debugger-args) (current-buffer))
       (insert ?\n))
      ;; debug-on-call, when the next thing is an eval.
      (`t
       (insert "--beginning evaluation of function call form:\n"))
      ;; User calls debug directly.
      (_
       (insert ": ")
       (prin1 (if (eq (car debugger-args) 'nil)
                  (cdr debugger-args) debugger-args)
              (current-buffer))
       (insert ?\n)))
    ;; After any frame that uses eval-buffer,
    ;; insert a line that states the buffer position it's reading at.
    (save-excursion
      (let ((tem eval-buffer-list))
        (while (and tem
                    (re-search-forward "^  eval-\\(buffer\\|region\\)(" nil t))
          (beginning-of-line)
          (insert (format "Error at line %d in %s: "
                          (with-current-buffer (car tem)
                            (line-number-at-pos (point)))
                          (with-current-buffer (car tem)
                            (buffer-name))))
          (pop tem))))
    (debugger-make-xrefs)))

With this the original question in the title should be answered. As for your problem with getting a backtrace in the first place, I'm out of useful ideas.

  • Thanks for your help, but I still don't understand how to get the line number. M-x debug ...? Then what do I press? – Jackson Feb 7 '15 at 21:56
  • With this code you'll see a line number in backtraces made by debug, you can check by visiting a faulty elisp file, doing M-x toggle-debug-on-error and M-x eval-buffer, then a backtrace with a line number at the problematic position should pop up. – wasamasa Feb 7 '15 at 23:37
  • Will this work if you don't use eval-buffer? For example, if you just press a keyboard shortcut that runs a private command that fails and opens the debugger in the *Backtrace* buffer.. – Håkon Hægland Mar 16 '15 at 18:05
  • No, it won't. You get the function value of the symbol (which can either be a list or something byte-compiled) and that's pretty much it. – wasamasa Mar 16 '15 at 18:36
4

Maybe because it's 2018 now, but in my case, I only had to turn on debugging like wasamasa suggested: M-x toggle-debug-on-error

After this, M-x eval-buffer on my faulty Elisp file gave context by providing the position of the error, like this: Debugger entered--Lisp error: (invalid-read-syntax ")") eval-buffer() ; Reading at buffer position 523 [....]

M-x goto-char jumps to the error position: M-x goto-char 523

  • Nice find! It appears this has been added in 2017, back when they reworked that function to work on a list of backtrace items. – wasamasa Sep 11 '18 at 7:39
1

I've extended wasamasa's answer to include additional information:

(save-excursion
  (let ((tem eval-buffer-list))
    (while (and tem
                (re-search-forward "^  eval-\\(buffer\\|region\\)(" nil t))
      (beginning-of-line)
      (insert (apply 'format "Error at line %d, column %d (point %d) in %s\n"
                     (with-current-buffer (car tem)
                       (list (line-number-at-pos (point))
                             (current-column)
                             (point)
                             (buffer-name)))))
      (pop tem))))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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