Overview

System Analyser is a simple application framework for analysing the properties of a system definition. The main feature of the framework is a declarative domain specific language called System Analysis Language (I can’t be bothered thinking of a more catchy name), which is supported by some services for analysis reports and system definition graphs.

Currently System Analyser will work with ASDF system definitions, but Mk-Defsystem support is planned for future releases.

To start this introduction will go through the basics of the System Analysis Language (SAL) and then move on to some of the domain specific services provided.

System Analysis Language

At the heart of SAL is a group of primitives that provide functionality very similar to set/list comprehension. If you are not familiar with set/list comprehension have a look here and here .

Set/List comprehension is an expression that returns a set of values that meet certain criteria. For the analysis of system definitions, set/list comprehension can be used to pick out modules from the system definition that have unwanted characteristics. More importantly set/list comprehension is declarative, ie it expresses what should be in the set, not how the set should be generated, and this makes it a powerful domain specific abstraction for system definition analysis.

Like set/list comprehension SAL provides the 3 primitives for building sets. These are:

  • quantifiers
  • generators
  • filters

The differences between the set/list comprehension provided in SAL and the more traditional versions are:

  1. The set built is a set of system module IDs
  2. The set generators draw the system modules IDs from a system definition graph, rather than some pre-defined range

The best way of describing the capabilities of SAL is to give a few examples.

Example 1

Let’s start with the simplest example, where we want to use System Analyser to find all modules that depend on another module that we have declared to be deprecated.


