VLS provides a facility for making it easy to instrument your Lisp code.
Code instrumentation help to both understand Lisp code and understand
what is going on in difficult to understand code bugs. Most Lisp
specifications provide tracing facilities. For example Common Lisp
specifies a trace function. These tracing facilities are quite
powerful and elegant but they are usually done only at the functional
interface and show the outline of function input and output arguments
over time. Sometimes this is not sufficient. A case in point is that
when you trace a function and find that the arguments are not what you
would expect. In one sense Code instrumentation takes over where code
tracing leaves off and helps to get a clear visualization of what is
happening inside the code. In fact you can use VLS code instrumentation
to do selective tracing.
VLS provides a given set of instruments to choose from, See section Given Instruments. But it is easy for you to modify or add to this set, See section Creating Instruments. The section on Given Instruments not only explains the given instruments but serves as an example of using instruments.
VLS instruments code in Emacs by placing an instrument at point (see Emacs point and mark) inside of a definition in a Lisp file buffer. A VLS instrument is any s-expression. And you could do exactly what VLS does yourself by making a copy of a definition and inserting such s-expressions and reevaluating the definition. But VLS automates this process for you to make code instrumentation faster, less tedious and less error prone.
VLS maintains a copy of such instrumented definitions and tracks the instruments you assert so that you can easily instrument, re-instrument and edit the existing instruments. It also provides a list of common instruments for you to choose from when you are creating instruments and makes it easy to add your own instruments to this list. There is some science to code instrumentation that makes it appropriate for VLS to provide a given list of useful instruments. For example a conditional breakpoint is an example of such an instrument.
VLS code instruments are a form of template that allows an s-expression
to have slot fillers depending on the code context in which the
instrument is inserted. When you choose an instrument at the current
point in a buffer VLS pops up an edit buffer with just the slot filled
instrument for your examination. At this point you can accept the
automatically provided instrument or edit the instrument to make it do
more complex things. When the instrument passes your inspection you hit
C-cC-c and the instrument along with all other previous
instruments are inserted in the copy of the definition and the
instrumented definition copy is reinstalled in the current Lisp
process.
When a definition is reinstalled by VLS in this way the user has the
option of installing just the interpreted version, the compiled version
or have VSL ask for the option. You can do this by setting the variable
vls-compile-instrumented in your ~/.emacs file or by
toggling it with the vls-instrument-toggle-compile command,
See section Instrument Commands.
This section contains the VLS commands for applying and using the given or user specified instruments. VLS instrument commands that operate on single definitions work by the user placing point inside or after the source definition and then executing the command. When we say "definition containing point" in this section we mean the definition that point is inside or after but not inside a subsequent s-expression. After doing this VLS automatically determines the parameters for the definition and effect of the command.
Command: vls-instrument Keys: C-c SPC SPC Action: Choose
an instrument and apply at point. The user chooses an instrument from
the list of instruments specified for the current Lisp and applies that
instrument at point in a definition in the current Lisp buffer. A list
of the specified instruments are displayed and the user chooses a number
for the instrument to be inserted at point. Just a RET for this choice
will choose the 0th instrument. After the instrument is accepted by the
user in the VLS edit buffer the definition is installed with all
existing instruments for that definition. If the definition has already
been instrumented and the definition has changed from when the
definition was instrumented vls-instrument will automatically
clear all the old instruments and only include this current instrument.
Variable: vls-compile-instrumented Meaning: Allows you to control
what happens when instrumented or uninstrumented code is installed.
After instrumenting a definition, nil means don't ask don't compile (the
default), t means don't ask just compile, and 'ask means ask to compile.
Command: vls-instrument-toggle-compile Keys: C-c SPC t
Action: Toggle the variable vls-compile-instrumented between
compile and do not compile instrumented code during its installation, A
C-u prefix argument means to set the variable to ask to compile
during the install. This command does not require point to be placed
inside any definition.
Command: vls-instrument-clear Keys: C-c SPC c Action:
Clear all instruments of the definition containing point and reinstalls
from that source containing point without the instruments.
Command: vls-instrument-clear-all Keys: C-c SPC C Action:
Clear all instruments associated with current Lisp process and
reinstall all pre-instrumented definitions. Note that this slightly
different from applying vls-instrument-clear to all instrumented
definitions since vls-instrument-clear installs the definition
containing point and vls-instrument-clear-all installs the
definitions that was saved before it was instrumented. What this
implies is that if any definition is edited since it was instrumented it
should be cleared with vls-instrument-clear. Or else if such an
edited definition was cleared through vls-instrument-clear-all
the pre-edited definition will be the installed version until the edited
version is reinstalled manually.
Command: vls-instrument-display Keys: C-c SPC d Action:
Displays the instruments of definition containing point in a VLS buffer.
Each instrument in this buffer is preceded by a comment indicating that
it is an instrument. This command is useful when a the user makes an
error in instrumenting the definition and wants to see what is going on
in the instrumented code.
Command: vls-instrument-display-all Keys: C-c SPC D Action:
Show all instrumented definitions and related information.
Command: vls-instrument-edit Keys: C-c SPC e Action: Edit
the instrument closest to point in current definition. If more than one
instrument is closest to point then this command will ask which one you
want. This allows you to edit a previously inserted instrument. VLS
brings up the same *vls* edit buffer that it did when you
executed vls-instrument for the original instrument. If you
clear this edit buffer below the header and accept, it results in the
particular instrument being removed.
Command: vls-instrument-again Keys: C-c SPC r Action:
Reinstall instruments of definition containing point. If for some
reason the definition containing point was redefined this command allows
you to reinstall the instrumented definition. If the definition
containing point was modified vls-instrument-again will detect
this and give the option of doing nothing or reinstalling the old
definition with its instruments.
Command: vls-instrument-view-variables Keys: C-c SPC v
Action: View any variables set by instruments of definition containing
point. Some instruments are capable of collecting results in global
variables a points in your code. VLS keeps track of these variables and
this command will print the contents of such variables in the current
Lisp buffer.
Command: vls-instrument-restart-variables Keys: C-c SPC V
Action: Restart any variables set by instruments of definition
containing point. Some instruments are capable of collecting results in
global variables at points in your code. Usually these are lists or
counters of such results at points in time. These variables will keep
growing from one test run of your code to the next. This command allows
such variables to be reset. VLS tries to do the correct thing here, for
example if the accumulated variable has an integer in it it will reset
it to 0, if it has a list it will reset it to nil, ... etc.
To illustrate the given instruments we will use a Common Lisp example. Those non Common Lisp users will have to use their imagination here. Suppose that we have a Lisp buffer with the following code for factorial
(in-package test)
(defun fact (x)
(if (= x 0) 1
(* x (fact (1- x)))))
Lets say that point is just before the if expression. When you
execute the command vls-instrument (C-c SPC SPC) described
in the previous section you get the selection prompt
VLS Instruments 0 = Breakpoint 1 = Wrap selected instrument before 2 = Wrap selected instrument after 3 = Print variables 4 = Capture variables 5 = Count passes
and are asked to enter a number associated with one of these listed
instruments. And lets say that you choose to enter the "Breakpoint"
instrument. VLS would then pop-up in a *vls* buffer something
like the following
Instrumenting definition: fact
---- Edit or enter below and type C-cC-c when done ----
(cond (t (break "test fact 1")))
Notice first of all that this is a conditional breakpoint with a
predicate of true by default which in effect makes it an unconditional
breakpoint if you accept the default. To make it a conditional
breakpoint replace the true predicate with any other predicate. When
you are happy with the contents of the *vls* buffer hit C-cC-c
and the instrument will be registered and installed into the current
Lisp process. At the same time VLS will pop-up a new *vsl*
buffer with the definition and all of the current instruments in place.
Each instrument will be clearly delineated with a Lisp comment.
Instrumented definition:
(defun fact (x)
;; *Instrument*
(cond (t (break "test fact 1")))
(if (= x 0) 1
(* x (fact (1- x)))))
Now when you run the factorial function in the current Lisp shell buffer it should break at the point you placed this instrument.
We will skip the "Wrap" instruments for now and go to an explanation of
the "Capture variables" since this will help explain a large number of
the instrument commands and the "Wrap" instruments. First clear all the
existing instruments by keeping the point inside the source of the
definition fact with the vls-instrument-clear command
(C-c SPC c). This clears out all the instruments in the
fact function and reinstalls fact from the source that
point is in.
In this next example we want to insert a "Capture variables" instrument
so that we can run a test case and then later view the variable x
each time that it comes into this section of the code. So position
point just before the inner fact recursive call expression. But
note that if we put an instrument at this point, just before the inner
recursive fact call expression, the instrument itself would be an
expression inside the times expression (* x (fact (1- x))). This
would produce an undesirable result since whatever the instrument
returned would become a factor in the times expression. This is what
the "Wrap" instruments are for.
In this case we would choose the "Wrap selected instrument before".
What this would do is wrap an expression around the instrument followed
by the (fact (1- x)) expression such that the effect of the
instrument would occur first but the result of the (fact (1- x))
expression would be returned and passed as an argument to the times
expression. (1).
Whenever you select a "Wrap" type of instrument VLS will ask for the
another instrument to be wrapped with the following expression in the
Lisp buffer after point. In this case we want to give it the "Capture
variables" instrument. After selecting the "Capture variables"
instrument, this particular instrument will continually ask for variable
names until you just type a RET by itself. So do this by entering
x RET RET. At this point you should see something like the
following in a *vls* buffer
Instrumenting definition: fact
---- Edit or enter below and type C-cC-c when done ----
(progn
(cond (t (unless (boundp '/fact-capture-1) (setq /fact-capture-1 nil))
(push (list (list 'x x)) /fact-capture-1)))
(fact (1- x)))
There are a three things to notice about the features of instruments,
which is why we chose this example. The first is to notice that the
(fact (1- x)) expression is wrapped in the progn with the
instrument. This provides the wrapping effect that we have been
discussing. The second thing is to notice that much like the
conditional breakpoint this instrument is embedded in a conditional
which in the same way allows a conditional collection of variable
values.
And finally notice the introduction of the /fact-capture-1 global
variable that this instrument introduces to collect the value of the
variable x. This global variable is specific case of VLS
instrument variables used for various purposes such as collecting data.
A VLS instrument variable is guaranteed to be unique among VLS
instrument variables and unique among all global variables if none other
have the VLS global debug variables prefix, See section Variable Specifics Parameters. The default prefix is "/" but the user can easily change
this prefix if for some reason the users application has other global
variables with this prefix.
Then evaluate in the Lisp shell buffer
(fact 5)
it should return 120 in the Lisp shell buffer and then it should
have collected the values in the VLS global instrument variable. To see
this execute the command vls-instrument-view-variables (C-c
SPC v). VLS should then print in the Lisp shell buffer something like
Instrument global variable: /fact-capture-1 = (((X 1)) ((X 2)) ((X 3)) ((X 4)) ((X 5)))
It is important to note that the variable values in the list are
captured in the reverse order that they occur. So in the above x
first took on the value 5 and its last value was 1.
It is also important to note that instead of just entering the variable
x we could have entered any number or variables or s-expressions.
For example, if to the instrument prompt above for variables to capture
you entered x RET (+ x 1) RET RET, then the result of
vls-instrument-view-variables would produce something like
Instrument global variable: /fact-capture-1 = (((X 1) ((+ X 1) 2)) ((X 2) ((+ X 1) 3)) ((X 3) ((+ X 1) 4)) ((X 4) ((+ X 1) 5)) ((X 5) ((+ X 1) 6)))
VLS keeps track of such registered VLS instrument variables and the
command vls-instrument-view-variables will print all such
variables collected for the definition that point is in. Furthermore
such instrument variables under an instrument like "Capture variables"
will keep accumulating on top of previous runs. If you don't want this
behavior at some point then execute the command
vls-instrument-restart-variables (C-c SPC V) while point
is inside of the definition. VLS tries to do the smart thing with this
command to reset the instrument variable. For example so that if the
instrument variable is collecting a list of things it will reset the
list to nil, if the instrument variable is an integer, it will
reset the instrument variable to 0.
If you wanted the effect of the instrument to take place after the
expression following point, the (fact (1- x)) expression in our
example above, then you would use the "Wrap selected instrument after"
instrument. All else works the same as the "Wrap selected instrument
before" including having the wrapped expression returning the value of
the expression following point.
At any time you can examine the existing instruments in your
instrumented definition with the command vls-instrument-display
(C-c SPC d). It displays the instrumented definition with the
instruments clearly delineated with Lisp comments in a *vls*
buffer.
Also at any time you can edit the instruments in a definition using
vls-instrument-edit (C-c SPC e). The way editing
instruments works is that where ever point is it, will edit the closest
instrument to where point is. After bringing up the *vls* buffer
with the instrument instance to be edited everything from there on is
exactly equivalent to creating an instrument in the first place. If you
want to remove an instrument you can just clear the *vls buffer
below the header that says "Edit or enter below" and then hit C-cC-c and
that instrument will be removed; all other existing instruments will
remain.
The "Print variables" instrument is the same as the "Capture variables" but instead of capturing the variables and their values it simply prints them out as the instrument instance is invoked.
The "Count passes" instrument is very simple and simply counts the number of passes through a point in the code in an instrument global variable.
It is easy to create your own instruments. You might need to know some
Elisp depending on the sophistication of the instrument. You can look
at the Lisp type specifics files at the existing ones for examples. In
the VLS directory the file types/elisp/cl.el is one example that
defines a variable vls-cl-instruments for Common Lisp
instruments. The Common Lisp type specifics file types/cl.el
then defines vls-cl-instruments as the value of the types
parameter v:instruments. So in the case of Common Lisp you can
add to this variable list of instruments or set it to your own list in
your ~/.emacs file.
If you want to create your own instruments it's a good idea to look at the given instruments as examples. However, this section give a more formal presentation for exactness.
Each Lisp type has an Lisp specifics file parameter called
v:instruments, as we mentioned about, that defines a variable
name. That variable in turn contains a list of instruments where each
instrument is a list of the form
(description template)
Where description is a string describing the instrument that will
appear in the selection list when the user executes the
vls-instrument command. And template is an Elisp form
that returns an initial instrument Elisp string that will appear in the
*vls* buffer when the user selects it through the
vls-instrument command.
In the most general sense a template is any Elisp form that
returns a string. It could be as simple as just an Elisp literal
string. More typically the template is an Elisp format
expression with %s fillers in the its first argument string.
These %s fillers are usually filled with data based on the
context of where the user is inserting the instrument in his code. For
example it may be filled with a string that includes the name the
definition that is being instrumented. Here is an example of the
Breakpoint Instrument from the Given Instruments for Common Lisp
("Breakpoint"
(format "(cond (t (break \"%s\")))" (vlsi-def-unique)))
The "Breakpoint" string is the description and will appear in the
VLS Instruments selection list. The format expression is, as we
explained above, as way to generate an initial instrument string
instance by substituting the %s with the value returned by the
function vlsi-def-unique. This function will return return a
unique string that also contains the identity of the definition that is
being instrumented. For a list of all provided helper functions
see the next section, See section Instrument Helpers.
The previous section, See section Creating Instruments, we described using
vlsi-def-unique which is one example of a number of VLS
instrument helper functions given by VLS. They all have the prefix
vlsi-. Of course you can write your own helper substitution
functions. Following are a list of the VLS helper functions and what
they return.
Function: vlsi-def-unique Action: Return a unique string
containing the identity of the definition that point is in.
Function: vlsi-gvar-unique Arguments: descriptor Action:
Return a string containing the identity of the definition that point is
in and the given descriptor string prefixed by the
v:global-debug-variable-prefix of the current Lisp. The returned
string represents a unique instrument global variable.
Function: vlsi-get-var-pairs Action: Prompts the user for
variables and returns list of variable value pairs of the current Lisp
as a string. It is a list or pairs of the form in effect
('symbol symbol)
where is symbol is the name of a variable that the user enters to
the prompt. The effect is that each pair when evaluated in the current
Lisp will result in the variable name as the first of the pair and the
value of the variable as the second of the pair. It does not check what
the user enters so no just variable but s-expressions and anything else
can be entered .
Function: vlsi-next-sexp Action: Returns the next s-expression
forward from point as a string.
Function: vlsi-select-instrument Action: The user selects an
instrument as usual and this functions returns the composed instrument
instance of that instrument. This is used by instruments that consume
other instruments, like the "Wrap" instruments.
Go to the first, previous, next, last section, table of contents.