Debugging Elisp
debug
debug
is a Lisp machine stack back-trace debugger.
Usual workflow:
- Choose the way to initiate debugger. E.g. call
debug-on-entry
and choose a function. See bellow for other options. - Do some operation that should eventually call the function.
- In the backtrace inspect the state, step through, look at locals, evaluate expressions etc. See bellow for useful shortcuts.
- Initiating debugger - useful shortcuts/functions
debug-on-error
- enters the debugger if an error is found.debug-on-entry
- starts debugger when entering the given function. Multiple function can be given by calling this function many times. To remove a function usecancel-debug-on-entry
.debug-on-message
- this is a variable. If non-nil it is interpreted as a regex which is matched against printed messages and it breaks into debugger if a match is found. Useful if you see a message printed and would like to investigate the location.debug-watch
- run debugger when variable changes. To remove use functioncancel-debug-watch
.- In addition, a call to
(debug)
can be used in the code to break into debugger. Thus, find a place wheredebug
call won’t introduce side-effects and insert it. Don’t forget to re-evaluatedefun
after change (eval-defun
). Also, don’t forget to remove(debug)
call and re-evaluatedefun
again when finished debugging.
- Useful shortcuts/functions in the backtrace buffer
g l - debugger-list-functions
- list function instrumented to break into.p - debugger-toggle-locals
- show/hide local variables for the given stack frame.z o - backtrace-multi-line
- pretty-print the stack backtrace line.z c - backtrace-single-line
- returns to a single line representation (undo previous).g b - debugger-frame
- request entry to debugger when the frame exits. Will be marked with asterisk at the left side.u - debugger-frame-clear
- undo previous command.d - debugger-step-through
- make a step through subexpressions evaluations.c - debugger-continue
- continue evaluating without stopping. Stop on the next marked frame withdebugger-frame
.r - debugger-return-value
-E - debugger-eval-expression
- evaluate given expression in the context of the stack frame at point. For investigating local variables seedebugger-toggle-locals
above. This should be checked!backtrace-goto-source
- this should go to the source location of the current stack frame, but it’s not working at the moment which is pity as it would really make a huge difference.
edebug
edebug
is a source-level debugger. It provides a step-by-step execution through
the source code.
Breaking into debugger is performed using the code instrumentation, i.e. the original code is replaced with a slightly modified code which calls the debugger.
- Useful shortcuts/functions
SPC u , e d - eval-defun
with universal prefix - instrument current function. Evaluating without the prefix will remove instrumentation.
Tracing
edebug
can record an execution trace capturing in a *edebug-trace*
buffer each
function called with parameters and return values.
Set edebug-trace
to non-nil to enable this mode.
This is handy to find out a call path to the interesting place for investigation.
profiling
Profiling is used to figure out why the code is running slow and to find critical places to optimize.
But, I find it also a convenient method to figure out a flow of control inside of a piece of complex code I’m not familiar with. I just run the profiler and then invoke a functionality I would like to investigate. After that, profiler will give me a nice tree of calls so I will quickly be able to figure out what functions are important and their dependencies.
Is navigation to the exact location in the source code possible?
To start the profiler run profiler-start
. Then, invoke functionality that is
under inspection. After that run profiler-report
to get the report. Don’t forget
to run profiler-stop
as the profiler introduces small run-time overhead.
References
- Emacs Info on Elisp -
SPC h i or M-x info
- followElisp
and thenDebugging
.