1    (for-some ((module (<-target-of sys-graph sys-name)))
2              (and (member module *deprecated-list* :test #'equalp)
3                   (not (eq '() (get-all-required-by-links-from-module sys-graph module)))))

On the left hand side of the example, I have provided line numbers to help explain the code. I’ll describe what’s happening line by line and provide an explanation of how all the parts work together at the end.

 

Line 1
(for-some ...) The outer most expression is a quantifier; in this case for-some is an existential quantifier. Each quantifier takes 2 arguments, a list of set generators and a filter
(module ...) This is a declared variable that will be bound to an element from the generated set. The variable can be referenced inside the filter
(<-target-of ...) <-target-of is a set generator returns the IDs of all the system modules that are a required to be built before the some specified module can be built. A set generator can take as many arguments as it requires. In this case it only requires 2
sys-graph This set generator argument is the system definition contained in a graph data structure
sys-name This set generator argument is the ID of the module whose build targets are being returned in the set. In this case we are passing in the ID of a top-level module, ie a system

 

Line 2
(and ...) This is the set comprehension filter, which is a predicate asserting what properties a system module must have to qualify for inclusion in the generated set. In this case the filter is a compound predicate combined through the and function
(member module *deprecated-list* :test #'equalp) This 1st predicate asserts that the module ID must match one of the IDs in some list (*deprecated-list*) representing the deprecated modules

 

Line 3
(not (eq '() (get-all-required-by-links-from-module sys-graph module))) This 2nd predicate asserts that we’re only interested in deprecated modules that are required by some other module

 

So what is this set comprehension expression trying to do? It is building a set of module IDs that are build targets of sys-name and have the properties of being deprecated and being required by other modules.

Now that we have the list of modules, what can with do with it? SAL provides other primitives to help report the analysis findings. Let’s explore this by continuing with the example used above:


1    (when-matches
2       (set (for-some ((module (<-target-of sys-graph sys-name)))
3                      (and (member module *deprecated-list* :test #'equalp)
4                           (not (eq '() (get-all-required-by-links-from-module sys-graph module))))))
5       (add-deprecated-chapters! sys-graph report set)))

Line 1
(when-matches ...) This is a conditional expression that takes 2 arguments – an expression that returns a list and an expression that is evaluated if the first argument returns a non-empty list
Line 2
(set ...) This is a declared variable that will be bound to the list returned by the set comprehension

The rest of line 2 and lines 3 & 4 were explained above

Line 5
(add-deprecated-chapters! sys-graph report set) This is a call to a function that takes the list of culprits (bound to the variable set that was declared in line 2), generates a deprecated report chapter for each of them and adds the chapters to the analysis report report. It will only be evaluated if the set generator returned a non-empty set

Overview
We now have a means to tie the module IDs generated by the set comprehension with a mechanism that can describe why they require attention in an analysis report. This example shows the core of SAL.

Example 2

The first example is rather simplistic and doesn’t highlight the true benefits of expressing system analysis in SAL. A more complex example will highlight this. In this example we want to find all modules that has one or more critical path dependencies whose weight is greater than some threshold.


1   (when-matches
2       (set (for-some* ((node (<-target-of sys-graph sys-name))
3                        (leaf (<-dependent-leaf-of sys-graph node)))
4                       (> (critical-path-weight node leaf) *weight-threshold*)))
5       (add-deep-dependency-chapters! sys-graph report set))

Again on the left hand side of the example, I have provided line numbers to help explain the code. I’ll describe what’s happening line by line and explain how all of the parts work together at the end.

 

Line 1
(when-matches ...) The same when-matches conditional from example1

 

Line 2
(set ...) The declared variable to bound to the return value from the set comprehension
(for-some* ...) This is an existential quantifier that nests each set generator passed as an argument. for-some and for-some* have the same relationship as let has with let*.
(node (<-target-of ...)) This is the same set generator used in the 1st example

 

Line 3
(leaf ...) This is a declared variable that will be bound to each module produced by the set generator to its right
(<-dependent-leaf-of ...) This is a set generator produces modules that are dependencies of the argument module, but are also leaves, ie modules that don’t have any dependencies themselves
sys-graph This set generator argument is the system definition contained in a graph data structure
node This set generator argument is the same variable that was declared in Line 2! This is possible because the quantifier is for-some* which nests the set generators.

 

Line 4
(> (critical-path-weight node leaf) *weight-threshold*) This predicate asserts whether the critical path weight between a node and one of it’s leaf depenedencies is greater than some threshold value. Notice that the arguments node and leaf are the variables declared in set generators of lines 2 & 3

 

Line 5
(add-deep-dependency-chapters! sys-graph report set) A call to another function that takes set and records them in the analysis report, but it records a differnt type of chapter (a deep dependency chapter) to the add-deprecated-chapters! function seen in the 1st example.

Overview
The key to understanding the semantics of example 2 is the nested existential quantifier for-some*. Due to the nesting <-dependent-leaf-of produces a set of leaf dependency modules for each module produced by <-target-of. As a result the predicate is checked against each combination the 2 set generators can produce.

For example if <-target-of generates module A & B and according to <-dependent-leaf-of module A’s leaf dependencies are modules W & X and for module B it’s leaf dependencies are modules Y & Z, the predicate will be checked against the following combination of modules:

  1. node = A, leaf = W
  2. node = A, leaf = X
  3. node = B, leaf = Y
  4. node = B, leaf = Z

If each of these combinations yeilds a true value from the predicate, the set generated is a list of module tuples:

((A W) (A X) (B Y) (B Z))

Other Features

The examples shown so far use an existential quantifier. SAL also provides the universal quantifiers for-all and for-all*. In set comprehension a universal quantifier asserts that all elements in the set pass the filter. In SAL for-all and for-all* also assert that every module in the generated set also passes the filter. If 1 or more modules returned by the set generators do not meet the filter then the universal quantifier will return an empty set, otherwise it will return the whole set.

Domain Services

At the moment the only 2 domain services provided
  • write-report! – writes a formatted analysis report to a file
  • read-asdf-sysdef – reads an ASDF system definition and structures the information into a system graph

There are many other services that could be added in future, such as:

  • drawing the system definition as a visual graph
  • highlighting modules in the visual graph that are culprits in the analysis report
  • persisting the system graph & analysis report to disk, which requires serialisation and deserialisation services
  • persisting the system graph & analysis report to a Berkeley database or a relational database
Revised on October 17, 2007 21:22 by kean? (172.16.0.254)