diff options
author | Marc R. Hoffmann <hoffmann@mountainminds.com> | 2012-12-23 10:09:46 +0100 |
---|---|---|
committer | Marc R. Hoffmann <hoffmann@mountainminds.com> | 2012-12-23 10:10:35 +0100 |
commit | 25fb3d4e2bac150627f091d310bafb6be465f37b (patch) | |
tree | 99a1b5da92484b3f58eb250e3e18074093bd6aca | |
parent | 27e32e433ba0ac1fdec9290d3454d5b369c969cc (diff) | |
download | jacoco-25fb3d4e2bac150627f091d310bafb6be465f37b.tar.gz |
Offline instrumentation APIs.
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; + } + +} |