[Sbcl-help] debugging help

140 views
Skip to first unread message

Will Senn

unread,
Mar 19, 2017, 12:44:38 PM3/19/17
to sbcl...@lists.sourceforge.net
Hi,

I've asked about this on the irc channel, but didn't get a satisfactory
response, so I thought I would ask y'all. I am trying to understand and
use debugging with SBCL. It isn't going swimmingly. Here is the scenario...

Given the following function:

(defun sum (n1 n2)
(if (zerop n1) n2
(sum (1- n1) (1+ n2))))

And the call:
(sum 999999999 0)

This takes a while to execute. So, I hit C-c and enter the debugger. If
it breaks in my code, I can then display the values of n1 and n2 and
continue the program, breaking, evaluating, and continuing as desired.
What I really want to do is single step through the code evaluating
things as I do so. Two problems arise in this, 1. I can't seem to break
into my code predictably. 2. I can't seem to cause the single step
functionality to be enabled.

Just FYI, I also tried adding inline and debug declarations to my code
(and I varied the debug level from 0 to 3):

(defun sum (n1 n2)
(declare (inline sum))
(declare (optimize (debug 1)))
(if (zerop n1) n2
(sum (1- n1) (1+ n2))))

By way of background, my experience with debuggers is in C and languages
that are similar. In these languages, you tell the debugger to break at
main (or the first line of main, or at a breakpoint) and then can step
into, over and out of function calls. In those languages, you can also
break on a signal (such as C-c) as well as near limitless number of
other conditions. I am not looking for the same level of support, but I
would like to be able to predictably break into my running code and be
able to "step" through it.

I've read the relevant sections of the manual, but they are not really
that clear on these points. I've tried using START, but it either just
continues the program or says it's not possible to continue (if there's
a fatal bug), so STEP always says - Not currently single-stepping. (Use
START to activate the single-stepper). I have used BACKTRACE when the
debugger stops outside of my code, but using DOWN and UP don't seem to
get me to a place in my code where I can evaluate N1 and N2, like I can
when it breaks inside my code. Here's a bit of a debug session
illustrating the point, followed by my questions, I apologize in advance
for the length of this discussion, but wasn't sure what was critically
important vs cruft:

;; C-c to enter the debugger
debugger invoked on a SB-SYS:INTERACTIVE-INTERRUPT in thread
#<THREAD "main thread" RUNNING {100216E5B3}>:
Interactive interrupt at #x200002B2.

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
0: [CONTINUE] Return from SB-UNIX:SIGINT.
1: [ABORT ] Exit debugger, returning to top level.

(SB-VM::GENERIC-+)

