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