155 lines
No EOL
8.1 KiB
HTML
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]> (defun fact (x) (if (< x 1) (/ 1 x) (* x (fact (1- x)))))
|
|
FACT
|
|
[222]> (defun pf (n) (loop :for i :from n :downto 1 :do (print `(,i ,(fact i)))))
|
|
PF
|
|
[223]> (pf 4)
|
|
|
|
*** - division by zero
|
|
The following restarts are available:
|
|
ABORT :R1 ABORT
|
|
Break 1 [224]>
|
|
</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]> :bt
|
|
<1> #< SYSTEM-FUNCTION EXT:SHOW-STACK > 3 ; These frames correspond
|
|
<2> #< COMPILED-FUNCTION SYSTEM::PRINT-BACKTRACE > ; to compiled functions
|
|
<3> #< COMPILED-FUNCTION SYSTEM::DEBUG-BACKTRACE > ; belonging to the
|
|
<4> #< SYSTEM-FUNCTION SYSTEM::READ-EVAL-PRINT > 2 ; debugged itself.
|
|
<5> #< COMPILED-FUNCTION SYSTEM::BREAK-LOOP-2-2 > ; Notice how little
|
|
<6> #< SYSTEM-FUNCTION SYSTEM::SAME-ENV-AS > 2 ; information we have:
|
|
<7> #< COMPILED-FUNCTION SYSTEM::BREAK-LOOP-2 > ; only the function name.
|
|
<8> #< SYSTEM-FUNCTION SYSTEM::DRIVER > ;
|
|
<9> #< COMPILED-FUNCTION SYSTEM::BREAK-LOOP > ;
|
|
<10> #< SYSTEM-FUNCTION INVOKE-DEBUGGER > 1 ;
|
|
<11> #< SYSTEM-FUNCTION / > 2 ; the divide function call
|
|
; who raised the error.
|
|
<12> #< SPECIAL-OPERATOR IF >
|
|
; And here we have the form from our code
|
|
; who called / with a zero divisor.
|
|
; Happily, it's the only division in the form
|
|
; so we can identify it immediately.
|
|
EVAL frame for form (IF (< X 1) (/ 1 X) (* X (FACT (1- X))))
|
|
APPLY frame for call (FACT '0)
|
|
<13>
|
|
#<F UNCTION FACT (X) (DECLARE (SYSTEM::IN-DEFUN FACT))
|
|
(BLOCK FACT (IF (< X 1) (/ 1 X) (* X (FACT (1- X))))) > 1
|
|
EVAL frame for form (FACT (1- X))
|
|
EVAL frame for form (* X (FACT (1- X)))
|
|
<14> #< SPECIAL-OPERATOR IF >
|
|
EVAL frame for form (IF (< X 1) (/ 1 X) (* X (FACT (1- X))))
|
|
APPLY frame for call (FACT '1)
|
|
[...]
|
|
EVAL frame for form
|
|
(LET ((I N))
|
|
(LET NIL
|
|
(TAGBODY SYSTEM::BEGIN-LOOP (WHEN (< 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 '4)
|
|
<25>
|
|
#<F UNCTION PF (N) (DECLARE (SYSTEM::IN-DEFUN PF))
|
|
(BLOCK PF (LOOP :FOR I :FROM N :DOWNTO 1 :DO (PRINT `(,I ,(FACT I))))) > 1
|
|
EVAL frame for form (PF 4)
|
|
Printed 25 frames
|
|
Break 1 [224]>
|
|
</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]> :u
|
|
<1> #< SPECIAL-OPERATOR IF >
|
|
EVAL frame for form (IF (< 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]> (< 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]> 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]> (defun fact (x) (if (< 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]> :rt
|
|
Values: 1
|
|
|
|
(4 24)
|
|
(3 6)
|
|
(2 2)
|
|
(1 1)
|
|
NIL
|
|
[225]>
|
|
</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 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&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 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> |