Previous Up

6.3  Line coverage

The line coverage library provides a means to ascertain exactly how many times individual clauses are called during the evaluation of a query.

The library works by placing coverage counters at strategic points throughout the code being analysed. These counters are incremented each time the evaluation of a query passes them. There are three locations in which coverage counters can be inserted.

  1. At the beginning of a code block.
  2. Between predicate calls within a code block.
  3. At the end of a code block.

Locations where coverage counters can be placed A code block is defined to be a conjunction of predicate calls. ie. a sequence of goals separated by commas.

As previously mentioned, by default, code coverage counters are inserted before and after every subgoal in the code. For instance, in the clause

p :- q, r, s.

four counters would be inserted: before the call to q, between q and r, between r and s, and after s:

p :- point(1), q, point(2), r, point(3), s, point(4).

This is the most precise form provided. The counter values do not only show whether all code points were reached but also whether subgoals failed or aborted (in which case the counter before a subgoal will have a higher value than the counter after it). For example, the result of running the above code is:

p :- 43  q, 25  r, 25  s 0  .

which indicates that q was called 43 times, but succeeded only 25 times, r was called 25 times and succeeded always, and s was called 25 times and never succeeded. Coverage counts of zero are displayed in red (the final box) because they indicate unreached code. The format of the display is explained in the next section.

6.3.1  Compilation

In order to add the coverage counters to code, it must be compiled with the ccompile/1 predicate which can be found in the coverage library.

The predicate ccompile/1 (note the initial ‘c’ stands for coverage) can be used in place of the normal compile/1 predicate to compile a file with coverage counters.

Here we see the results of compiling the n-queens example given in the previous section.

?- coverage:ccompile(queens).
coverage: inserted 22 coverage counters into module eclipse
foo.ecl    compiled traceable 5744 bytes in 0.00 seconds

Yes (0.00s cpu)

Once compiled, predicates can be called as usual and will (by default) have no visible side effects. Internally however, the counters will be incremented as the execution progresses. To see this in action, consider issuing the following query having compiled the previously defined code using ccompile/1.

?- queens([1,2,3,4,5,6,7,8,9], Out).

The default behaviour of the ccompile/1 predicate is to place coverage counters as explained above, however such a level of detail may be unnecessary. If one is interested in reachability analysis the two argument predicate ccompile/2 can take a list of name:value pairs which can be used to control the exact manner in which coverage counters are inserted.

See ccompile/2 for a full list of the available flags.

In particular by specifying the option blocks_only:on, counters will only be inserted at the beginning and end of code blocks. Reusing the above example this would result in counters at point(1) and point(4).

p :- 43  q,  r,  s 0  .

This can be useful in tracking down unexpected failures by looking for exit counters which differ from entry counters, for example.

6.3.2  Results

To generate an html file containing the coverage counter results issue the following query.

?- coverage:result(queens).

This will create the result file coverage/queens.html which can be viewed using any browser. It contains a pretty-printed form of the source, annotated with the values of the code coverage counters as described above. An example is shown in figure 6.1.

Figure 6.1: Results of running queens([1,2,3,4,5,6,7,8,9],_)

For extra convenience the predicate result/0 is provided which will create results for all files which have been compiled with coverage counters.

Creates results for all files which have been compiled with coverage counters.
This predicate takes a single argument which is the name of the file to print the coverage counters for.
The result predicate has a two argument form, the second argument defining a number of flags which control (amongst other things)
  • The directory in which to create the results file. Default: coverage.
  • The format of the results file (html or text). Default: html.
See coverage library and pretty_printer library for more details

Figure 6.2: Result generating commands

Having generated and viewed results for one run, the coverage counters can be reset by calling

?- coverage:reset_counters.

Yes (0.00s cpu)

Previous Up