;; well, it's not my code
0] backtrace

Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING {100216E5B3}>
0: (SB-VM::GENERIC-+)
1: (SB-INT:SIMPLE-EVAL-IN-LEXENV (SUM 999999999 0) #<NULL-LEXENV>)
2: (EVAL (SUM 999999999 0))
3: (INTERACTIVE-EVAL (SUM 999999999 0) :EVAL NIL)
4: (SB-IMPL::REPL-FUN NIL)
5: ((LAMBDA NIL :IN SB-IMPL::TOPLEVEL-REPL))
6: (SB-IMPL::%WITH-REBOUND-IO-SYNTAX #<CLOSURE (LAMBDA NIL :IN
SB-IMPL::TOPLEVEL-REPL) {10027DA9EB}>)
7: (SB-IMPL::TOPLEVEL-REPL NIL)
8: (SB-IMPL::TOPLEVEL-INIT)
9: ((FLET #:WITHOUT-INTERRUPTS-BODY-78 :IN SAVE-LISP-AND-DIE))
10: ((LABELS SB-IMPL::RESTART-LISP :IN SAVE-LISP-AND-DIE))

;; navigate to something looking like my code
0] d 2
(SB-INT:SIMPLE-EVAL-IN-LEXENV (SUM 999999999 0) #<NULL-LEXENV>)
1] n1

;
; caught WARNING:
; undefined variable: N1
;
; compilation unit finished
; Undefined variable:
; N1
; caught 1 WARNING condition

debugger invoked on a UNBOUND-VARIABLE in thread
#<THREAD "main thread" RUNNING {100216E5B3}>:
The variable N1 is unbound.

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
0: [ABORT ] Reduce debugger level (to debug level 1).
1: [CONTINUE] Return from SB-UNIX:SIGINT.
2: Exit debugger, returning to top level.

((LAMBDA (#:G458)) #<unused argument>)
source: (PROGN N1)
0[2] 0

1] backtrace

Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING {100216E5B3}>
0: (SB-INT:SIMPLE-EVAL-IN-LEXENV (SUM 999999999 0) #<NULL-LEXENV>)
1: (EVAL (SUM 999999999 0))
2: (INTERACTIVE-EVAL (SUM 999999999 0) :EVAL NIL)
3: (SB-IMPL::REPL-FUN NIL)
4: ((LAMBDA NIL :IN SB-IMPL::TOPLEVEL-REPL))
5: (SB-IMPL::%WITH-REBOUND-IO-SYNTAX #<CLOSURE (LAMBDA NIL :IN
SB-IMPL::TOPLEVEL-REPL) {10027DA9EB}>)
6: (SB-IMPL::TOPLEVEL-REPL NIL)
7: (SB-IMPL::TOPLEVEL-INIT)
8: ((FLET #:WITHOUT-INTERRUPTS-BODY-78 :IN SAVE-LISP-AND-DIE))
9: ((LABELS SB-IMPL::RESTART-LISP :IN SAVE-LISP-AND-DIE))

;; maybe the next line is my code?

1] d
(EVAL (SUM 999999999 0))
2] n1

;
; caught WARNING:
; undefined variable: N1
;
; compilation unit finished
; Undefined variable:
; N1
; caught 1 WARNING condition

debugger invoked on a UNBOUND-VARIABLE in thread
#<THREAD "main thread" RUNNING {100216E5B3}>:
The variable N1 is unbound.

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
0: [ABORT ] Reduce debugger level (to debug level 1).
1: [CONTINUE] Return from SB-UNIX:SIGINT.
2: Exit debugger, returning to top level.

((LAMBDA (#:G459)) #<unused argument>)
source: (PROGN N1)
0[2] 0


;; or the next line?
2] d
(INTERACTIVE-EVAL (SUM 999999999 0) :EVAL NIL)
3] n1

;
; caught WARNING:
; undefined variable: N1
;
; compilation unit finished
; Undefined variable:
; N1
; caught 1 WARNING condition

debugger invoked on a UNBOUND-VARIABLE in thread
#<THREAD "main thread" RUNNING {100216E5B3}>:
The variable N1 is unbound.

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
0: [ABORT ] Reduce debugger level (to debug level 1).
1: [CONTINUE] Return from SB-UNIX:SIGINT.
2: Exit debugger, returning to top level.

((LAMBDA (#:G460)) #<unused argument>)
source: (PROGN N1)
0[2] 0

;; hmm, the rest doesn't look promising at all
3] d
(SB-IMPL::REPL-FUN NIL)
4] backtrace

Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING {100216E5B3}>
0: (SB-IMPL::REPL-FUN NIL)
1: ((LAMBDA NIL :IN SB-IMPL::TOPLEVEL-REPL))
2: (SB-IMPL::%WITH-REBOUND-IO-SYNTAX #<CLOSURE (LAMBDA NIL :IN
SB-IMPL::TOPLEVEL-REPL) {10027DA9EB}>)
3: (SB-IMPL::TOPLEVEL-REPL NIL)
4: (SB-IMPL::TOPLEVEL-INIT)
5: ((FLET #:WITHOUT-INTERRUPTS-BODY-78 :IN SAVE-LISP-AND-DIE))
6: ((LABELS SB-IMPL::RESTART-LISP :IN SAVE-LISP-AND-DIE))

4] u
(INTERACTIVE-EVAL (SUM 999999999 0) :EVAL NIL)
3] u
(EVAL (SUM 999999999 0))
2] u
(SB-INT:SIMPLE-EVAL-IN-LEXENV (SUM 999999999 0) #<NULL-LEXENV>)
1] u
(SB-VM::GENERIC-+)
0] c

;; C-c again and this time, it's my code

debugger invoked on a SB-SYS:INTERACTIVE-INTERRUPT in thread
#<THREAD "main thread" RUNNING {100216E5B3}>:
Interactive interrupt at #x1002472BAA.

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
0: [CONTINUE] Return from SB-UNIX:SIGINT.
1: [ABORT ] Exit debugger, returning to top level.

(SUM 70277999 929722000)
unknown location: using block start

source: (SUM (1- N1) (1+ N2))
0] n1

70277999
0]

