CoverageTransformer.java revision c572b099c3b59e6e03d9bdaa2ba5e27d4845dcdf
1e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov/*******************************************************************************
2e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * Copyright (c) 2009, 2012 Mountainminds GmbH & Co. KG and Contributors
3e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * All rights reserved. This program and the accompanying materials
4e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * are made available under the terms of the Eclipse Public License v1.0
5e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * which accompanies this distribution, and is available at
6e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * http://www.eclipse.org/legal/epl-v10.html
7e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov *
8e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * Contributors:
9e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov *    Marc R. Hoffmann - initial API and implementation
10e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov *
11e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov *******************************************************************************/
12e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovpackage org.jacoco.agent.rt;
13e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
14e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport static java.lang.String.format;
15e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
16e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.lang.instrument.ClassFileTransformer;
17e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.lang.instrument.IllegalClassFormatException;
18e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.security.ProtectionDomain;
19e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
20e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.jacoco.core.instr.Instrumenter;
21e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.jacoco.core.runtime.AgentOptions;
22e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.jacoco.core.runtime.IRuntime;
23e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.jacoco.core.runtime.WildcardMatcher;
24e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
25e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov/**
26e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * Class file transformer to instrument classes for code coverage analysis.
27e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov */
28e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovpublic class CoverageTransformer implements ClassFileTransformer {
29e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
30e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private static final String AGENT_PREFIX;
31e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
32e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	static {
33e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		final String name = CoverageTransformer.class.getName();
34e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		AGENT_PREFIX = toVMName(name.substring(0, name.lastIndexOf('.')));
35e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
36e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
37e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private final IRuntime runtime;
38e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
39e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private final Instrumenter instrumenter;
40e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
41e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private final IExceptionLogger logger;
42e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
43e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private final WildcardMatcher includes;
44e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
45e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private final WildcardMatcher excludes;
46e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
47e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private final WildcardMatcher exclClassloader;
48e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
49e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private final ClassFileDumper classFileDumper;
50e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
51e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	/**
52e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * New transformer with the given delegates.
53e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *
54e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param runtime
55e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            coverage runtime
56e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param options
57e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            configuration options for the generator
58e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param logger
59e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            logger for exceptions during instrumentation
60e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 */
61e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	public CoverageTransformer(final IRuntime runtime,
62e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final AgentOptions options, final IExceptionLogger logger) {
63e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		this.runtime = runtime;
64e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		this.instrumenter = new Instrumenter(runtime);
65e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		this.logger = logger;
66e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		// Class names will be reported in VM notation:
67e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		includes = new WildcardMatcher(
68e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov				toWildcard(toVMName(options.getIncludes())));
69e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		excludes = new WildcardMatcher(
70e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov				toWildcard(toVMName(options.getExcludes())));
71e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		exclClassloader = new WildcardMatcher(
72e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov				toWildcard(options.getExclClassloader()));
73e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		classFileDumper = new ClassFileDumper(options.getClassDumpDir());
74e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
75e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
76e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	public byte[] transform(final ClassLoader loader, final String classname,
77e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final Class<?> classBeingRedefined,
78e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final ProtectionDomain protectionDomain,
79e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final byte[] classfileBuffer) throws IllegalClassFormatException {
80e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
81e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		if (!filter(loader, classname)) {
82e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			return null;
83e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		}
84e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
85e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		try {
86e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			classFileDumper.dump(classname, classfileBuffer);
87e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			if (classBeingRedefined != null) {
88e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov				// For redefined classes we must clear the execution data
89e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov				// reference as probes might have changed.
90e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov				runtime.disconnect(classBeingRedefined);
91e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			}
92e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			return instrumenter.instrument(classfileBuffer);
93e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		} catch (final Exception ex) {
94e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final IllegalClassFormatException wrapper = new IllegalClassFormatException(
95e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov					format("Error while instrumenting class %s.", classname));
96c572b099c3b59e6e03d9bdaa2ba5e27d4845dcdfMarc R. Hoffmann			wrapper.initCause(ex);
97e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			// Report this, as the exception is ignored by the JVM:
98e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			logger.logExeption(wrapper);
99c572b099c3b59e6e03d9bdaa2ba5e27d4845dcdfMarc R. Hoffmann			throw wrapper;
100e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		}
101e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
102e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
103e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	/**
104e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * Checks whether this class should be instrumented.
105e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *
106e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param loader
107e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            loader for the class
108e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @param classname
109e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 *            VM name of the class to check
110e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 * @return <code>true</code> if the class should be instrumented
111e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	 */
112e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	protected boolean filter(final ClassLoader loader, final String classname) {
113e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		// Don't instrument classes of the bootstrap loader:
114e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		return loader != null &&
115e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
116e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		!classname.startsWith(AGENT_PREFIX) &&
117e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
118e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		!exclClassloader.matches(loader.getClass().getName()) &&
119e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
120e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		includes.matches(classname) &&
121e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
122e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		!excludes.matches(classname);
123e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
124e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
125e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private String toWildcard(final String src) {
126e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		if (src.indexOf('|') != -1) {
127e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			final IllegalArgumentException ex = new IllegalArgumentException(
128e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov					"Usage of '|' as a list separator for JaCoCo agent options is deprecated and will not work in future versions - use ':' instead.");
129e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			logger.logExeption(ex);
130e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov			return src.replace('|', ':');
131e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		}
132e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		return src;
133e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
134e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
135e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	private static String toVMName(final String srcName) {
136e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov		return srcName.replace('.', '/');
137e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov	}
138e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov
139e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov}
140