by Eric Marsden
The cmucl cross-referencing facility
(abbreviated XREF) assists in the analysis of static dependency
relationships in a program. It provides introspection capabilities
such as the ability to know which functions may call a given
function, and the program contexts in which a particular global
variable is used. The compiler populates a database of
cross-reference information, which can be queried by the user to
know:
- the list of program contexts (functions, macros, top-level
forms) where a given function may be called at runtime, either
directly or indirectly (via its function-object);
- the list of program contexts where a given global variable may
be read;
- the list of program contexts that bind a global variable;
- the list of program contexts where a given global variable may
be modified during the execution of the program.
A global variable is either a dynamic variable or a constant
variable, for instance declared using defvar
or defparameter or defconstant.
12.1 |
Populating the
cross-reference database |
|
[Variable]
c:*record-xref-info*
When non-NIL, code that is compiled (either using
compile-file, or by calling compile from the listener), will be analyzed for
cross-references. Defaults to nil.
Cross-referencing information is only generated by the compiler;
the interpreter does not populate the cross-reference database.
XREF analysis is independent of whether the compiler is generating
native code or byte code, and of whether it is compiling from a
file, from a stream, or is invoked interactively from the
listener.
[Function]
xref:init-xref-database
Reinitializes the database of cross-references. This
can be used to reclaim the space occupied by the database contents,
or to discard stale cross-reference information.
12.2 |
Querying the
cross-reference database |
|
cmucl provides a number of functions in
the XREF package that may be used to query the cross-reference
database:
[Function]
xref:who-calls function
Returns the list of xref-contexts where function (either a symbol that names a function, or
a function object) may be called at runtime. XREF does not record
calls to macro-functions (such as defun) or
to special forms (such as eval-when).
[Function]
xref:who-references global-variable
Returns the list of program contexts that may reference
global-variable.
[Function]
xref:who-binds global-variable
Returns a list of program contexts where the specified
global variable may be bound at runtime (for example using
LET).
[Function]
xref:who-sets global-variable
Returns a list of program contexts where the given
global variable may be modified at runtime (for example using
SETQ).
An xref-context is the originating site of a
cross-reference. It identifies a portion of a program, and is
defined by an xref-context structure, that
comprises a name, a source file and a source-path.
[Function]
xref:xref-context-name context
Returns the name slot of an xref-context, which is one
of:
[Function]
xref:xref-context-file context
Return the truename (in the sense of the variable
*compile-file-truename*) of the source file from which
the referencing forms were compiled. This slot will be nil if the code was compiled from a stream, or
interactively from the listener.
[Function]
xref:xref-context-source-path context
Return a list of positive integers identifying the form
that contains the cross-reference. The first integer in the
source-path is the number of the top-level form containing the
cross-reference (for example, 2 identifies the second top-level
form in the source file). The second integer in the source-path
identifies the form within this top-level form that contains the
cross-reference, and so on. This function will always return
nil if the file slot of an xref-context is
nil.
In this section, we will illustrate use of the XREF facility on a
number of simple examples.
Consider the following program fragment, that defines a global
variable and a function.
(defvar *variable-one* 42)
(defun function-one (x)
(princ (* x *variable-one*)))
We save this code in a file named example.lisp, enable cross-referencing, clear any
previous cross-reference information, compile the file, and can
then query the cross-reference database (output has been modified
for readability).
USER> (setf c:*record-xref-info* t)
USER> (xref:init-xref-database)
USER> (compile-file "example")
USER> (xref:who-calls 'princ)
(#<xref-context function-one in #p"example.lisp">)
USER> (xref:who-references '*variable-one*)
(#<xref-context function-one in #p"example.lisp">)
From this example, we see that the compiler has noted the call to
the global function princ in function-one, and the reference to the global variable
*variable-one*.
Suppose that we add the following code to the previous file.
(defconstant +constant-one+ 1)
(defstruct struct-one
slot-one
(slot-two +constant-one+ :type integer)
(slot-three 42 :read-only t))
(defmacro with-different-one (&body body)
`(let ((*variable-one* 666))
,@body))
(defun get-variable-one () *variable-one*)
(defun (setf get-variable-one) (new-value)
(setq *variable-one* new-value))
In the following example, we detect references x and y.
The following function illustrates the effect that various forms of
optimization carried out by the cmucl
compiler can have on the cross-references that are reported for a
particular program. The compiler is able to detect that the
evaluated condition is always false, and that the first clause of
the if will never be taken (this optimization
is called dead-code elimination). XREF will therefore not register
a call to the function sin from the function
foo. Likewise, no calls to the functions
sqrt and are registered, because the compiler
has eliminated the code that evaluates the condition. Finally, no
call to the function expt is generated,
because the compiler was able to evaluate the result of the
expression (expt 3 2) at compile-time (though
a process called constant-folding).
;; zero call references are registered for this function!
(defun constantly-nine (x)
(if (< (sqrt x) 0)
(sin x)
(expt 3 2)))
12.4 |
Limitations of
the cross-referencing facility |
|
No cross-reference information is available for interpreted
functions. The cross-referencing database is not persistent: unless
you save an image using save-lisp, the
database will be empty each time cmucl is
restarted. There is no mechanism that saves cross-reference
information in FASL files, so loading a system from compiled code
will not populate the cross-reference database.
The cross-referencing facility is only able to analyze the static
dependencies in a program; it does not provide any information
about runtime (dynamic) dependencies. For instance, XREF is able to
identify the list of program contexts where a given function may be
called, but is not able to determine which contexts will be
activated when the program is executed with a specific set of input
parameters. However, the static analysis that is performed by the
cmucl compiler does allow XREF to provide
more information than would be available from a mere syntactic
analysis of a program. References that occur from within
unreachable code will not be displayed by XREF, because the
cmucl compiler deletes dead code before
cross-references are analyzed. Certain ``trivial'' function calls
(where the result of the function call can be evaluated at
compile-time) may be eliminated by optimizations carried out by the
compiler; see the example below.
If you examine the entire database of cross-reference information
(by accessing undocumented internals of the XREF package), you will
note that XREF notes ``bogus'' cross-references to function calls
that are inserted by the compiler. For example, in safe code, the
cmucl compiler inserts a call to an
internal function called c::%verify-argument-count, so that the number of
arguments passed to the function is checked each time it is called.
The XREF facility does not distinguish between user code and these
forms that are introduced during compilation. This limitation
should not be visible if you use the documented functions in the
XREF package.
As of the 18e release of cmucl, the
cross-referencing facility is experimental; expect details of its
implementation to change in future releases. In particular, the
names given to CLOS methods and to inner functions will change in
future releases.
© 1995-2003 CMUCL Project