Here are my questions:
1. Is it possible to set a breakpoint in the code(say at the first line)?
2. Is it possible to navigate back into the function source code, if a
C-c stops the environment outside of the code (say, (SB-VM::GENERIC-+))?
3. How does one single step the debugger?

Thanks,

Will

--
GPG Fingerprint: 208A 38D6 D1BF 5A6B 7F4D CC0D 21CB 91B3 21E5 671F



------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Sbcl-help mailing list
Sbcl...@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sbcl-help

Stas Boukarev

unread,
Mar 19, 2017, 12:57:47 PM3/19/17
to Will Senn, SBCL help
You can't step from the debugger, you have to call your function with stepping.
And it has to be compiled with debug 3.
And using INLINE is a good way to defeat stepping, and inlining of recursive functions isn't going to end well.
--
With best regards, Stas.

Carlos Konstanski

unread,
Mar 19, 2017, 12:57:56 PM3/19/17
to sbcl...@lists.sourceforge.net
Two quick observations:

1. you can add a (break) form anywhere in your code which will cause the program
to stop and fall into the debugger.

2. In SLIME at least you can hit "s" while in the debugger to step. You can also
(in SLIME) evaluate any expression in the context of a frame.

------------------------------------------------------------------------------

Stas Boukarev

unread,
Mar 19, 2017, 1:02:06 PM3/19/17
to Will Senn, SBCL help
Actually, you can start stepping from the debugger, using START.
I just typed HELP and found that out...

Will Senn

unread,
Mar 19, 2017, 1:04:24 PM3/19/17
to Stas Boukarev, SBCL help

Stas,

I can debug 3, I can remove inline. How do I "call my function with stepping"?

Thanks,

Will

Stas Boukarev

unread,
Mar 19, 2017, 1:05:06 PM3/19/17
to Will Senn, SBCL help
Using the STEP macro, naturally.

Will Senn

unread,
Mar 19, 2017, 1:05:44 PM3/19/17
to Stas Boukarev, SBCL help

Hmm. Help says that this will work, but as I explained below, I haven't been able to see it work. Can you provide an example?

Douglas Katzman

unread,
Mar 19, 2017, 1:08:52 PM3/19/17
to Will Senn, SBCL help
As Stas said, you're gonna have a problem due to recursion.
Compiling with DEBUG 3 will not let you invoke (sum 999999999 0) due to stack overflow.
There's a trick that works, which I would have expected you to know.  Try as follows:

* (defun sum (n1 n2)
  (declare (optimize (debug 2)))
  (if (zerop n1) n2
    (locally
     (declare (optimize (debug 3)))
     (sum (1- n1) (1+ n2)))))
SUM

* (step(sum 9999999 0))
(step(sum 9999999 0))
; Evaluating call:
;   (- N1 1)
; With unknown arguments

0] step
step
; Evaluating call:
;   (+ N2 1)
; With unknown arguments

0] step
step
; Evaluating call:
;   (SUM (1- N1) (1+ N2))
; With arguments:
;   9999998
;   1

1] step
step
; Evaluating call:
;   (- N1 1)
; With unknown arguments

