aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc R. Hoffmann <hoffmann@mountainminds.com>2012-12-23 10:09:46 +0100
committerMarc R. Hoffmann <hoffmann@mountainminds.com>2012-12-23 10:10:35 +0100
commit25fb3d4e2bac150627f091d310bafb6be465f37b (patch)
tree99a1b5da92484b3f58eb250e3e18074093bd6aca
parent27e32e433ba0ac1fdec9290d3454d5b369c969cc (diff)
downloadjacoco-25fb3d4e2bac150627f091d310bafb6be465f37b.tar.gz
Offline instrumentation APIs.
-rw-r--r--org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/AgentTest.java (renamed from org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/JacocoAgentTest.java)13
-rw-r--r--org.jacoco.agent.rt/pom.xml28
-rw-r--r--org.jacoco.agent.rt/src/org/jacoco/agent/rt/Agent.java (renamed from org.jacoco.agent.rt/src/org/jacoco/agent/rt/JacocoAgent.java)133
-rw-r--r--org.jacoco.agent.rt/src/org/jacoco/agent/rt/IExceptionLogger.java9
-rw-r--r--org.jacoco.agent.rt/src/org/jacoco/agent/rt/PreMain.java54
-rw-r--r--org.jacoco.agent.rt/src/org/jacoco/agent/rt/RT.java52
-rw-r--r--org.jacoco.build/pom.xml2
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/JaCoCoTest.java5
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/runtime/AgentOptionsTest.java44
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/runtime/OfflineInstrumentationAccessGeneratorTest.java150
-rw-r--r--org.jacoco.core/src/org/jacoco/core/JaCoCo.java4
-rw-r--r--org.jacoco.core/src/org/jacoco/core/jacoco.properties3
-rw-r--r--org.jacoco.core/src/org/jacoco/core/runtime/AgentOptions.java18
-rw-r--r--org.jacoco.core/src/org/jacoco/core/runtime/OfflineInstrumentationAccessGenerator.java58
14 files changed, 456 insertions, 117 deletions
diff --git a/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/JacocoAgentTest.java b/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/AgentTest.java
index 2727b17c..969caa41 100644
--- a/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/JacocoAgentTest.java
+++ b/org.jacoco.agent.rt.test/src/org/jacoco/agent/rt/AgentTest.java
@@ -21,13 +21,17 @@ import org.jacoco.core.runtime.AgentOptions;
import org.jacoco.core.runtime.AgentOptions.OutputMode;
import org.junit.Test;
-public class JacocoAgentTest {
+/**
+ * Unit tests for {@link Agent}.
+ */
+public class AgentTest {
+
@Test
public void shouldCreateController() {
AgentOptions options = new AgentOptions();
options.setOutput(OutputMode.file);
- JacocoAgent agent = new JacocoAgent(options, null);
+ Agent agent = new Agent(options, null);
assertTrue(agent.createAgentController() instanceof LocalController);
options.setOutput(OutputMode.tcpserver);
@@ -40,9 +44,4 @@ public class JacocoAgentTest {
assertTrue(agent.createAgentController() instanceof MBeanController);
}
- @Test
- public void shouldParseOptions() {
- JacocoAgent agent = new JacocoAgent("output=file", null);
- assertTrue(agent.createAgentController() instanceof LocalController);
- }
}
diff --git a/org.jacoco.agent.rt/pom.xml b/org.jacoco.agent.rt/pom.xml
index fa0c2831..bcf06294 100644
--- a/org.jacoco.agent.rt/pom.xml
+++ b/org.jacoco.agent.rt/pom.xml
@@ -42,26 +42,6 @@
<sourceDirectory>src</sourceDirectory>
<plugins>
- <!-- Generates random number for relocation of packages -->
- <plugin>
- <groupId>org.codehaus.groovy.maven</groupId>
- <artifactId>gmaven-plugin</artifactId>
- <executions>
- <execution>
- <phase>validate</phase>
- <goals>
- <goal>execute</goal>
- </goals>
- <configuration>
- <source>
- def id = Math.abs(new java.util.Random().nextInt())
- project.properties['rt'] = "rt_" + Integer.toString(id, Character.MAX_RADIX)
- </source>
- </configuration>
- </execution>
- </executions>
- </plugin>
-
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
@@ -78,21 +58,21 @@
<relocations>
<relocation>
<pattern>org.jacoco.agent.rt</pattern>
- <shadedPattern>org.jacoco.agent.${rt}</shadedPattern>
+ <shadedPattern>${jacoco.runtime.package.name}</shadedPattern>
</relocation>
<relocation>
<pattern>org.jacoco.core</pattern>
- <shadedPattern>org.jacoco.agent.${rt}.core</shadedPattern>
+ <shadedPattern>${jacoco.runtime.package.name}.core</shadedPattern>
</relocation>
<relocation>
<pattern>org.objectweb.asm</pattern>
- <shadedPattern>org.jacoco.agent.${rt}.asm</shadedPattern>
+ <shadedPattern>${jacoco.runtime.package.name}.asm</shadedPattern>
</relocation>
</relocations>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
- <Premain-Class>org.jacoco.agent.${rt}.JacocoAgent</Premain-Class>
+ <Premain-Class>${jacoco.runtime.package.name}.PreMain</Premain-Class>
</manifestEntries>
</transformer>
</transformers>
diff --git a/org.jacoco.agent.rt/src/org/jacoco/agent/rt/JacocoAgent.java b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/Agent.java
index 1f858b5c..67ccdccc 100644
--- a/org.jacoco.agent.rt/src/org/jacoco/agent/rt/JacocoAgent.java
+++ b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/Agent.java
@@ -11,7 +11,6 @@
*******************************************************************************/
package org.jacoco.agent.rt;
-import java.lang.instrument.Instrumentation;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -23,14 +22,40 @@ import org.jacoco.agent.rt.controller.TcpServerController;
import org.jacoco.core.runtime.AbstractRuntime;
import org.jacoco.core.runtime.AgentOptions;
import org.jacoco.core.runtime.AgentOptions.OutputMode;
-import org.jacoco.core.runtime.IRuntime;
-import org.jacoco.core.runtime.ModifiedSystemClassRuntime;
import org.jacoco.core.runtime.RuntimeData;
/**
- * The agent which is referred as the <code>Premain-Class</code>.
+ * The agent manages the life cycle of JaCoCo runtime.
*/
-public class JacocoAgent {
+public class Agent {
+
+ private static Agent singleton;
+
+ /**
+ * Returns a global instance which is already started. If the method is
+ * called the first time the instance is created with the given options.
+ *
+ * @param options
+ * options to configure the instance
+ * @return global instance
+ * @throws Exception
+ * in case of startup failures of the corresponding controller
+ */
+ public static synchronized Agent getInstance(final AgentOptions options)
+ throws Exception {
+ if (singleton == null) {
+ final Agent agent = new Agent(options, IExceptionLogger.SYSTEM_ERR);
+ agent.startup();
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+ agent.shutdown();
+ }
+ });
+ singleton = agent;
+ }
+ return singleton;
+ }
private final AgentOptions options;
@@ -38,6 +63,8 @@ public class JacocoAgent {
private IAgentController controller;
+ private final RuntimeData data;
+
/**
* Creates a new agent with the given agent options.
*
@@ -46,51 +73,57 @@ public class JacocoAgent {
* @param logger
* logger used by this agent
*/
- public JacocoAgent(final AgentOptions options, final IExceptionLogger logger) {
+ public Agent(final AgentOptions options, final IExceptionLogger logger) {
this.options = options;
this.logger = logger;
+ this.data = new RuntimeData();
}
/**
- * Creates a new agent with the given agent options string.
+ * Returns the runtime data object created by this agent
*
- * @param options
- * agent options as text string
- * @param logger
- * logger used by this agent
+ * @return runtime data for this agent instance
*/
- public JacocoAgent(final String options, final IExceptionLogger logger) {
- this(new AgentOptions(options), logger);
+ public RuntimeData getData() {
+ return data;
}
/**
* Initializes this agent.
*
- * @param inst
- * instrumentation services
* @throws Exception
* internal startup problem
*/
- public void init(final Instrumentation inst) throws Exception {
- final IRuntime runtime = createRuntime(inst);
- final RuntimeData data = new RuntimeData();
+ public void startup() throws Exception {
String sessionId = options.getSessionId();
if (sessionId == null) {
sessionId = createSessionId();
}
data.setSessionId(sessionId);
- runtime.startup(data);
- inst.addTransformer(new CoverageTransformer(runtime, options, logger));
controller = createAgentController();
controller.startup(options, data);
}
/**
+ * Shutdown the agent again.
+ */
+ public void shutdown() {
+ try {
+ if (options.getDumpOnExit()) {
+ controller.writeExecutionData();
+ }
+ controller.shutdown();
+ } catch (final Exception e) {
+ logger.logExeption(e);
+ }
+ }
+
+ /**
* Create controller implementation as given by the agent options.
*
* @return configured controller implementation
*/
- protected IAgentController createAgentController() {
+ IAgentController createAgentController() {
final OutputMode controllerType = options.getOutput();
switch (controllerType) {
case file:
@@ -116,62 +149,4 @@ public class JacocoAgent {
return host + "-" + AbstractRuntime.createRandomId();
}
- /**
- * Creates the specific coverage runtime implementation.
- *
- * @param inst
- * instrumentation services
- * @return coverage runtime instance
- * @throws Exception
- * creation problem
- */
- protected IRuntime createRuntime(final Instrumentation inst)
- throws Exception {
- return ModifiedSystemClassRuntime.createFor(inst, "java/util/UUID");
- }
-
- /**
- * Shutdown the agent again.
- */
- public void shutdown() {
- try {
- if (options.getDumpOnExit()) {
- controller.writeExecutionData();
- }
- controller.shutdown();
- } catch (final Exception e) {
- logger.logExeption(e);
- }
- }
-
- /**
- * This method is called by the JVM to initialize Java agents.
- *
- * @param options
- * agent options
- * @param inst
- * instrumentation callback provided by the JVM
- * @throws Exception
- * in case initialization fails
- */
- public static void premain(final String options, final Instrumentation inst)
- throws Exception {
-
- final JacocoAgent agent = new JacocoAgent(options,
- new IExceptionLogger() {
- public void logExeption(final Exception ex) {
- ex.printStackTrace();
- }
- });
-
- agent.init(inst);
-
- Runtime.getRuntime().addShutdownHook(new Thread() {
- @Override
- public void run() {
- agent.shutdown();
- }
- });
- }
-
}
diff --git a/org.jacoco.agent.rt/src/org/jacoco/agent/rt/IExceptionLogger.java b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/IExceptionLogger.java
index 6c7da4eb..8aa7c548 100644
--- a/org.jacoco.agent.rt/src/org/jacoco/agent/rt/IExceptionLogger.java
+++ b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/IExceptionLogger.java
@@ -18,6 +18,15 @@ package org.jacoco.agent.rt;
public interface IExceptionLogger {
/**
+ * Default implementation which dumps the stack trace to System.err.
+ */
+ IExceptionLogger SYSTEM_ERR = new IExceptionLogger() {
+ public void logExeption(final Exception ex) {
+ ex.printStackTrace();
+ }
+ };
+
+ /**
* Logs the given exception.
*
* @param ex
diff --git a/org.jacoco.agent.rt/src/org/jacoco/agent/rt/PreMain.java b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/PreMain.java
new file mode 100644
index 00000000..f6822147
--- /dev/null
+++ b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/PreMain.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2012 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.agent.rt;
+
+import java.lang.instrument.Instrumentation;
+
+import org.jacoco.core.runtime.AgentOptions;
+import org.jacoco.core.runtime.IRuntime;
+import org.jacoco.core.runtime.ModifiedSystemClassRuntime;
+
+/**
+ * The agent which is referred as the <code>Premain-Class</code>. The agent
+ * configuration is provided with the agent parameters in the command line.
+ */
+public class PreMain {
+
+ /**
+ * This method is called by the JVM to initialize Java agents.
+ *
+ * @param options
+ * agent options
+ * @param inst
+ * instrumentation callback provided by the JVM
+ * @throws Exception
+ * in case initialization fails
+ */
+ public static void premain(final String options, final Instrumentation inst)
+ throws Exception {
+
+ final AgentOptions agentOptions = new AgentOptions(options);
+
+ final Agent agent = Agent.getInstance(agentOptions);
+
+ final IRuntime runtime = createRuntime(inst);
+ runtime.startup(agent.getData());
+ inst.addTransformer(new CoverageTransformer(runtime, agentOptions,
+ IExceptionLogger.SYSTEM_ERR));
+ }
+
+ private static IRuntime createRuntime(final Instrumentation inst)
+ throws Exception {
+ return ModifiedSystemClassRuntime.createFor(inst, "java/util/UUID");
+ }
+
+}
diff --git a/org.jacoco.agent.rt/src/org/jacoco/agent/rt/RT.java b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/RT.java
new file mode 100644
index 00000000..87bbc190
--- /dev/null
+++ b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/RT.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2012 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.agent.rt;
+
+import org.jacoco.core.runtime.AgentOptions;
+import org.jacoco.core.runtime.RuntimeData;
+
+/**
+ * The API for classes instrumented in "offline" mode. The agent configuration
+ * is provided through system properties prefixed with <code>jacoco.</code>.
+ */
+public class RT {
+
+ private static final RuntimeData data;
+
+ static {
+ try {
+ final Agent agent = Agent.getInstance(new AgentOptions(
+ System.getProperties()));
+ data = agent.getData();
+ } catch (final Exception e) {
+ throw new RuntimeException("Error while creating JaCoCo Runtime", e);
+ }
+ }
+
+ /**
+ * API for offline instrumented classes.
+ *
+ * @param classid
+ * class identifier
+ * @param classname
+ * VM class name
+ * @param probecount
+ * probe count for this class
+ * @return probe array instance for this class
+ */
+ public static boolean[] getProbes(final long classid,
+ final String classname, final int probecount) {
+ return data.getExecutionData(Long.valueOf(classid), classname,
+ probecount).getProbes();
+ }
+
+}
diff --git a/org.jacoco.build/pom.xml b/org.jacoco.build/pom.xml
index a4ac8667..ab3d3463 100644
--- a/org.jacoco.build/pom.xml
+++ b/org.jacoco.build/pom.xml
@@ -117,6 +117,8 @@
<maven.build.timestamp.format>yyyyMMddhhmm</maven.build.timestamp.format>
<jacoco.home.url>http://www.eclemma.org/jacoco</jacoco.home.url>
+ <!-- TODO: create random id -->
+ <jacoco.runtime.package.name>org.jacoco.agent.rt_xxxxxx</jacoco.runtime.package.name>
<jvm.args></jvm.args>
<argLine>${jvm.args}</argLine>
diff --git a/org.jacoco.core.test/src/org/jacoco/core/JaCoCoTest.java b/org.jacoco.core.test/src/org/jacoco/core/JaCoCoTest.java
index 231e3433..a277bb19 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/JaCoCoTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/JaCoCoTest.java
@@ -30,4 +30,9 @@ public class JaCoCoTest {
assertNotNull(JaCoCo.HOMEURL);
}
+ @Test
+ public void testRUNTIMEPACKAGE() {
+ assertNotNull(JaCoCo.RUNTIMEPACKAGE);
+ }
+
}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/runtime/AgentOptionsTest.java b/org.jacoco.core.test/src/org/jacoco/core/runtime/AgentOptionsTest.java
index ea28d1cd..99a37100 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/runtime/AgentOptionsTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/runtime/AgentOptionsTest.java
@@ -17,6 +17,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.File;
+import java.util.Properties;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -44,11 +45,12 @@ public class AgentOptionsTest {
options.getExclClassloader());
assertNull(options.getSessionId());
assertTrue(options.getDumpOnExit());
- assertEquals(6300, options.getPort());
- assertNull(options.getAddress());
assertEquals(AgentOptions.OutputMode.file, options.getOutput());
- assertEquals("", options.toString());
+ assertNull(options.getAddress());
+ assertEquals(6300, options.getPort());
assertNull(options.getClassDumpDir());
+
+ assertEquals("", options.toString());
}
@Test
@@ -59,11 +61,41 @@ public class AgentOptionsTest {
@Test
public void testNullOptions() {
- AgentOptions options = new AgentOptions(null);
+ AgentOptions options = new AgentOptions((String) null);
assertEquals("", options.toString());
}
@Test
+ public void testPropertiesOptions() {
+ Properties properties = new Properties();
+ properties.put("jacoco.destfile", "/target/test/test.exec");
+ properties.put("jacoco.append", "false");
+ properties.put("jacoco.includes", "org.*:com.*");
+ properties.put("jacoco.excludes", "*Test");
+ properties.put("jacoco.exclclassloader", "org.jacoco.test.TestLoader");
+ properties.put("jacoco.sessionid", "testsession");
+ properties.put("jacoco.dumponexit", "false");
+ properties.put("jacoco.output", "tcpserver");
+ properties.put("jacoco.address", "remotehost");
+ properties.put("jacoco.port", "1234");
+ properties.put("jacoco.classdumpdir", "target/dump");
+
+ AgentOptions options = new AgentOptions(properties);
+
+ assertEquals("/target/test/test.exec", options.getDestfile());
+ assertFalse(options.getAppend());
+ assertEquals("org.*:com.*", options.getIncludes());
+ assertEquals("*Test", options.getExcludes());
+ assertEquals("org.jacoco.test.TestLoader", options.getExclClassloader());
+ assertEquals("testsession", options.getSessionId());
+ assertFalse(options.getDumpOnExit());
+ assertEquals(AgentOptions.OutputMode.tcpserver, options.getOutput());
+ assertEquals("remotehost", options.getAddress());
+ assertEquals(1234, options.getPort());
+ assertEquals("target/dump", options.getClassDumpDir());
+ }
+
+ @Test
public void testGetDestile() {
AgentOptions options = new AgentOptions("destfile=/var/test.exec");
assertEquals("/var/test.exec", options.getDestfile());
@@ -123,8 +155,8 @@ public class AgentOptionsTest {
@Test
public void testGetIncludes() {
- AgentOptions options = new AgentOptions("includes=org.*|com.*");
- assertEquals("org.*|com.*", options.getIncludes());
+ AgentOptions options = new AgentOptions("includes=org.*:com.*");
+ assertEquals("org.*:com.*", options.getIncludes());
}
@Test
diff --git a/org.jacoco.core.test/src/org/jacoco/core/runtime/OfflineInstrumentationAccessGeneratorTest.java b/org.jacoco.core.test/src/org/jacoco/core/runtime/OfflineInstrumentationAccessGeneratorTest.java
new file mode 100644
index 00000000..c2bf4f71
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/runtime/OfflineInstrumentationAccessGeneratorTest.java
@@ -0,0 +1,150 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2012 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.runtime;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import org.jacoco.core.JaCoCo;
+import org.jacoco.core.instr.MethodRecorder;
+import org.jacoco.core.internal.instr.InstrSupport;
+import org.jacoco.core.test.TargetLoader;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.GeneratorAdapter;
+import org.objectweb.asm.commons.Method;
+
+/**
+ * Unit tests for {@link OfflineInstrumentationAccessGenerator}.
+ */
+public class OfflineInstrumentationAccessGeneratorTest {
+
+ private IExecutionDataAccessorGenerator generator;
+
+ private static boolean[] probes;
+
+ // runtime stub
+ public static boolean[] getProbes(final long classid,
+ final String classname, final int probecount) {
+ return probes;
+ }
+
+ @BeforeClass
+ public static void setupClass() {
+ probes = new boolean[3];
+ }
+
+ @Before
+ public void setup() {
+ String name = getClass().getName().replace('.', '/');
+ generator = new OfflineInstrumentationAccessGenerator(name);
+ }
+
+ @Test
+ public void testRuntimeAccess() throws Exception {
+ ITarget target = generateAndInstantiateClass(123);
+ assertSame(probes, target.get());
+ }
+
+ @Test
+ public void testRuntimeClassName() throws Exception {
+ generator = new OfflineInstrumentationAccessGenerator();
+ MethodRecorder actual = new MethodRecorder();
+ generator.generateDataAccessor(987654321, "foo/Bar", 17,
+ actual.getVisitor());
+
+ MethodRecorder expected = new MethodRecorder();
+ expected.getVisitor().visitLdcInsn(Long.valueOf(987654321));
+ expected.getVisitor().visitLdcInsn("foo/Bar");
+ expected.getVisitor().visitIntInsn(Opcodes.BIPUSH, 17);
+ String rtname = JaCoCo.RUNTIMEPACKAGE.replace('.', '/') + "/RT";
+ expected.getVisitor().visitMethodInsn(Opcodes.INVOKESTATIC, rtname,
+ "getProbes", "(JLjava/lang/String;I)[Z");
+
+ assertEquals(expected, actual);
+ }
+
+ /**
+ * Creates a new class with the given id, loads this class and instantiates
+ * it. The constructor of the generated class will request the probe array
+ * from the access generator under test.
+ */
+ private ITarget generateAndInstantiateClass(int classid)
+ throws InstantiationException, IllegalAccessException {
+
+ final String className = "org/jacoco/test/targets/RuntimeTestTarget_"
+ + classid;
+ Type classType = Type.getObjectType(className);
+
+ final ClassWriter writer = new ClassWriter(0);
+ writer.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, className, null,
+ "java/lang/Object",
+ new String[] { Type.getInternalName(ITarget.class) });
+
+ writer.visitField(InstrSupport.DATAFIELD_ACC,
+ InstrSupport.DATAFIELD_NAME, InstrSupport.DATAFIELD_DESC, null,
+ null);
+
+ // Constructor
+ GeneratorAdapter gen = new GeneratorAdapter(writer.visitMethod(
+ Opcodes.ACC_PUBLIC, "<init>", "()V", null, new String[0]),
+ Opcodes.ACC_PUBLIC, "<init>", "()V");
+ gen.visitCode();
+ gen.loadThis();
+ gen.invokeConstructor(Type.getType(Object.class), new Method("<init>",
+ "()V"));
+ gen.loadThis();
+ final int size = generator.generateDataAccessor(classid, className, 2,
+ gen);
+ gen.putStatic(classType, InstrSupport.DATAFIELD_NAME,
+ Type.getObjectType(InstrSupport.DATAFIELD_DESC));
+ gen.returnValue();
+ gen.visitMaxs(size + 1, 0);
+ gen.visitEnd();
+
+ // get()
+ gen = new GeneratorAdapter(writer.visitMethod(Opcodes.ACC_PUBLIC,
+ "get", "()[Z", null, new String[0]), Opcodes.ACC_PUBLIC, "get",
+ "()[Z");
+ gen.visitCode();
+ gen.getStatic(classType, InstrSupport.DATAFIELD_NAME,
+ Type.getObjectType(InstrSupport.DATAFIELD_DESC));
+ gen.returnValue();
+ gen.visitMaxs(1, 0);
+ gen.visitEnd();
+
+ writer.visitEnd();
+
+ final TargetLoader loader = new TargetLoader(
+ className.replace('/', '.'), writer.toByteArray());
+ return (ITarget) loader.newTargetInstance();
+ }
+
+ /**
+ * With this interface access read coverage data of the generated class.
+ */
+ public interface ITarget {
+
+ /**
+ * Returns a reference to the probe array.
+ *
+ * @return the probe array
+ */
+ boolean[] get();
+
+ }
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/JaCoCo.java b/org.jacoco.core/src/org/jacoco/core/JaCoCo.java
index c79fd1d2..20c2cca3 100644
--- a/org.jacoco.core/src/org/jacoco/core/JaCoCo.java
+++ b/org.jacoco.core/src/org/jacoco/core/JaCoCo.java
@@ -24,11 +24,15 @@ public final class JaCoCo {
/** Absolute URL of the current JaCoCo home page */
public static final String HOMEURL;
+ /** Name of the runtime package of this build */
+ public static final String RUNTIMEPACKAGE;
+
static {
final ResourceBundle bundle = ResourceBundle
.getBundle("org.jacoco.core.jacoco");
VERSION = bundle.getString("VERSION");
HOMEURL = bundle.getString("HOMEURL");
+ RUNTIMEPACKAGE = bundle.getString("RUNTIMEPACKAGE");
}
private JaCoCo() {
diff --git a/org.jacoco.core/src/org/jacoco/core/jacoco.properties b/org.jacoco.core/src/org/jacoco/core/jacoco.properties
index d9c32698..09493370 100644
--- a/org.jacoco.core/src/org/jacoco/core/jacoco.properties
+++ b/org.jacoco.core/src/org/jacoco/core/jacoco.properties
@@ -1,2 +1,3 @@
VERSION=$qualified.bundle.version$
-HOMEURL=$jacoco.home.url$ \ No newline at end of file
+HOMEURL=$jacoco.home.url$
+RUNTIMEPACKAGE=$jacoco.runtime.package.name$ \ No newline at end of file
diff --git a/org.jacoco.core/src/org/jacoco/core/runtime/AgentOptions.java b/org.jacoco.core/src/org/jacoco/core/runtime/AgentOptions.java
index b3744f6b..1b45cd30 100644
--- a/org.jacoco.core/src/org/jacoco/core/runtime/AgentOptions.java
+++ b/org.jacoco.core/src/org/jacoco/core/runtime/AgentOptions.java
@@ -18,6 +18,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
+import java.util.Properties;
/**
* Utility to create and parse options for the runtime agent. Options are
@@ -197,6 +198,23 @@ public final class AgentOptions {
}
}
+ /**
+ * New instance read from the given {@link Properties} object. All keys are
+ * prefixed with <code>jacoco.</code>.
+ *
+ * @param properties
+ * {@link Properties} object to read configuration options from
+ */
+ public AgentOptions(final Properties properties) {
+ this();
+ for (final String key : VALID_OPTIONS) {
+ final String value = properties.getProperty("jacoco." + key);
+ if (value != null) {
+ setOption(key, value);
+ }
+ }
+ }
+
private void validateAll() {
validatePort(getPort());
getOutput();
diff --git a/org.jacoco.core/src/org/jacoco/core/runtime/OfflineInstrumentationAccessGenerator.java b/org.jacoco.core/src/org/jacoco/core/runtime/OfflineInstrumentationAccessGenerator.java
new file mode 100644
index 00000000..63d51bd5
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/runtime/OfflineInstrumentationAccessGenerator.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2012 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.runtime;
+
+import org.jacoco.core.JaCoCo;
+import org.jacoco.core.internal.instr.InstrSupport;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * This implementation of {@link IExecutionDataAccessorGenerator} generate a
+ * direct dependency to the JaCoCo runtime agent to initialize the runtime and
+ * obtain probe arrays. This generator is designed for offline instrumentation
+ * only.
+ */
+public class OfflineInstrumentationAccessGenerator implements
+ IExecutionDataAccessorGenerator {
+
+ private final String runtimeClassName;
+
+ /**
+ * Creates a new instance for offline instrumentation.
+ */
+ public OfflineInstrumentationAccessGenerator() {
+ this(JaCoCo.RUNTIMEPACKAGE.replace('.', '/') + "/RT");
+ }
+
+ /**
+ * Creates a new instance with the given runtime class name for testing
+ * purposes
+ *
+ * @param runtimeClassName
+ * VM name of the runtime class
+ */
+ OfflineInstrumentationAccessGenerator(final String runtimeClassName) {
+ this.runtimeClassName = runtimeClassName;
+ }
+
+ public int generateDataAccessor(final long classid, final String classname,
+ final int probecount, final MethodVisitor mv) {
+ mv.visitLdcInsn(Long.valueOf(classid));
+ mv.visitLdcInsn(classname);
+ InstrSupport.push(mv, probecount);
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeClassName, "getProbes",
+ "(JLjava/lang/String;I)[Z");
+ return 4;
+ }
+
+}