Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Debugging Lisp code (stupid newbie question)

546 views
Skip to first unread message

Joe Knapka

unread,
Sep 5, 2004, 10:37:06 PM9/5/04
to
Hi everyone,

I am getting into Lisp again after a brief exposure in college,
followed by a lengthy absence (> 10 years). I seem to be missing the
blindingly obvious. I've now got a few CLs installed on my Linux box
(gcl, clisp, sbcl), and after poking around for hours in the man
pages, this group's archive and FAQ, Google, and Paul Graham's "ANSI
Common Lisp", I haven't been able to find any way to set a breakpoint,
in any of the CLs I've installed. (trace) is nice, but I'd like a way
to say, "please stop any time my-func is evaluated," and then end up
at a prompt where I can see what's about to be evaluated, step over or
into forms, inspect objects, and so forth. Does such a thing exist?

Please forgive me if this is a FAQ. A pointer to relevant online
documentation would be more than sufficient.

Thanks,

-- Joe

--
"We sat and watched as this whole <-- (Died Pretty -- "Springenfall")
blue sky turned to black..."
... Re-defeat Bush in '04.
--
pub 1024D/BA496D2B 2004-05-14 Joseph A Knapka
Key fingerprint = 3BA2 FE72 3CBA D4C2 21E4 C9B4 3230 94D7 BA49 6D2B
If you really want to get my attention, send mail to
jknapka .at. kneuro .dot. net.

Pascal Bourguignon

unread,
Sep 5, 2004, 11:14:20 PM9/5/04
to
Joe Knapka <jkn...@kneuro.net> writes:

> Hi everyone,
>
> I am getting into Lisp again after a brief exposure in college,
> followed by a lengthy absence (> 10 years). I seem to be missing the
> blindingly obvious. I've now got a few CLs installed on my Linux box
> (gcl, clisp, sbcl), and after poking around for hours in the man
> pages, this group's archive and FAQ, Google, and Paul Graham's "ANSI
> Common Lisp", I haven't been able to find any way to set a breakpoint,
> in any of the CLs I've installed. (trace) is nice, but I'd like a way
> to say, "please stop any time my-func is evaluated," and then end up
> at a prompt where I can see what's about to be evaluated, step over or
> into forms, inspect objects, and so forth. Does such a thing exist?
>
> Please forgive me if this is a FAQ. A pointer to relevant online
> documentation would be more than sufficient.

Another useful trick is the function APROPOS:

