811 lines
32 KiB
HTML
811 lines
32 KiB
HTML
<!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> – 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> – 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-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
|
||
what’s 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”. Let’s 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> doesn’t 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 won’t 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 isn’t the only one. Download it:</p>
|
||
|
||
<pre><code class="language-lisp">(ql:quickload "log4cl")
|
||
</code></pre>
|
||
|
||
<p>and let’s 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*)
|
||
;; <INFO> [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*)
|
||
;; <INFO> [13:37:22] cl-user () - foo is *FOO*: (:A :B :C)
|
||
(log:info "foo is ~{~a~}" *foo*)
|
||
;; <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><img src="assets/log4cl.png" alt=""changing the log level with log4slime"" /></p>
|
||
|
||
<p>We invite you to read log4cl’s 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 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><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> 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>
|
||
|
||
<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
|
||
> 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><code class="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><img src="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>
|
||
|
||
<p><img src="assets/lispworks-graphical-inspector.png" alt=""The LispWorks inspector window"" /></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 don’t see recursive calls, that may be because of the
|
||
compiler’s 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>
|
||
|
||
<p>In Slime, we also have an interactive trace dialog with <code>M-x
|
||
slime-trace-dialog</code> bound to <code>C-c T</code>.</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. That’s 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. Here’s 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> (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> (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 (#<THREAD "repl-thread" RUNNING {1003551BC3}>)
|
||
|
||
Backtrace:
|
||
0: (FACTORIAL 1)
|
||
Locals:
|
||
N = 1 <---------- 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 didn’t list all available options, so please refer to
|
||
your implementation’s 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>
|
||
|
||
<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 can’t 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 <---------- 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
|
||
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)) #<NULL-LEXENV>)
|
||
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 didn’t 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, LispWorks has a graphical stepper.</p>
|
||
|
||
<h3 id="resume-a-program-execution-from-anywhere-in-the-stack">Resume a program execution from anywhere in the stack</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 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 -->
|
||
|
||
<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 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
|
||
don’t 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 > 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 you’re 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, let’s 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>, you’ll see your Swank server running on port 4006, as well
|
||
as the other thread ready to do stuff:</p>
|
||
|
||
<pre><code>(#<SB-THREAD:THREAD "do-stuff" RUNNING {10027CEDC3}>
|
||
#<SB-THREAD:THREAD "Swank Sentinel" waiting on:
|
||
#<WAITQUEUE {10027D0003}>
|
||
{10027CE8B3}>
|
||
#<SB-THREAD:THREAD "Swank 4006" RUNNING {10027CEB63}>
|
||
#<SB-THREAD:THREAD "main thread" RUNNING {1007C40393}>)
|
||
</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 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><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>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>
|
||
|
||
<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 author’s 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&feature=youtu.be&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/>
|
||
© 2002–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">vindarel's Lisp 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>
|