blob: b8040f34c54dd286d94bb19eca3a857a61fde310 [file] [log] [blame]
Marc R. Hoffmanna2af15d2009-06-07 21:15:05 +00001<?xml version="1.0" encoding="ISO-8859-1" ?>
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=ISO-8859-1" />
Marc R. Hoffmann15888492009-07-30 11:46:53 +00006 <link rel="stylesheet" href=".resources/doc.css" charset="ISO-8859-1" type="text/css" />
Marc R. Hoffmanna760f322010-03-10 22:23:52 +00007 <link rel="stylesheet" href="../coverage/.resources/prettify.css" charset="ISO-8859-1" type="text/css" />
Marc R. Hoffmannd7d2f752010-05-06 21:12:31 +00008 <link rel="shortcut icon" href=".resources/report.gif" type="image/gif" />
Marc R. Hoffmanna760f322010-03-10 22:23:52 +00009 <script type="text/javascript" src="../coverage/.resources/prettify.js"></script>
Marc R. Hoffmanna2af15d2009-06-07 21:15:05 +000010 <title>JaCoCo - Implementation Design</title>
11</head>
Marc R. Hoffmanna760f322010-03-10 22:23:52 +000012<body onload="prettyPrint()">
Marc R. Hoffmanna2af15d2009-06-07 21:15:05 +000013
Marc R. Hoffmann15888492009-07-30 11:46:53 +000014<div class="breadcrumb">
Marc R. Hoffmannd7d2f752010-05-06 21:12:31 +000015 <a href="../index.html" class="el_report">JaCoCo</a> &gt;
Marc R. Hoffmann15888492009-07-30 11:46:53 +000016 <a href="index.html" class="el_group">Documentation</a> &gt;
17 <span class="el_source">Implementation Design</span>
18</div>
Marc R. Hoffmann17be2692010-02-02 05:44:47 +000019<div id="content">
Marc R. Hoffmann15888492009-07-30 11:46:53 +000020
21<h1>Implementation Design</h1>
Marc R. Hoffmanna2af15d2009-06-07 21:15:05 +000022
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
Marc R. Hoffmann15888492009-07-30 11:46:53 +000037<p class="intro">
Marc R. Hoffmanna2af15d2009-06-07 21:15:05 +000038 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<ul>
51 <li>Runtime Profiling
52 <ul>
53 <li>Java Virtual Machine Profiler Interface (JVMPI), until Java 1.4</li>
54 <li>Java Virtual Machine Tool Interface (JVMTI), since Java 1.5</li>
55 </ul>
56 </li>
Marc R. Hoffmanne52a0ef2009-06-16 20:28:45 +000057 <li><span class="high">Instrumentation*</span>
Marc R. Hoffmanna2af15d2009-06-07 21:15:05 +000058 <ul>
59 <li>Java Source Instrumentation</li>
Marc R. Hoffmannc4b20782009-10-02 13:28:46 +000060 <li><span class="high">Byte Code Instrumentation*</span>
Marc R. Hoffmanna2af15d2009-06-07 21:15:05 +000061 <ul>
62 <li>Offline
63 <ul>
64 <li>Replace Original Classes In-Place</li>
65 <li>Inject Instrumented Classes into the Class Path</li>
66 </ul>
67 </li>
Marc R. Hoffmanne52a0ef2009-06-16 20:28:45 +000068 <li><span class="high">On-The-Fly*</span>
Marc R. Hoffmanna2af15d2009-06-07 21:15:05 +000069 <ul>
70 <li>Special Classloader Implementions or Framework Specific Hooks</li>
Marc R. Hoffmanne52a0ef2009-06-16 20:28:45 +000071 <li><span class="high">Java Agent*</span></li>
Marc R. Hoffmanna2af15d2009-06-07 21:15:05 +000072 </ul>
73 </li>
74 </ul>
75 </li>
76 </ul>
77 </li>
78</ul>
79
80<p>
81 Byte code instrumentation is very fast, can be implemented in pure Java and
82 works with every Java VM. On-the-fly instrumentation with the Java agent
83 hook can be added to the JVM without any modification of the target
84 application.
85</p>
86
87<p>
Radek Libaad5fbc92009-10-26 13:26:53 +000088 The Java agent hook requires at least 1.5 JVMs. Class files compiled with
89 debug information (line numbers) allow for source code highlighting. Unluckily
90 some Java language constructs get compiled to byte code that produces
91 unexpected highlighting results, especially in case of implicitly generated
92 code like default constructors or control structures for finally statements.
Marc R. Hoffmanna2af15d2009-06-07 21:15:05 +000093</p>
94
Marc R. Hoffmann5267b6c2009-07-05 16:34:27 +000095
Marc R. Hoffmann5267b6c2009-07-05 16:34:27 +000096<h2>Coverage Agent Isolation</h2>
97
Marc R. Hoffmann15888492009-07-30 11:46:53 +000098<p class="intro">
Marc R. Hoffmann5267b6c2009-07-05 16:34:27 +000099 The Java agent is loaded by the application class loader. Therefore the
Radek Libaad5fbc92009-10-26 13:26:53 +0000100 classes of the agent live in the same name space like the application classes
Marc R. Hoffmann5267b6c2009-07-05 16:34:27 +0000101 which can result in clashes especially with the third party library ASM. The
102 JoCoCo build therefore moves all agent classes into a unique package.
103</p>
104
105<p>
106 The JaCoCo build renames all classes contained in the
107 <code>jacocoagent.jar</code> into classes with a
Marc R. Hoffmanna942c892010-03-10 21:33:26 +0000108 <code>org.jacoco.agent.rt_&lt;randomid&gt;</code> prefix, including the
109 required ASM library classes. The identifier is created from a random number.
110 As the agent does not provide any API, no one should be affected by this
111 renaming. This trick also allows that JaCoCo tests can be verified with
112 JaCoCo.
Marc R. Hoffmann5267b6c2009-07-05 16:34:27 +0000113</p>
114
115
Marc R. Hoffmanna2af15d2009-06-07 21:15:05 +0000116<h2>Minimal Java Version</h2>
117
Marc R. Hoffmann15888492009-07-30 11:46:53 +0000118<p class="intro">
Marc R. Hoffmanne52a0ef2009-06-16 20:28:45 +0000119 JaCoCo requires Java 1.5.
120</p>
121
122<p>
123 The Java agent mechanism used for on-the-fly instrumentation became available
Radek Libaad5fbc92009-10-26 13:26:53 +0000124 with Java 1.5 VMs. Coding and testing with Java 1.5 language level is more
125 efficient, less error-prone &ndash; and more fun than with older versions.
126 JaCoCo will still allow to run against Java code compiled for these.
Marc R. Hoffmanna2af15d2009-06-07 21:15:05 +0000127</p>
128
129
130<h2>Byte Code Manipulation</h2>
131
Marc R. Hoffmann15888492009-07-30 11:46:53 +0000132<p class="intro">
Marc R. Hoffmanne52a0ef2009-06-16 20:28:45 +0000133 Instrumentation requires mechanisms to modify and generate Java byte code.
Radek Libaad5fbc92009-10-26 13:26:53 +0000134 JaCoCo uses the ASM library for this purpose internally.
Marc R. Hoffmanna2af15d2009-06-07 21:15:05 +0000135</p>
136
Marc R. Hoffmanne52a0ef2009-06-16 20:28:45 +0000137<p>
Radek Libaad5fbc92009-10-26 13:26:53 +0000138 Implementing the Java byte code specification would be an extensive and
Marc R. Hoffmanne52a0ef2009-06-16 20:28:45 +0000139 error-prone task. Therefore an existing library should be used. The
140 <a href="http://asm.objectweb.org/">ASM</a> library is lightweight, easy to
141 use and very efficient in terms of memory and CPU usage. It is actively
142 maintained and includes as huge regression test suite. Its simplified BSD
143 license is approved by the Eclipse Foundation for usage with EPL products.
144</p>
Marc R. Hoffmanna2af15d2009-06-07 21:15:05 +0000145
146<h2>Java Class Identity</h2>
147
Marc R. Hoffmann15888492009-07-30 11:46:53 +0000148<p class="intro">
Marc R. Hoffmanna2af15d2009-06-07 21:15:05 +0000149 Each class loaded at runtime needs a unique identity to associate coverage data with.
150 JaCoCo creates such identities by a CRC64 hash code of the raw class definition.
151</p>
152
153<p>
154 In multi-classloader environments the plain name of a class does not
155 unambiguously identify a class. For example OSGi allows to use different
156 versions of the same class to be loaded within the same VM. In complex
157 deployment scenarios the actual version of the test target might be different
158 from current development version. A code coverage report should guarantee that
Marc R. Hoffmann5267b6c2009-07-05 16:34:27 +0000159 the presented figures are extracted from a valid test target. A hash code of
Radek Libaad5fbc92009-10-26 13:26:53 +0000160 the class definitions allows to differentiate between classes and versions of
161 classes. The CRC64 hash computation is simple and fast resulting in a small 64
Marc R. Hoffmann5267b6c2009-07-05 16:34:27 +0000162 bit identifier.
Marc R. Hoffmanna2af15d2009-06-07 21:15:05 +0000163</p>
164
165<p>
166 The same class definition might be loaded by class loaders which will result
167 in different classes for the Java runtime system. For coverage analysis this
168 distinction should be irrelevant. Class definitions might be altered by other
169 instrumentation based technologies (e.g. AspectJ). In this case the hash code
170 will change and identity gets lost. On the other hand code coverage analysis
171 based on classes that have been somehow altered will produce unexpected
Radek Libaad5fbc92009-10-26 13:26:53 +0000172 results. The CRC64 code might produce so called <i>collisions</i>, i.e.
Marc R. Hoffmanna2af15d2009-06-07 21:15:05 +0000173 creating the same hash code for two different classes. Although CRC64 is not
174 cryptographically strong and collision examples can be easily computed, for
175 regular class files the collision probability is very low.
176</p>
177
178<h2>Coverage Runtime Dependency</h2>
179
Marc R. Hoffmann15888492009-07-30 11:46:53 +0000180<p class="intro">
Marc R. Hoffmanne52a0ef2009-06-16 20:28:45 +0000181 Instrumented code typically gets a dependency to a coverage runtime which is
182 responsible for collecting and storing execution data. JaCoCo uses JRE types
Marc R. Hoffmanna942c892010-03-10 21:33:26 +0000183 only in generated instrumentation code.
Marc R. Hoffmanne52a0ef2009-06-16 20:28:45 +0000184</p>
185
186<p>
187 Making a runtime library available to all instrumented classes can be a
Radek Libaad5fbc92009-10-26 13:26:53 +0000188 painful or impossible task in frameworks that use their own class loading
Marc R. Hoffmann9263b7b2010-01-31 10:20:30 +0000189 mechanisms. Since Java 1.6 <code>java.lang.instrument.Instrumentation</code>
190 has an API to extends the bootsstrap loader. As our minimum target is Java 1.5
191 JaCoCo decouples the instrumented classes and the coverage runtime through
Marc R. Hoffmanna942c892010-03-10 21:33:26 +0000192 official JRE API types only. The instrumented classes communicate through the
193 <code>Object.equals(Object)</code> method with the runtime. A instrumented
194 class can retrieve its probe array instance with the following code. Note
195 that only JRE APIs are used:
196</p>
197
198
Marc R. Hoffmanna760f322010-03-10 22:23:52 +0000199<pre class="source lang-java">
Marc R. Hoffmanna942c892010-03-10 21:33:26 +0000200<span class="nr"> 1</span>Object access = ... // Retrieve instance
201<span class="nr"> 2</span>
202<span class="nr"> 3</span>Object[] args = new Object[3];
203<span class="nr"> 4</span>args[0] = Long.valueOf(8060044182221863588); // class id
204<span class="nr"> 5</span>args[1] = "com/example/MyClass"; // class name
205<span class="nr"> 6</span>args[2] = Integer.valueOf(24); // probe count
206<span class="nr"> 7</span>
207<span class="nr"> 8</span>access.equals(args);
208<span class="nr"> 9</span>
209<span class="nr"> 10</span>boolean[] probes = (boolean[]) args[0];
210</pre>
211
212<p>
213 The most tricky part takes place in line 1 and is not shown in the snippet
214 above. The object instance providing access to the coverage runtime through
215 its <code>equals()</code> method has to be obtained. Different approaches have
216 been implemented and tested so far:
Marc R. Hoffmanna2af15d2009-06-07 21:15:05 +0000217</p>
218
Marc R. Hoffmann402370f2009-08-10 14:02:23 +0000219<ul>
Marc R. Hoffmanna942c892010-03-10 21:33:26 +0000220 <li><b><code>SystemPropertiesRuntime</code></b>: This approach stores the
221 object instance under a system property. This solution breaks the contract
222 that system properties must only contain <code>java.lang.String</code>
223 values and therefore causes trouble in applications that rely on this
224 definition (e.g. Ant).</li>
Marc R. Hoffmann9263b7b2010-01-31 10:20:30 +0000225 <li><b><code>LoggerRuntime</code></b>: Here we use a shared
Marc R. Hoffmanna942c892010-03-10 21:33:26 +0000226 <code>java.util.logging.Logger</code> and communicate through the logging
227 parameter array instead of a <code>equals()</code> method. The coverage
228 runtime registers a custom <code>Handler</code> to receive the parameter
229 array. This approach might break environments that install their own log
Marc R. Hoffmann9263b7b2010-01-31 10:20:30 +0000230 managers (e.g. Glassfish).</li>
Marc R. Hoffmanna942c892010-03-10 21:33:26 +0000231 <li><b><code>ModifiedSystemClassRuntime</code></b>: This approach adds a
232 public static field to an existing JRE class through instrumentation. Unlike
233 the other methods above this is only possible for environments where a Java
Marc R. Hoffmann9263b7b2010-01-31 10:20:30 +0000234 agent is active.</li>
Marc R. Hoffmann402370f2009-08-10 14:02:23 +0000235</ul>
236
Marc R. Hoffmann9263b7b2010-01-31 10:20:30 +0000237<p>
238 The current JaCoCo Java agent implementation uses the
Marc R. Hoffmanna942c892010-03-10 21:33:26 +0000239 <code>ModifiedSystemClassRuntime</code> adding a field to the class
Marc R. Hoffmann6751fe42010-02-01 18:18:24 +0000240 <code>java.sql.Types</code>.
Marc R. Hoffmann9263b7b2010-01-31 10:20:30 +0000241</p>
242
Marc R. Hoffmann402370f2009-08-10 14:02:23 +0000243
Marc R. Hoffmann5267b6c2009-07-05 16:34:27 +0000244<h2>Memory Usage</h2>
245
Marc R. Hoffmann15888492009-07-30 11:46:53 +0000246<p class="intro">
Marc R. Hoffmann58d76212009-10-08 15:40:46 +0000247 Coverage analysis for huge projects with several thousand classes or hundred
248 thousand lines of code should be possible. To allow this with reasonable
249 memory usage the coverage analysis is based on streaming patterns and
250 "depth first" traversals.
Marc R. Hoffmann5267b6c2009-07-05 16:34:27 +0000251</p>
252
253<p>
Marc R. Hoffmann58d76212009-10-08 15:40:46 +0000254 The complete data tree of a huge coverage report is too big to fit into a
255 reasonable heap memory configuration. Therefore the coverage analysis and
256 report generation is implemented as "depth first" traversals. Which means that
Radek Libaad5fbc92009-10-26 13:26:53 +0000257 at any point in time only the following data has to be held in working memory:
Marc R. Hoffmann5267b6c2009-07-05 16:34:27 +0000258</p>
259
Marc R. Hoffmann58d76212009-10-08 15:40:46 +0000260<ul>
261 <li>A single class which is currently processed.</li>
262 <li>The summary information of all parents of this class (package, groups).</li>
263</ul>
264
Marc R. Hoffmann5267b6c2009-07-05 16:34:27 +0000265<h2>Java Element Identifiers</h2>
266
Marc R. Hoffmann15888492009-07-30 11:46:53 +0000267<p class="intro">
Marc R. Hoffmann5267b6c2009-07-05 16:34:27 +0000268 The Java language and the Java VM use different String representation formats
269 for Java elements. For example while a type reference in Java reads like
270 <code>java.lang.Object</code>, the VM references the same type as
271 <code>Ljava/lang/Object;</code>. The JaCoCo API is based on VM identifiers only.
272</p>
273
274<p>
275 Using VM identifiers directly does not cause any transformation overhead at
276 runtime. There are several programming languages based on the Java VM that
277 might use different notations. Specific transformations should therefore only
Radek Libaad5fbc92009-10-26 13:26:53 +0000278 happen at the user interface level, for example during report generation.
Marc R. Hoffmann5267b6c2009-07-05 16:34:27 +0000279</p>
280
281<h2>Modularization of the JaCoCo implementation</h2>
282
Marc R. Hoffmann15888492009-07-30 11:46:53 +0000283<p class="intro">
Marc R. Hoffmann5267b6c2009-07-05 16:34:27 +0000284 JaCoCo is implemented in several modules providing different functionality.
285 These modules are provided as OSGi bundles with proper manifest files. But
Radek Libaad5fbc92009-10-26 13:26:53 +0000286 there are no dependencies on OSGi itself.
Marc R. Hoffmann5267b6c2009-07-05 16:34:27 +0000287</p>
288
289<p>
Radek Libaad5fbc92009-10-26 13:26:53 +0000290 Using OSGi bundles allows well defined dependencies at development time and
Marc R. Hoffmann5267b6c2009-07-05 16:34:27 +0000291 at runtime in OSGi containers. As there are no dependencies on OSGi, the
Radek Libaad5fbc92009-10-26 13:26:53 +0000292 bundles can also be used like regular JAR files.
Marc R. Hoffmann5267b6c2009-07-05 16:34:27 +0000293</p>
294
Marc R. Hoffmann17be2692010-02-02 05:44:47 +0000295</div>
Marc R. Hoffmann15888492009-07-30 11:46:53 +0000296<div class="footer">
Marc R. Hoffmannb623ffb2010-05-06 19:48:08 +0000297 <span class="right"><a href="@jacoco.home.url@">JaCoCo</a> @qualified.bundle.version@</span>
Marc R. Hoffmanndf6ff962010-04-09 15:31:22 +0000298 <a href="license.html">Copyright</a> &copy; @copyright.years@ Mountainminds GmbH &amp; Co. KG and Contributors
Marc R. Hoffmann15888492009-07-30 11:46:53 +0000299</div>
Marc R. Hoffmanna2af15d2009-06-07 21:15:05 +0000300
301</body>
302</html>