emacs.d/clones/lisp/www.cliki.net/TutorialClispDebugger.html
2022-10-07 15:47:14 +02:00

155 lines
No EOL
8.1 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>CLiki: TutorialClispDebugger</title>
<link rel="alternate" type="application/atom+xml" title="ATOM feed of edits to current article"
href="https://www.cliki.net/site/feed/article.atom?title=TutorialClispDebugger">
<link rel="stylesheet" href="static/css/style.css">
<link rel="stylesheet" href="static/css/colorize.css">
</head>
<body>
<span class="hidden">CLiki - TutorialClispDebugger</span>
<div id="content"><div id="content-area"><div id="article-title">TutorialClispDebugger</div><div id="article">Small Clisp Debugger Tutorial<p>
Debugging is implementation dependant.<p>With <a href="clisp.html" class="internal">clisp</a>, debugging can only be done on interpreted functions. Too
little information remains in compiled functions to do any useful
debugging.<p>So, let's take care not to compile our new functions:
<pre>
[221]&gt; (defun fact (x) (if (&lt; x 1) (/ 1 x) (* x (fact (1- x)))))
FACT
[222]&gt; (defun pf (n) (loop :for i :from n :downto 1 :do (print `(,i ,(fact i)))))
PF
[223]&gt; (pf 4)
*** - division by zero
The following restarts are available:
ABORT :R1 ABORT
Break 1 [224]&gt;
</pre><p>The first thing to do, is to dump the stack backtrace. Often, this is
all you'll have to do to identify and correct the bug, because clisp
displays a lot of information about non compiled functions:<p><pre>
Break 1 [224]&gt; :bt
&lt;1&gt; #&lt; SYSTEM-FUNCTION EXT:SHOW-STACK &gt; 3 ; These frames correspond
&lt;2&gt; #&lt; COMPILED-FUNCTION SYSTEM::PRINT-BACKTRACE &gt; ; to compiled functions
&lt;3&gt; #&lt; COMPILED-FUNCTION SYSTEM::DEBUG-BACKTRACE &gt; ; belonging to the
&lt;4&gt; #&lt; SYSTEM-FUNCTION SYSTEM::READ-EVAL-PRINT &gt; 2 ; debugged itself.
&lt;5&gt; #&lt; COMPILED-FUNCTION SYSTEM::BREAK-LOOP-2-2 &gt; ; Notice how little
&lt;6&gt; #&lt; SYSTEM-FUNCTION SYSTEM::SAME-ENV-AS &gt; 2 ; information we have:
&lt;7&gt; #&lt; COMPILED-FUNCTION SYSTEM::BREAK-LOOP-2 &gt; ; only the function name.
&lt;8&gt; #&lt; SYSTEM-FUNCTION SYSTEM::DRIVER &gt; ;
&lt;9&gt; #&lt; COMPILED-FUNCTION SYSTEM::BREAK-LOOP &gt; ;
&lt;10&gt; #&lt; SYSTEM-FUNCTION INVOKE-DEBUGGER &gt; 1 ;
&lt;11&gt; #&lt; SYSTEM-FUNCTION / &gt; 2 ; the divide function call
; who raised the error.
&lt;12&gt; #&lt; SPECIAL-OPERATOR IF &gt;
; And here we have the form from our code
; who called / with a zero divisor.
; Happily, it&#039;s the only division in the form
; so we can identify it immediately.
EVAL frame for form (IF (&lt; X 1) (/ 1 X) (* X (FACT (1- X))))
APPLY frame for call (FACT &#039;0)
&lt;13&gt;
#&lt;F UNCTION FACT (X) (DECLARE (SYSTEM::IN-DEFUN FACT))
(BLOCK FACT (IF (&lt; X 1) (/ 1 X) (* X (FACT (1- X))))) &gt; 1
EVAL frame for form (FACT (1- X))
EVAL frame for form (* X (FACT (1- X)))
&lt;14&gt; #&lt; SPECIAL-OPERATOR IF &gt;
EVAL frame for form (IF (&lt; X 1) (/ 1 X) (* X (FACT (1- X))))
APPLY frame for call (FACT &#039;1)
[...]
EVAL frame for form
(LET ((I N))
(LET NIL
(TAGBODY SYSTEM::BEGIN-LOOP (WHEN (&lt; I 1) (GO SYSTEM::END-LOOP))
(PRINT (LIST I (FACT I))) (PSETQ I (- I 1)) (GO SYSTEM::BEGIN-LOOP)
SYSTEM::END-LOOP)))
APPLY frame for call (PF &#039;4)
&lt;25&gt;
#&lt;F UNCTION PF (N) (DECLARE (SYSTEM::IN-DEFUN PF))
(BLOCK PF (LOOP :FOR I :FROM N :DOWNTO 1 :DO (PRINT `(,I ,(FACT I))))) &gt; 1
EVAL frame for form (PF 4)
Printed 25 frames
Break 1 [224]&gt;
</pre><p>But let's assume that there was another division in the other branch
of the IF. How could we know what branch was being executed? We
could re-evaluate the condition. First move up the frames to the EVAL
frame, were we'll have access to the lexical variables. Only one :u
command is needed because it skips over the compiled frames where we
cannot get at the lexical variables (they might be optimized out!).<p><pre>
Break 1 [224]&gt; :u
&lt;1&gt; #&lt; SPECIAL-OPERATOR IF &gt;
EVAL frame for form (IF (&lt; X 1) (/ 1 X) (* X (FACT (1- X))))
</pre><p>Then we can enter any lisp form, like the condition:<p><pre>
Break 1 [224]&gt; (&lt; X 1)
T
</pre>
and see that it's the "then" branch that was taken, and therefore it's
(/ 1 X) which raises the error. Let's check that X is <p><pre>
Break 1 [224]&gt; x
0
</pre><p>Indeed.<p>Now, we have the choice of aborting the debuging session and going
back to the source file to correct the bug, reload the file, or just
reevaluate the defun, and run it again, or try to correct the bug
inside the debugger, and proceed from there. <p>In the first case, we can use :q to go back to the toplevel.<p>In the second case, let's redefine fact:<p><pre>
Break 1 [224]&gt; (defun fact (x) (if (&lt; x 1) 1 (* x (fact (1- x)))))
FACT
</pre><p>then we return from the failed EVAL frame, giving the result it should
return:<p><pre>
Break 1 [224]&gt; :rt
Values: 1
(4 24)
(3 6)
(2 2)
(1 1)
NIL
[225]&gt;
</pre><p>And we see:<p><ol>
<li> how the current call (fact 4) proceeds to completion, and</li>
<li> how the next calls (fact 3) ... (fact 1) invoke the new function.</li>
</ol><p>Note that if the failed EVAL frame had been in a loop inside the
function, the loop would still call iteratively the wrong version.
The new version of the function is only taken into account for the new
function calls. To proceed in such a case, we would have to move up
the frames (:u) until we could return (:rt) from the whole function.<p>Use also the :help command to get the list of debugger commands,
and see: <a href="http://clisp.cons.org/impnotes/debugger.html">CLISP Implementation Notes - Debugger Chapter</a><p><hr>
Categories: <a href="Online&#32;Tutorial.html" class="category">Online Tutorial</a> <a href="CLISP.html" class="category">CLISP</a> <a href="Debugging.html" class="category">Debugging</a>
Keywords: debug debugger debugging tutorial clisp</div></div>
<div id="footer" class="buttonbar"><ul><li><a href="TutorialClispDebugger.html">Current version</a></li>
<li><a href="https://www.cliki.net/site/history?article=TutorialClispDebugger">History</a></li>
<li><a href="https://www.cliki.net/site/backlinks?article=TutorialClispDebugger">Backlinks</a></li><li><a href="https://www.cliki.net/site/edit-article?title=TutorialClispDebugger&amp;from-revision=3642668066">Edit</a></li><li><a href="https://www.cliki.net/site/edit-article?create=t">Create</a></li></ul></div>
</div>
<div id="header-buttons" class="buttonbar">
<ul>
<li><a href="https://www.cliki.net/">Home</a></li>
<li><a href="https://www.cliki.net/site/recent-changes">Recent Changes</a></li>
<li><a href="CLiki.html">About</a></li>
<li><a href="Text&#32;Formatting.html">Text Formatting</a></li>
<li><a href="https://www.cliki.net/site/tools">Tools</a></li>
</ul>
<div id="search">
<form action="https://www.cliki.net/site/search">
<label for="search_query" class="hidden">Search CLiki</label>
<input type="text" name="query" id="search_query" value="" />
<input type="submit" value="search" />
</form>
</div>
</div>
<div id="pageheader">
<div id="header">
<span id="logo">CLiki</span>
<span id="slogan">the common lisp wiki</span>
<div id="login"><form method="post" action="https://www.cliki.net/site/login">
<label for="login_name" class="hidden">Account name</label>
<input type="text" name="name" id="login_name" class="login_input" />
<label for= "login_password" class="hidden">Password</label>
<input type="password" name="password" id="login_password" class="login_input" />
<input type="submit" name="login" value="login" id="login_submit" /><br />
<div id="register"><a href="https://www.cliki.net/site/register">register</a></div>
<input type="submit" name="reset-pw" value="reset password" id="reset_pw" />
</form>
</div>
</div>
</div>
</body></html>