<astyle="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>
;; <INFO> [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><imgsrc="assets/log4cl.png"alt=""changing the log level with log4slime""/></p>
<p>We invite you to read log4cl’s README.</p>
<h2id="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 doesn’t 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 definition’s 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><codeclass="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> won’t 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>
<h2id="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
> 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>Let’s have a quick look with a more interesting structure, an object:</p>
<pre><codeclass="language-lisp">(defclass foo ()
((a :accessor foo-a :initform '(:a :b :c))
(b :accessor foo-b :initform :b)))
;; #<STANDARD-CLASS FOO>
(make-instance 'foo)
;; #<FOO {100F2B6183}>
</code></pre>
<p>We right-click on the <code>#<FOO</code> object and choose “inspect”. We are
presented an interactive pane (in Slime):</p>
<p><imgsrc="assets/slime-inspector.png"alt=""Slime's inspector, a textual window with buttons""/></p>
<p>When we click or press enter on the line of slot A, we inspect it further:</p>
<pre><code>#<CONS {100F5E2A07}>
--------------------
A proper list:
0: :A
1: :B
2: :C
</code></pre>
<p>In LispWorks, we can use a graphical inspector:</p>
N = 1 <---------- before calling (factorial 0), n equals 1.
</code></pre>
<h3id="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 didn’t list all available options, so please refer to
<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>
<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>
<h2id="step">Step</h2>
<p><ahref="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><codeclass="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 <---------- 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) <----------- press Enter to fold/unfold.
Locals:
N = 3 <----------- want to check? Move the point here and
<!-- 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, LispWorks has a graphical stepper.</p>
<h3id="resume-a-program-execution-from-anywhere-in-the-stack">Resume a program execution from anywhere in the stack</h3>
<p>In <ahref="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 resume the program execution 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>
<!-- epub-exclude-start -->
<iframewidth="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>
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
don’t know who), this can be extremely helpful.</p>
<h2id="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 > 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>
<h2id="sly-stepper-and-sly-stickers">SLY stepper and SLY stickers</h2>
<p>SLY has an improved
<ahref="https://github.com/joaotavora/sly-stepper">stepper</a> and a unique
feature, <ahref="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>
<h2id="unit-tests">Unit tests</h2>
<p>Last but not least, automatic testing of functions in isolation might
be what you’re looking for! See the <ahref="testing.html">testing</a> section and a list of
<ahref="https://github.com/CodyReichert/awesome-cl#unit-testing">test frameworks and libraries</a>.</p>
<h2id="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, let’s define a function that prints forever.</p>
<p>this will securely forward port 4006 on the server at example.com to
our local computer’s 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><codeclass="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>That’s 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>
<h2id="references">References</h2>
<ul>
<li><ahref="https://successful-lisp.blogspot.com/p/httpsdrive.html">“How to understand and use Common Lisp”</a>, chap. 30, David Lamkins (book download from author’s site)</li>
<li><ahref="https://two-wrongs.com/debugging-common-lisp-in-slime.html">Two Wrongs: debugging Common Lisp in Slime</a></li>
<li><ahref="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><ahref="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><ahref="http://www.flownet.com/gat/jpl-lisp.html#1994-1999%20-%20Remote%20Agent">Ron Garret: Lisping at the JPL</a></li>
<li><ahref="https://www.youtube.com/watch?v=_gZK0tW8EhQ&feature=youtu.be&t=4175">the Remote Agent experiment: debugging code from 60 million miles away (youtube)</a> (<ahref="https://www.reddit.com/r/lisp/comments/a7156w/lisp_and_the_remote_agent/">“AMA” on reddit</a>)</li>