From: Dr. Edmund Weitz
Subject: trace calls from within mapcar or similar functions
Date: 
Message-ID: <MPG.15d7b89fbfa13230989686@news.cis.dfn.de>
Hi all!

I'm pretty new to CL, so please bear with me if I'm asking something 
obvious. I'm trying to trace a function in LispWorks PE on Win2k, but I 
don't see any trace output if the function is called by functions like 
MAPCAR, EVERY, or SOME. To give a simple example:

  (defun f (lis) (mapcar #'g lis))
  (defun g (x) x)
  (trace g)

In CMUCL I get

   (f '(7 8 9))
     0: (G 7)
     0: G returned 7
     0: (G 8)
     0: G returned 8
     0: (G 9)
     0: G returned 9
   (7 8 9)

In LispWorks I just get

   (f '(7 8 9))
   (7 8 9)

which is not what I expect from tracing.

I suppose there is some special variable to alter this behaviour but I 
couldn't find anything in the documentation.

My apologies for once again bothering this newsgroup with something that 
seems to be specific to Xanalys, but I have the feeling that I am the 
only one who subscribes to the mailing list at ········@xanalys.com.

Thanks in advance for your help,
Dr. Edmund Weitz
Hamburg
Germany

From: Kent M Pitman
Subject: Re: trace calls from within mapcar or similar functions
Date: 
Message-ID: <sfw66c25lon.fsf@world.std.com>
Dr. Edmund Weitz <···@agharta.de> writes:

> Hi all! I'm pretty new to CL,

Hi.  Welcome.

> so please bear with me if I'm asking something obvious.

It sometimes takes a few of these to get one's bearings.

> I'm trying to trace a function in LispWorks PE on Win2k, 

PE could mean Personal Edition or Professional Edition, btw.  Better
to spell it out, only because now there are patches for one and not
for the other and so they are effectively different versions.

> but I don't see any trace output if the function is called by 
functions like MAPCAR, EVERY, or SOME. To give a simple example:
> 
>   (defun f (lis) (mapcar #'g lis))
>   (defun g (x) x)
>   (trace g)

This is an anomaly of how LispWorks does things.  I'm not sure if it's
conforming, and it's been on my list of things to construct some test
cases for, but either way, I can explain the problem and how to work
around it.

TRACE can be implemented in a variety of ways, and depending on how it
does it, some subtle differences may occur.  TRACE can just mark the
symbol or it can actually put something different in the function
cell.  If it does the latter, then a reference like #'G will see the
updated definition; but if it does the former, since #'G may be
compiled to point directly to the function cell (bypassing any lookup
via the symbol), it may not get traced.

For LW, #'g compiles somehow to something that points more directly to
G's function value.  The reason I'm not sure it's conforming is not
that they do this per se, but that there's a rule that says you have to
be willing to notice updates.  If they are noting function cell updates,
and if the function cell has been updated, then they are not conforming.
If TRACE works by another means, then they are conforming.
 
There are other cases this comes up, too, for example APPLY and FUNCALL
and readtable functions and so on.  In all cases I know of, substituting
the symbol name instead of the function reference will fix it (though it's
not always semantically the same). e.g.,  (mapcar 'g lis), though you 
should call your variable LIST, in my opinion, since there is no conflict
between the LIST function name and a LIST variable name in a multi-namespace
Lisp like CL.  (Mostly it's Scheme where people learn to misspell 
variables in order to avoid name clashes with functions.)

> In CMUCL I get
> 
>    (f '(7 8 9))
>      0: (G 7)
>      0: G returned 7
>      0: (G 8)
>      0: G returned 8
>      0: (G 9)
>      0: G returned 9
>    (7 8 9)
> 
> In LispWorks I just get
> 
>    (f '(7 8 9))
>    (7 8 9)
> 
> which is not what I expect from tracing.

I'm sure with (mapcar 'g ...) you'll win.
 
> I suppose there is some special variable to alter this behaviour but I 
> couldn't find anything in the documentation.

I don't think so.

> My apologies for once again bothering this newsgroup with something that 
> seems to be specific to Xanalys, but I have the feeling that I am the 
> only one who subscribes to the mailing list at ········@xanalys.com.

Heh.  I forgot that list existed.  I suppose should put myself on it.
From: Dr. Edmund Weitz
Subject: Re: trace calls from within mapcar or similar functions
Date: 
Message-ID: <MPG.15d7c5541bc0072989687@news.cis.dfn.de>
In article <···············@world.std.com>, ······@world.std.com says...
> This is an anomaly of how LispWorks does things.  I'm not sure if it's
> conforming, and it's been on my list of things to construct some test
> cases for, but either way, I can explain the problem and how to work
> around it.

Thanks. After having been on this newsgroup for a couple of weeks I 
somehow wished that you would answer my question... :)

Your explanation was very clear and helpful as always, and of course 
your workaround does what it's supposed to.

Thanks again,
Edi.
From: Coby Beck
Subject: Re: trace calls from within mapcar or similar functions
Date: 
Message-ID: <D2mb7.108485$o01.15758831@typhoon.tampabay.rr.com>
"Kent M Pitman" <······@world.std.com> wrote in message
····················@world.std.com...
> Dr. Edmund Weitz <···@agharta.de> writes:
> > but I don't see any trace output if the function is called by
> functions like MAPCAR, EVERY, or SOME. To give a simple example:
> >
> >   (defun f (lis) (mapcar #'g lis))
> >   (defun g (x) x)
> >   (trace g)
>
> This is an anomaly of how LispWorks does things.  I'm not sure if it's
> conforming, and it's been on my list of things to construct some test
> cases for, but either way, I can explain the problem and how to work
> around it.

Reading the description of trace in the HS didn't seem to me to leave much
wriggle-room.  I have to say this bothers me, it is not what I would expect
and in fact it explains a "what the...?" debugging frustration I just went
through.  There are many ways to skin a cat so I found and squashed the
offending critter, but I remember distinctly being sent in the wrong
direction because trace did not report in
a similar situation.

I was under the impression that #'function is considered better style (from
reading this group) but more importantly, I recently discovered (this part
in ACL) that if you don't use the # in a mapcar, the cross-referencing
facilities will not tell you that you have called the function!

I have not checked this in LW but now I am afraid of being forced to chose:
do I want to be able to trace this function or do I want to be able to find
out who calls it!

Coby

--
(remove #\space "coby . beck @ opentechgroup . com")
(I use lisp regularly, so I am used to having my cake and eating it too!)
From: Kent M Pitman
Subject: Re: trace calls from within mapcar or similar functions
Date: 
Message-ID: <sfwd769lrd3.fsf@world.std.com>
"Coby Beck" <·····@mercury.bc.ca> writes:

> "Kent M Pitman" <······@world.std.com> wrote in message
> ····················@world.std.com...
> > Dr. Edmund Weitz <···@agharta.de> writes:
> > > but I don't see any trace output if the function is called by
> > functions like MAPCAR, EVERY, or SOME. To give a simple example:
> > >
> > >   (defun f (lis) (mapcar #'g lis))
> > >   (defun g (x) x)
> > >   (trace g)
> >
> > This is an anomaly of how LispWorks does things.  I'm not sure if it's
> > conforming, and it's been on my list of things to construct some test
> > cases for, but either way, I can explain the problem and how to work
> > around it.
> 
> Reading the description of trace in the HS didn't seem to me to leave much
> wriggle-room.  I have to say this bothers me, it is not what I would expect
> and in fact it explains a "what the...?" debugging frustration I just went
> through.  There are many ways to skin a cat so I found and squashed the
> offending critter, but I remember distinctly being sent in the wrong
> direction because trace did not report in
> a similar situation.

I somewhat agree with you (though it may have been my sloppy editing
that led to this wording).  I do think there's a case for you to make,
arguably even a strong one.  And I wish they would agree.  But none of
us has the power to assert them unambiguously wrong.  There is no
"conformance committee" that is empowered to do that.  The marketplace 
decides.

ON THE OTHER HAND, not that it matters formally, the X3J13 committee has
tended to de-emphasize over time the issue of so-called "environment" 
issues, which can affect performance.  Consider the following:

(defparameter *fn-count* 0)

(compile
 (defun fn-maker () 
  (let ((i (incf *fn-count*)))
   (compile 'fn
      `(lambda (x)
         (prog1 `((fn ,x) ,',i ,*fn-count*)
           (fn-maker)))))))

(fn-maker)

(compile
 (defun map-function-fn (x) (mapcar #'fn x)))

(compile
 (defun map-symbol-fn (x) (mapcar 'fn x)))

(map-function-fn '(a b c))
=> (((FN A) 1 1) ((FN B) 1 2) ((FN C) 1 3))

(map-symbol-fn '(a b c))
=> (((FN A) 4 4) ((FN B) 5 5) ((FN C) 6 6))


This is what LispWorks returns and it's what I'd expect.
It shows that (a) they are being sensitive to update of the
function definition at exactly the points that I would expect and hope.
Calling 'FN gets the current dynamic value of FN, while calling #'FN
calls the value that was current at the time the #'FN was done.

We all want TRACE to be as useful as possible, but asking them to make
TRACE work *might* (I haven't seen the code) mean making the
implementation less efficient, so be careful what you ask for.

The reason that EVALHOOK went away from the language was that user
pressure to make it work right would just slow some things down.
Better to let vendors handle this on the basis of commercial market
pressure, not a spec requirement.

> I was under the impression that #'function is considered better style (from
> reading this group) but more importantly, I recently discovered (this part
> in ACL) that if you don't use the # in a mapcar, the cross-referencing
> facilities will not tell you that you have called the function!

I absolutely agree with you that #'foo is better style, and I encourage it.

> I have not checked this in LW but now I am afraid of being forced to chose:
> do I want to be able to trace this function or do I want to be able to find
> out who calls it!

You can always change your call to
 #'(lambda (x) (f x)) ;or whatever arg convention works
if you really want to assure you have a visible call to F.

I think this is a situation where you should program as you think you
should and then report the bug if/when you are a customer and find
the implementation doesn't perform up to your needs, whatever they 
might be.
From: Martin Simmons
Subject: Re: trace calls from within mapcar or similar functions
Date: 
Message-ID: <997124259.103256@cnn>
"Kent M Pitman" <······@world.std.com> wrote in message
····················@world.std.com...
> This is an anomaly of how LispWorks does things.  I'm not sure if it's
> conforming, and it's been on my list of things to construct some test
> cases for, but either way, I can explain the problem and how to work
> around it.
>
> TRACE can be implemented in a variety of ways, and depending on how it
> does it, some subtle differences may occur.  TRACE can just mark the
> symbol or it can actually put something different in the function
> cell.  If it does the latter, then a reference like #'G will see the
> updated definition; but if it does the former, since #'G may be
> compiled to point directly to the function cell (bypassing any lookup
> via the symbol), it may not get traced.
>
> For LW, #'g compiles somehow to something that points more directly to
> G's function value.  The reason I'm not sure it's conforming is not
> that they do this per se, but that there's a rule that says you have to
> be willing to notice updates.  If they are noting function cell updates,
> and if the function cell has been updated, then they are not conforming.
> If TRACE works by another means, then they are conforming.

In LW, TRACE and UNTRACE modify the symbol function cell, not the original
function object.  I'm not sure if it is conforming or not, but the reason that
#' returns the original function object is to prevent the tracing function
getting stuck in some data structure making it impossible to untrace.

E.g.

(defun foo ())
(trace foo)
(setq *foo* #'foo)
(untrace)
(funcall *foo*)   ; would it be useful if this printed trace info for ever more?

--
Martin Simmons, Xanalys Software Tools
······@xanalys.com
rot13 to reply
From: Eric Dahlman
Subject: fwrap and defadvice (was: Re: trace calls [...])
Date: 
Message-ID: <tz4elqo514z.fsf_-_@flatt.cs.colostate.edu>
"Martin Simmons" <······@xanalys.com> writes:

> In LW, TRACE and UNTRACE modify the symbol function cell, not the
> original function object.  I'm not sure if it is conforming or not,
> but the reason that #' returns the original function object is to
> prevent the tracing function getting stuck in some data structure
> making it impossible to untrace.

[snip example]

If it is not a big secret how does ACL handle this with its new fwrap
facility?  They claim that you can trace anything (well fwrap which
entails defadvice and thereby trace) functions, methods and I think
any lambda like thing. This is all supposed to happen while preserving
the identity of the function object so 

        (let ((foo (symbol-function 'compile)))
           (fwrap-defadvice-traceify compile) ;; ya get the idea
           (when (eq foo (symbol-function 'compile))
                 'scratch-head))       

Would evaluate to 'scratch-head. Which leaves me wondering how they
are able to accomplish this without adding another level of
indirection or explicit checks on every function call, which I think
would be kind of extreme.

-Eric
From: Duane Rettig
Subject: Re: fwrap and defadvice (was: Re: trace calls [...])
Date: 
Message-ID: <466c0rgkr.fsf@beta.franz.com>
Eric Dahlman <····@lossage.org> writes:

> "Martin Simmons" <······@xanalys.com> writes:
> 
> > In LW, TRACE and UNTRACE modify the symbol function cell, not the
> > original function object.  I'm not sure if it is conforming or not,
> > but the reason that #' returns the original function object is to
> > prevent the tracing function getting stuck in some data structure
> > making it impossible to untrace.
> 
> [snip example]
> 
> If it is not a big secret how does ACL handle this with its new fwrap
> facility?

It's no secret.  I answered this in another article on this thread.

>  They claim that you can trace anything (well fwrap which
> entails defadvice and thereby trace) functions, methods and I think
> any lambda like thing.

This is mostly true.  However, fwrap doesn't entail or use defadvice,
it is a new functionality which completely replaces defadvice.  The
defadvice facility, which does use function encapsulation, still exists
and can be used as before, but it has nothing to do with fwrap.

> This is all supposed to happen while preserving
> the identity of the function object so 
> 
>         (let ((foo (symbol-function 'compile)))
>            (fwrap-defadvice-traceify compile) ;; ya get the idea
>            (when (eq foo (symbol-function 'compile))
>                  'scratch-head))       
> 
> Would evaluate to 'scratch-head.

True.  But even more important, try

 (typep #'print-object 'generic-function)

before and after tracing it.  Before version 6.0, the identity changed,
and this caused big problems in discrimination, leaving one to shy away
from tracing generic-functions.

> Which leaves me wondering how they
> are able to accomplish this without adding another level of
> indirection or explicit checks on every function call, which I think
> would be kind of extreme.

Well, in Allegro CL every compiled function call includes a jump
through a start address.  If the discrimination of a traced/fwrapped
vs non traced/fwrapped function is only in its start address, then
the general mechanism is free.  The only additional runtime costs are
added _after_ the fwrap facility is entered by a traced/fwrapped
function, so it is a pay-as-you-go situation.

-- 
Duane Rettig          Franz Inc.            http://www.franz.com/ (www)
1995 University Ave Suite 275  Berkeley, CA 94704
Phone: (510) 548-3600; FAX: (510) 548-8253   ·····@Franz.COM (internet)
From: Eric Dahlman
Subject: Re: fwrap and defadvice (was: Re: trace calls [...])
Date: 
Message-ID: <tz466bz4s2o.fsf@flatt.cs.colostate.edu>
Duane Rettig <·····@franz.com> writes:

> Eric Dahlman <····@lossage.org> writes:
> 
> > "Martin Simmons" <······@xanalys.com> writes:
> > 
> > > In LW, TRACE and UNTRACE modify the symbol function cell, not the
> > > original function object.  I'm not sure if it is conforming or not,
> > > but the reason that #' returns the original function object is to
> > > prevent the tracing function getting stuck in some data structure
> > > making it impossible to untrace.
> > 
> > [snip example]
> > 
> > If it is not a big secret how does ACL handle this with its new fwrap
> > facility?
> 
> It's no secret.  I answered this in another article on this thread.

Thanks, I saw your other post about half and hour after I sent this
one.;-)

> 
> >  They claim that you can trace anything (well fwrap which
> > entails defadvice and thereby trace) functions, methods and I think
> > any lambda like thing.
> 
> This is mostly true.  However, fwrap doesn't entail or use defadvice,
> it is a new functionality which completely replaces defadvice.  The
> defadvice facility, which does use function encapsulation, still exists
> and can be used as before, but it has nothing to do with fwrap.

Sorry if I confused things with my lax use of terminology there.  I
didn't mean to imply that fwrap used defadvice but rather that
logically one could derive defadvice from fwrap and similarly trace
follows from defadvice.

> > This is all supposed to happen while preserving
> > the identity of the function object so 
> > 
> >         (let ((foo (symbol-function 'compile)))
> >            (fwrap-defadvice-traceify compile) ;; ya get the idea
> >            (when (eq foo (symbol-function 'compile))
> >                  'scratch-head))       
> > 
> > Would evaluate to 'scratch-head.
> 
> True.  But even more important, try
> 
>  (typep #'print-object 'generic-function)
> 
> before and after tracing it.  Before version 6.0, the identity changed,
> and this caused big problems in discrimination, leaving one to shy away
> from tracing generic-functions.

It was always in the back of my mind as something which one ought not do.

> 
> > Which leaves me wondering how they
> > are able to accomplish this without adding another level of
> > indirection or explicit checks on every function call, which I think
> > would be kind of extreme.
> 
> Well, in Allegro CL every compiled function call includes a jump
> through a start address.  If the discrimination of a traced/fwrapped
> vs non traced/fwrapped function is only in its start address, then
> the general mechanism is free.  The only additional runtime costs are
> added _after_ the fwrap facility is entered by a traced/fwrapped
> function, so it is a pay-as-you-go situation.

Thanks, that helps.  If I understand this correctly then all functions
are routed through a little bit of trampoline code and by fwrapping the
function you alter the destination of the trampoline.  While the
identity of the trampoline, which can be though of the "function
object", remains unchanged.


Thanks for the info,
-Eric
From: Duane Rettig
Subject: Re: fwrap and defadvice (was: Re: trace calls [...])
Date: 
Message-ID: <4bslreiq9.fsf@beta.franz.com>
Eric Dahlman <····@lossage.org> writes:

> Duane Rettig <·····@franz.com> writes:
> 
> > Eric Dahlman <····@lossage.org> writes:
> > 
> > >  They claim that you can trace anything (well fwrap which
> > > entails defadvice and thereby trace) functions, methods and I think
> > > any lambda like thing.
> > 
> > This is mostly true.  However, fwrap doesn't entail or use defadvice,
> > it is a new functionality which completely replaces defadvice.  The
> > defadvice facility, which does use function encapsulation, still exists
> > and can be used as before, but it has nothing to do with fwrap.
> 
> Sorry if I confused things with my lax use of terminology there.  I
> didn't mean to imply that fwrap used defadvice but rather that
> logically one could derive defadvice from fwrap and similarly trace
> follows from defadvice.

Yes, they perform similar duties, and could be thought of as similar
functionality, and trace could be implemented from either fwrap or
encapsulations (the same mechanism that defadvice is implemented
from).

However, I make a strong distinction because they are so different
semantically.  Defadvice/encapsulation tends to be forms-based; with
a kind of evaluate-on-the-fly dynamic style that forces the compiler
to be present if one doesn't want interpreted code running.  Instead,
def-fwrapper forms are expanded and processed in much the same way
as defun or defmacro, and thus the compiler can separate the
different eval-when times and generate very efficient code in a fasl
file for later fwrapping.  Also, argument lists are handled with
a special variable in defadvice, whereas arguments can be fully
destructured in def-fwrapper.  Thus, if you had a function foo
(for illustration only):

(defun foo (a &optional b &key c)
  ; [ ... ]
  )

You could fwrap it with either of the following:

(def-fwrapper foo-wrapper-1 (a &optional b &key c)
  ; [ do some stuff using a, b, and c ... ]
  (multiple-value-prog1 (call-next-fwrapper)
    ; [ do some more stuff ...]
    ))

or:

(def-fwrapper foo-wrapper-1 (&rest arglist
  ; [ do some stuff using arglist ... ]
  (multiple-value-prog1 (call-next-fwrapper)
    ; [ do some more stuff ...]
    ))

The second example gives you similar capabilities as does
encapsulation.  But the first version gives you much better
debugging capabilities, without having to decide where in the
arglist a particular argument is.

To find out what the def-fwrapper is in fact doing, you can
do the same thing you have always been able to do with other
defining forms like defun - you can macroexpand it.  Either
do so using the emacs-lisp interface automatically, or you
can do

 (pprint (macroexpand '(def-fwrapper ... )))

if you are only using a line oriented interface.


Over the past years, we accumulated many bugs and rfes (requests
for enhancement) on trace, most of which had to do with the fact
that it was implemented with encapsulation.  We also got a few
bugs and rfes on encapsulation and the advice facility itself.
By designing a new system which took into consideration all of
these problems and the opportunities to improve, we were able to
retire almost all such reports against trace and advice, and to
provide a facility which I believe will become very useful to
programmers who become experienced in its use.

> > > This is all supposed to happen while preserving
> > > the identity of the function object so 
> > > 
> > >         (let ((foo (symbol-function 'compile)))
> > >            (fwrap-defadvice-traceify compile) ;; ya get the idea
> > >            (when (eq foo (symbol-function 'compile))
> > >                  'scratch-head))       
> > > 
> > > Would evaluate to 'scratch-head.
> > 
> > True.  But even more important, try
> > 
> >  (typep #'print-object 'generic-function)
> > 
> > before and after tracing it.  Before version 6.0, the identity changed,
> > and this caused big problems in discrimination, leaving one to shy away
> > from tracing generic-functions.
> 
> It was always in the back of my mind as something which one ought not do.

And that was there either because of a bad experience, or from someone
telling you it shouldn't be done.  But now, you can chase that thought
out; it can be done.

> > > Which leaves me wondering how they
> > > are able to accomplish this without adding another level of
> > > indirection or explicit checks on every function call, which I think
> > > would be kind of extreme.
> > 
> > Well, in Allegro CL every compiled function call includes a jump
> > through a start address.  If the discrimination of a traced/fwrapped
> > vs non traced/fwrapped function is only in its start address, then
> > the general mechanism is free.  The only additional runtime costs are
> > added _after_ the fwrap facility is entered by a traced/fwrapped
> > function, so it is a pay-as-you-go situation.
> 
> Thanks, that helps.  If I understand this correctly then all functions
> are routed through a little bit of trampoline code and by fwrapping the
> function you alter the destination of the trampoline.  While the
> identity of the trampoline, which can be though of the "function
> object", remains unchanged.

Correct.  This is also how we are able to provide call-counting
profiles without having to recompile/reinstrument the functions to
be counted.

Now, of course, someone is going to look at this and say "What!
Every function call goes through a trampoline? How inefficient!"
But in fact, it is not inefficient; there is one extra jump to get
to the trampoline itself, but that "extra" time is hidden by the
memory pipeline interlocks due to chained memory accesses.  It
turns out that there is no measurable difference between using
a trampoline and performing the required register manipulation
and memory acesses in-line.  In fact, because such calling
sequences are performed in one place instead of at every call
point, the compiled code is actually smaller, and thus the paging
behavior of the lisp calling sequence is more efficient than if
it were in-line coded.

-- 
Duane Rettig          Franz Inc.            http://www.franz.com/ (www)
1995 University Ave Suite 275  Berkeley, CA 94704
Phone: (510) 548-3600; FAX: (510) 548-8253   ·····@Franz.COM (internet)
From: Kent M Pitman
Subject: Re: trace calls from within mapcar or similar functions
Date: 
Message-ID: <sfwae1dnde4.fsf@world.std.com>
"Martin Simmons" <······@xanalys.com> writes:

> In LW, TRACE and UNTRACE modify the symbol function cell, not the original
> function object.  I'm not sure if it is conforming or not, but the reason that
> #' returns the original function object is to prevent the tracing function
> getting stuck in some data structure making it impossible to untrace.
> 
> E.g.
> 
> (defun foo ())
> (trace foo)
> (setq *foo* #'foo)
> (untrace)
> (funcall *foo*)   ; would it be useful if this printed trace info for ever more?

Well, I certainly think this is an issue for all systems.

You could also make the encapsulated function (redundantly) check to see
if it was traced before printing the trace info.  (Compared to the 
I/O overhead, the speed cost of 
 (member 'fn-name *probably-short-list-of-traced-functions*)
would be negligible.

What do other implementations do?  (I wish my lispm weren't in pieces.)
From: Duane Rettig
Subject: Re: trace calls from within mapcar or similar functions
Date: 
Message-ID: <4ae1cri80.fsf@beta.franz.com>
Kent M Pitman <······@world.std.com> writes:

> "Martin Simmons" <······@xanalys.com> writes:
> 
> > In LW, TRACE and UNTRACE modify the symbol function cell, not the original
> > function object.  I'm not sure if it is conforming or not, but the reason that
> > #' returns the original function object is to prevent the tracing function
> > getting stuck in some data structure making it impossible to untrace.
> > 
> > E.g.
> > 
> > (defun foo ())
> > (trace foo)
> > (setq *foo* #'foo)
> > (untrace)
> > (funcall *foo*)   ; would it be useful if this printed trace info for ever more?
> 
> Well, I certainly think this is an issue for all systems.

Not any more...

> You could also make the encapsulated function (redundantly) check to see
> if it was traced before printing the trace info.  (Compared to the 
> I/O overhead, the speed cost of 
>  (member 'fn-name *probably-short-list-of-traced-functions*)
> would be negligible.
> 
> What do other implementations do?  (I wish my lispm weren't in pieces.)

Before version 6.0, Allegro CL used to trace by inserting encapsulations
between the function name and the real function object (similar to how
LW is being described as doing it).  The problem with such encapsulations
is that there are many times you can't easily get hold of a function object
for the purpose of inserting a trace encapsulation in it.  I don't remember
what we did for the above example, but I believe that the "encapsulation"
hash table we kept would allow us to properly track redefinitions and to
keep pointers to all such traced functions, so that none were lost.

In version 6.0 and later, we use a new implementation we call
function-wrapping.  An fwrapped function has had its start address
modified so that the function call gets turned "inside out" i.e.,
the low-level mechanism gets called quickly, as does any other function
call, and once inside the mechanism, it does the lookup for any wrappers
to run, with the real function being applied specially (so as not to
invoke the fwrap mechanism recursively) on the inside level.  It is
as if these fwrappers are "around" methods on the function call.
There can be any number of fwrappers wrapping the function, and each
is named for convenience and uniqueness.  Arguments can be destructured,
and can be examined and modified, and the next functionality in
the chain is invoked by the form (call-next-fwrapper) (look familiar?)
Unwrapping a function is implemented by simply replacing the normal
start-address into the function object (and doing some bookwork in
the fwrapper tables).

This is all described in the following docuent:
http://www.franz.com/support/documentation/6.0/doc/fwrappers-and-advice.htm

The trace mechanism uses this facility by establishing a :trace fwrapper.
Also, the TRACE macro has a functional interface (although not yet exported)
which allows either function names _or_ function objects to be traced or
untraced.  This is because the fwrap facility also accepts either
function names or function objects and so it is possible to control
tightly which functions get "wrapped".  It is even possible to control the
order of the fwrappers as they fire. 

The redundant check for tracing that you describe could easily be done
with fwrappers, but I haven't seen the need for it so far in the trace
package itrself.  Perhaps someone will request such a thing...

-- 
Duane Rettig          Franz Inc.            http://www.franz.com/ (www)
1995 University Ave Suite 275  Berkeley, CA 94704
Phone: (510) 548-3600; FAX: (510) 548-8253   ·····@Franz.COM (internet)
From: Erik Naggum
Subject: Re: trace calls from within mapcar or similar functions
Date: 
Message-ID: <3206118893924779@naggum.net>
* "Martin Simmons" <······@xanalys.com>
> In LW, TRACE and UNTRACE modify the symbol function cell, not the
> original function object.  I'm not sure if it is conforming or not, but
> the reason that #' returns the original function object is to prevent the
> tracing function getting stuck in some data structure making it
> impossible to untrace.
> 
> E.g.
> 
> (defun foo ())
> (trace foo)
> (setq *foo* #'foo)
> (untrace)
> (funcall *foo*)   ; would it be useful if this printed trace info for ever more?

  If you could add something in or to the function object itself to cause
  tracing, you would obviously remove it upon untracing, just like you
  would for the functional value cell of the symbol.  This requires that
  the function object is more than just some code.  In every Common Lisp
  implementation I have seen, this is the case.  From a casual inspection
  of LispWorks for Linux, it appears to be true there, too.  Even if you
  redefine the function after it has been requested traced, you can make
  sure the trace flag is turned off if you remember which function objects
  you have modified for what reason.   ///