Examine values of local variables in SLIME debugger? - emacs

I'm in search for a better technique for doing it. My general struggle is with the fact that debugger enters either too late or too early to be able to catch the value of the variables.
What I tried first:
(loop for i from 0 to 10 do
(break))
When debugger enter on break, I can't access i :( So it's a wasted effort. I've tried e option of debugger (eval in frame), but SLIME generally just bugs out, and I have to reconnect to SWANK. v or t don't help, because the variable just "isn't there".
What I ended up doing:
(loop for i from 0 to 10 do
(signal i))
This is stupid, but works, because it puts i on the stack of the frame I can examine in debugger. But this is just... well, it's hackish in the worst sense of the word. Isn't there some way to "watch" a variable, or have a more meaningful way to put a breakpoint, such that I can see more variables around the place the breakpoint is entered?

Your first snippet works just fine for me with CCL (default optimize settings), Emacs 24, and a recently pulled Slime:
Break
[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-BREAK] Reset this thread
4: [ABORT] Kill this thread
Backtrace:
0: (#<Anonymous Function #x186F9B7E>)
Locals:
I = 0
1: (CCL::CHEAP-EVAL (LOOP FOR I FROM 0 TO 10 DO (BREAK)))
⋮
sldb-eval-in-frame works fine for me, too. Maybe you should try a different Lisp implementation or a different version of Slime.
Also, note that different optimize settings might be important here, and some implementations give better debugging results for interpreted code (if an interpreter is available, that is). Try something like (declaim (optimize (debug 3) (speed 0) (space 0))).

Related

Emacs 26 flymake: customizing the mode line format

I'm considering switching back from flycheck to flymake after the Emacs 26 rewrite. One thing that bothers me about flymake is how much room on the mode line it takes up. It has a string Flymake plus the results. It seems like a silly thing but this is 10% of the mode line on an 80-char frame, just for a name! I have lots of important info I'd like to see on my mode line so I try to remove things that aren't helpful to me--I know what minor modes etc. are enabled in my buffers, since I configured them! Personally I'd prefer to not see a name at all, just the results, or at most F or FM.
I could use diminish to get rid of the mode line info completely but of course I don't want that: I want to be able to see the state of my buffer.
I took a look at flymake.el and the flymake--mode-line-format defun and it doesn't seem like this string is configurable, or easy to change at all.
Anyone have any thoughts about this?
You'd need to redefine flymake--mode-line-format function. It should probably be more customizable, but it's not. Possibly the least intrusive way to do it is to define :filter-return advice on that function to transform the output.
(defun flymake--transform-mode-line-format (ret)
"Change the output of `flymake--mode-line-format'."
(setf (seq-elt (car ret) 1) " FM")
ret)
(advice-add #'flymake--mode-line-format
:filter-return #'flymake--transform-mode-line-format)

Emacs won't start slime: MAKE-AUTO-FLUSH-THREAD is undefined

After loading emacs25 -nw (or just emacs, doesn't matter) and telling him to start slime (M-x slime), this is what i get.
The function SWANK/BACKEND::MAKE-AUTO-FLUSH-THREAD is undefined.
[Condition of type UNDEFINED-FUNCTION]
Restarts:
0: [CONTINUE] Retry calling SWANK/BACKEND::MAKE-AUTO-FLUSH-THREAD.
1: [USE-VALUE] Call specified function.
2: [RETURN-VALUE] Return specified values.
3: [RETURN-NOTHING] Return zero values.
4: [*ABORT] Return to SLIME's top level.
5: [ABORT] abort thread (#<THREAD "worker" RUNNING {1006CCF143}>)
Backtrace:
0: ("undefined function" #<SWANK/GRAY::SLIME-OUTPUT-STREAM
{10072A4F33}>)
--more--
However, if I the just press 3, it seems to work out and start what I want. I'd like to know what's going on tho, since it is a good idea in general.
Since you are using sbcl I find the following configuration works for me:
(setq inferior-lisp-program "/usr/bin/sbcl")
(load (expand-file-name "~/quicklisp/slime-helper.el"))
you wil need to install quicklisp and set sbcl as inferior-lisp.
follow Astray_BI's Blog

Getting the CL-USER prompt back

This happened:
CL-USER> (/4 5.)
Invoking restart: Retry applying /4 to (5).
Invoking restart: Reset this thread
; Evaluation aborted on #<CCL::UNDEFINED-FUNCTION-CALL #x302000B0B6AD>.
CL-USER> (/ 4 5.)
sljk
No matter what I type, the CL-USER> prompt does not return. Obviously, I had a typo and didn't include a space after the / which gave me the error screen (what's that REPL screen called?). I must have chosen an option that was one of the abort options, but while it gave me the CL-USER> prompt right after that, it is somewhat "inactive" as you can see here.
How to exit out of this situation?
This happens to me when I manage to fry the underlying Lisp instance. Alternatively, if I manage to kill the thread that SLIME is interactive with... that also damages the instance.
Check the *inferior-lisp* buffer for clues; if it's hung, M-x slime-restart-inferior-lisp will restart your underlying Common Lisp.

Turn-off debugger in Emacs SLIME

According to this question, I can customize the variable *DEBUGGER-HOOK* so that it falls back to toplevel (in REPL) instead of the debugger. I've added this line to my ~/.sbclrc and it's all fine when I start sbcl from command line.
(setf *debugger-hook* #'(lambda (c h) (declare (ignore h)) (print c) (abort)))
But, the above doesn't work for Emacs SLIME. Whenever I compile/load a file (C-c C-k), it still invokes the debugger (with options like abort calculation, restart, enter new value etc.). How can I ask SLIME to just print the error message and throw me back to toplevel? Yea, it's with SBCL and the same ~/.sbclrc as before. Looks like SLIME doesn't respect a user's setting of *DEBUGGER-HOOK*.
As per http://common-lisp.net/project/slime/doc/html/Other-configurables.html setting SWANK:*GLOBAL-DEBUGGER* to nil in ~/.swank.lisp file should force SLIME to not replace *DEBUGGER-HOOK* to SWANK:SWANK-DEBUGGER-HOOK (which shows list of restarts etc.), but it somehow doesn't work for me, i.e. SWANK:*GLOBAL-DEBUGGER* is nil but anyway *DEBUGGER-HOOK* is replaced by SLIME. Maybe you'll be more lucky.
As a workaround I can propose to set *DEBUGGER-HOOK* to whatever you want in the slime-repl buffer manually, which is worked for me.

Evaluation expressions in frame only yields UNBOUND-VARIABLE

I'm using SLIME to debug my Common Lisp function. Inside the function, I've made it artificially signal an error (trying to "debug"—perhaps I should be stepping) like so:
(define-condition unknown-zone (error)
((text :initarg :text :reader text)))
(defun parse-mime-date (date)
(let ((last-space (position #\Space date :from-end t)))
(let ((date-time (net.telent.date:parse-time (subseq date 0 last-space)))
(zone (subseq date (1+ last-space))))
(unless (or (char= (elt zone 0) #\+)
(char= (elt zone 0) #\-))
(error 'unknown-zone :text (format nil "Unknown timezone: ~a" zone)))
(let ((hours (parse-integer (subseq zone 0 3)))
(minutes (parse-integer
(concatenate 'string
(list (elt zone 0))
(subseq zone 3)))))
(error 'unknown-zone :text "LOL")
(let ((adjusted-date-time (- date-time (* 60 (+ minutes (* 60 hours))))))
(format t "date-time: ~a; zone: ~a~%" date-time zone)
(format t "adjusted: ~a" (net.telent.date:universal-time-to-http-date adjusted-date-time)))))))
I'm trying to work around what appears to be a deficiency in net.telent.date:parse-time (it seems to botch up timezone handling, though I'm not 100% yet).
The "LOL" unknown-zone error is of course the artificial breakpoint.
When it hits this part of the function, SLDB faithfully opens up with the backtrace:
Bad type argument:
NS-MAIL2ZD::UNKNOWN-ZONE
[Condition of type SIMPLE-TYPE-ERROR]
Restarts:
0: [RETRY] Retry SLIME REPL evaluation request.
1: [*ABORT] Return to SLIME's top level.
2: [REMOVE-FD-HANDLER] Remove #<SB-IMPL::HANDLER INPUT on descriptor 7: #<CLOSURE (LABELS SWANK-BACKEND::RUN :IN SWANK-BACKEND:ADD-FD-HANDLER) {10030AD9FB}>>
3: [ABORT] Exit debugger, returning to top level.
Backtrace:
0: (MAKE-CONDITION NS-MAIL2ZD::UNKNOWN-ZONE :TEXT "LOL")
1: (ERROR NS-MAIL2ZD::UNKNOWN-ZONE :TEXT "LOL")
2: (NS-MAIL2ZD:PARSE-MIME-DATE "Wed, 14 Mar 2012 06:59:36 +1100")
3: (SB-INT:SIMPLE-EVAL-IN-LEXENV (NS-MAIL2ZD:PARSE-MIME-DATE *LOL*) #<NULL-LEXENV>)
4: (EVAL (NS-MAIL2ZD:PARSE-MIME-DATE *LOL*))
--more--
Then I page down to the frame:
Backtrace:
0: (MAKE-CONDITION NS-MAIL2ZD::UNKNOWN-ZONE :TEXT "LOL")
1: (ERROR NS-MAIL2ZD::UNKNOWN-ZONE :TEXT "LOL")
Locals:
SB-KERNEL::ARGUMENTS = (:TEXT "LOL")
SB-KERNEL::DATUM = NS-MAIL2ZD::UNKNOWN-ZONE
2: (NS-MAIL2ZD:PARSE-MIME-DATE "Wed, 14 Mar 2012 06:59:36 +1100")
Now I hit e to invoke sldb-eval-in-frame and type last-space, as that should be available where the error was signaled.
It seems this isn't how it's meant (?) to work:
The variable LAST-SPACE is unbound.
[Condition of type UNBOUND-VARIABLE]
Restarts:
0: [ABORT] Return to sldb level 1.
1: [RETRY] Retry SLIME REPL evaluation request.
2: [*ABORT] Return to SLIME's top level.
3: [REMOVE-FD-HANDLER] Remove #<SB-IMPL::HANDLER INPUT on descriptor 7: #<CLOSURE (LABELS SWANK-BACKEND::RUN :IN SWANK-BACKEND:ADD-FD-HANDLER) {10030AD9FB}>>
4: [ABORT] Exit debugger, returning to top level.
Backtrace:
0: ((LAMBDA (#:G1144)) #<unavailable argument>)
--more--
Is there a way to do what I want? Am I over-complicating matters?
Thanks!
Addendum: I've tried using (break) (this seems a tad more canonical), but I still can't see let-bound variables with e. :<
There is another option to debug a Lisp function: use an interpreter if available. Most implementations can switch between interpreted and compiled code. You could run all code compiled, but the function you want to debug could be running interpreted. In the extreme case you could even break into a stepper for the interpreted function (if available).
Note that it makes sense to define a default optimization setting based on your requirements.
don't set safety very low globally. Do that only in code sections where this is useful.
don't set speed very high globally. Do that only in code sections where this is useful.
keep debug information, use a setting of 2. Depending on the implementation a high debug setting may shut down tail call optimization (TCO) - which can be undesirable for execution and desirable for debugging.
I would propose different settings for:
development: safe + debug friendly
deployment: safe
optimization of numeric code, locally for speed
By default you should use a setting that is useful for interactive use:
speed 1-2. speed is somewhat important
safety 2-3. safety is very important. all operations are checked at runtime
debug 2-3. debug information is kept and the operations should be interruptible by a debugger.
space 1. The size of the code is not important.
compilation-speed 1. The speed of the compilation process is not so important.
The range is from 0 to 3. No number defaults to 3. 3 is higher.
Depending on the implementation there might be additional optimization settings and also some variables to configure the compiler.
In speed-critical sections you can set speed higher than safety and debug. But be aware that in some cases this changes the semantics of the code execution (error detection, overflows, ...) and that missing runtime checks may make it possible that your code corrupts the Lisp heap.
In some delivery situations it also may be useful to to have a very low debug setting. But if your Lisp compiler is set to high speed optimizations and low debug, debugging the code by looking at the backtrace of the stack may get much harder.
Try using the complete name of the symbol (i.e. package::symbol-name) instead of just the symbol name (i.e. symbol-name). In the current case, maybe net.telent.date::last-space.
Also make sure that you compiled with debugging support set to maximum (for instance, try placing a (declaim (optimize debug)) before your function and recompile the file - C-c C-k).
Checking that the code you're trying to debug has enough debug information: in the sldb window, put the cursor on the relevant line in the backtrace and press t - this should expand the frame and show values for all the locals. Pressing t again collapses the local frame information. If there is not enough debug information, you won't see the local variables, but some made up names (such as SB-DEBUG:ARG-0 under SBCL). Inside the sldb window, if you press Enter with the cursor on a value, it will expand that value into an inspector window (useful if the value is a long list which is shown truncated).
Also, SLIME debugging support seems to vary with the implementation. The advice above works on SBCL under Linux, YMMV under different implementations.
The variable seems to be optimized away. From the SBCL Manual:
The value of a variable may be unavailable for these reasons:
The value of the debug optimization quality may have omitted debug
information needed to determine whether the variable is available.
Unless a variable is an argument, its value will only be available
when debug is at least
The compiler did lifetime analysis and
determined that the value was no longer needed, even though its scope
had not been exited. Lifetime analysis is inhibited when the debug
optimization quality is
The variable’s name is an uninterned
symbol (gensym). To save space, the compiler only dumps debug
information about uninterned variables when the debug optimization
quality is
The frame’s location is unknown (see Section 5.3.5 [Unknown Locations and Interrupts], page 31) because the debugger was entered
due to an interrupt or unexpected hardware error. Under these
conditions the values of arguments will be available, but might be
incorrect. This is the exception mentioned above.
The variable (or
the code referencing it) was optimized out of existence. Variables
with no reads are always optimized away. The degree to which the
compiler deletes variables will depend on the value of the
compilation-speed optimization quality, but most source-level
optimizations are done under all compilation policies.
The variable is never set and its definition looks like (LET ((var1 var2)) ...) In this case, var1 is substituted with var2.
The variable is never set and is referenced exactly once. In this case, the reference is substituted with the variable initial value
You can see all available local variables with t.
If you add a reference to your variable after your error conditions, probably you'll be able to get its value in the corresponding debug frame.