This page explains few details how Clover code instrumentation works and how the coverage data is being collected at runtime.
This article might be helpful for you in case you have significant performance problems or your application runs in a restricted environment.
Every time Clover instruments the code (via <clover-instr/> or <clover-setup/> - both Java and Groovy) it records information about the code structure (packages, files, classes, method, statements, branches, test methods etc) into the Clover database. Because of fact that the same database can be used multiple times (for instance in case of incremental compilation; or when project has several modules compiled separately), it maintains history of changes in blocks named 'instrumentation session'.
Every instrumented class is enhanced by adding code which requests a Clover's coverage recorder instance. Such coverage recorder getter is called with some arguments, which allows to determine which Clover database file and which instrumentation session in this file contains information about the class structure (like indexes of statements, methods, branches etc).
As project contains many classes, Clover has an optimization so that some of these classes will share the same coverage recorder instance - exact strategy depends on selected recorder type - see below.
Diagram: code instrumentation process
Information about code structured is stored in Clover database. Every instrumented class contains
information about database (initstring) and the instrumentation session time stamp. Thanks to this
it's possible to map compiled class file to a corresponding source file (also in correct version -
Fixed Coverage Recorder
This is a default coverage recorder and we strongly recommend using it.
It's using an in-memory fixed-size long array for recording hit counts for methods, statements and branches. Calculation of the long array size requires access to the Clover Database (clover.db) at runtime, however. All classes which were compiled in the same instrumentation session (i.e. within the same <clover-instr> or javac or groovyc call) will share the same instance of the coverage recorder.
Growable Coverage Recorder
This recorder simplifies a deployment and test process as clover.db is not required at runtime. It's useful for cases like in-container tests, tests executed on application server, builds on remote agents or for Android applications.
It's using a dynamically resizeable two-dimensional long array for recording hit counts for methods. Thanks to this it does not require access to the Clover Database at runtime and this is its main advantage. It's performance is slightly lower than the Fixed Coverage Recorder due to memory allocation and two-level indexing. All classes which were compiled in the same instrumentation session (i.e. within the same <clover-instr> or javac or groovyc call) will share the same instance of the coverage recorder.
Shared Coverage Recorder
Use this coverage recorder only in case when:
- you have a large Grails project with hundreds of Domain Classes or Services and
- you instrument and run test classes and
- you have a significant performance problem related with coverage data writing and/or generating Clover reports
It's a modification of the Growable Coverage Recorder designed specially for Grails-based projects. It shares the same coverage recorder instance for every instrumented class which was compiled with the same database initstring and configuration settings (like flush policy). It means that it ignores instrumentation session timestamps.
Grails build system works in such way that it compiles every domain class and service class separately. As a consequence, Clover "sees" this as a separate instrumentation session. It means that in case of the fixed or growable coverage recorder it creates a separate instance of the recorder for every domain or service class.
It might become a performance problem if you have many such classes and you execute test methods, because end of every test method will force creation of the per-test coverage file (clover.db*.s) from all coverage recorder instances. For example:
- 500 domain classes * 1000 unit tests = 500 coverage recorders * 1000 snapshots = 500'000 files on disk
Note that the performance problem does not occur during normal application run or when test code is not instrumented. For example:
- 500 domain classes (normal app execution) = 500 coverage recorders * 1-2 snapshots (depends on flush policy) = 500-1000 files on disk
If you decide to use a Shared Coverage Recorder, you must keep in mind that:
- you cannot have multiple modules which have the same initstring value,
but they actually point to different files (it's a typical case if you have a multi-module Maven
project and you use the same relative initstring for each module)
- TIP: use an absolute path for initstrings or share a same database among modules
- you cannot deploy outdated class files, for which information in the
Clover database is not longer valid (as instrumentation timestamp is ignored);
- TIP: perform a full project build, deleting all old classes as well as Clover database and coverage recording files (see "Using shared coverage recorder" example for Grails)