Home | History | Annotate | Download | only in doc
      1 <?xml version="1.0" encoding="UTF-8" ?>
      2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
      3 <html xmlns="http://www.w3.org/1999/xhtml" lang="en">
      4 <head>
      5   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
      6   <link rel="stylesheet" href=".resources/doc.css" charset="UTF-8" type="text/css" />
      7   <link rel="stylesheet" href="../coverage/.resources/prettify.css" charset="UTF-8" type="text/css" />
      8   <link rel="shortcut icon" href=".resources/report.gif" type="image/gif" />
      9   <script type="text/javascript" src="../coverage/.resources/prettify.js"></script>
     10   <title>JaCoCo - Implementation Design</title>
     11 </head>
     12 <body onload="prettyPrint()">
     13 
     14 <div class="breadcrumb">
     15   <a href="../index.html" class="el_report">JaCoCo</a> &gt;
     16   <a href="index.html" class="el_group">Documentation</a> &gt;
     17   <span class="el_source">Implementation Design</span>
     18 </div>
     19 <div id="content"> 
     20 
     21 <h1>Implementation Design</h1>
     22 
     23 <p>
     24   This is a unordered list of implementation design decisions. Each topic tries
     25   to follow this structure:
     26 </p>
     27 
     28 <ul>
     29   <li>Problem statement</li>
     30   <li>Proposed Solution</li>
     31   <li>Alternatives and Discussion</li>
     32 </ul>
     33 
     34 
     35 <h2>Coverage Analysis Mechanism</h2>
     36 
     37 <p class="intro">
     38   Coverage information has to be collected at runtime. For this purpose JaCoCo
     39   creates instrumented versions of the original class definitions. The
     40   instrumentation process happens on-the-fly during class loading using so
     41   called Java agents.  
     42 </p>
     43 
     44 <p>
     45   There are several different approaches to collect coverage information. For
     46   each approach different implementation techniques are known. The following
     47   diagram gives an overview with the techniques used by JaCoCo highlighted:
     48 </p>
     49 
     50 <img src=".resources/implementation.png" alt="Coverage Implementation Techniques"/>
     51 
     52 <p>
     53   Byte code instrumentation is very fast, can be implemented in pure Java and
     54   works with every Java VM. On-the-fly instrumentation with the Java agent
     55   hook can be added to the JVM without any modification of the target
     56   application.
     57 </p>
     58 
     59 <p>
     60   The Java agent hook requires at least 1.5 JVMs. Class files compiled with
     61   debug information (line numbers) allow for source code highlighting. Unluckily
     62   some Java language constructs get compiled to byte code that produces
     63   unexpected highlighting results, especially in case of implicitly generated
     64   code like default constructors or control structures for finally statements.
     65 </p>
     66 
     67 
     68 <h2>Coverage Agent Isolation</h2>
     69 
     70 <p class="intro">
     71   The Java agent is loaded by the application class loader. Therefore the
     72   classes of the agent live in the same name space like the application classes
     73   which can result in clashes especially with the third party library ASM. The
     74   JoCoCo build therefore moves all agent classes into a unique package.  
     75 </p>
     76 
     77 <p>
     78   The JaCoCo build renames all classes contained in the
     79   <code>jacocoagent.jar</code> into classes with a 
     80   <code>org.jacoco.agent.rt_&lt;randomid&gt;</code> prefix, including the
     81   required ASM library classes. The identifier is created from a random number.
     82   As the agent does not provide any API, no one should be affected by this
     83   renaming. This trick also allows that JaCoCo tests can be verified with
     84   JaCoCo.
     85 </p>
     86 
     87 
     88 <h2>Minimal Java Version</h2>
     89 
     90 <p class="intro">
     91   JaCoCo requires Java 1.5.
     92 </p>
     93 
     94 <p>
     95   The Java agent mechanism used for on-the-fly instrumentation became available
     96   with Java 1.5 VMs. Coding and testing with Java 1.5 language level is more
     97   efficient, less error-prone &ndash; and more fun than with older versions.
     98   JaCoCo will still allow to run against Java code compiled for these.
     99 </p>
    100 
    101 
    102 <h2>Byte Code Manipulation</h2>
    103 
    104 <p class="intro">
    105   Instrumentation requires mechanisms to modify and generate Java byte code.
    106   JaCoCo uses the ASM library for this purpose internally.
    107 </p>
    108 
    109 <p>
    110   Implementing the Java byte code specification would be an extensive and
    111   error-prone task. Therefore an existing library should be used. The
    112   <a href="http://asm.objectweb.org/">ASM</a> library is lightweight, easy to
    113   use and very efficient in terms of memory and CPU usage. It is actively
    114   maintained and includes as huge regression test suite. Its simplified BSD
    115   license is approved by the Eclipse Foundation for usage with EPL products.
    116 </p>
    117 
    118 <h2>Java Class Identity</h2>
    119 
    120 <p class="intro">
    121   Each class loaded at runtime needs a unique identity to associate coverage data with.
    122   JaCoCo creates such identities by a CRC64 hash code of the raw class definition.
    123 </p>
    124 
    125 <p>
    126   In multi-classloader environments the plain name of a class does not
    127   unambiguously identify a class. For example OSGi allows to use different
    128   versions of the same class to be loaded within the same VM. In complex
    129   deployment scenarios the actual version of the test target might be different
    130   from current development version. A code coverage report should guarantee that
    131   the presented figures are extracted from a valid test target. A hash code of
    132   the class definitions allows to differentiate between classes and versions of
    133   classes. The CRC64 hash computation is simple and fast resulting in a small 64
    134   bit identifier. 
    135 </p>
    136 
    137 <p>
    138   The same class definition might be loaded by class loaders which will result
    139   in different classes for the Java runtime system. For coverage analysis this
    140   distinction should be irrelevant. Class definitions might be altered by other
    141   instrumentation based technologies (e.g. AspectJ). In this case the hash code
    142   will change and identity gets lost. On the other hand code coverage analysis
    143   based on classes that have been somehow altered will produce unexpected
    144   results. The CRC64 code might produce so called <i>collisions</i>, i.e.
    145   creating the same hash code for two different classes. Although CRC64 is not
    146   cryptographically strong and collision examples can be easily computed, for
    147   regular class files the collision probability is very low. 
    148 </p>
    149 
    150 <h2>Coverage Runtime Dependency</h2>
    151 
    152 <p class="intro">
    153   Instrumented code typically gets a dependency to a coverage runtime which is
    154   responsible for collecting and storing execution data. JaCoCo uses JRE types
    155   only in generated instrumentation code. 
    156 </p>
    157 
    158 <p>
    159   Making a runtime library available to all instrumented classes can be a
    160   painful or impossible task in frameworks that use their own class loading
    161   mechanisms. Since Java 1.6 <code>java.lang.instrument.Instrumentation</code>
    162   has an API to extends the bootsstrap loader. As our minimum target is Java 1.5 
    163   JaCoCo decouples the instrumented classes and the coverage runtime through
    164   official JRE API types only. The instrumented classes communicate through the
    165   <code>Object.equals(Object)</code> method with the runtime. A instrumented
    166   class can retrieve its probe array instance with the following code. Note
    167   that only JRE APIs are used:   
    168 </p>
    169 
    170 
    171 <pre class="source lang-java linenums">
    172 Object access = ...                          // Retrieve instance
    173 
    174 Object[] args = new Object[3];
    175 args[0] = Long.valueOf(8060044182221863588); // class id 
    176 args[1] = "com/example/MyClass";             // class name
    177 args[2] = Integer.valueOf(24);               // probe count
    178 
    179 access.equals(args);
    180 
    181 boolean[] probes = (boolean[]) args[0];
    182 </pre>
    183 
    184 <p>
    185   The most tricky part takes place in line 1 and is not shown in the snippet
    186   above. The object instance providing access to the coverage runtime through
    187   its <code>equals()</code> method has to be obtained. Different approaches have
    188   been implemented and tested so far:
    189 </p>
    190 
    191 <ul>
    192   <li><b><code>SystemPropertiesRuntime</code></b>: This approach stores the
    193     object instance under a system property. This solution breaks the contract
    194     that system properties must only contain <code>java.lang.String</code>
    195     values and therefore causes trouble in applications that rely on this
    196     definition (e.g. Ant).</li>
    197   <li><b><code>LoggerRuntime</code></b>: Here we use a shared
    198     <code>java.util.logging.Logger</code> and communicate through the logging
    199     parameter array instead of a <code>equals()</code> method. The coverage
    200     runtime registers a custom <code>Handler</code> to receive the parameter
    201     array. This approach might break environments that install their own log
    202     managers (e.g. Glassfish).</li> 
    203   <li><b><code>URLStreamHandlerRuntime</code></b>: This runtime registers a
    204     <code>URLStreamHandler</code> for a "jacoco-xxxxx" protocol. Instrumented
    205     classes open a connection on this protocol. The returned connection object
    206     is the one that provides access to the coverage runtime through its
    207     <code>equals()</code> method. However to register the protocol the runtime
    208     needs to access internal members of the <code>java.net.URL</code> class.</li> 
    209   <li><b><code>ModifiedSystemClassRuntime</code></b>: This approach adds a
    210     public static field to an existing JRE class through instrumentation. Unlike
    211     the other methods above this is only possible for environments where a Java
    212     agent is active.</li> 
    213 </ul>
    214 
    215 <p>
    216   The current JaCoCo Java agent implementation uses the 
    217   <code>ModifiedSystemClassRuntime</code> adding a field to the class
    218   <code>java.sql.Types</code>.
    219 </p>
    220 
    221 
    222 <h2>Memory Usage</h2>
    223 
    224 <p class="intro">
    225   Coverage analysis for huge projects with several thousand classes or hundred
    226   thousand lines of code should be possible. To allow this with reasonable
    227   memory usage the coverage analysis is based on streaming patterns and
    228   "depth first" traversals.  
    229 </p>
    230 
    231 <p>
    232   The complete data tree of a huge coverage report is too big to fit into a
    233   reasonable heap memory configuration. Therefore the coverage analysis and
    234   report generation is implemented as "depth first" traversals. Which means that
    235   at any point in time only the following data has to be held in working memory:  
    236 </p>
    237 
    238 <ul>
    239   <li>A single class which is currently processed.</li>
    240   <li>The summary information of all parents of this class (package, groups).</li>
    241 </ul>
    242 
    243 <h2>Java Element Identifiers</h2>
    244 
    245 <p class="intro">
    246   The Java language and the Java VM use different String representation formats
    247   for Java elements. For example while a type reference in Java reads like 
    248   <code>java.lang.Object</code>, the VM references the same type as
    249   <code>Ljava/lang/Object;</code>. The JaCoCo API is based on VM identifiers only.
    250 </p>
    251 
    252 <p>
    253   Using VM identifiers directly does not cause any transformation overhead at
    254   runtime. There are several programming languages based on the Java VM that
    255   might use different notations. Specific transformations should therefore only
    256   happen at the user interface level, for example during report generation.
    257 </p>
    258 
    259 <h2>Modularization of the JaCoCo implementation</h2>
    260 
    261 <p class="intro">
    262   JaCoCo is implemented in several modules providing different functionality.
    263   These modules are provided as OSGi bundles with proper manifest files. But
    264   there are no dependencies on OSGi itself.
    265 </p>
    266 
    267 <p>
    268   Using OSGi bundles allows well defined dependencies at development time and
    269   at runtime in OSGi containers. As there are no dependencies on OSGi, the
    270   bundles can also be used like regular JAR files.  
    271 </p>
    272 
    273 </div>
    274 <div class="footer">
    275   <span class="right"><a href="@jacoco.home.url@">JaCoCo</a> @qualified.bundle.version@</span>
    276   <a href="license.html">Copyright</a> &copy; @copyright.years@ Mountainminds GmbH &amp; Co. KG and Contributors
    277 </div>
    278 
    279 </body>
    280 </html>
    281