> (APROPOS 'DEBUG)
==> a lot of symbols, and eventually:
INVOKE-DEBUGGER function


You can invoke it minimally with:

(invoke-debugger (make-condition 'error))

Unless you have a sophisticated IDE, you'll have to insert this call
manually in the functions where you want to break in. For example:

(defun bp () (invoke-debugger (make-condition 'error)))

(defun fact (n) (do ((n n (1- n)) (f n (* n f))) ((= 0 n) f) (bp)))


BUT, I never had to use the debugger like this in my Lisp programming
because I use rather a bottom-up building mode: I start with a simple
sexp at the REPL, and when I got a reasonable intermediary result, I
encapsulate it in a bigger sexp and up and up until I've got the
result I need, or until it's big enough to be called a function (and
then I proceeed with a new sexp invoking this new function).
Generally, the functions written this way are bug free, because
they've been debugged while being constructed, at the REPL.


--
__Pascal Bourguignon__ http://www.informatimago.com/

Our enemies are innovative and resourceful, and so are we. They never
stop thinking about new ways to harm our country and our people, and
neither do we.

Coby Beck

unread,
Sep 5, 2004, 11:36:22 PM9/5/04
to

"Pascal Bourguignon" <sp...@mouse-potato.com> wrote in message
news:874qmcf...@thalassa.informatimago.com...

> Joe Knapka <jkn...@kneuro.net> writes:
> > Common Lisp", I haven't been able to find any way to set a breakpoint,
> > in any of the CLs I've installed. (trace) is nice, but I'd like a way
> > to say, "please stop any time my-func is evaluated," and then end up
> > at a prompt where I can see what's about to be evaluated, step over or
> > into forms, inspect objects, and so forth. Does such a thing exist?
> >
> > Please forgive me if this is a FAQ. A pointer to relevant online
> > documentation would be more than sufficient.
>
> Another useful trick is the function APROPOS:
>
> > (APROPOS 'DEBUG)
> ==> a lot of symbols, and eventually:
> INVOKE-DEBUGGER function
>
>
> You can invoke it minimally with:
>
> (invoke-debugger (make-condition 'error))

What in the world is wrong with BREAK?

--
Coby Beck
(remove #\Space "coby 101 @ big pond . com")


Peter Seibel

unread,
Sep 5, 2004, 11:36:50 PM9/5/04
to
Joe Knapka <jkn...@kneuro.net> writes:

> Hi everyone,
>
> I am getting into Lisp again after a brief exposure in college,
> followed by a lengthy absence (> 10 years). I seem to be missing the
> blindingly obvious. I've now got a few CLs installed on my Linux box
> (gcl, clisp, sbcl), and after poking around for hours in the man
> pages, this group's archive and FAQ, Google, and Paul Graham's "ANSI
> Common Lisp", I haven't been able to find any way to set a
> breakpoint, in any of the CLs I've installed. (trace) is nice, but
> I'd like a way to say, "please stop any time my-func is evaluated,"
> and then end up at a prompt where I can see what's about to be
> evaluated, step over or into forms, inspect objects, and so forth.
> Does such a thing exist?

Try the BREAK function. As in, change my-func to this:

(defun my-func (args)
(break "Entering my func with args: ~a" args)
...)

where the '...' is the original code. Now when my-func is called
you'll enter the debugger. What exactly you can do there depends on
the impl but all the things you mentioned are typically available. The
only thing to watch out for is the backtrace may contain a bunch of
stuff that isn't your code--particularly if your code is running in an
interpreter some of the stack frames will be the interpreter itself.
And in some environments (such as the excelent SLIME for Emacs) the
debugger itself may add a few frames above your code. But if you look
for the function you broke from the neighboring frames will be the
ones you're interested in.

BTW, while may seem primitave to some mouse-addicted folks, compared
IDE's that let you click on a line to insert a break point, having the
debugger built into the languge is actually quite powerful--you could
just as well write:

(defun my-func (args)
(when (some-condition-i-care-about args)
(break "Entering my func with args: ~a" args))
...)

> Please forgive me if this is a FAQ. A pointer to relevant online
> documentation would be more than sufficient.

So I haven't yet written about debugging in Lisp but you might find
some useful stuff in the draft chapters of the book I'm working on.
They're available at:

<http://www.gigamonkeys.com/book/>

If you have a chance to look at this and have any comments about what
you find useful and/or confusing I'd love to hear them.

-Peter

--
Peter Seibel pe...@javamonkey.com

Lisp is the red pill. -- John Fraser, comp.lang.lisp

Peter Lewerin

unread,
Sep 6, 2004, 4:18:29 AM9/6/04
to
Joe Knapka <jkn...@kneuro.net> wrote

> Common Lisp", I haven't been able to find any way to set a breakpoint,

(break)
(break "optionally with ~A (~A, ~A)" "format arguments" foo bar)

This puts you in the debugger, and prints the string if given any.
You can then use your system's continue command in which case the
function returns NIL.

> ... Re-defeat Bush in '04.

Yes, please stop this nightmare.

Pascal Bourguignon

unread,
Sep 6, 2004, 4:28:18 AM9/6/04
to

"Coby Beck" <cb...@mercury.bc.ca> writes:
> > INVOKE-DEBUGGER function

> What in the world is wrong with BREAK?

That I haven't completely read the Condition chapter yet...

Timothy Moore

unread,
Sep 6, 2004, 6:11:00 AM9/6/04
to
Peter Seibel <pe...@javamonkey.com> writes:

> Joe Knapka <jkn...@kneuro.net> writes:
>
> > Hi everyone,
> >
> > I am getting into Lisp again after a brief exposure in college,
> > followed by a lengthy absence (> 10 years). I seem to be missing the
> > blindingly obvious. I've now got a few CLs installed on my Linux box
> > (gcl, clisp, sbcl), and after poking around for hours in the man
> > pages, this group's archive and FAQ, Google, and Paul Graham's "ANSI
> > Common Lisp", I haven't been able to find any way to set a
> > breakpoint, in any of the CLs I've installed. (trace) is nice, but
> > I'd like a way to say, "please stop any time my-func is evaluated,"
> > and then end up at a prompt where I can see what's about to be
> > evaluated, step over or into forms, inspect objects, and so forth.
> > Does such a thing exist?
>
> Try the BREAK function. As in, change my-func to this:
>
> (defun my-func (args)
> (break "Entering my func with args: ~a" args)
> ...)
>

Also, TRACE in some implementations takes extra arguments that let you
break before and functions conditionally. The TRACE in SBCL, one of
the implementations you mention, has a large number of options; try
typing (documentation 'trace 'function).

Tim

Peter Lewerin

unread,
Sep 6, 2004, 7:47:22 AM9/6/04
to
Peter Seibel <pe...@javamonkey.com> wrote

> Lisp is the red pill. -- John Fraser, comp.lang.lisp

You know, the other day I was eating a juicy and delicious steak, and
it made me think about your .sig. It's getting close to twenty years
since I first used Lisp, and some eleven of those years I've used
ALGOL-style languages. After nine years, you know what I realize?

Lisp isn't always doing what the Man tells you, fighting unkillable
bugs, being tired and cold, eating the same goddamn goop everyday...

Lisp is bliss. No more trying to make sense of C or Perl alphabet
soup: all I see now is blonde, brunette, redhead.

Lisp isn't the red pill. It's the blue pill.

Lisp. What a mind-job.

niko...@random-state.net

unread,
Sep 6, 2004, 8:39:03 AM9/6/04
to
Joe Knapka <jkn...@kneuro.net> wrote:

> Common Lisp", I haven't been able to find any way to set a breakpoint,
> in any of the CLs I've installed. (trace) is nice, but I'd like a way
> to say, "please stop any time my-func is evaluated," and then end up

The following should work in SBCL and CMUCL:

(trace my-fun :break t)

For extra goodness

(trace my-fun :break t :wherein 'other-fun)

will break only if the call to my-fun occurs within the dynamic scope
of a call to other-fun.

CMUCL should allow you to start stepping from the debugger, SBCL doesn't
yet, but if all goes to plan a new stepper that will allow this will be
merged this month.

Cheers,

-- Nikodemus "Not as clumsy or random as a C++ or Java.
An elegant weapon for a more civilized time."

Joe Knapka

unread,
Sep 6, 2004, 10:44:09 AM9/6/04
to
Hi Peter,

Peter Seibel <pe...@javamonkey.com> writes:

> Joe Knapka <jkn...@kneuro.net> writes:

> [How do I set a breakpoint?]

> Try the BREAK function. As in, change my-func to this:
>
> (defun my-func (args)
> (break "Entering my func with args: ~a" args)
> ...)

My first reaction to this is, "You mean I have to edit my source and
recompile to set a simple breakpoint?!?" I am, perhaps, spoiled, but
sheesh, even gdb permits me to non-intrusively set a breakpoint. And
if I hit a breakpoint, notice some fsck'd data further up the call
chain, decide I need to break somewhere in the fsck'd frame and
restart the test case... I have to recompile my code to do that?
That seems like a serious speed-bump in the path of the edit/test
cycle. And what if, after a gnarly bug-squishing marathon, I forget
to remove a (break) from the source in some little-used code path and
accidentally check it into CVS? (And knowing me, this is absolutely
guaranteed to happen.)

[scissors of brevity]

> BTW, while may seem primitave to some mouse-addicted folks,
> compared
> IDE's that let you click on a line to insert a break point, having the
> debugger built into the languge is actually quite powerful--you could
> just as well write:
>
> (defun my-func (args)
> (when (some-condition-i-care-about args)
> (break "Entering my func with args: ~a" args))
> ...)

My first reaction to that is, "Cool!" I've often wanted such
functionality. But then my second reaction is that of course, in any
other language with an external debugger I could get it in exactly the
same way:

int foo(int* args) {
if (fsckd(args)) {
cout << "Set a breakpoint here";
}
...
}

def foo(*args):
if fsckd(*args):
print "Set a breakpoint here"
...

foo(Args) :-
(fsckd(Args),
write("Set a breakpoint here")
; true),
... .

: foo fsckd if
." Set a breakpoint here"
then ... ;

So in CL, doing the common thing is a relative pain, and doing the
flexible-but-rarely-needed thing is no easier than it would be in any
other environment. Am I missing something here?

> > Please forgive me if this is a FAQ. A pointer to relevant online
> > documentation would be more than sufficient.
>
> So I haven't yet written about debugging in Lisp but you might find
> some useful stuff in the draft chapters of the book I'm working on.
> They're available at:
>
> <http://www.gigamonkeys.com/book/>

I have begun reading your book on-line, and it looks quite good so
far. I'll buy a copy if I like the first two-three chapters :-)

Thanks for your response,

Joe Knapka

unread,
Sep 6, 2004, 10:57:59 AM9/6/04
to
niko...@random-state.net writes:

> Joe Knapka <jkn...@kneuro.net> wrote:
>
> > Common Lisp", I haven't been able to find any way to set a breakpoint,
> > in any of the CLs I've installed. (trace) is nice, but I'd like a way
> > to say, "please stop any time my-func is evaluated," and then end up
>
> The following should work in SBCL and CMUCL:
>
> (trace my-fun :break t)
>
> For extra goodness
>
> (trace my-fun :break t :wherein 'other-fun)
>
> will break only if the call to my-fun occurs within the dynamic scope
> of a call to other-fun.

Thank you! (I wish I'd seen this article before I replied to Peter.)

> CMUCL should allow you to start stepping from the debugger, SBCL doesn't
> yet, but if all goes to plan a new stepper that will allow this will be
> merged this month.

I am in a maze of twisty CL implementations, all ever-so-slightly
different...

Friedrich Dominicus

unread,
Sep 6, 2004, 1:10:24 PM9/6/04
to
Joe Knapka <jkn...@kneuro.net> writes:

> My first reaction to this is, "You mean I have to edit my source and
> recompile to set a simple breakpoint?!?"

Well that is a portable way, however there exists non-portable ways
which allow you to set a breakpoint on a function you like.

>
> [scissors of brevity]
>
>> BTW, while may seem primitave to some mouse-addicted folks,
>> compared
>> IDE's that let you click on a line to insert a break point, having the
>> debugger built into the languge is actually quite powerful--you could
>> just as well write:
>>
>> (defun my-func (args)
>> (when (some-condition-i-care-about args)
>> (break "Entering my func with args: ~a" args))
>> ...)
>
> My first reaction to that is, "Cool!" I've often wanted such
> functionality. But then my second reaction is that of course, in any
> other language with an external debugger I could get it in exactly the
> same way:
>
> int foo(int* args) {
> if (fsckd(args)) {
> cout << "Set a breakpoint here";
> }
> ...
> }

No, that is exactly not enough, because break stops the execution of
the program and does not just print something.

Regards
Friedrich

--
Please remove just-for-news- to reply via e-mail.

Message has been deleted

Pascal Costanza

unread,
Sep 6, 2004, 11:17:01 AM9/6/04
to
Joe Knapka wrote:

> My first reaction to this is, "You mean I have to edit my source and
> recompile to set a simple breakpoint?!?"

[...]

> So in CL, doing the common thing is a relative pain, and doing the
> flexible-but-rarely-needed thing is no easier than it would be in any
> other environment. Am I missing something here?

Yes, you are experiencing a culture clash. ;)

What you describe (setting breakpoints without changing the source code)
is usually a matter of using the right tools, not the programming
language's business. However, since Common Lisp does define a BREAK
operator people misunderstood your original question and expected that
you want an answer from the language perspective, not from the tool
perspective. (Common Lispers are generally used to expressing more
things within the language than outside of the language, because that's
one of Lisp's definitive strengths and gets extremely handy once you
know how to make use of it.)

Of course, setting breakpoints without changing the source code is also
possible for Common Lisp, but this depends on the actual implementation
that you use. I know that this feature exists for LispWorks, but I
rarely use it. I don't know enough about implementations, again because
I rarely use such breakpoints.

Pascal

--
Pascal Costanza University of Bonn
mailto:cost...@web.de Institute of Computer Science III
http://www.pascalcostanza.de Römerstr. 164, D-53117 Bonn (Germany)

Tayssir John Gabbour

unread,
Sep 6, 2004, 11:57:05 AM9/6/04
to
Joe Knapka wrote:

> Peter Seibel <pe...@javamonkey.com> writes:
> > Try the BREAK function. As in, change my-func to this:
> >
> > (defun my-func (args)
> > (break "Entering my func with args: ~a" args)
> > ...)

>
> My first reaction to this is, "You mean I have to edit my source and
> recompile to set a simple breakpoint?!?" I am, perhaps, spoiled, but
> sheesh, even gdb permits me to non-intrusively set a breakpoint. And
> if I hit a breakpoint, notice some fsck'd data further up the call
> chain, decide I need to break somewhere in the fsck'd frame and
> restart the test case... I have to recompile my code to do that?
> That seems like a serious speed-bump in the path of the edit/test
> cycle. And what if, after a gnarly bug-squishing marathon, I forget
> to remove a (break) from the source in some little-used code path and
> accidentally check it into CVS? (And knowing me, this is absolutely
> guaranteed to happen.)

Um... guys? I could be saying something particularly embarrassing
here, but isn't this lisp and there's a way to do this?

Now I haven't been coding for days, so maybe a neuron isn't hooked up
right, therefore feel free to cluestick me:


(defun breaker (fn name)
"Return a version of fn which calls a break before executing fn."
(setf (get name 'broken-function) fn)
(lambda (&rest args)
(break "Entering ~A with args: ~A" name args)
(apply fn args)))

(defun unbreak (fn-name)
"Revert the function to what it was before a break was added."
(let ((broken-function (get fn-name 'broken-function)))
(when broken-function
(setf (symbol-function fn-name) broken-function)
(setf (get fn-name 'broken-function) nil))))

(defun breakize (fn-name)
"Replace fn-name's global definition with a version which calls a
break beforehand."
(unbreak fn-name)
(setf (symbol-function fn-name)
(breaker (symbol-function fn-name)
fn-name)))


;; Example
(defun a (x y) (b x y))
(defun b (x y) (+ x y))

(breakize 'b)

(a 1 2) ;this should break, then if continued, return 3


(unbreak 'b)


MfG,
Tayssir

--
Observations of a circus which travelled to Iraq:
http://www.circus2iraq.org/updates.asp

Jens Axel Søgaard

unread,
Sep 6, 2004, 12:12:31 PM9/6/04
to
Joe Knapka wrote:
> Peter Seibel <pe...@javamonkey.com> writes:
>>Joe Knapka <jkn...@kneuro.net> writes:

>>[How do I set a breakpoint?]
>
>
>>Try the BREAK function. As in, change my-func to this:
>>
>> (defun my-func (args)
>> (break "Entering my func with args: ~a" args)
>> ...)
>
>
> My first reaction to this is, "You mean I have to edit my source and
> recompile to set a simple breakpoint?!?"

Edit the source - yes. Recompile - nope.

Just enter the new function definition in the repl,
and start the program without recompiling.

--
Jens Axel Søgaard

Vassil Nikolov

unread,
Sep 6, 2004, 1:05:50 PM9/6/04
to
tayss...@yahoo.com (Tayssir John Gabbour) writes:

> [...]


> (defun breakize (fn-name)
> "Replace fn-name's global definition with a version which calls a
> break beforehand."
> (unbreak fn-name)
> (setf (symbol-function fn-name)
> (breaker (symbol-function fn-name)
> fn-name)))

> [...]


Advise?


---Vassil.

--
Vassil Nikolov <vnik...@poboxes.com>

Hollerith's Law of Docstrings: Everything can be summarized in 72 bytes.

Fred Gilham

unread,
Sep 6, 2004, 1:26:51 PM9/6/04
to

> My first reaction to this is, "You mean I have to edit my source and
> recompile to set a simple breakpoint?!?" I am, perhaps, spoiled,
> but sheesh, even gdb permits me to non-intrusively set a breakpoint.
> And if I hit a breakpoint, notice some fsck'd data further up the
> call chain, decide I need to break somewhere in the fsck'd frame and
> restart the test case... I have to recompile my code to do that?

Ah, but GDB is not part of, say, the "C" language. It's an external
tool.

So for example in CMU Lisp, you can enter the debugger by typing
(break) at the REPL. Then you see the following:

* (break)

Break
[Condition of type SIMPLE-CONDITION]

Restarts:
0: [CONTINUE] Return from BREAK.
1: [ABORT ] Return to Top-Level.

Debug (type H for help)

(INTERACTIVE-EVAL (BREAK))
0] h

The prompt is right square brackets, the number indicating how many
recursive command loops you are in.
Debug commands do not affect * and friends, but evaluation in the
debug loop
do affect these variables.
Any command may be uniquely abbreviated.

Getting in and out of DEBUG:
Q throws to top level.
GO calls CONTINUE which tries to proceed with the restart
'continue.
RESTART invokes restart numbered as shown (prompt if not given).
ERROR prints the error condition and restart cases.
FLUSH toggles *flush-debug-errors*, which is initially t.

The name of any restart, or its number, is a valid command, and is
the same
as using RESTART to invoke that restart.

Changing frames:
U up frame D down frame T top frame B bottom
frame

[RETURN FOR MORE, Q TO QUIT HELP TEXT]:
F n goes to frame n.

Inspecting frames:
BACKTRACE [n] shows n frames going down the stack.
L lists locals in current function.
P, PP displays current function call.
SOURCE [n] displays frame's source form with n levels of
enclosing forms.
VSOURCE [n] displays frame's source form without any ellipsis.

Breakpoints and steps:
LIST-LOCATIONS [{function | :c}] list the locations for
breakpoints.
Specify :c for the current frame. Abbreviation: LL
LIST-BREAKPOINTS list the active breakpoints.
Abbreviations: LB, LBP
DELETE-BREAKPOINT [n] remove breakpoint n or all
breakpoints.
Abbreviations: DEL, DBP
BREAKPOINT {n | :end | :start} [:break form] [:function function]
[{:print form}*] [:condition form] set a breakpoint.
Abbreviations: BR, BP
STEP [n] step to the next location or step
n times.
[RETURN FOR MORE, Q TO QUIT HELP TEXT]:

Actions on frames:
DEBUG-RETURN expression
returns expression's values from the current frame, exiting the
debugger.
Abbreviations: R

See the CMU Common Lisp User's Manual for more information.

0]


You see that there is breakpoint functionality in the CMUCL debugger.
Most Lisps seem to have something like this.

Acutally I seldom use that functionality because when developing I use
an IDE such as SLIME that allows extremely convenient function-level
recompilation. So I can stick things in the function I'm debugging,
type "^C^C" and the function is redefined. This makes adding print
statements or even break statements and recompiling much less
intrusive than it would seem.

What's really magic is the way an IDE like SLIME can make it seem like
you can redefine functions while the program is still running. I've
done this more than once. I'm running my program from SLIME. I see
it's misbehaving, and realize what the problem is. I fix the source,
hit "^C^C" with the cursor in the source of the function I changed ---
without stopping the program --- and suddenly the program is doing the
right thing.

--
Fred Gilham gil...@csl.sri.com
The words of Judge Alex Kozinski, written in 1991, are not very
encouraging. "What we have learned from the experience of Eastern
Europe and the Soviet Union ... is that you need capitalism to make
socialism work." In other words, capitalism must produce what
socialism is to distribute. -- Janice Rogers Brown

Mark McConnell

unread,
Sep 6, 2004, 1:29:59 PM9/6/04
to
Joe Knapka <jkn...@kneuro.net> wrote in message news:<m3brgkyv...@localhost.localdomain>...
> Hi everyone,

>
> I haven't been able to find any way to set a breakpoint,
> in any of the CLs I've installed. (trace) is nice, but I'd like a way
> to say, "please stop any time my-func is evaluated," and then end up
> at a prompt where I can see what's about to be evaluated

[snip]

The posters who wrote about (break) or (break ...) were right on.
Once you're in the debugger, type :help or :? to find out what you can
do. Some typical things to try, from my mish-mash memory of various
Lisps:
:bt for backtrace
:up, :down or :dn, :down 2 to go down 2 stack frames
:loc for the local variables in the current frame
:q or :quit

If you used a variable that you forgot to define, the debugger should
let you substitute a value for it, either just that one time or by
setting the variable permanently. Sometimes you can even redefine or
recompile functions in the debugger, then continue using the new
version from the same point in the debugger as if nothing had
happened.

Also try (step [my-lisp-expression]) typed directly into the
interpreter.

Coby Beck

unread,
Sep 6, 2004, 1:44:07 PM9/6/04
to
"Joe Knapka" <jkn...@kneuro.net> wrote in message
news:m3pt4zx...@localhost.localdomain...

> Peter Seibel <pe...@javamonkey.com> writes:
> > Joe Knapka <jkn...@kneuro.net> writes:
>
> > [How do I set a breakpoint?]
>
> > Try the BREAK function. As in, change my-func to this:
> >
> > (defun my-func (args)
> > (break "Entering my func with args: ~a" args)
> > ...)
>
> My first reaction to this is, "You mean I have to edit my source and
> recompile to set a simple breakpoint?!?"

No. You can, but often you might just edit the code, *evaluate* the new
definition, undo the edit and run your test again. I used to do that
sometimes. Or I also used to copy the definition into another buffer or the
REPL and work on it there until I was satisfied and them put it back were it
belongs deleting the old definition. For some heavier and long lasting
sessions I often used to have a global *im-debugging* variable (sometimes a
whole set of them for different issues) and make sure breaks and prints and
debug-data caching things were always inside a (when *im-debugging* ...)
form. No harm for this to be left in shipped code.

Even better is a macro that will expand to nothing if expanded while your
debug var is nil but expand into debugging code otherwise.

> I am, perhaps, spoiled, but
> sheesh, even gdb permits me to non-intrusively set a breakpoint. And
> if I hit a breakpoint, notice some fsck'd data further up the call
> chain, decide I need to break somewhere in the fsck'd frame and
> restart the test case... I have to recompile my code to do that?

No. You can simply redefine some code, set some local variable values and
restart at a frame higher up. You never have to recompile for testing.

Allegro's and LispWorks IDE both let you set breakpoints without editing
code anyway.

> That seems like a serious speed-bump in the path of the edit/test
> cycle. And what if, after a gnarly bug-squishing marathon, I forget
> to remove a (break) from the source in some little-used code path and
> accidentally check it into CVS? (And knowing me, this is absolutely
> guaranteed to happen.)

This absolutely could happen if this is how you were debugging. I used to
debug that way alot.

> [scissors of brevity]
>
> > BTW, while may seem primitave to some mouse-addicted folks,
> > compared
> > IDE's that let you click on a line to insert a break point, having the
> > debugger built into the languge is actually quite powerful--you could
> > just as well write:
> >
> > (defun my-func (args)
> > (when (some-condition-i-care-about args)
> > (break "Entering my func with args: ~a" args))
> > ...)
>
> My first reaction to that is, "Cool!" I've often wanted such
> functionality. But then my second reaction is that of course, in any
> other language with an external debugger I could get it in exactly the
> same way:
>
> int foo(int* args) {
> if (fsckd(args)) {
> cout << "Set a breakpoint here";
> }
> ...
> }

[snip other similar examples]

There is actually a very substantial difference: BREAK puts you in the
Common Lisp debugger which is obviously more than a PRINT statement and
another world apart from debuggers you may be used to.

> So in CL, doing the common thing is a relative pain, and doing the
> flexible-but-rarely-needed thing is no easier than it would be in any
> other environment. Am I missing something here?

The only thing you are missing is that the common things in CL are not the
same as in other languages and (see just above) the
flexible-but-rarely-needed thing you are referring to is not even possible
in most other languages.

In general, your problem comes mostly from trying to do things in an unusual
and less than ideal (for Common Lisp) way.

Use the top level prompt, use incremental evaluation, test your code in
small pieces, get your mind away from the
"edit-compile-run-test-read-logs-of-output-fix" way of working. Get into
the "edit-evaluate-fix" mode it is much better!

HTH!

Petter Gustad

unread,
Sep 6, 2004, 1:44:20 PM9/6/04
to
Fred Gilham <gil...@snapdragon.csl.sri.com> writes:

> What's really magic is the way an IDE like SLIME can make it seem like
> you can redefine functions while the program is still running. I've
> done this more than once. I'm running my program from SLIME. I see
> it's misbehaving, and realize what the problem is. I fix the source,
> hit "^C^C" with the cursor in the source of the function I changed ---
> without stopping the program --- and suddenly the program is doing the
> right thing.

In SLIME you have a neat debugger interface where you can move up and
down stack frames, inspect data, do evals in minibuffers, etc.

Petter
--
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

Peder O. Klingenberg

unread,
Sep 6, 2004, 3:19:52 PM9/6/04
to
Fred Gilham <gil...@snapdragon.csl.sri.com> writes:

>> My first reaction to this is, "You mean I have to edit my source and
>> recompile to set a simple breakpoint?!?" I am, perhaps, spoiled,
>> but sheesh, even gdb permits me to non-intrusively set a breakpoint.
>> And if I hit a breakpoint, notice some fsck'd data further up the
>> call chain, decide I need to break somewhere in the fsck'd frame and
>> restart the test case... I have to recompile my code to do that?
>
> Ah, but GDB is not part of, say, the "C" language. It's an external
> tool.

And one that is rarely used in production code. That's IMO one of the
great features of the CL debugger. It's always there, even in
delivered images (at least mine).

Just the other day, I (re-)discovered how immensely useful this
feature is. A process of mine in a production environment encountered
some data which it didn't expect. I hadn't been paranoid enough, and
had assumed our communication partner actually managed to follow their
own detailed protocol. A unique constraint in the database was broken
as a result.

My process in turn feeds data to a lot of other processes, and
unscheduled restarts are a hassle. But because lisp is such a
friendly language, instead of crashing and giving me a core dump, the
failing program landed me in a debugger I could attach to. Using
this, it was a breeze not only diagnosing the problem, but fixing it
without restarting the whole chain of processes.

After testing and checking in the fix, I pasted the new, paranoid
definition of my function into the debugger. The new function would
just return nil in these cases. Then I manually told the debugger to
return nil in the current case, and watched the wheels starting to
turn again.

GDB would possibly have helped in diagnosing the problem from the core
dump, if a core dump had been produced and if the problem hadn't been
obvious from the error messages prior to the crash.

If I had been extremely cautious, I could maybe have arranged for the
production process to be started under GDB every time, and so when the
crash occured, I would again have been in a debugger, and could
probably manually have returned NULL. I've never known production
processes that were run this way, but I suppose it's possible.

But fixing the running image? I have certainly never seen GDB used
that way. I haven't used GDB enough to say that it's impossible,
maybe if you're a C/assembly god, you could have done some magic by
calling dlopen from GDB or something, but give me a break (pun
intended).

> Acutally I seldom use that functionality because when developing I use
> an IDE such as SLIME that allows extremely convenient function-level
> recompilation. So I can stick things in the function I'm debugging,
> type "^C^C" and the function is redefined. This makes adding print
> statements or even break statements and recompiling much less
> intrusive than it would seem.

Ditto. I don't think I've ever used the lisp debugger to insert
breakpoints. For exactly the same reasons.

...Peder...
--
I wish a new life awaited _me_ in some off-world colony.

Pascal Bourguignon

unread,
Sep 6, 2004, 3:53:26 PM9/6/04
to
pe...@news.klingenberg.no (Peder O. Klingenberg) writes:
> If I had been extremely cautious, I could maybe have arranged for the
> production process to be started under GDB every time, and so when the
> crash occured, I would again have been in a debugger, and could
> probably manually have returned NULL. I've never known production
> processes that were run this way, but I suppose it's possible.
>
> But fixing the running image? I have certainly never seen GDB used
> that way. I haven't used GDB enough to say that it's impossible,
> maybe if you're a C/assembly god, you could have done some magic by
> calling dlopen from GDB or something, but give me a break (pun
> intended).

The only know instance where it's possible is with Apple's Xcode, but
only if you compile with the debugging option.

Martin Ginkel

unread,
Sep 6, 2004, 4:03:23 PM9/6/04
to
For debugging, there is no standard:
look into the doc of your implementation.
In ACL this is simply:

:trace ((fun :break-before t))

Without redefining a lot of code...

Martin

Matthew Danish

unread,
Sep 6, 2004, 4:18:35 PM9/6/04
to
Joe Knapka <jkn...@kneuro.net> writes:
> My first reaction to this is, "You mean I have to edit my source and
> recompile to set a simple breakpoint?!?" I am, perhaps, spoiled, but
> sheesh, even gdb permits me to non-intrusively set a breakpoint. And
> if I hit a breakpoint, notice some fsck'd data further up the call
> chain, decide I need to break somewhere in the fsck'd frame and
> restart the test case... I have to recompile my code to do that?
> That seems like a serious speed-bump in the path of the edit/test
> cycle. And what if, after a gnarly bug-squishing marathon, I forget
> to remove a (break) from the source in some little-used code path and
> accidentally check it into CVS? (And knowing me, this is absolutely
> guaranteed to happen.)

Well, the first and obvious point is that GDB is not part of the ANSI
C language; and in the same manner, CL debuggers are permitted to go
above and beyond the capabilities described in the standard. Most
people do get by with the plain old BREAK function, and this is not so
bad in CL because of the almost instant feedback you get from being
able to evaluate any expression into a running image, unlike C where
you have to recompile/rerun/debug.

From a broader perspective, however, you can see that BREAK is merely
a simple wrapper around the more versatile CERROR function; which
itself is a simplified interface to the restart and condition system.
The Lisp debugger is in fact merely a user-interface to this system; a
system which provides a very powerful protocol for indicating and
resolving exceptional states in a program. You probably will not
begin to make use of this system in an advanced way for quite a while,
but it is good to know about.

Kent Pitman has written a paper on the condition system:
http://www.nhplace.com/kent/Papers/Condition-Handling-2001.html

Peter Seibel has a chapter in his book about it:
http://www.gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html

--
;;;; Matthew Danish -- user: mrd domain: cmu.edu
;;;; OpenPGP public key: C24B6010 on keyring.debian.org

Coby Beck

unread,
Sep 6, 2004, 5:12:14 PM9/6/04
to

"Martin Ginkel" <martin...@epost.de> wrote in message
news:chig08$kej$1...@news.boerde.de...

> For debugging, there is no standard:

What is all this about then! ;)

HyperSpec: 25.1.2 Debugging Utilities
The next figure shows defined names relating to debugging.


*debugger-hook* documentation step
apropos dribble time
apropos-list ed trace
break inspect untrace
describe invoke-debugger
Figure 25-2. Defined names relating to debugging

This is the base minimum, any decent implementation will provide more stuff.

Jon Boone

unread,
Sep 6, 2004, 5:52:55 PM9/6/04
to
On 2004-09-06 15:53, in article 87isard...@thalassa.informatimago.com,
"Pascal Bourguignon" <sp...@mouse-potato.com> wrote:

> pe...@news.klingenberg.no (Peder O. Klingenberg) writes:
>> If I had been extremely cautious, I could maybe have arranged for the
>> production process to be started under GDB every time, and so when the
>> crash occured, I would again have been in a debugger, and could
>> probably manually have returned NULL. I've never known production
>> processes that were run this way, but I suppose it's possible.
>>
>> But fixing the running image? I have certainly never seen GDB used
>> that way. I haven't used GDB enough to say that it's impossible,
>> maybe if you're a C/assembly god, you could have done some magic by
>> calling dlopen from GDB or something, but give me a break (pun
>> intended).
>
> The only know instance where it's possible is with Apple's Xcode, but
> only if you compile with the debugging option.

There was a debugger that I used back in the early 1990s on Unix systems
at CMU that not only had an X-based GUI interface which allowed breakpoint
insertion via point-and-click, but it also allowed you to modify values and
even insert new code, without recompiling. I can't remember what it's
called, but I've always missed that functionality.

--jon

Frode Vatvedt Fjeld

unread,
Sep 6, 2004, 5:53:18 PM9/6/04
to
Matthew Danish <mrd+n...@cmu.edu> writes:

> From a broader perspective, however, you can see that BREAK is
> merely a simple wrapper around the more versatile CERROR function;
> which itself is a simplified interface to the restart and condition
> system.

I believe break should not be considered an interface to the condition
syste, and that it cannot possibly be implemented as a wrapper around
cerror. Rather, it's a wrapper around invoke-debuger, which is
something quite different.

As the CLHS says:

break could be defined by:

(defun break (&optional (format-control "Break") &rest format-arguments)
(with-simple-restart (continue "Return from BREAK.")
(let ((*debugger-hook* nil))
(invoke-debugger
(make-condition 'simple-condition
:format-control format-control
:format-arguments format-arguments))))
nil)

--
Frode Vatvedt Fjeld

Carl Shapiro

unread,
Sep 6, 2004, 9:01:07 PM9/6/04
to
pe...@news.klingenberg.no (Peder O. Klingenberg) writes:

> If I had been extremely cautious, I could maybe have arranged for the
> production process to be started under GDB every time, and so when the
> crash occured, I would again have been in a debugger, and could
> probably manually have returned NULL. I've never known production
> processes that were run this way, but I suppose it's possible.
>
> But fixing the running image? I have certainly never seen GDB used
> that way. I haven't used GDB enough to say that it's impossible,
> maybe if you're a C/assembly god, you could have done some magic by
> calling dlopen from GDB or something, but give me a break (pun
> intended).

If you are running your object code on a system that actually has an
operating-system supported debugging facility (such as Windows) you
are given the option at fault time to attach a debugger to the running
process. From within the debugger you can monkey around with the
suspended process, single step for a while, evaluate function
definitions, detach the debugger, and resume process execution.

Since the Cretaceous Period of computing, systems have had the ability
to link symbolic debuggers into applications to handle faults as Lisp
debuggers do today. UNIX, a notable exception, was a huge step
backwards for process debuggability. I hate to see Lisp systems
compared to monstrously programmer-unfriendly artifacts such as UNIX
and GDB.

Icarus Sparry

unread,
Sep 6, 2004, 9:40:05 PM9/6/04
to
On Mon, 06 Sep 2004 17:52:55 -0400, Jon Boone wrote:
> There was a debugger that I used back in the early 1990s on Unix systems
> at CMU that not only had an X-based GUI interface which allowed breakpoint
> insertion via point-and-click, but it also allowed you to modify values and
> even insert new code, without recompiling. I can't remember what it's
> called, but I've always missed that functionality.

Maybe 'ups', now hosted at ups.sourceforge.net, but originally written at
the University of Kent in the UK. Includes a 'C' interpreter which allows
you to patch compiled code.

Jon Boone

unread,
Sep 6, 2004, 9:51:26 PM9/6/04
to
On 2004-09-06 21:40, in article
pan.2004.09.07....@icarus.freeuk.com, "Icarus Sparry"
<use...@icarus.freeuk.com> wrote:

Yes, that's it. Quite nice.

--jon

Greg Menke

unread,
Sep 6, 2004, 10:02:53 PM9/6/04
to
Joe Knapka <jkn...@kneuro.net> writes:


> Hi Peter,
>
> Peter Seibel <pe...@javamonkey.com> writes:
>
> My first reaction to this is, "You mean I have to edit my source and
> recompile to set a simple breakpoint?!?" I am, perhaps, spoiled, but
> sheesh, even gdb permits me to non-intrusively set a breakpoint.

Its not quite that simple. Depending on the archtecture involved, gdb
may have to resort to hand-compiling hardware break instructions into
the selected addresses, taking the exeception, then re-introducing the
old instructions so the they can be executed on return from the
debugger. In the cases where hardcoded break instructions aren't
required and there is hardware support for a breakpoint, gdb still has
to manage the breakpoints and single-steps- so its by no means
non-intrusive and to debug usefully. In general you need to compile
with no optimization anyhow- so the very act of introducing the
debugger will perturb the runtime structure of the program you're
trying to debug.

It only looks non-intrusive because gdb is taking advantage of the
nice simple execution environment. That said, sometimes I also miss a
nice step-into, step-over interface when debugging Lisp code. Though
too be fair, those are often the same times I'm trying to debuging a
complicated, over-long function...

Gregm

Joel Ray Holveck

unread,
Sep 7, 2004, 1:45:31 AM9/7/04
to
> My first reaction to this is, "You mean I have to edit my source and
> recompile to set a simple breakpoint?!?"
[snip]

> restart the test case... I have to recompile my code to do that?
> That seems like a serious speed-bump in the path of the edit/test
> cycle.

Most people have told you about the difference between BREAK as a
feature of Common Lisp in general, as opposed to setting breakpoints
using your Lisp system. So I'm not going to go into detail on that
one. Instead, I'd like to talk about something that makes Lisp great:
incremental refinement.

The idea of restarting a Lisp program to test a fix or find a bug is
as alien to most Lisp programmers as restarting Apache to test a
changed CGI script would be web developers. It's just a different
development cycle.

In the programming languages most people are used to, there's a cycle
something like this:
1. Edit the source code
2. Compile it
3. Run a test case
4. Note the most obvious bug
5. Run the debugger
6. Set a breakpoint in the region of the bug
7. Examine the program's state around the breakpoint
8. Think of a solution
9. Return to step 1

Why does this cycle occur? Because in most languages, a program must
be complete before it can be compiled, and compiled before it can be
run, and must run subroutines in the natural order of running. This
cycle is pretty much mandated by these prerequisites.

This is reflected in the tool structure: the editor, compiler,
execution shell, and debugger are all separate programs. In Lisp,
things are different.

In Lisp, you're sitting "within" the running program at all times.
The execution shell is the Lisp REPL (that is, the Lisp prompt). The
debugger is always present, and takes control whenever there's an
unhandled exception. The compiler is just part of your execution
shell. And the shell has access to all of your subroutines. The only
thing that's not necessarily integrated is the editor, and most Lisp
coders use editors that integrate themselves with Lisp; usually, this
is ilisp or SLIME under Emacs, or an editor built into the vendor's
Lisp.

In a very real sense, a Lisp programmer has the internals of his
program as well as all his Lisp tools available to him all the time.
This is very different than other systems, in which the tools are
separate from each other and, in many senses, from your program.

Let's suppose you had the following simple program to test numbers
that a user enters, and tell whether they're divisible by 10. It has
an obvious bug. (This also isn't the best way to implement this, but
it's just a demonstration.)

(defun divisible-by-10-p (number)
(zerop (mod number 11)))
(defun list-multiples-of-10 ()
(loop
for number = (progn (print "Enter a number, or NIL to exit:") (read))
while number
when (divisible-by-10-p number)
do (print "It's divisible by 10!")))

Now, as I said, this has an obvious bug: it lists numbers that are
divisible by 11 instead of 10.

Let's suppose you've loaded this into your Lisp. When you run it, the
code prints some numbers that aren't right. You want to set a
breakpoint. You don't need to restart your test case; you don't even
need to stop your main loop. Just leave it running as it is.
Remember, in Lisp, you're inside the development system, rather than
outside of it.

Now, in your editor, you go exactly where you want the breakpoint to
be. Add a (break):
(defun divisible-by-10-p (number)
(break)
(zerop (mod number 11)))
In most Lisp editors (including the ones I mentioned above), you can
give it an "evaluate defun" command of some sort. In mine, I press
Control-C twice while I'm in the defun. Again, in most Lisp editors,
this will happen while you're still in the main loop. You'll still be
in the main loop, right where you left off.

Here, I used the example of adding a break, although as many people
mentioned, most Lisp environments have the ability to turn on a
break-on-entry without you needing to modify your code. But to fix
the bug, you will need to edit the code, and the same thing applies
there.

You can see how this would scale very well. For example, the main
loop could be a large program's GUI event handler. You may have gone
through several steps to get the bug's reproduction scenario set up.
Being able to modify your code and test it while your program is still
running may be very, very valuable. It's valuable in production
programs too: at my workplace, I use the same idea to load patches
into a running server without needing to take 30 minutes to repopulate
its data. But I'm jumping ahead; let's stay on the focus of debugging.

Let's suppose you don't have a Lisp editor. Maybe you haven't learned
Emacs or your vendor's editor yet, and are using vi to edit your code
and running your Lisp from a command prompt. That's okay too. You
can press a break key (usually Control-C) while your program is
running, and you'll get a debugger prompt. From there, you can either
load your modified source file, or if the new function is short, just
type it in by hand. Then tell the debugger to continue where it left
off.

If you want, then at the debugger prompt, you can call your inner
function by itself, such as (divisible-by-10-p 10). You can look at
what it does, and call it again and again as you refine it. If you
need variables, such as complex data structures, that your program
maintains while it's running, they're all available. All your
subroutines are available. In this respect, it's similar to gdb's
"call" and "print" commands, with one important difference: you can
edit your function while you're testing.

Eventually, either because you think you've got things working or
because you don't feel like testing your function in the debugger, you
tell the debugger to continue. Your program picks up right where it
left off, and you can try the errant command again (in this case,
submit test numbers like "10" and "11" back to the loop's prompt).

Remember, this entire debugging session-- editing, tracing, debugging,
correcting, testing-- took place while your program was still running.
You don't need separate tools for these steps that run outside of your
program; they're integrated with the environment your program is
running in. And once you've tested your addition, you can compile it
with (compile 'divisible-by-10-p) if you want to. (Most editors have
a command to load and compile a modified defun in the same command.)
Or just leave it interpreted while you go debug another part of your
program, still within the same session.

In real life, I rarely use the debugger like this, though. Usually,
I'll test an inner function directly from the REPL. If it fails, I
hypothesize about the problematic function and call it directly, or
possibly turn on some traces instead. Since you have all of your
program's subroutines and variables at the Lisp REPL, you can do
one-shot tests from the prompt, without having to write and compile
tests. In C, a test often has to build a data structure artificially,
then a function call to the function in question, then have a function
to print the resulting data structure. This means that people rarely
test small things when writing in C.

In Lisp, though, things are different. Since your entire program is
available, you can have it load up its data in the normal way-- by
interacting with the user, or reading its data files, or whatever.
Then you can call small functions individually, editing their
definitions as you go, and check the results. You don't need to write
specialized printers for your test cases, though: Lisp has a built-in
customizable printer for most data structures, DESCRIBE and EXAMINE
can get you through the rest.

Once you've got your program's data loaded, you can go wild with
development. It's not uncommon for me to have a session that lasts a
week, debugging existing code and writing new code, all without ever
restarting my program.

There's a lot of things available to make debugging easy in Lisp that
aren't so easy in other languages. But the key concept, and this is
one that takes time to get used to, is that the development cycle is
different. When you're at the REPL, you're immersed in both your
program and all of Lisp's tools. In a very real sense, you're within
your program. If you think in these terms-- and it's hard at first,
after years of ALGOL-descended conditioning-- then Lisp debugging will
make a lot more sense.

joelh

Peder O. Klingenberg

unread,
Sep 7, 2004, 8:07:15 AM9/7/04
to
Carl Shapiro <cshapi...@panix.com> writes:

> If you are running your object code on a system that actually has an
> operating-system supported debugging facility (such as Windows) you
> are given the option at fault time to attach a debugger to the running
> process.

You can redefine functions from the debugger in Windows? Cool. I
admit I don't know much about Windows. (nor many other non-unixish
OSes. I have a Symbolics machine with a ton of documentation sitting
around that I want to play with, but I seem to have a shortage of
round tuits.)

> I hate to see Lisp systems compared to monstrously
> programmer-unfriendly artifacts such as UNIX and GDB.

But the comparisons generally come out in favor of Lisp. :)

Really, my post was intended more to convey how happy I am with the
debugger in Lisp than how unhappy and/or ignorant I am with any other
environment. GDB was mentioned in the parent post, so I just took it
from there.

The point is, I like the fact that the Lisp debugger is there by
default and powerful enough to let me rescue my processes with
relative ease. Regardless of the capabilities of "competing"
environments, and despite the fact that the CL spec does not specify
how to set breakpoints without editing code.

Peter Seibel

unread,
Sep 7, 2004, 10:45:06 AM9/7/04
to
peter....@swipnet.se (Peter Lewerin) writes:

> Peter Seibel <pe...@javamonkey.com> wrote
>
>> Lisp is the red pill. -- John Fraser, comp.lang.lisp
>
> You know, the other day I was eating a juicy and delicious steak, and
> it made me think about your .sig. It's getting close to twenty years
> since I first used Lisp, and some eleven of those years I've used
> ALGOL-style languages. After nine years, you know what I realize?
>
> Lisp isn't always doing what the Man tells you, fighting unkillable
> bugs, being tired and cold, eating the same goddamn goop everyday...
>
> Lisp is bliss. No more trying to make sense of C or Perl alphabet
> soup: all I see now is blonde, brunette, redhead.
>
> Lisp isn't the red pill. It's the blue pill.
>
> Lisp. What a mind-job.

;-)

-Peter

--
Peter Seibel pe...@javamonkey.com

Lisp is the red pill. -- John Fraser, comp.lang.lisp

Fred Gilham

unread,
Sep 7, 2004, 10:46:43 AM9/7/04
to

Here's a session using SLIME in the spirit of Joel's example:

I took the following code and ran it under SLIME:

(defun divisible-by-10-p (number)
(zerop (mod number 11)))
(defun list-multiples-of-10 ()

(do ((number 0 (1+ number)))
((> number 100000))
(when (divisible-by-10-p number)
(format t "~A is divisible by 10.~%" number)
(force-output)
(sleep 1))))


In the slime-repl window I got the following output:

CL-USER> (list-multiples-of-10)
0 is divisible by 10.
11 is divisible by 10.
22 is divisible by 10.
33 is divisible by 10.
44 is divisible by 10.
55 is divisible by 10.
66 is divisible by 10.
77 is divisible by 10.
88 is divisible by 10.
99 is divisible by 10.


While this was happening "it hit me" that the program was doing the
wrong thing. So, without stopping the program, in the source window I
made the following change:

(defun divisible-by-10-p (number)
(zerop (mod number 10)))


Then, in the source window I hit ^C^C while the cursor was in the
DIVISIBLE-BY-10-P defun.

The output in the repl window, which was continuing while I did this,
gave the following:

; Converted DIVISIBLE-BY-10-P.
; Compiling DEFUN DIVISIBLE-BY-10-P:

; In: DEFUN DIVISIBLE-BY-10-P

; (MOD NUMBER 10)
; --> BLOCK LET IF AND IF AND IF PLUSP >
; ==>
; NUMBER
; Note: Deleting unreachable code.
;
; Byte Compiling Top-Level Form:

; Compilation unit finished.
; 1 note

100 is divisible by 10.
110 is divisible by 10.
120 is divisible by 10.
130 is divisible by 10.
140 is divisible by 10.
150 is divisible by 10.
160 is divisible by 10.
170 is divisible by 10.


This all happened without entering the debugger at all --- at least
visibly.

--
Fred Gilham gil...@csl.sri.com
Comprehensive Computer Language Preference Survey
Do you like Lisp? (Check one)
[ ] Yes [ ] Sure [ ] You bet [ ] Yep [ ] Da

Joel Ray Holveck

unread,
Sep 7, 2004, 3:10:40 PM9/7/04
to
> Here's a session using SLIME in the spirit of Joel's example:

After I wrote that message, I decided to check out this SLIME thing.
I'd been stuck in my ilisp ways for years now. I'd tried out SLIME a
while ago (it seems like years, but has it been around that long?) and
couldn't use it for some reason, but since they released a 1.0 this
month I thought I'd check it out.

Wow! Boy, was I blown away! I mean to tell you, this is a seriously
cool interface! The first time the debugger popped up, I started to
go to the REPL window to enter a restart like I usually do under
ilisp. Then I thought, "Hey, that's not the normal error formatting.
Waitasec..." I moved my mouse over the restart I wanted, middle
clicked, at sat there looking at the screen saying "wooooah".
Finally, a Lisp debugger in Emacs! If only we had source-level
stepping like edebug, but I'm sure somebody's got that in mind.

I gotta say, SLIME is something else! CL scratch buffers, C-c :,
multiple connections, an icomplete-style M-Tab, a built-in inspector,
marking your source with compiler warnings, there's just a lot of Wow
to make my coding life easier.

This is seriously cool.

Cheers,
joelh

Joe Knapka

unread,
Sep 7, 2004, 4:13:43 PM9/7/04
to
pe...@news.klingenberg.no (Peder O. Klingenberg) writes:

> Carl Shapiro <cshapi...@panix.com> writes:
>
> > If you are running your object code on a system that actually has an
> > operating-system supported debugging facility (such as Windows) you
> > are given the option at fault time to attach a debugger to the running
> > process.
>
> You can redefine functions from the debugger in Windows? Cool.

Maybe. It doesn't always work (you might get a "Sorry, I couldn't
relink this code because the current call stack passes through it").
Anyway, by the time you've got a segmentation fault on your hands
it's usually too late, since any attempt to restart the code only
signals the same error again.

> --
> I wish a new life awaited _me_ in some off-world colony.

Ditto. (The actualization of your sig would render mine irrelevant :-)

-- Joe

--
"We sat and watched as this whole <-- (Died Pretty -- "Springenfall")
blue sky turned to black..."
... Re-defeat Bush in '04.
--
pub 1024D/BA496D2B 2004-05-14 Joseph A Knapka
Key fingerprint = 3BA2 FE72 3CBA D4C2 21E4 C9B4 3230 94D7 BA49 6D2B
If you really want to get my attention, send mail to
jknapka .at. kneuro .dot. net.

Joe Knapka

unread,
Sep 7, 2004, 4:33:16 PM9/7/04
to
"Coby Beck" <cb...@mercury.bc.ca> writes:

> "Joe Knapka" <jkn...@kneuro.net> wrote in message
> news:m3pt4zx...@localhost.localdomain...
> > Peter Seibel <pe...@javamonkey.com> writes:
> > > Joe Knapka <jkn...@kneuro.net> writes:
> >
> > > [How do I set a breakpoint?]
> >
> > > Try the BREAK function. As in, change my-func to this:
> > >
> > > (defun my-func (args)
> > > (break "Entering my func with args: ~a" args)
> > > ...)
> >
> > My first reaction to this is, "You mean I have to edit my source and
> > recompile to set a simple breakpoint?!?"
>
> No. You can, but often you might just edit the code, *evaluate* the new
> definition, undo the edit and run your test again. I used to do that
> sometimes. Or I also used to copy the definition into another buffer or the
> REPL and work on it there until I was satisfied and them put it back were it
> belongs deleting the old definition. For some heavier and long lasting
> sessions I often used to have a global *im-debugging* variable (sometimes a
> whole set of them for different issues) and make sure breaks and prints and
> debug-data caching things were always inside a (when *im-debugging* ...)
> form. No harm for this to be left in shipped code.
>
> Even better is a macro that will expand to nothing if expanded while your
> debug var is nil but expand into debugging code otherwise.

OK, I'm with you so far, I think...

But of course, I could always set a breakpoint on the "set a breakpoint"
line, and thence inspect values, step code, etc. True, in most cases
I couldn't actually redefine code from the debug prompt, so that is a
big difference.

> > So in CL, doing the common thing is a relative pain, and doing the
> > flexible-but-rarely-needed thing is no easier than it would be in any
> > other environment. Am I missing something here?
>
> The only thing you are missing is that the common things in CL are not the
> same as in other languages and (see just above) the
> flexible-but-rarely-needed thing you are referring to is not even possible
> in most other languages.
>
> In general, your problem comes mostly from trying to do things in an unusual
> and less than ideal (for Common Lisp) way.
>
> Use the top level prompt, use incremental evaluation, test your code in
> small pieces, get your mind away from the
> "edit-compile-run-test-read-logs-of-output-fix" way of working. Get into
> the "edit-evaluate-fix" mode it is much better!

So let me be sure I understand this: when my code stops in the
debugger due to an error condition, I should be able to redefine the
function in which the error occurred, and restart execution at the
point of the failed call? Without having to restart my entire
program? That sounds really useful, but it's not obvious exactly how
to accomplish it at the debug prompt (and I assume this is also
implementation-specific, but it's not obvious in any of the CLs
I've tried so far).

I am, BTW, using SLIME, but I'd also like to understand how to do
these magic debug things at the (various) REPL(s).

Thanks,

Joe Knapka

unread,
Sep 7, 2004, 4:46:35 PM9/7/04
to
Greg Menke <gregm...@toadmail.com> writes:

> Joe Knapka <jkn...@kneuro.net> writes:
>
>
> > Hi Peter,
> >
> > Peter Seibel <pe...@javamonkey.com> writes:
> >
> > My first reaction to this is, "You mean I have to edit my source and
> > recompile to set a simple breakpoint?!?" I am, perhaps, spoiled, but
> > sheesh, even gdb permits me to non-intrusively set a breakpoint.
>
> Its not quite that simple.

True, but it certainly //looks// that simple from the developer's
perspective.

> Depending on the archtecture involved, gdb
> may have to resort to hand-compiling hardware break instructions into
> the selected addresses, taking the exeception, then re-introducing the
> old instructions so the they can be executed on return from the
> debugger. In the cases where hardcoded break instructions aren't
> required and there is hardware support for a breakpoint, gdb still has
> to manage the breakpoints and single-steps- so its by no means
> non-intrusive and to debug usefully. In general you need to compile
> with no optimization anyhow- so the very act of introducing the
> debugger will perturb the runtime structure of the program you're
> trying to debug.

Yes, and this fact has caused problems for me from time to time :-)
However, in the vast majority of cases code compiled with debugging
information does not perform noticably different than optimized code
for my purposes. No doubt there are domains, such as hard-realtime
work, where that wouldn't be the case.

Joe Knapka

unread,
Sep 7, 2004, 4:57:35 PM9/7/04
to
Wow. Thank you for this detailed explanation. You have described
some very impressive capabilities that I am going to explore
ASAP.

Thanks to everyone else in this thread, as well, for the very
educational dialogue.

-- Joe

Joel Ray Holveck <jo...@juniper.net> writes:

[A bunch of very cool stuff, which I have snipped]

jayessay

unread,
Sep 7, 2004, 5:27:11 PM9/7/04
to
Joe Knapka <jkn...@kneuro.net> writes:

> "Coby Beck" <cb...@mercury.bc.ca> writes:

> > Use the top level prompt, use incremental evaluation, test your code in
> > small pieces, get your mind away from the
> > "edit-compile-run-test-read-logs-of-output-fix" way of working. Get into
> > the "edit-evaluate-fix" mode it is much better!
>
> So let me be sure I understand this: when my code stops in the
> debugger due to an error condition, I should be able to redefine the
> function in which the error occurred, and restart execution at the
> point of the failed call? Without having to restart my entire
> program?

Absolutely. Works like magic.


> That sounds really useful, but it's not obvious exactly how to
> accomplish it at the debug prompt (and I assume this is also
> implementation-specific, but it's not obvious in any of the CLs I've
> tried so far).

You can get this implementation independently via Ilisp or Slime.


> I am, BTW, using SLIME, but I'd also like to understand how to do
> these magic debug things at the (various) REPL(s).

Check out the Slime doc. Actually you don't even need to look at the
REPL - the Slime facility will handle all that stuff for you as well
as poping up the results in your favorite way.


/Jon

--
'j' - a n t h o n y at romeo/charley/november com

Coby Beck

unread,
Sep 7, 2004, 6:07:46 PM9/7/04
to

"Joe Knapka" <jkn...@kneuro.net> wrote in message
news:m3zn41x...@localhost.localdomain...

> "Coby Beck" <cb...@mercury.bc.ca> writes:
> > Even better is a macro that will expand to nothing if expanded while
your
> > debug var is nil but expand into debugging code otherwise.
>
> OK, I'm with you so far, I think...

I mean something like this:

CL-USER 39 > (defvar *development-build* nil)
;; have this somewhere in your file loading
CL-USER 40 > (setf *development-build* t)
T ;; have this in some local file on your machine
CL-USER 41 > ;; define something like this
(defmacro debug-when (condition &optional (format-string "Break")
&rest format-args)
(when *development-build*
`(when ,condition
(break ,format-string ,@format-args))))
DEBUG-WHEN

;; put it in some trouble spot
CL-USER 42 > (defun foo (n)
(debug-when (= n 0)
"the time is: ~A" (get-universal-time))
(* n n))
FOO

CL-USER 43 > (foo 1)
1

CL-USER 44 > (foo 0)

the time is: 3303582168
1 (continue) Return from break.
2 (abort) Return to level 0.
3 Return to top loop level 0.

Type :b for backtrace, :c <option number> to proceed, or :? for other
options

CL-USER 45 : 1 > :a

;; make sure this is somewhere local to the build machine
CL-USER 46 > (setf *development-build* nil)
NIL

CL-USER 47 > (defun foo (n)
(debug-when (= n 0)
"the time is: ~A" (get-universal-time))
(* n n))
FOO

CL-USER 48 > (foo 0)
0
;; no break!
CL-USER 49 >

This can get more and more full featured until you one day realize you are
just reinventing the existing condition system!

> > Use the top level prompt, use incremental evaluation, test your code in
> > small pieces, get your mind away from the
> > "edit-compile-run-test-read-logs-of-output-fix" way of working. Get
into
> > the "edit-evaluate-fix" mode it is much better!
>
> So let me be sure I understand this: when my code stops in the
> debugger due to an error condition, I should be able to redefine the
> function in which the error occurred, and restart execution at the
> point of the failed call? Without having to restart my entire
> program? That sounds really useful, but it's not obvious exactly how

Yes it is, it can be great. I've even logged in remotely to a client's
running application, patched code (ie redefined methods etc.) and told them
"Okay, try that again"

Then put the code into a patch file so the next restart would have the
correct code and made sure the corrected code was now in the main repository
so the next delivery would not need the patch.

> to accomplish it at the debug prompt (and I assume this is also
> implementation-specific, but it's not obvious in any of the CLs
> I've tried so far).

The tools can really vary alot. I recall with ACL it was very easy. An
error put you in the graphical debugger, you could click stack frames,
inspect object state, return an object to the top level, play with it then
restart at whatever frame you chose. Or maybe look and say to yourself,
"hmm, I forgot that slot might not have been initialized..." Edit the code
to handle that situation and restart at that function call. This is a HUGE
win when you have really complex state that takes a long time to get to and
may ber very hard to reproduce exactly.

LW I am less adept with.

These thing are great and often hard to live without but I find almost
equally valuable is being able to test your code line by line as you build
it. I find I need the debugger less in the kind of development I do now, I
take more advantage or incremental development and MACROEXPAND.

Carl Shapiro

unread,
Sep 7, 2004, 7:08:05 PM9/7/04
to
Joe Knapka <jkn...@kneuro.net> writes:

> Maybe. It doesn't always work (you might get a "Sorry, I couldn't
> relink this code because the current call stack passes through it").
> Anyway, by the time you've got a segmentation fault on your hands
> it's usually too late, since any attempt to restart the code only
> signals the same error again.

First off, runtime patching of functions on the call stack is not a
problem. Second, you can almost always continue from access violation
exceptions. Naturally, if your program has overwhelmingly corrupted
itself there may be no reason to. That's life. For the record, I
routinely "edit and continue" as well restart from various types of
exceptions on Windows when working on the CMUCL runtime. (How's that
for introducing circularity to this discussion?) It just works.

Joe Knapka

unread,
Sep 7, 2004, 8:35:00 PM9/7/04
to
Carl Shapiro <cshapi...@panix.com> writes:

We're talking (in this tangent) about Visual Studio, right? I haven't
tried "edit & continue" under .NET yet, but I recall getting
"Sorry, couldn't edit and continue" errors sufficiently frequently
under VS6 that I quit trying to use it. Maybe I'm remembering something
else though; it's been quite a while since I did any C++ on Windows.

Pascal Bourguignon

unread,
Sep 7, 2004, 10:18:46 PM9/7/04