1
0
Fork 0
cl-sites/lispcookbook.github.io/cl-cookbook/debugging.html

866 lines
37 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta name="generator" content=
"HTML Tidy for HTML5 for Linux version 5.2.0">
<title>Debugging</title>
<meta charset="utf-8">
<meta name="description" content="A collection of examples of using Common Lisp">
<meta name="viewport" content=
"width=device-width, initial-scale=1">
<link rel="icon" href=
"assets/cl-logo-blue.png"/>
<link rel="stylesheet" href=
"assets/style.css">
<script type="text/javascript" src=
"assets/highlight-lisp.js">
</script>
<script type="text/javascript" src=
"assets/jquery-3.2.1.min.js">
</script>
<script type="text/javascript" src=
"assets/jquery.toc/jquery.toc.min.js">
</script>
<script type="text/javascript" src=
"assets/toggle-toc.js">
</script>
<link rel="stylesheet" href=
"assets/github.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<h1 id="title-xs"><a href="index.html">The Common Lisp Cookbook</a> &ndash; Debugging</h1>
<div id="logo-container">
<a href="index.html">
<img id="logo" src="assets/cl-logo-blue.png"/>
</a>
<div id="searchform-container">
<form onsubmit="duckSearch()" action="javascript:void(0)">
<input id="searchField" type="text" value="" placeholder="Search...">
</form>
</div>
<div id="toc-container" class="toc-close">
<div id="toc-title">Table of Contents</div>
<ul id="toc" class="list-unstyled"></ul>
</div>
</div>
<div id="content-container">
<h1 id="title-non-xs"><a href="index.html">The Common Lisp Cookbook</a> &ndash; Debugging</h1>
<!-- Announcement we can keep for 1 month or more. I remove it and re-add it from time to time. -->
<!-- <p class="announce"> -->
<!-- 📢 🤶 ⭐ -->
<!-- <a style="font-size: 120%" href="https://www.udemy.com/course/common-lisp-programming/?couponCode=LISPY-XMAS2023" title="This course is under a paywall on the Udemy platform. Several videos are freely available so you can judge before diving in. vindarel is (I am) the main contributor to this Cookbook."> Discover our contributor's Lisp course with this Christmas coupon.</a> -->
<!-- <strong> -->
<!-- Recently added: 18 videos on MACROS. -->
<!-- </strong> -->
<!-- <a style="font-size: 90%" href="https://github.com/vindarel/common-lisp-course-in-videos/">Learn more</a>. -->
<!-- </p> -->
<p class="announce">
📢 New videos: <a href="https://www.youtube.com/watch?v=h_noB1sI_e8">web dev demo part 1</a>, <a href="https://www.youtube.com/watch?v=xnwc7irnc8k">dynamic page with HTMX</a>, <a href="https://www.youtube.com/watch?v=Zpn86AQRVN8">Weblocks demo</a>
</p>
<p class="announce-neutral">
📕 <a href="index.html#download-in-epub">Get the EPUB and PDF</a>
</p>
<div id="content"
<p>You entered this new world of Lisp and now wonder: how can we debug
whats going on? How is it more interactive than other platforms?
What does the interactive debugger bring, apart from stack traces?</p>
<h2 id="print-debugging">Print debugging</h2>
<p>Well of course we can use the famous technique of “print
debugging”. Lets just recap a few print functions.</p>
<p><code>print</code> works, it prints a <code>read</code>able representation of its argument,
which means what is <code>print</code>ed can be <code>read</code> back in by the Lisp
reader. It accepts only one argument.</p>
<p><code>princ</code> focuses on an <em>aesthetic</em> representation.</p>
<p><code>(format t "~a" …)</code>, with the <em>aesthetic</em> directive, prints a string (in <code>t</code>, the standard output
stream) and returns nil, whereas <code>format nil …</code> doesnt print anything
and returns a string. With many format controls we can print several
variables at once.</p>
<p><code>print</code> has this useful debugging feature that it prints <em>and</em> returns
the result form it was given as argument. You can intersperse <code>print</code>
statements in the middle of your algorithm, it wont break it.</p>
<pre><code class="language-lisp">(+ 2 (print 40))
</code></pre>
<h2 id="logging">Logging</h2>
<p>Logging is already a good evolution from print debugging ;)</p>
<p><a href="https://github.com/sharplispers/log4cl/">log4cl</a> is the popular,
de-facto logging library although it isnt the only one. Download it:</p>
<pre><code class="language-lisp">(ql:quickload "log4cl")
</code></pre>
<p>and lets have a dummy variable:</p>
<pre><code class="language-lisp">(defvar *foo* '(:a :b :c))
</code></pre>
<p>We can use log4cl with its <code>log</code> nickname, then it is as simple to use as:</p>
<pre><code class="language-lisp">(log:info *foo*)
;; &lt;INFO&gt; [13:36:49] cl-user () - *FOO*: (:A :B :C)
</code></pre>
<p>We can interleave strings and expressions, with or without <code>format</code>
control strings:</p>
<pre><code class="language-lisp">(log:info "foo is " *foo*)
;; &lt;INFO&gt; [13:37:22] cl-user () - foo is *FOO*: (:A :B :C)
(log:info "foo is ~{~a~}" *foo*)
;; &lt;INFO&gt; [13:39:05] cl-user () - foo is ABC
</code></pre>
<p>With its companion library <code>log4slime</code>, we can interactively change
the log level:</p>
<ul>
<li>globally</li>
<li>per package</li>
<li>per function</li>
<li>and by CLOS methods and CLOS hierarchy (before and after methods)</li>
</ul>
<p>It is very handy, when we have a lot of output, to turn off the
logging of functions or packages we know to work, and thus narrowing
our search to the right area. We can even save this configuration and
re-use it in another image, be it on another machine.</p>
<p>We can do all this through commands, keyboard shortcuts and also through a
menu or mouse clicks.</p>
<p><img src="assets/log4cl.png" alt="&quot;changing the log level with log4slime&quot;" /></p>
<p>We invite you to read log4cls README.</p>
<h2 id="using-the-powerful-repl">Using the powerful REPL</h2>
<p>Part of the joy of Lisp is the excellent REPL. Its existence usually
delays the need to use other debugging tools, if it doesnt annihilate
them for the usual routine.</p>
<p>As soon as we define a function, we can try it in the REPL. In Slime,
compile a function with <code>C-c C-c</code> (the whole buffer with <code>C-c C-k</code>),
switch to the REPL with <code>C-c C-z</code> and try it. Eventually enter the
package you are working on with <code>(in-package :your-package)</code>
or <code>C-c ~</code> (<code>slime-sync-package-and-default-directory</code>,
which will also change the default working directory to the package definitions directory).</p>
<p>The feedback is immediate. There is no need to recompile everything,
nor to restart any process, nor to create a main function and define
command line arguments for use in the shell (which we can of course do later on
when needed).</p>
<p>We usually need to create some data to test our function(s). This is a
subsequent art of the REPL existence and it may be a new discipline
for newcomers. A trick is to write the test data alongside your
functions but below a <code>#+nil</code> feature test (or safer, <code>+(or)</code>: it is still possible that someone pushed <code>NIL</code> to the <code>*features*</code> list) so that only you can
manually compile them:</p>
<pre><code class="language-lisp">#+nil
(progn
(defvar *test-data* nil)
(setf *test-data* (make-instance 'foo …)))
</code></pre>
<p>When you load this file, <code>*test-data*</code> wont exist, but you can
manually create it with <code>C-c C-c</code>.</p>
<p>We can define tests functions like this.</p>
<p>Some do similarly inside <code>#| … |#</code> comments.</p>
<p>All that being said, keep in mind to write unit tests when time comes ;)</p>
<h2 id="inspect-and-describe">Inspect and describe</h2>
<p>These two commands share the same goal, printing a description of an
object, <code>inspect</code> being the interactive one.</p>
<pre><code>(inspect *foo*)
The object is a proper list of length 3.
0. 0: :A
1. 1: :B
2. 2: :C
&gt; q
</code></pre>
<p>We can also, in editors that support it, right-click on any object in
the REPL and <code>inspect</code> them (or <code>C-c I</code> on the object to inspect in Slime).
We are presented a screen where we can
dive deep inside the data structure and even change it.</p>
<p>Lets have a quick look with a more interesting structure, an object:</p>
<pre><code class="language-lisp">(defclass foo ()
((a :accessor foo-a :initform '(:a :b :c))
(b :accessor foo-b :initform :b)))
;; #&lt;STANDARD-CLASS FOO&gt;
(make-instance 'foo)
;; #&lt;FOO {100F2B6183}&gt;
</code></pre>
<p>We right-click on the <code>#&lt;FOO</code> object and choose “inspect”. We are
presented an interactive pane (in Slime):</p>
<p><img src="assets/slime-inspector.png" alt="&quot;Slime's inspector, a textual window with buttons&quot;" /></p>
<p>When we click or press enter on the line of slot A, we inspect it further:</p>
<pre><code>#&lt;CONS {100F5E2A07}&gt;
--------------------
A proper list:
0: :A
1: :B
2: :C
</code></pre>
<p>In LispWorks, we can use a graphical inspector:</p>
<p><img src="assets/lispworks-graphical-inspector.png" alt="&quot;The LispWorks inspector window&quot;" /></p>
<h2 id="trace">Trace</h2>
<p><a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_tracec.htm">trace</a> allows us to see when a
function was called, what arguments it received, and the value it
returned.</p>
<pre><code class="language-lisp">(defun factorial (n)
(if (plusp n)
(* n (factorial (1- n)))
1))
</code></pre>
<p>To start tracing a function, just call <code>trace</code> with the function name
(or several function names):</p>
<pre><code class="language-lisp">(trace factorial)
(factorial 2)
0: (FACTORIAL 3)
1: (FACTORIAL 2)
2: (FACTORIAL 1)
3: (FACTORIAL 0)
3: FACTORIAL returned 1
2: FACTORIAL returned 1
1: FACTORIAL returned 2
0: FACTORIAL returned 6
6
(untrace factorial)
</code></pre>
<p>To untrace all functions, just evaluate <code>(untrace)</code>.</p>
<p>To get a list of currently traced functions, evaluate <code>(trace)</code> with no arguments.</p>
<p>In Slime we have the shortcut <code>C-c M-t</code> to trace or untrace a
function.</p>
<p>If you dont see recursive calls, that may be because of the
compilers optimizations. Try this before defining the function to be
traced:</p>
<pre><code class="language-lisp">(declaim (optimize (debug 3))) ;; or C-u C-c C-c to compile with maximal debug settings.
</code></pre>
<p>The output is printed to <code>*trace-output*</code> (see the CLHS).</p>
<h3 id="trace-options">Trace options</h3>
<p><code>trace</code> accepts options. For example, you can use <code>:break t</code> to invoke
the debugger at the start of the function, before it is called (more on break below):</p>
<pre><code class="language-lisp">(trace factorial :break t)
(factorial 2)
</code></pre>
<p>We can define many things in one call to <code>trace</code>. For instance,
options that appear before the first function name to trace are
<em>global</em>, they affect all traced functions that we add afterwards. Here,
<code>:break t</code> is set for every function that follows: <code>factorial</code>, <code>foo</code>
and <code>bar</code>:</p>
<pre><code class="language-lisp">(trace :break t factorial foo bar)
</code></pre>
<p>On the contrary, if an option comes after a function name, it acts as
a <em>local</em> option, only for its <em>preceding</em> function. Thats how we first
did. Below <code>foo</code> and <code>bar</code> come after, they are not affected by <code>:break</code>:</p>
<pre><code class="language-lisp">(trace factorial :break t foo bar)
</code></pre>
<p>But do you actually want to <code>break</code> <em>before</em> the function call or just
<em>after</em> it? With <code>:break</code> as with many options, you can choose. These
are the options for <code>:break</code>:</p>
<pre><code>:break form ;; before
:break-after form
:break-all form ;; before and after
</code></pre>
<p><code>form</code> can be any form that evaluates to true.</p>
<p>Note that we explained the trace function of SBCL. Other
implementations may have the same feature with another syntax and
other option names. For example, in LispWorks it is “:break-on-exit”
instead of “:break-after”, and we write <code>(trace (factorial :break t))</code>.</p>
<p>Below are some other options but first, a trick with <code>:break</code>.</p>
<h3 id="trace-options-break">Trace options: break</h3>
<p>The argument to an option can be any form. Heres a trick, on SBCL, to
get the break window when we are about to call <code>factorial</code>
with 0. <code>(sb-debug:arg 0)</code> refers to <code>n</code>, the first argument.</p>
<pre><code class="language-lisp">CL-USER&gt; (trace factorial :break (equal 0 (sb-debug:arg 0)))
;; WARNING: FACTORIAL is already TRACE'd, untracing it first.
;; (FACTORIAL)
</code></pre>
<p>Running it again:</p>
<pre><code>CL-USER&gt; (factorial 3)
0: (FACTORIAL 3)
1: (FACTORIAL 2)
2: (FACTORIAL 1)
3: (FACTORIAL 0)
breaking before traced call to FACTORIAL:
[Condition of type SIMPLE-CONDITION]
Restarts:
0: [CONTINUE] Return from BREAK.
1: [RETRY] Retry SLIME REPL evaluation request.
2: [*ABORT] Return to SLIME's top level.
3: [ABORT] abort thread (#&lt;THREAD "repl-thread" RUNNING {1003551BC3}&gt;)
Backtrace:
0: (FACTORIAL 1)
Locals:
N = 1 &lt;---------- before calling (factorial 0), n equals 1.
</code></pre>
<h3 id="trace-options-trace-on-conditions-trace-if-called-from-another-function">Trace options: trace on conditions, trace if called from another function</h3>
<p><code>:condition</code> enables tracing only if the condition in <code>form</code> evaluates to true.</p>
<pre><code>:condition form
:condition-after form
:condition-all form
</code></pre>
<blockquote>
<p>If :condition is specified, then trace does nothing unless Form
evaluates to true at the time of the call. :condition-after is
similar, but suppresses the initial printout, and is tested when the
function returns. :condition-all tries both before and after.</p>
</blockquote>
<p><code>:wherein</code> can be super useful:</p>
<pre><code>:wherein Names
</code></pre>
<blockquote>
<p>If specified, Names is a function name or list of names. trace does nothing unless a call to one of those functions encloses the call to this function (i.e. it would appear in a backtrace.) Anonymous functions have string names like “DEFUN FOO”.</p>
</blockquote>
<pre><code>:report Report-Type
</code></pre>
<blockquote>
<p>If Report-Type is trace (the default) then information is reported
by printing immediately. If Report-Type is nil, then the only effect
of the trace is to execute other options (e.g. print or
break). Otherwise, Report-Type is treated as a function designator
and, for each trace event, funcalled with 5 arguments: trace depth
(a non-negative integer), a function name or a function object, a
keyword (:enter, :exit or :non-local-exit), a stack frame, and a
list of values (arguments or return values).</p>
</blockquote>
<p>See also <code>:print</code> to enrich the trace output.</p>
<p>It is expected that implementations extend <code>trace</code> with non-standard
options. And we didnt list all available options, so please refer to
your implementations documentation:</p>
<ul>
<li><a href="http://www.sbcl.org/manual/index.html#Function-Tracing">SBCL trace</a></li>
<li><a href="https://ccl.clozure.com/manual/chapter4.2.html">CCL trace</a></li>
<li><a href="http://www.lispworks.com/documentation/lw80/lw/lw-tracer-ug-2.htm">LispWorks trace</a></li>
<li><a href="https://franz.com/support/documentation/current/doc/debugging.htm#tracer-1">Allegro trace</a></li>
</ul>
<h3 id="tracing-method-invocation">Tracing method invocation</h3>
<p>In SBCL, we can use <code>(trace foo :methods t)</code> to trace the execution order of method combination (before, after, around methods). For example:</p>
<pre><code class="language-lisp">(trace foo :methods t)
(foo 2.0d0)
0: (FOO 2.0d0)
1: ((SB-PCL::COMBINED-METHOD FOO) 2.0d0)
2: ((METHOD FOO (FLOAT)) 2.0d0)
3: ((METHOD FOO (T)) 2.0d0)
3: (METHOD FOO (T)) returned 3
2: (METHOD FOO (FLOAT)) returned 9
2: ((METHOD FOO :AFTER (DOUBLE-FLOAT)) 2.0d0)
2: (METHOD FOO :AFTER (DOUBLE-FLOAT)) returned DOUBLE
1: (SB-PCL::COMBINED-METHOD FOO) returned 9
0: FOO returned 9
9
</code></pre>
<p>It is also possible in CCL.</p>
<p>See the <a href="clos.html">CLOS</a> section for a tad more information.</p>
<h3 id="interactive-trace-dialog">Interactive Trace Dialog</h3>
<p>Both SLIME and SLY provide an <a href="https://slime.common-lisp.dev/doc/html/SLIME-Trace-Dialog.html#SLIME-Trace-Dialog">interactive view for traces</a> that features better visualization of traces, and also access to the arguments and return values in their real form, via inspectors, not just the printed representation.</p>
<p><img src="trace-dialog.png" alt="trace-dialog" title="Trace dialog" /></p>
<p>How it works: (the following instructions are for SLIME)</p>
<ol>
<li>Select the functions to trace using <code>M-x slime-trace-dialog-toggle-trace</code> bound to <code>C-c M-t</code>.</li>
<li>Evaluate code that calls the traced functions.</li>
<li>Open the trace dialog tool via <code>M-x slime-trace-dialog</code> bound to <code>C-c T</code>.</li>
<li>The list of traced functions appear under <code>Traced specs</code>.
Traces are fetched in batches. So use the the <code>[refresh]</code> button to update status information about tracing (number of available traces that can be fetched).</li>
<li>Then use either the <code>[fetch next batch]</code> or <code>[fetch all]</code> buttons to fetch the traces. Traces appear under <code>Traced specs</code> after that, and you can use the SLIME inspector to visualize their data (arguments and return values).</li>
<li>After more code that calls the traced functions is evaluated, repeat the process (go to step 4).</li>
</ol>
<p>But, that flow can get a bit tedious, because of the separation between updating the status of the traces and fetching them. Sometimes it is better to just fetch the traces without updating the status first. We can do that invoking the command <code>M-x slime-trace-dialog-fetch-traces</code> bound to <code>G</code>. So, instead of steps 4 and 5, just press <code>G</code> to update the user interface.</p>
<p>These are some of the Emacs commands bound to useful keys:</p>
<p><code>g</code>
<code>M-x slime-trace-dialog-fetch-status</code></p>
<pre><code>Update information on the trace collection and traced specs.
</code></pre>
<p><code>G</code>
<code>M-x slime-trace-dialog-fetch-traces</code></p>
<pre><code>Fetch the next batch of outstanding (not fetched yet) traces. With a C-u prefix argument, repeat until no more outstanding traces.
</code></pre>
<p><code>C-k</code>
<code>M-x slime-trace-dialog-clear-fetched-traces</code></p>
<pre><code>Prompt for confirmation, then clear all traces, both fetched and outstanding.
</code></pre>
<p>Finally, the arguments and return values for each trace entry are interactive buttons. Clicking them opens the SLIME inspector on them. Invoking <code>M-RET</code> <code>M-x slime-trace-dialog-copy-down-to-repl</code> returns them to the REPL for manipulation . The number left of each entry indicates its absolute position in the calling order, which might differ from display order in case multiple threads call the same traced function.</p>
<p><code>M-x slime-trace-dialog-hide-details-mode</code> hides arguments and return values so you can concentrate on the calling logic. Additionally, <code>M-x slime-trace-dialog-autofollow-mode</code> will automatically display additional detail about an entry when the cursor moves over it.</p>
<h2 id="the-interactive-debugger">The interactive debugger</h2>
<p>Whenever an exceptional situation happens (see
<a href="error_handling.html">error handling</a>), or when you ask for it (using <code>step</code> or <code>break</code>),
the interactive debugger pops up.</p>
<p>It presents the error message, the available actions (<em>restarts</em>),
and the backtrace. A few remarks:</p>
<ul>
<li>the restarts are programmable, we can create our own.</li>
<li>in Slime, press <code>v</code> on a stack trace frame to view the corresponding
source file location.</li>
<li>hit Enter (or <code>t</code>) on a frame to toggle more details,</li>
<li>use <code>e</code> to evaluate some code from within that frame,</li>
<li>hit <code>r</code> to restart a given frame (see below).</li>
<li>we can explore the functionality with the menu that should appear
in our editor.</li>
</ul>
<h3 id="compile-with-maximum-debugging-information">Compile with maximum debugging information</h3>
<p>Usually your compiler will optimize things out and this will reduce
the amount of information available to the debugger. For example
sometimes we cant see intermediate variables of computations. We can
change the optimization choices with:</p>
<pre><code class="language-lisp">(declaim (optimize (speed 0) (space 0) (debug 3)))
</code></pre>
<p>and recompile our code. You can achieve the same with a handy shortcut: <code>C-u C-c C-c</code>: the form is compiled with maximum debug settings. You can on the contrary use a negative prefix argument (<code>M--</code>) to compile for speed. And use a numeric argument to set the setting to it (you should read the docstring of <code>slime-compile-defun</code>).</p>
<h2 id="step">Step</h2>
<p><a href="http://www.lispworks.com/documentation/HyperSpec/Body/m_step.htm">step</a> is an interactive command with similar scope than <code>trace</code>. This:</p>
<pre><code class="language-lisp">;; note: we copied factorial over to a file, to have more debug information.
(step (factorial 3))
</code></pre>
<p>gives an interactive pane with available actions (restarts) and the backtrace:</p>
<pre><code>Evaluating call:
(FACTORIAL 3)
With arguments:
3
[Condition of type SB-EXT:STEP-FORM-CONDITION]
Restarts:
0: [STEP-CONTINUE] Resume normal execution &lt;-------------------- stepping actions
1: [STEP-OUT] Resume stepping after returning from this function
2: [STEP-NEXT] Step over call
3: [STEP-INTO] Step into call
4: [RETRY] Retry SLIME REPL evaluation request.
5: [*ABORT] Return to SLIME's top level.
--more--
Backtrace:
0: (FACTORIAL 3) &lt;----------- press Enter to fold/unfold. Fix your code and press "r" to restart it.
Locals:
N = 3 &lt;----------- want to check? Move the point here and
press "e" to evaluate code on that frame.
1: (SB-INT:SIMPLE-EVAL-IN-LEXENV (LET ((SB-IMPL::*STEP-OUT* :MAYBE)) (UNWIND-PROTECT (SB-IMPL::WITH-STEPPING-ENABLED #))) #S(SB-KERNEL:LEXENV :FUNS NIL :VARS NIL :BLOCKS NIL :TAGS NIL :TYPE-RESTRICTIONS ..
2: (SB-INT:SIMPLE-EVAL-IN-LEXENV (STEP (FACTORIAL 3)) #&lt;NULL-LEXENV&gt;)
3: (EVAL (STEP (FACTORIAL 3)))
--more--
</code></pre>
<p><em>(again, be sure you compiled your function with maximum debug
settings (see above). Otherwise, your compiler might do optimizations
under the hood and you might not see useful information such as local
variables, or you might not be able to step at all.)</em></p>
<p>You have many options here. If you are using Emacs (or any other
editor actually), keep in mind that you have a “SLDB” menu that shows
you the available actions, in addition to the step window.</p>
<ul>
<li>follow the restarts to <strong>continue stepping</strong>: continue the
execution, step out of this function, step into the function call
the point is on, step over to the next function call, or abort
everything. The shortcuts are:
<ul>
<li><code>c</code>: continue</li>
<li><code>s</code>: step</li>
<li><code>x</code>: step next</li>
<li><code>o</code>: step out</li>
</ul>
</li>
<li>
<p><strong>inspect the backtrace</strong> and the source code. You can go to the
source file with <code>v</code>, on each stackframe (each line of the
backtrace). Press <code>Enter</code> or <code>t</code> (“toggle details”) on the
stackframe to see more information, such as the function parameters
for this call. Use <code>n</code> and <code>p</code> to navigate, use <code>M-n</code> and <code>M-p</code> to
navigate to the next or previous stackframe <em>and</em> to open the
corresponding source file at the same time. The point will be placed
on the function being called.</p>
</li>
<li>
<p><strong>evaluate code from within the context</strong> of that stackframe. In
Slime, use <code>e</code> (“eval in frame” and <code>d</code> to pretty-pint the result) and
type a Lisp form. It will be executed in the context of the
stackframe the point is on. Look, you can even inspect variables and
have Slime open another inspector window. If you are on the first
frame (<code>0:</code>), press <code>i</code>, then “n” to inspect the intermediate
variable.</p>
</li>
<li><strong>resume execution</strong> from where you want. Use <code>r</code> to restart the
frame the point is on. For example, go change the source code
(without quitting the interactive debugger), re-compile it, re-run
the frame to see if it works better. You didnt restart all the
program execution, you just restarted your program from a precise
point. Use <code>R</code> to return from a stackframe, by giving its return
value.</li>
</ul>
<div class="info-box info" style="margin-bottom: 1em">
<!-- if inside a <p> then bootstrap adds 10px padding to the bottom -->
<strong>NB:</strong> let's think about it, <strong>this is awesome!</strong> We just restarted our program from any point in time. If we work with long-running computations, we don't need to restart it from the start. We can change, re-compile our erroneous code and resume execution from where it is needed to pass, no more.
</div>
<p>Stepping is precious. However, if you find yourself inspecting the
behaviour of a function a lot, it may be a sign that you need to
simplify it and divide it in smaller pieces.</p>
<p>And again, <strong>LispWorks</strong> has a <strong>graphical stepper</strong>.</p>
<div class="info" style="background-color: #e7f3fe; border-left: 6px solid #2196F3; padding: 17px; margin-bottom: 1em;">
<!-- if inside a <p> then bootstrap adds 10px padding to the bottom -->
<strong>TIP:</strong> the <a href="https://github.com/mmontone/slime-breakpoints">slime-breakpoints</a> package adds stepping and breaking buttons to Slime too.
</div>
<p><img src="https://raw.githubusercontent.com/mmontone/slime-star/master/screenshots/toolbars.png" alt="" /></p>
<h3 id="resume-a-program-execution-from-anywhere-in-the-stack-demo">Resume a program execution from anywhere in the stack (demo)</h3>
<p>In <a href="https://www.youtube.com/watch?v=jBBS4FeY7XM">this video</a> you will
find a demo that shows the process explained above: how to fix a buggy
function and how to <strong>resume the program execution</strong> from anywhere in the
stack, without running everything from zero again. The video shows it
with Emacs and Slime, the Lem editor, both with SBCL.</p>
<p>They key point is to use <code>r</code> (<code>sldb-restart-frame</code>) on a stack frame to restart it.</p>
<!-- epub-exclude-start -->
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/jBBS4FeY7XM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<!-- epub-exclude-end -->
<h2 id="break">Break</h2>
<p>A call to
<a href="http://www.lispworks.com/documentation/HyperSpec/Body/f_break.htm">break</a>
makes the program enter the debugger, from which we can inspect the
call stack, and do everything described above in the stepper.</p>
<h3 id="breakpoints-in-slime">Breakpoints in Slime</h3>
<p>Look at the <code>SLDB</code> menu, it shows navigation keys and available
actions. Of which:</p>
<ul>
<li><code>e</code> (<em>sldb-eval-in-frame</em>) prompts for an expression and evaluates
it in the selected frame. This is how we can explore our
intermediate variables</li>
<li><code>d</code> is similar with the addition of pretty printing the result</li>
</ul>
<p>Once we are in a frame and detect a suspicious behavior, we can even
re-compile a function at runtime and resume the program execution from
where it stopped (using the “step-continue” restart
or using <code>r</code> (“restart frame”) on a given stackframe).</p>
<p>See also the <a href="https://github.com/mmontone/slime-star">Slime-star</a> Emacs extension mentioned above to set breakpoints without code annotations.</p>
<h2 id="advise-and-watch">Advise and watch</h2>
<p><em>advise</em> and <em>watch</em> are available in some
implementations, like CCL
(<a href="https://ccl.clozure.com/manual/chapter4.3.html#Advising">advise</a> and
<a href="https://ccl.clozure.com/manual/chapter4.12.html#watched-objects">watch</a>)
and LispWorks. They do exist in
SBCL but are not exported. <code>advise</code> allows to modify a function without changing its
source, or to do something before or after its execution, similar
to CLOS method combination (before, after, around methods).</p>
<p><code>watch</code> will signal a condition when a thread attempts to write to an
object being watched. It can be coupled with the display of the
watched objects in a GUI.
For a certain class of bugs (someone is changing this value, but I
dont know who), this can be extremely helpful.</p>
<h2 id="cross-referencing">Cross-referencing</h2>
<p>Your Lisp can tell you all the places where a function is referenced
or called, where a global variable is set, where a macro is expanded,
and so on. For example, <code>slime-who-calls</code> (<code>C-c C-w C-c</code> or the Slime &gt; Cross-Reference menu) will show you all the places where a function is called.</p>
<p>See our Emacs page for a complete list of commands.</p>
<h2 id="sly-stepper-and-sly-stickers">SLY stepper and SLY stickers</h2>
<p>SLY has an improved
<a href="https://github.com/joaotavora/sly-stepper">stepper</a> and a unique
feature, <a href="https://joaotavora.github.io/sly/#Stickers">stickers</a>. You
mark a piece of code, you run your code, SLY captures the results for
each sticker and lets you examine the program execution
interactively. It allows to see what sticker was captured, or
not, so we can see at a glance the code coverage of that function
call.</p>
<p>They are a non-intrusive alternative to <code>print</code> and <code>break</code>.</p>
<h2 id="unit-tests">Unit tests</h2>
<p>Last but not least, automatic testing of functions in isolation might
be what youre looking for! See the <a href="testing.html">testing</a> section and a list of
<a href="https://github.com/CodyReichert/awesome-cl#unit-testing">test frameworks and libraries</a>.</p>
<h2 id="remote-debugging">Remote debugging</h2>
<p>You can have your software running on a machine over the network,
connect to it and debug it from home, from your development
environment.</p>
<p>The steps involved are to start a <strong>Swank server</strong> on the remote machine (Swank is the backend companion of Slime), create an
ssh tunnel and connect to the Swank server from our editor. Then we
can browse and evaluate code on the running instance transparently.</p>
<p>To test this, lets define a function that prints forever.</p>
<p>If needed, import the dependencies first:</p>
<pre><code class="language-lisp">(ql:quickload '("swank" "bordeaux-threads"))
</code></pre>
<pre><code class="language-lisp">;; a little common lisp swank demo
;; while this program is running, you can connect to it from
;; another terminal or machine
;; and change the definition of doprint to print something else out!
(require :swank)
(require :bordeaux-threads)
(defparameter *counter* 0)
(defun dostuff ()
(format t "hello world ~a!~%" *counter*))
(defun runner ()
(swank:create-server :port 4006)
(format t "we are past go!~%")
(bt:make-thread (lambda ()
(loop repeat 5 do
(sleep 5)
(dostuff)
(incf *counter*)))
:name "do-stuff"))
(runner)
</code></pre>
<p>On the server, we can run this code with</p>
<pre><code>sbcl --load demo.lisp
</code></pre>
<p>If you check with <code>(bt:all-threads)</code>, youll see your Swank server running on port 4006, as well
as the other thread ready to do stuff:</p>
<pre><code>(#&lt;SB-THREAD:THREAD "do-stuff" RUNNING {10027CEDC3}&gt;
#&lt;SB-THREAD:THREAD "Swank Sentinel" waiting on:
#&lt;WAITQUEUE {10027D0003}&gt;
{10027CE8B3}&gt;
#&lt;SB-THREAD:THREAD "Swank 4006" RUNNING {10027CEB63}&gt;
#&lt;SB-THREAD:THREAD "main thread" RUNNING {1007C40393}&gt;)
</code></pre>
<p>We do port forwarding on our development machine:</p>
<pre><code>ssh -L4006:127.0.0.1:4006 username@example.com
</code></pre>
<p>this will securely forward port 4006 on the server at example.com to
our local computers port 4006 (Swank only accepts connections from
localhost).</p>
<p>We connect to the running Swank with <code>M-x slime-connect</code>, choosing localhost for the host
and port 4006.</p>
<p>We can write new code:</p>
<pre><code class="language-lisp">(defun dostuff ()
(format t "goodbye world ~a!~%" *counter*))
(setf *counter* 0)
</code></pre>
<p>and eval it as usual with <code>C-c C-c</code> or <code>M-x slime-eval-region</code> for instance. The output should change.</p>
<p>Thats how Ron Garret debugged the Deep Space 1 spacecraft from the earth
in 1999:</p>
<blockquote>
<p>We were able to debug and fix a race condition that had not shown up during ground testing. (Debugging a program running on a $100M piece of hardware that is 100 million miles away is an interesting experience. Having a read-eval-print loop running on the spacecraft proved invaluable in finding and fixing the problem.</p>
</blockquote>
<h2 id="references">References</h2>
<ul>
<li><a href="https://successful-lisp.blogspot.com/p/httpsdrive.html">“How to understand and use Common Lisp”</a>, chap. 30, David Lamkins (book download from authors site)</li>
<li><a href="https://malisper.me/debugging-lisp-part-1-recompilation/">Malisper: debugging Lisp series</a></li>
<li><a href="https://two-wrongs.com/debugging-common-lisp-in-slime.html">Two Wrongs: debugging Common Lisp in Slime</a></li>
<li><a href="https://common-lisp.net/project/slime/doc/html/Connecting-to-a-remote-lisp.html#Connecting-to-a-remote-lisp">Slime documentation: connecting to a remote Lisp</a></li>
<li><a href="http://cvberry.com/tech_writings/howtos/remotely_modifying_a_running_program_using_swank.html">cvberrycom: remotely modifying a running Lisp program using Swank</a></li>
<li><a href="http://www.flownet.com/gat/jpl-lisp.html#1994-1999%20-%20Remote%20Agent">Ron Garret: Lisping at the JPL</a></li>
<li><a href="https://www.youtube.com/watch?v=_gZK0tW8EhQ&amp;feature=youtu.be&amp;t=4175">the Remote Agent experiment: debugging code from 60 million miles away (youtube)</a> (<a href="https://www.reddit.com/r/lisp/comments/a7156w/lisp_and_the_remote_agent/">“AMA” on reddit</a>)</li>
</ul>
<p class="page-source">
Page source: <a href="https://github.com/LispCookbook/cl-cookbook/blob/master/debugging.md">debugging.md</a>
</p>
</div>
<script type="text/javascript">
// Don't write the TOC on the index.
if (window.location.pathname != "/cl-cookbook/") {
$("#toc").toc({
content: "#content", // will ignore the first h1 with the site+page title.
headings: "h1,h2,h3,h4"});
}
$("#two-cols + ul").css({
"column-count": "2",
});
$("#contributors + ul").css({
"column-count": "4",
});
</script>
<div>
<footer class="footer">
<hr/>
&copy; 2002&ndash;2023 the Common Lisp Cookbook Project
<div>
📹 Discover <a style="color: darkgrey; text-decoration: underline", href="https://www.udemy.com/course/common-lisp-programming/?referralCode=2F3D698BBC4326F94358">our contributor's Common Lisp video course on Udemy</a>
</div>
</footer>
</div>
<div id="toc-btn">T<br>O<br>C</div>
</div>
<script text="javascript">
HighlightLisp.highlight_auto({className: null});
</script>
<script type="text/javascript">
function duckSearch() {
var searchField = document.getElementById("searchField");
if (searchField && searchField.value) {
var query = escape("site:lispcookbook.github.io/cl-cookbook/ " + searchField.value);
window.location.href = "https://duckduckgo.com/?kj=b2&kf=-1&ko=1&q=" + query;
// https://duckduckgo.com/params
// kj=b2: blue header in results page
// kf=-1: no favicons
}
}
</script>
<script async defer data-domain="lispcookbook.github.io/cl-cookbook" src="https://plausible.io/js/plausible.js"></script>
</body>
</html>