Go to the first, previous, next, last section, table of contents.


Instrumenting Code

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.

Instrument Basics

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.

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.

Given Instruments

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.

Creating Instruments

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.

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.