[Openmcl-devel] STEP function

Gary Byers gb at clozure.com
Sat Jan 9 17:00:32 PST 2010

Just to fill in the blanks a bit (with stuff that may be painfully

Historically, most CL implementations contained an interpreter as well
as a compiler.  (ANSI CL defines compilation in terms of things like
how EVAL-WHEN is handled and when macroexpansion takes place; it's not
necessary for an implemenation's "compiler" to ever generate machine-
or byte-code; likewise, there's no requirement that EVAL be
implemented in terms of an interpreter - "evaluating a form" should
always have the same general effects as "compiling that form and
calling the resulting function", except in some fairly pathological
cases.)  So, the first sentence above should be read as something
like "historically, most CL implementations contained an interpreter
that evaluated lisp forms at runtime, and a compiler which translated
those forms to functionally equivalent machine code for a real or
virtual machine."

For other (mostly historical) reasons, it was typically the case that
forms typed interactively (or LOADed from a source file) were
processed by the interpreter and functions defined in those
environments were "interpreted functions"; compiled functions were
generally only produced by calling COMPILE explicitly or by loading
the binary (FASL) file produced by COMPILE-FILE.  Some of the reasons
for this strategy had to do with resource constraints: compilers were
(relatively speaking) big and slow components of a lisp system, and
compiling things that were defined interactively was likely (or was
seen as likely) to disrupt the interactive development process.
Interpreters were also more likely to offer better debugging
facilities than were available in compiled code (for instance, the
mapping between variable names and values was more likely to be
available at runtime) and pre-ANSI CL defined interfaces to the
interpreter (EVALHOOK, APPLYHOOK, and friends) that could be used
by the interpreter (to implement STEP) and/or the user.

When MCL was being developed (on 68K Macs with at most a few MB of
memory) in the mid-late '80s, resource constraints were certainly
a factor, but we evaluated the tradeoffs differently.  The compiler
was (relatively) small and fast, compiled functions were not only
much faster than their interpreted equivalents but were often much
smaller as well, and that was a very, very important consideration
in those days.  We supported interpreted functions (largely in order
to support STEP), but because of the way that some things were 
implemented and some design decisions, debugging in the presence
of interpreted functions was generally worse than it was in their
absence (backtrace would show internal interpreter functions and
not the functions that the interpreter was working on, etc.)

MCL offered the ability to turn off the automatic compilation
of things defined interactively; the primary reason for doing
this was to use STEP for debugging.  Some people might have
preferred to run with automatic compilation turned off for that
reason, but it tended to make things slower, bigger, and otherwise
harder to debug.  There was an option (that may still be present
in CCL) to compile definitions but leave the lambda expression
attached to the resulting function, so that STEP could work on
compiled code (by interpreting that lambda expression.)  That
worked fairly well much of the time, but could fail if (for instance)
macro definitions that were present at compile time were different
or missing at runtime.

Aside from the issues of how to arrange for code to be STEPpable
(well, perhaps related to them), there were real question as to
how useful STEP is/was as a debugging tool.  One often does debugging
by binary search - "everything's OK at point A, something's wrong at
point B, so I need to try again and take a closer look at what happens
between those points."  MCL's stepper possibly could have offered
better support for this kind of thing than it did, but I think that
many people (not all) found that the facilities that it did offer -
"step into", "step over", "step out of" - weren't often adequate
to debug non-trivial problems (and having to run large bodies of
code under an interpreter also made it impractical to use STEP to
debug non-trivial problems.)  Not everyone shared this opinion -
there were/are users who found it useful - but I think that I 
successfully used STEP to find a real bug exactly once in several
attempts over more than 10 years, and I think that many people
(including most people involved in the implementation) thought of
it as a toy.

OpenMCL inherited all of this from MCL, and at some point 5-6 years
ago I ran into a bug in the interpreter, and the simplest fix (given
that the interpreter existed only to support STEP, the limited utility
of STEP, and the cost of trying to maintain the interpreter) was to
simply excise the interpreter (and the concept of "interpreted
functions").  Part of the justification for doing that is that since
we're in practice primarily (if not entirely) a compiled-only
implementation, it's of more benefit to improve facilities for
debugging compiled code than it is to provide parallel facilities
(STEP and interpreted code) that don't seem to scale well enough
to be useful.

Debugging facilities for compiled code have probably improved some
over the last few years, but the work that Bill's referring to - the
ability to do STEP-like things on compiled code - would likely be a
more dramatic improvement.  One prerequisite for this - the ability to
map between points in a function's source code and corresponding points
in its machine code - is in place, though there are obviously other
issues and that correspondence may be less precise than it would be
in an interpreter.  If the tradeoffs are that you can do at least
some STEP-like debugging (enough to be useful) in aggressively
optimized code and lots of useful debugging of code compiled under
less agressive settings, that seems far preferable to the traditional
interpreter-based approach.

On Sat, 9 Jan 2010, Bill St. Clair wrote:

> I think the only reason we don't do it is because nobody has yet
> considered it important enough to pay for. We've probably done at
> least part of the work with Gail's coverage facility. Gail, how much
> effort do you think STEP would be?
> -Bill
> On Sat, Jan 9, 2010 at 3:58 PM, Philippe Sismondi <psismondi at arqux.com> wrote:
>> I am just now trying to improve my beginner's skills at interactive debugging in CL. As far as I can tell the ccl STEP fn does nothing but evaluate the form. I see that this is permitted by the spec.
>> If that is correct, is there any likelihood that STEP will be more fully implemented in ccl? Is "single stepping" not considered useful in CL debugging?
>> Best,
>> - Phil -
>> _______________________________________________
>> Openmcl-devel mailing list
>> Openmcl-devel at clozure.com
>> http://clozure.com/mailman/listinfo/openmcl-devel
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel

More information about the Openmcl-devel mailing list