Cross Reference Facility

The index for the Allegro CL Documentation is in index.htm. The documentation is described in introduction.htm.

This document contains the following sections:

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

1.0 Cross reference introduction

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).

1.2 The cross-reference database

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. 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.

1.3 When is the cross-reference information generated?

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 *record-xref-info* is true. However, in certain cases the specific value of that variable can be overridden locally as we describe next.

1.4 When is xref information in a fasl file added to the database?

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 *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 *load-xref-info* is true or false.

Of course, if you have a fasl file with xref info and you load it with *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 *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.

1.5 Things to note about the compiler and macros

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 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):

record-source-file was converted by the compiler to a macro call that expanded to forms that included a call to the internal function :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.

2.0 What is in and how to access the xref database

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:

(defun foo () (bar))
(defun foo (x) (apply #'bar x))

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.

2.1 Clearing the database

The function xref:discard-all-xref-info clears all stored information in the database.

2.2 General description of query functions and commands

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-callswho-indirectly-callsmacros-called-bywho-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 true 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.

2.3 The function-name and object arguments

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.

2.4 File information and the :in-files keyword argument

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 *record-source-file-info* and *load-source-file-info* are true. (See source-file-recording.htm 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.

2.5 The :in-functions 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.

2.6 The :inverse 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.)

3.0 A cross-referencer example

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.

user(22): :who-calls (method blarf (fooclass)) :inverse t
  
  (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 true 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-2000, Franz Inc. Berkeley, CA., USA. All rights reserved. Created 2000.10.5.