Notes re. debugging Common Lisp with SBCL and Slime
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Context from IRC logs for future reference. | |
[12:36] <kushal> nikodemus, can you point me to some tutorial on debugging on sbcl ? | |
[12:37] * andreer (andreer@flode.pvv.ntnu.no) has joined #quicklisp | |
[12:38] <nikodemus> kushal: no really good ones actually exist at the moment. i'm planning on writing one before the year is out, but... in the meanwhile, i can give some pointers | |
[12:38] <nikodemus> a couple of questions: is this a general question, or do you have a specific issue you need to debug? | |
[12:39] <kushal> right now , general, managed to debug the specific issue somehow :) | |
[12:39] <nikodemus> ok. are you using slime? | |
[12:39] <kushal> nikodemus, yes and no both | |
[12:40] <nikodemus> good. :) | |
[12:40] <kushal> some cases I am running with dynamic-size | |
[12:40] <nikodemus> ah | |
[12:40] <nikodemus> let me write down a few notes. be back in 5 | |
[12:40] <kushal> nikodemus, ok thanks :) | |
[12:47] * pavelludiq (~quassel@87.246.31.34) has joined #quicklisp | |
[13:12] * arbscht_ is now known as arbscht | |
[13:13] * cmpitg has quit (Quit: leaving) | |
[13:22] <nikodemus> kushal: http://gist.github.com/659495 # took a bit longer, and it's still just a quick braindump | |
[13:23] <kushal> nikodemus, ok checking | |
[13:25] <kushal> nikodemus, ok, | |
[13:26] <kushal> nikodemus, how to find the exact line where the error happens in code ? source command never helped that much | |
[13:27] <nikodemus> kushal: let me think. i do that by instinct, so i'll have to check :) | |
[13:29] <nikodemus> v in the debugger should take you to the right frame | |
[13:29] <nikodemus> sorry, "on the right frame" should take you to the function responsible | |
[13:30] <kushal> ok | |
[13:30] <nikodemus> eg. if you have (/ x 0) somewhere, the frame of interest is the one after INTEGER-/-INTEGER or similar | |
[13:30] <kushal> ok | |
[13:31] <kushal> I was getting (SB-IMPL::GETHASH3 "i686" NIL NIL) | |
[13:31] <nikodemus> if the debug policy for that function is 2 or more, you should get the exact location | |
[13:31] <kushal> source showed #<SB-DI::COMPILED-DEBUG-FUN (SB-C::TL-XEP SB-IMPL::GETHASH3)> has no debug-block information. | |
[13:32] <kushal> nikodemus, how to set debug policy in my code ? | |
[13:32] <nikodemus> so you're trying to use NIL where there should be a hash-table -- you want the previous frame | |
[13:32] <kushal> up ? | |
[13:32] <kushal> or v ? | |
[13:32] <nikodemus> (defun foo () (declare (optimize (debug 2)) ...) locally or (declaim (optimize (debug 2)) per file -- it's actually more complex than "per file", but that's an approximation you can live with | |
[13:33] <nikodemus> the one below the the GETHASH frame in the backtrace, it's caller | |
[13:33] <nikodemus> that (or earlier) is where the error is | |
[13:33] <kushal> nikodemus, ok | |
[13:34] <nikodemus> slime v should be made to fall back on other methods if there is not debug block information, like most internals don't | |
[13:34] <kushal> nikodemus, after going one frame down, source command ? | |
[13:34] <nikodemus> in the repl? | |
[13:34] <kushal> yes ? | |
[13:34] <nikodemus> is that your function that is responsible for that frame? | |
[13:35] <kushal> nikodemus, let me introduce the bug again and try live :) | |
[13:35] <nikodemus> i don't actually almost ever use the source command in the plain sbcl repl, but probably | |
[13:36] <nikodemus> when you land in the sbcl debugger in the plain repl, a good first thing to do is "ba 30" -- get the backtrace for a modest number of frames to give you an idea of what's going on | |
[13:36] * mathrick has quit (Excess Flood) | |
[13:37] <kushal> nikodemus, yes, that is actually helped me to fix the error in the first place |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
0. Test case reduction is an essential skill, no matter the language | |
or the environment. It is the art of reducing the code that provokes | |
the issue (wrong result, an error, whatever) down to manageable size | |
-- including the full call path involved, and environmental issues | |
like Slime vs no Slime, or current directory. The smaller the better | |
in general, but it is a balancing act: if you can identify the issue | |
using other methods in five minutes, it doesn't make sense to spend an | |
hour or two boiling down the test case. ...but when other methods are | |
not producing results, and time is dragging on then you should | |
consider this. | |
1. Defensive programming. Not just coding to protect against errors, | |
but coding so that your code is easy to debug. Avoid IGNORE-ERRORS and | |
generally swallowing errors silently. Sometimes it is the right thing | |
to do, but the more you do it, the harder *BREAK-ON-SIGNALS* becomes | |
to use when you need it. Avoid SAFETY 0 like the plague -- it can hide | |
a multitude of sins. Avoid DEBUG 0 -- it doesn't pay. Write | |
PRINT-OBJECT methods for your objects, give your objects names to use | |
when printing. Check that slots are bound before you use them in your | |
PRINT-OBJECT methods. NEVER use INTERRUPT-THREAD or WITH-TIMEOUT | |
unless you really know what you are doing and exactly why I'm telling | |
you not to use them. | |
2. Stop to think. Read the error messages, if any, carefully. | |
Sometimes they're no help at all, but sometimes there are nuggets of | |
information in them that a casual glance will miss. | |
3. Know your environment. (This is what the question was really about, I know...) | |
3.0. M-. is gold. Plain v on a backtrace frame in Slime may also take | |
you places if your code has a sufficiently high debug setting, but M-. | |
should work pretty much always. | |
3.1. The Slime Inspector is one of my primary debugging tools -- but | |
that is probably affected by the kind of code I work on, so it might | |
not be the same for everyone. Still, you should familiarize yourself | |
with it -- and use the fancy one. :) | |
3.2. While SBCL backtraces aren't at the moment the pretties ones in | |
the world, try to make sense out of them. Just do (error "foo") in the | |
REPL, and figure out what is going on. Experiment with both the plain | |
SBCL debugger and the fancy Slime Debugger before you need to use them | |
for real. They'll feel a lot less hostile that way. I'll write advice | |
on interpreting the backtraces at another time. | |
3.3. Learn about *BREAK-ON-SIGNALS* and TRACE. Also note the SBCL | |
extensions to TRACE. | |
3.4. The stepper isn't really a debugging tool, IMO -- it is a tool | |
for understanding control flow, which sometimes helps in debugging -- | |
but if you compile your code with DEBUG 3, then (STEP (FOO)) can take | |
you to places. | |
3.5. Learn about M-RET (macroexpand) in Slime. Discover what happens | |
if you do (SETF *PRINT-GENSYM* NIL) first, and understand the | |
potential danger there -- but also the utility of being easily able to | |
copy-paste the expansion into your test case when you're trying to | |
reduce it. (Replacing expansions of macros in the COMMON-LISP package | |
is typically pointless, but replacing those from user packages can be | |
golden.) | |
3.6. If all else fails, do (sb-ext:restrict-compiler-policy 'safety 3) | |
and (sb-ext:restrict-compiler-policy 'debug 3) and recompile your | |
code. Debugging should be easier now. If the error goes away, either | |
(a) you had a type-error or similar in SAFETY 0 code that was breaking | |
stuff but is now swallowed by an IGNORE-ERRORS or a HANDLER-CASE or | |
(b) you may have found on SBCL bug: compiler policy should not | |
generally speaking change codes behaviour -- though there are some | |
ANSI mandated things for SAFETY 3, and high DEBUG can inhibit | |
tail-call optimizations which, as Schemers know, can matter. | |
3.7. DISASSEMBLE isn't normally a debugging tool, but sometimes it can | |
help too. Depends on what the issue is. | |
4. Extensions to printf() debugging. Sometimes this is just the | |
easiest thing. No shame in there. | |
4.1. Special variables are nice. | |
(LET ((*DEBUG* :MAGIC)) ...) and elsewhere | |
(WHEN (EQ :MAGIC *DEBUG*) (PRINT (LIST :FOO FOO))) | |
Because I'm lazy, I tend to use * as the magic variable, so I can also | |
trivially set it in the REPL. This allows you to get the debugging | |
output for stuff you are interested in only when certain conditions | |
are true. Or you can use it to tell which code path the call is coming | |
from, etc. | |
4.2. Don't be afraid to add (break "foo=~S" foo) and similar calls to | |
the code you're debugging. | |
4.3. SB-DEBUG:BACKTRACE can sometimes be of use. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment