I hear lots about how one can interactively debug a Lisp program as it
is running. I take this to mean that I can manipulate data structures
and modify functions as my program is running its main loop. Or are
people just meaning the REPL when they say interactive?
If it is possible to tinker with a running image, how does one go about
doing it? Or is this not a topic for a beginner :)
Thanks
Brad
From: Christopher C. Stacy
Subject: Re: Interactive debugging tips...
Date:
Message-ID: <uirwnrcvt.fsf@news.dtpq.com>
"bradb" <··············@gmail.com> writes:
> I hear lots about how one can interactively debug a Lisp program as it
> is running. I take this to mean that I can manipulate data structures
> and modify functions as my program is running its main loop. Or are
> people just meaning the REPL when they say interactive?
> If it is possible to tinker with a running image, how does one go about
> doing it? Or is this not a topic for a beginner :)
I'm not quite sure what you heard, but all computer programming
languages allow you to manipulate data structures as the program
is running. Lisp allows you to do things like programatically
introduce new functions, or alter the data-type of an existing
data structure, while it is running.
What is it that you are trying to do?
:)
What I mean is - can I change the code/data of a running Lisp program,
without stopping the program?
ie, my program is
(defvar *stop* nil)
(while (= stop nil)
.... do body )
Then from another "thread" that has a REPL can I go (setf stop t) and
have the loop terminate?
Brad
"bradb" <··············@gmail.com> writes:
> :)
> What I mean is - can I change the code/data of a running Lisp program,
> without stopping the program?
>
> ie, my program is
> (defvar *stop* nil)
> (while (= stop nil)
> .... do body )
>
> Then from another "thread" that has a REPL can I go (setf stop t) and
> have the loop terminate?
yes, but you can do that in any language (your example amounts to a
simple form of ipc via shared state).
--
-Marco
Ring the bells that still can ring.
Forget the perfect offering.
There is a crack in everything.
That's how the light gets in.
-Leonard Cohen
That is true I guess. I should perhaps ask "is this feature already
built in?" At the moment I am using Vim + Vilisp. If Slime has this
IPC feature then it might be a compelling reason to invest in it. I
really ought to invest in learning it anyhow - but at the moment a new
editor + a new language might be a bit much.
BTW - I watched your Slime demo video last night - impressive stuff
Brad
"bradb" <··············@gmail.com> writes:
> That is true I guess. I should perhaps ask "is this feature already
> built in?" At the moment I am using Vim + Vilisp. If Slime has this
> IPC feature then it might be a compelling reason to invest in it. I
> really ought to invest in learning it anyhow - but at the moment a new
> editor + a new language might be a bit much.
> BTW - I watched your Slime demo video last night - impressive stuff
What you're thinking of is probably not what you want to do. You
certainly *can* do that from another thread, and slime's thread
browser is useful here. However, you probably want to use C-c C-c to
interrupt the Lisp, redefine a function, then continue; or then
restart a stack frame. Of course, you can also interrupt any thread
this way (Slime's thread browser is really useful here).
--
/|_ .-----------------------.
,' .\ / | Free Mumia Abu-Jamal! |
,--' _,' | Abolish the racist |
/ / | death penalty! |
( -. | `-----------------------'
| ) |
(`-. '--.)
`. )----'
brad.beveridge asks
> What I mean is - can I change the code/data of a running Lisp program,
> without stopping the program?
Something that I've done (to delay having to learn about
threads) is to use LISTEN, which is ANSI CL, from the
streams dictionary.
For a simple example consider this little loop that slowly
prints out the values of a function.
* (defvar *i* 0)
*I*
* (defun foo () (print (expt *i* 4)))
FOO
* (loop (incf *i*)(foo)(sleep 2))
1
16
81
256
Interrupted at #x280CB958.
How can we keep talking to the REPL while this is going on?
We can do this in a single thread by listening periodically
to see if there is input which we pass to our own embedded
REPL.
(loop (incf *i*)(foo)(sleep 2)
(when (listen)
(print (eval (read)))))
Then we can do things such as reset the count
20736
28561
(setf *i* 0)
38416
0
1
16
or change the function
50625
65536
83521
104976
(defun foo () (format t "~R~%" *i*))
130321
FOO twenty
twenty-one
twenty-two
twenty-three
Under CMUCL this rapidly becomes frustrating because when
you make an error the only restart is to return to the
top-level. This breaks you out of your loop.
I wrote an interaction macro to wrap the body code and the
REPL in appropriate restarts.
;;;; repeat-interactively
;;;
;;; Allows the programmer to work on running code
;;;
(defmacro repeat-interactively ((&optional (tick 1)
(count-var 'count)
(prompt "~&>>-~A-> "))
&body code)
`(flet ((prompt-user()
(declare (special prompt ,count-var ))
(format t prompt ,count-var)
(force-output)))
(let ((,count-var 0)
(prompt ,prompt)
(tick ,tick))
(declare (special ,count-var prompt tick))
(prompt-user)
(catch 'stop
(loop
(restart-case
(progn ,@code (incf ,count-var)(sleep tick))
(fixed () :report "Resume processing."))
(when (listen)
(tagbody top
(restart-case
(progn
(print (eval (read)))
(prompt-user))
(shrug () :report "Continue processing."
(prompt-user))
(retry () :report "Evaluate another form."
(prompt-user)
(go top)))))
)))))
I don't use it much. It is usually adequate to interrupt the
running code with ctrl-C ctrl-C, make the change, and then
use the continue restart.
There is one cool trick which I suggest you try. Make the
loop write to a file. Use finish-output to flush the buffers
and the Unix command tail -f to what the output.
Make it tabulate something that is compute intensive, so
that interpreted code is taking a second or two for each
entry. Then compile the code and watch it suddenly get faster.
Coming from a C background, I find this completely
mind-boggling.
I don't think that there is anything difficult about using a
REPL in a separater thread to change function
definitions. However, if you try writing code that uses
multiple threads in a serious way, you have to use locks to
stop the different threads from stepping on each others
toes, and it is this that I have shied away from.
Alan Crowe
Edinburgh
Scotland
bradb wrote:
> :)
> What I mean is - can I change the code/data of a running Lisp program,
> without stopping the program?
>
> ie, my program is
> (defvar *stop* nil)
> (while (= stop nil)
> .... do body )
>
> Then from another "thread" that has a REPL can I go (setf stop t) and
> have the loop terminate?
Shucks, I did exactly that once with debug output streaming to my
listener window. I just typed more or less blindly and hit return. I was
careful not to leave off the asterisks on *stop*, so it worked.
:)
--
Kenny
Why Lisp? http://wiki.alu.org/RtL_Highlight_Film
"I've wrestled with reality for 35 years, Doctor, and I'm happy to state
I finally won out over it."
Elwood P. Dowd, "Harvey", 1950
"bradb" <··············@gmail.com> writes:
> I hear lots about how one can interactively debug a Lisp program as it
> is running. I take this to mean that I can manipulate data structures
> and modify functions as my program is running its main loop. Or are
> people just meaning the REPL when they say interactive?
> If it is possible to tinker with a running image, how does one go about
> doing it? Or is this not a topic for a beginner :)
they mean 'just the REPL,' but you don't realize what that means :)
there's been a lot of discussion in this group on this exact topic,
use goole. you could also look at some of the entries in bill
clementson's blog or this c.l.l thread:
http://groups.google.com/group/comp.lang.lisp/browse_frm/thread/dd7a9e6b1985ee09/c7eda44807809c9d
--
-Marco
Ring the bells that still can ring.
Forget the perfect offering.
There is a crack in everything.
That's how the light gets in.
-Leonard Cohen
bradb wrote:
> I hear lots about how one can interactively debug a Lisp program as it
> is running. I take this to mean that I can manipulate data structures
> and modify functions as my program is running its main loop. Or are
> people just meaning the REPL when they say interactive?
> If it is possible to tinker with a running image, how does one go about
> doing it? Or is this not a topic for a beginner :)
Here is an example session:
CL-USER 1 > (defun f (x)
(g (+ x 1)))
F
CL-USER 2 > (f 42)
Error: Undefined operator G in form (G (+ X 1)).
1 (continue) Try invoking G again.
2 Return some values from the form (G (+ X 1)).
3 Try invoking something other than G with the same arguments.
4 Set the symbol-function of G to another function.
5 Set the macro-function of G to another function.
6 (abort) Return to level 0.
7 Return to top loop level 0.
Type :b for backtrace, :c <option number> to proceed, or :? for other
options
CL-USER 3 : 1 > (defun g (x)
(* x x))
G
CL-USER 4 : 1 > :c 1
1849
CL-USER 5 > (defclass person ()
((name :initarg :name
:accessor person-name)))
#<STANDARD-CLASS PERSON 10076A1F>
CL-USER 6 > (defparameter *pascal*
(make-instance 'person :name "Pascal"))
*PASCAL*
CL-USER 7 > (person-name *pascal*)
"Pascal"
CL-USER 8 > (person-address *pascal*)
Error: Undefined operator PERSON-ADDRESS in form (PERSON-ADDRESS *PASCAL*).
1 (continue) Try invoking PERSON-ADDRESS again.
2 Return some values from the form (PERSON-ADDRESS *PASCAL*).
3 Try invoking something other than PERSON-ADDRESS with the same
arguments.
4 Set the symbol-function of PERSON-ADDRESS to another function.
5 Set the macro-function of PERSON-ADDRESS to another function.
6 (abort) Return to level 0.
7 Return to top loop level 0.
Type :b for backtrace, :c <option number> to proceed, or :? for other
options
CL-USER 9 : 1 > (defclass person ()
((name :initarg :name
:accessor person-name)
(address :initarg :address
:accessor person-address)))
#<STANDARD-CLASS PERSON 10C786F7>
CL-USER 10 : 1 > :c 1
Error: The slot ADDRESS is unbound in the object #<PERSON 10C77513> (an
instance of class #<STANDARD-CLASS PERSON 10C786F7>).
1 (continue) Try reading slot ADDRESS again.
2 Specify a value to use this time for slot ADDRESS.
3 Specify a value to set slot ADDRESS to.
4 (abort) Return to level 0.
5 Return to top loop level 0.
Type :b for backtrace, :c <option number> to proceed, or :? for other
options
CL-USER 11 : 1 > :c 3
Enter a form to be evaluated: "Brussels"
"Brussels"
CL-USER 12 > (person-address *pascal*)
"Brussels"
I hope this gives you the basic idea. Common Lisp development
environments come with more convenient means to do these things (i.e.,
to add and change definitions on the go).
Pascal
--
OOPSLA'05 tutorial on generic functions & the CLOS Metaobject Protocol
++++ see http://p-cos.net/oopsla05-tutorial.html for more details ++++
"bradb" <··············@gmail.com> writes:
> I hear lots about how one can interactively debug a Lisp program as it
> is running. I take this to mean that I can manipulate data structures
> and modify functions as my program is running its main loop. Or are
> people just meaning the REPL when they say interactive?
> If it is possible to tinker with a running image, how does one go about
> doing it? Or is this not a topic for a beginner :)
The beginner can do this:
[22]> (defun next (i) (1+ i))
NEXT
[23]> (loop
:for i = 0 then (next i)
:while (<= 0 i)
:do (print i))
0
1
2
3
...
10572
10573
10574
10575
** - Continuable Error
EVAL: User break
If you continue (by typing 'continue'): Continue execution
The following restarts are also available:
ABORT :R1 ABORT
Break 1 [24]> (defun next (i) (1- i))
NEXT
Break 1 [24]> continue
10574
10573
10572
10571
10570
...
3
2
1
0
NIL
[25]>
Et hop! One bug corrected at run-time!
With an implementation that support threads, you can even redefine the
function without breaking into the debugger, with a REPL in another
thread.
--
__Pascal Bourguignon__ http://www.informatimago.com/
You never feed me.
Perhaps I'll sleep on your face.
That will sure show you.
On 2005-09-26, bradb <··············@gmail.com> wrote:
> If it is possible to tinker with a running image, how does one go about
> doing it? Or is this not a topic for a beginner :)
Frequently I run a program which creates its own window and has
graphical output and interaction there, and at the same time I change
parts of the source and update them with C-c C-c in SLIME, and inspect
and modify things from the REPL. Try things in the debugger, too. It's
pretty easy to get this sort of stuff working.
Cheers.
--
Julian Squires