$Revision: 5.0.2.5 $
The document introduction.htm provides an overview of the Allegro CL documentation with links to all major documents. The document index.htm is an index with pointers to every documented object (operators, variables, etc.) The revision number of this document is below the title. These documents may be revised from time to time between releases.
1.0
Cross reference introduction
1.1 The cross-reference package
1.2 The cross-reference database
1.3 When is the
cross-reference information generated?
1.4
When is xref information in a fasl file added to the database?
1.5 Things to note
about the compiler and macros
2.0 What is in and how to
access the xref database
2.1 Clearing the database
2.2 General description of
query functions and commands
2.3 The function-name and
object arguments
2.4 File
information and the :in-files keyword argument
2.5 The :in-functions keyword argument
2.6 The :inverse keyword argument
3.0 A cross-referencer example
The cross-reference facility provides the ability to analyze compiled code, finding out information such as which functions call which other functions and what global variables are used. The cross-referencer maintains a database of cross-reference information which can be queried by the user to provide answers to questions like `who calls function foo?'
1.1 The cross-reference package
The cross-referencer functions are in the cross-reference
package
(nicknamed xref
). Unless otherwise indicated, all the symbols defined in this
chapter are exported from the cross-reference
package. Users must either use
the qualifier xref:
on most symbols naming xref functionality or evaluate (use-package
:xref)
to make exported symbols accessible with the package qualifier.
The code for the cross-referencer is not contained in the basic Allegro CL image. It is
loaded only when needed. Executing any of the user-visible cross-reference functions will
cause the correct module to be loaded, but you can ensure that the code is loaded by
evaluating the following form: (require :xref)
.
Lisp contains a database of cross-reference information. This database can be queried with the functions and commands described in 2.2 General description of query functions and commands below. The next several headings describe how information is added and removed from the database. 2.0 What is in and how to access the xref database below describes what information is available and how it can be accessed.
Cross reference information is only generated by the compiler. For that reason, no cross reference information is available for interpreted functions and loading a fasl (compiled lisp) file which does not contain cross reference information does not add information to the cross reference database.
The general rule is that the information is generated when the value of the variable excl:*record-xref-info* is non-nil
.
However, in certain cases the specific value of that variable can be overridden locally as
we describe next.
nil
or if the compilation is done in the body of the macro xref:with-xref.:xref
keyword argument to compile-file
is non-nil
. The default value of the variable is the value of excl:*record-xref-info* but you
may override that value when calling compile-file by explicitly
specifying a value for the :xref
keyword argument (or also by calling compile-file
within the body of the xref:with-xref
macro). Note that the :cf and :cload top-level commands do not accept
keyword arguments so whether or not cross-reference information is generated is controlled
by excl:*record-xref-info*.
The cross-reference information generated for a file is stored in the resulting fasl
(compiled lisp) file and cannot be added to the database until the file is loaded.Even if cross-reference information is generated when a file is compiled, it is never added to the cross-reference database until the file is loaded. Further the cross-reference information is loaded only if the value of excl:*load-xref-info* is non-nil or the load is done within the body of the macro xref:with-xref.
Note that there is no argument to load that specifies whether cross-reference information should or should not be loaded. The command analogs to load (:ld and :cload) also load or do not load xref information as excl:*load-xref-info* is true or false.
Of course, if you have a fasl file with xref info and you load it with excl:*load-xref-info* nil, you can load it again with that variable true and the xref information will be added to the database.
When you change and recompile a file for which cross-reference information exists in the database, you should be sure to load the revised compiled file with excl:*load-xref-info* true in order to update the database. Warning: if you do change a file for which xref information exists in the database, you must compile it so that xref information is generated and load it so that xref information is loaded or the database will become out of date.
Since macros are expanded by the compiler, the function calls into which a macro form is expanded appear even though they are not evident in the source. Further, because the compiler will transform certain functions into macros (using either the public facility define-compiler-macro or internal Allegro CL-specific functionality), some calls that you expect to be function calls turn out to be macro calls.
Here is a simple example using the utilities in this chapter. Note that the function bar calls the function excl:record-source-file. Look what happens when bar is compiled.
USER(10): (xref:start-xref)
T
USER(11): (defun bar ()
(excl:record-source-file 'foo
:type :operator))
BAR
USER(12): (compile 'bar)
BAR
NIL
NIL
USER(13): (xref:who-calls 'bar :inverse t)
BAR directly calls:
EXCL::RECORD-SOURCE-FILE-1
No indirect callees of BAR were found in the database
BAR calls as a macro:
RECORD-SOURCE-FILE
USER(14):
excl:record-source-file was converted by the compiler to a macro call that expanded to forms that included a call to the internal function excl::record-source-file-1. This behavior is intended since real (rather than apparent) cross-reference information is being reported. However, you may be surprised by this behavior the first time you see it.
The cross-reference database contains information about the relations between different parts of code. There is a single database that holds all information. This database itself does not store file information: that means that after a fasl file and its associated xref information is loaded, the fact that the information came from that fasl file is forgotten. However, if source file information is collected and stored (as described in source_file_recording.htm), that information can be used to provide file-specific cross-reference data, as we describe below.
The information stored deals with which operators (functions or macros) call or are called by what other operators and which operators use (in a fashion we will describe) global variables and which global variables are used by operators.
Specifically, the set of relations between program parts that the cross-referencer records contains the following information:
:direct-calls
- this is true if one function directly calls another. Thus foo
directly calls bar in the following case:
(defun foo () (bar))
:indirect-calls
- this is true if one function contains a indirect
reference to another, by, for example, referring to its function object. Thus foo
indirectly calls bar in the following case:
(defun foo (x) (apply #'bar x))
:macro-calls
- this is true if a function calls a macro. :calls
- this is true if any of :direct-calls
, :indirect-calls
,
or :macro-calls
are true.:references
- this is true when a function makes reference to a global
variable.:binds
- this is true when a function binds a global variable.:sets
- this is true when a function sets the value of a global variable.:uses
- this is true when a function :references, :binds, or :sets a global
variable.Information in the database can be retrieved with a number of functions and top-level commands. Many functions have top-level analogs. In addition, the function xref:get-relation is available for programmatic querying of the database.
The function xref:discard-all-xref-info clears all stored information in the database.
There are query functions, query top-level commands, and a programmatic function (that returns rather than prints information and so can be used within a program). We list them here.
The following functions ask specific questions to the database (all are named by
symbols in the xref
package but we suppress the package qualifier in the list
for clarity). We have divided the functions into two groups: the calls group (which asks
who calls functions and macros) and the uses group (which ask who uses global variables).
We give the top-level command equivalent where it exists.
Calls group:
who-directly-calls
who-indirectly-calls
macros-called-by
who-calls (and :who-calls)Uses group:
who-binds (and :who-binds)
who-references (and :who-references)
who-sets (and :who-sets)
who-uses (and :who-uses)
Each of these functions (and equivalent top-level commands) takes either a function
name or an object as its first argument and also accepts three keyword arguments: :inverse
,
:in-files
, and :in-functions
. We describe these arguments under
the next series of headings.
Note that the macros-called-by
function asks its question in the reverse way than the other calls questions: the rest ask
for all the functions that call a particular function. macros-called-by asks for all macros
called by a particular function. We have provided the question that we believe will be
most frequently asked in each case. As we describe below, the sense of a question can be
reversed by the :inverse
keyword argument.
There are two other query functions: xref-describe
and get-relation. xref-describe is a shorthand for calling
who-calls and who-uses. get-relation returns the information
(either nil
or a non-nil
value or a list) about the relation
between specified objects. In contrast, the regular query functions print information but
do not return anything. get-relation
is useful for programs which wish to use cross reference data.
In general, the first (required) argument to the query functions (and commands) is a function name or an object (typically a global variable) according to what would make sense if the function name and the first argument were an English sentence. Thus, the who-calls function takes a function name as its first argument since calling the function asks the question `who calls <function>' while who-uses takes an object (typically a global variable) since `who uses <variable>' is the question asked by that function.
The :inverse
keyword argument acts (as we describe below) to reverse the
sense of the question. Thus who-calls with :inverse
true transforms the
question to `who is called by <function>'. In any of the call queries, :inverse
being true still requires a function name as the first argument. In the uses queries,
however, :inverse
true requires the first argument to be a function name. who-uses with :inverse
true asks
the question `what is used by <function>' while (as we said above) with :inverse
nil
it asks `who uses <object>'.
A function name is either a symbol or a list. Note that when using the top-level command equivalents of query functions, you do not put a quote before the symbol or list naming the function.
An object can be any Lisp object but is typically a global variable. Cross reference information is only stored for global variables so no information will be printed for other types of objects. However, we permit other objects in order to avoid unnecessary errors and to allow for easy extension in later releases.
File information is available with the cross referencer only if source file information is recorded at the time files are compiled (and cross reference information is generated). In general, source file information will be recorded when both excl:*record-source-file-info* and excl:*load-source-file-info* are true. (See source_file_recording.htm above for a complete description of source file information.)
The value of the :in-files
argument should be either nil
(the
default) or a list of files that have been loaded into Lisp (the list many contain the
keyword :top-level
for functions defined directly to the Lisp top-level). If
the value is nil
, any information in the database is provided. If the value
is a list of files (perhaps with :top-level
), only functions defined in those
files are considered when the database is searched.
For example, suppose we have the following files:
File foo.cl:
(defun foo () (bar))
File baz.cl:
(defun baz () (bar))
And we define at the top-level:
(defun bar nil nil)
(defun hoo () (bar))
We compile and load all the files with *record-source-file-info*, *load-source-file-info*, *record-xref-info*, and *load-xref-info* all true. We also compile the top-level functions. Then, the form
(xref:who-calls 'bar)
prints the information that foo, baz, and hoo all call bar. However,
(xref:who-calls 'bar :in-files '("foo")
prints only that foo calls bar while
(xref:who-calls 'bar '("hoo" :top-level))
prints that baz and hoo call bar.
The filenames should be specified in the usual way, that is they can be strings, symbols, or pathname objects. The cl type (extension) will be added by default if it is not supplied (as in our example).
All the query functions including xref-describe and get-relation take this keyword argument.
The value of :in-functions
keyword argument should be nil
or
a list of function names. If the value is nil
, the entire database is
searched and all information is printed. If the value is a list of functions, the
information from the database is intersected with the list of functions and only those
functions in the intersection are printed.
When you are asking for which global variables a function uses (i.e. with who-uses and :inverse
true), the
list which is the value of :in-functions
can contain the names of global
variables and in that case, only the global variables called and in the supplied list will
be printed.
For example, we define the following globals and functions:
(defvar *var1* 10)
(defvar *var2* 20)
(defun foo (x) (+ x *var1*))
(defun bar () (+ *var1* *var2*))
Now,
(who-uses '*var1*)
prints that foo and bar use *var1*
,
while
(who-uses '*var1* :in-functions '(foo))
prints that foo uses *var1*
.
(who-uses 'bar :inverse t)
prints the globals used by bar, that is *var1*
and *var2*
.
However,
(who-uses 'bar :inverse t :in-functions '(*var1*))
prints that bar uses *var1*
since the list is restricted
by the :in-functions
keyword argument.
All the query functions including xref-describe and get-relation take this keyword argument.
This argument can be used to change the sense of the question asked by the query
function. In the case of the call queries, specifying :inverse
true changes
the question from who calls to who is called by. In the case of the use queries,
specifying :inverse
true changes the question from who uses a global variable
to what global variables does a particular function use (and thus changes the first
argument from being an object - typically a global variable - to being a function name).
Here are the query functions and the question asked when :inverse
is t.
who-directly-calls -> who is directly called by <arg>?
who-indirectly-calls -> who is indirectly called by <arg>?
macros-called-by -> who macro calls <arg>
who-calls (and :who-calls) -> who is called by <arg>?
who-binds (and :who-binds) -> what is bound by <arg>?
who-references (and :who-references) -> what is referenced by <arg>?
who-sets (and :who-sets) -> what is set by <arg>?
who-uses (and :who-uses) -> what is used by <arg>?
xref-describe -> what is called or used by <arg>?
Note that the macros-called-by function asks its question in the reverse sense to the other functions. It asks directly for all macros called by a function and for all functions which call a macro when :inverse is true. We have chosen for the direct call what we believe will be the most frequently asked question.
All query functions except xref:get-relation
accept the :inverse
keyword argument. (In get-relation, the sense of the
question is determined by the order of the arguments and so the sense can be reversed by
reversing the order.)
Let's say we have a file called example.cl containing the following:
(defvar var1 3)
(+ var1 3)
(defmacro addit (x) `(+ var1 ,x))
(defun calladdit (x y) (expt (addit x) y))
(defun call2 (a b) (calladdit a b))
(defclass fooclass ()
((name :initarg :name :reader foo-name)
(barg :initarg :barg :accessor get-foo-barg)))
(defgeneric blarf (x)
)
(defmethod blarf ((x t))
(car x))
(defmethod blarf ((x fooclass))
(get-foo-barg x)
(foo-name x)
(setf var1 3)
var1)
Then in Allegro CL, we enable the cross-referencer:
user(4): (xref:start-xref)
t
Then compile and load the example file (the compiler messages have been deleted below):
user(5): :cload example.cl
Now we can examine the code with cross-referencer. The output is a list of
function-names of forms that reference var1
. The (:top-level-form
"example.cl")
corresponds to the forms
(defvar var1 3)
and
(+ var1 3)
in the source file that reference var1
.
user(6): (xref:who-references 'var1)
var1 is referenced by:
calladdit
(:top-level-form "example.cl")
(method blarf (fooclass))
Now we try who-calls. Note that the call to the addit macro has been expanded so we see that calladdit calls +.
user(12): (xref:who-calls '+)
+ is directly called by:
calladdit
(:top-level-form "example.cl")
No indirect callers of + were found in the database
No macro callers of + were found in the database
Now let's look at the callers of calladdit.
user(13): (xref:who-calls 'calladdit)
calladdit is directly called by:
call2
No indirect callers of calladdit were found in the database
No macro callers of calladdit were found in the database
We can also see who calladdit calls by asking for the inverse relation. Here we can see the macro invoked by calladdit.
user(14): (xref:who-calls 'calladdit :inverse t)
calladdit directly calls:
+
expt
No indirect callees of calladdit were found in the database
calladdit calls as a macro:
addit
We can see all the relevant information about calladdit at once with xref-describe.
user(15): (xref:xref-describe 'calladdit)
calladdit is directly called by:
call2
No indirect callers of calladdit were found in the database
No macro callers of calladdit were found in the database
calladdit directly calls:
+
expt
No indirect callees of calladdit were found in the database
calladdit calls as a macro:
addit
calladdit references:
var1
No symbols bound by calladdit were found in the database
No symbols set by calladdit were found in the database
We can also use top-level commands to access cross-reference information.
user(9): :who-calls expt
expt is directly called by:
calladdit
No indirect callers of expt were found in the database
No macro callers of expt were found in the database
Notice the use of a CLOS function-name to obtain information about one of the methods defined for the generic function blarf.
(method blarf (fooclass)) directly calls:
foo-name
get-foo-barg
No indirect callees of (method blarf (fooclass)) were found in the database
No macro callees of (method blarf (fooclass)) were found in the database
The low-level programmer's interface to the cross-referencer is through the function
get-relation. It can take :wild
as a wildcard argument for queries of the
database. The next example returns a list of all forms that call +.
user(23): (xref:get-relation :calls :wild '+)
(calladdit (:top-level-form "example.cl"))
get-relation can be called without
a wildcard to determine the truth of a relation. It returns non-nil
if the
relation exists in the database.
user(27): (xref:get-relation :calls 'calladdit 'addit)
addit
user(28): (xref:get-relation :calls 'calladdit 'notaddit)
nil
Copyright (C) 1998-1999, Franz Inc., Berkeley, CA. All Rights Reserved.