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.
-
At the beginning of a code block.
- Between predicate calls within a code block.
- 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
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.
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.
-
result/0
- Creates
results for all files which have been compiled with coverage counters.
- result/1
- This
predicate takes a single argument which is the name of the file to
print the coverage counters for.
- result/2
-
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)