0] :l
:l
N1  =  9999998
N2  =  1

Douglas Katzman

unread,
Mar 19, 2017, 1:09:08 PM3/19/17
to Will Senn, SBCL help
Apology, I meant would NOT have expected you to know.
Very sorry about that typo.

Stas Boukarev

unread,
Mar 19, 2017, 1:09:45 PM3/19/17
to Will Senn, SBCL help
You can't interrupt a function and hope to debug it, especially the one that quickly runs out of stack space.

Will Senn

unread,
Mar 19, 2017, 1:26:26 PM3/19/17
to Douglas Katzman, SBCL help

Ha, I was about to reply that I'm a complete newb, but I'm pretty sure you knew that :). Thanks for the example! It's working.

So, taking off on what you suggested, I did this:

(defun sum (n1 n2)
  (declare (optimize (debug 3)))
  (break)


  (if (zerop n1) n2
      (sum (1- n1) (1+ n2))))

now when I execute the function, it breaks on each call.

CL-USER> (sum 99999 0)

debugger invoked on a SIMPLE-CONDITION in thread


#<THREAD "main thread" RUNNING {100216E5B3}>:

  break



Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):

  0: [CONTINUE] Return from BREAK.


  1: [ABORT   ] Exit debugger, returning to top level.

(SUM 99999 0)
   source: (BREAK)
0] c

debugger invoked on a SIMPLE-CONDITION in thread


#<THREAD "main thread" RUNNING {100216E5B3}>:

  break



Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):

  0: [CONTINUE] Return from BREAK.


  1: [ABORT   ] Exit debugger, returning to top level.

(SUM 99998 1)
   source: (BREAK)
0] c

debugger invoked on a SIMPLE-CONDITION in thread


#<THREAD "main thread" RUNNING {100216E5B3}>:

  break



Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):

  0: [CONTINUE] Return from BREAK.


  1: [ABORT   ] Exit debugger, returning to top level.

(SUM 99997 2)
   source: (BREAK)

It seems reasonable and in line with what I would expect. Hallelujah! The function is just too simple and quick for it to be very interesting, but I gather that a more complex function that has more forms to process will feel more like a traditional debugging session. I can break on entering the function and step through the forms. I'm not entirely sure what role START has in any session though, any insight on when I would need/use START?

Thanks,


Will

Stas Boukarev

unread,
Mar 19, 2017, 1:28:12 PM3/19/17
to Will Senn, SBCL help
CONTINUE doesn't invoke stepping, you need to use one of

  STEP  Steps into the current form.
  NEXT  Steps over the current form.
  OUT   Stops stepping temporarily, but resumes it when the topmost frame that
        was stepped into returns.

If you wanted just to watch all the calls to SUM you can do (trace sum) before calling it.

Will Senn

unread,
Mar 19, 2017, 1:35:25 PM3/19/17
to Stas Boukarev, SBCL help

Success! I appreciate your help.

(defun sum (n1 n2)
  (declare (optimize (debug 3)))
  (break)
  (if (zerop n1) n2
      (sum (1- n1) (1+ n2))))

SUM
CL-USER> (sum 999999999 0)



debugger invoked on a SIMPLE-CONDITION in thread
#<THREAD "main thread" RUNNING {100216E5B3}>:
  break

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [CONTINUE] Return from BREAK.
  1: [ABORT   ] Exit debugger, returning to top level.

(SUM 999999999 0)
   source: (BREAK)
0] s



Not currently single-stepping. (Use START to activate the single-stepper)

0] start


; Evaluating call:
;   (- N1 1)
; With unknown arguments

0] s

; Evaluating call:
;   (+ N2 1)
; With unknown arguments

0] n1

999999999
0] n2

0
0] p
(SUM 999999999 0)
0] s


; Evaluating call:
;   (SUM (1- N1) (1+ N2))
; With arguments:

;   999999998
;   1

1]

Reply all
Reply to author
Forward
0 new messages