Emacs Debugging Basics


If you are new to Emacs, you may run into some errors, especially after you absorb elisp snippets from the Internet or elsewhere.

Don't panic. It happens. To ease your pain, in this post I will introduce some Emacs built-in features to help you solve the problems yourself.

Start Emacs Without Any Configurations

When you have some problems/errors with built-in features, the first thing you should do is to run emacs -Q to start another Emacs instance without any configurations from your emacs.d.

Here is how to do it in detail if you are not familiar with the terminal:

  1. If you use Linux/macOS, open a terminal, then type the command emacs -Q.

    /img/2022-07-17-emacs-q-linux.png
    emacs -Q on Linux

    If you prefer to use Emacs in a terminal or run Emacs on a server, you can append an argument -nw to the command, that is, emacs -Q -nw.

    P.S. If you want to know more about the command line arguments, you can look it up in the man manual (type man emacs in the shell) or type emacs --help to see the help information, so as to get a better understanding of how to run Emacs from the command line.

  2. Or if you use Windows, you can run the cmd program first (type Super+r cmd ENTER), cd into the Emacs bin directory, then type runemacs.exe -Q to run it.

    /img/2022-07-17-emacs-q-windows.png
    emacs -Q on Windows cmd

Investigate the Faulty Package Within a Clean Emacs

emacs -Q helps to debug built-in features. But when it comes to debugging third-party packages, like the ones you install from melpa, we need to do a bit more, as we need to load the package.

There are roughly two ways to load it.

Using the package machinery

For example, if we now have a problem with my ppcompile package, we can prepare a snippet (e.g. in /tmp/try-ppcompile.el) to prepare the debugging environment:

;; you don't need this if you never change this variable
(setq package-user-dir "~/.emacs.local.d/elpa")

;; init the package manager
(require 'package)
(package-initialize)

;; load the problematic package
(require 'ppcompile)

Now open up a terminal/cmd, type emacs -Q -l /tmp/try-ppcompile.el, and it will start a fairly clean Emacs with ppcompile loaded. From there, we can investigate further to see the problem.

Directly load from a specific directory

There are times that you can't utilize the package machinery to load the package, for example, you git-clone a package and load it from your local file system directly. In that case, you can use Emacs' -L argument to specify the directory.

For example, type emacs -Q -L /tmp/ppcompile/ --eval "(require 'ppcompile)" to launch a new Emacs with the package and start debugging immediately.

Note that you can specify more than one directory.

Use --debug-init To Debug Start-up Errors

It's not uncommon that when you update your emacs.d, emacs errors when you run it next time.

/img/2022-08-07-emacs-startup-error.png
Emacs errors out while starting up

Don't panic! Look carefully, and you will see that Emacs already tells you how to troubleshoot it. (Pay attention to the second red line on the above screenshot)

Open a terminal, type emacs --debug-init, as Emacs tells us in the message, to start a new one. It will tell you where the error exactly comes from and the backtrace. Be patient and try to understand the problem, you will come up with a solution, hopefully! (The root cause in the screenshot is that the a-bug-function function is not defined)

/img/2022-08-07-emacs-debug-init.png
Append --debug-init to run Emacs

M-x toggle-debug-on-error to Enable Debugging

(In case you do not know yet, here is how to type M-x toggle-debug-on-error on the keyboard: ALT+x toggle-debug-on-error RETURN)

You may see something like the below when you're using Emacs:

/img/2022-07-17-symbols-function-definition-is-void.png
No Function Definition Error in the Echo Area

To simulate such an error, for example, say we have a silly welcome command as below:

(defun welcome ()
  (interactive)
  (this-func-not-exist))

Now when you type M-x welcome to invoke the command, you will see the error as in the above screenshot.

Now, type M-x toggle-debug-on-error to enable debugging, then it will bring up a new window with the backtrace when we M-x welcome once again.

/img/2022-07-17-elisp-debugger-backtrace.png
elisp debugger backtrace 1: Symbol's function definition is void

From the backtrace, we can see that the error is because the function of this-func-not-exist does not exist, so the welcome command fails.

Set the debug-on-message Variable

Other times, you may see some unexpected message on the echo area at the bottom. You can try to play around the debug-on-message variable to get the backtrace of where it comes from and figure it out.

For example, the below command will send out a message welcome who? when you invoke it:

(defun welcome ()
  (interactive)
  (message "welcome who?"))

Let's pretend that we now want to find out who print the nonsense message of welcome who, and we can utilize the debug-on-message variable to set up a regular expression, for instance, (setq debug-on-message "welcome who").

Once there is a message that matches the regexp, Emacs will trap it into the debugger, and we will see who prints that message:

/img/2022-07-17-elisp-debugger-backtrace.png
elisp debugger backtrace 2

Other Debugging Helpers and Facilities

Variable ways to look up help docs:

  1. C-h f to look up a function(C-h f means to type CTRL+h f in the keyboard)
  2. C-h v to look up a variable
  3. C-h k to look up a keybinding
  4. C-h m to look up which major mode and minor modes are enabled in the current buffer
  5. C-h l to view lossage, put it in another way, you will see the history of the invoked commands in a dedicated buffer

Misc:

  1. M-x describe-char to see the properties of the character at point, for example, its font face.
  2. debug-on-entry function Emacs starts the debugger automatically when your function has an error, also refer to debug-on-entry (Programming in Emacs Lisp) - www.gnu.org.
  3. debug-on-quit variable refer to debug-on-quit (Programming in Emacs Lisp) - www.gnu.org
  4. --init-directory Emacs 29 adds a command line argument of --init-directory to specify a directory other than ~/.emacs.d/ to run Emacs, where we can put our debugging code to do the investigation separately.

See also

comments powered by Disqus