1e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov/******************************************************************************* 2398ee59bebad6835dab57b60157eff16d511709eMarc R. Hoffmann * Copyright (c) 2009, 2015 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 *******************************************************************************/ 12ae1034c608eeca9765a43bec34bcb8e5bf23eaffMarc R. Hoffmannpackage org.jacoco.agent.rt.internal; 13e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 14e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.lang.instrument.ClassFileTransformer; 15e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.lang.instrument.IllegalClassFormatException; 16bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmannimport java.security.CodeSource; 17e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.security.ProtectionDomain; 18e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 19e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.jacoco.core.instr.Instrumenter; 20e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.jacoco.core.runtime.AgentOptions; 21e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.jacoco.core.runtime.IRuntime; 22e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.jacoco.core.runtime.WildcardMatcher; 23e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 24e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov/** 25e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * Class file transformer to instrument classes for code coverage analysis. 26e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov */ 27e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovpublic class CoverageTransformer implements ClassFileTransformer { 28e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 29e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private static final String AGENT_PREFIX; 30e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 31e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov static { 32e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final String name = CoverageTransformer.class.getName(); 33e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov AGENT_PREFIX = toVMName(name.substring(0, name.lastIndexOf('.'))); 34e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 35e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 36e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private final Instrumenter instrumenter; 37e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 38e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private final IExceptionLogger logger; 39e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 40e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private final WildcardMatcher includes; 41e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 42e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private final WildcardMatcher excludes; 43e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 44e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private final WildcardMatcher exclClassloader; 45e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 46e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private final ClassFileDumper classFileDumper; 47e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 48310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann private final boolean includeBootstrapClasses; 49310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann 50e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov /** 51e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * New transformer with the given delegates. 52e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * 53e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param runtime 54e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * coverage runtime 55e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param options 56e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * configuration options for the generator 57e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param logger 58e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * logger for exceptions during instrumentation 59e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov */ 60e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov public CoverageTransformer(final IRuntime runtime, 61e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final AgentOptions options, final IExceptionLogger logger) { 62e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov this.instrumenter = new Instrumenter(runtime); 63e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov this.logger = logger; 64e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov // Class names will be reported in VM notation: 658bcab03b77d20a4f33ae5aa24e56efb439b4308cMarc R. Hoffmann includes = new WildcardMatcher(toVMName(options.getIncludes())); 668bcab03b77d20a4f33ae5aa24e56efb439b4308cMarc R. Hoffmann excludes = new WildcardMatcher(toVMName(options.getExcludes())); 678bcab03b77d20a4f33ae5aa24e56efb439b4308cMarc R. Hoffmann exclClassloader = new WildcardMatcher(options.getExclClassloader()); 68e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov classFileDumper = new ClassFileDumper(options.getClassDumpDir()); 691d8389bf86f8a49d31bb5182b3ec1592e2f7289aMarc R. Hoffmann includeBootstrapClasses = options.getInclBootstrapClasses(); 70e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 71e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 72e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov public byte[] transform(final ClassLoader loader, final String classname, 73e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final Class<?> classBeingRedefined, 74e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final ProtectionDomain protectionDomain, 75e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final byte[] classfileBuffer) throws IllegalClassFormatException { 76e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 77bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann // We do not support class retransformation: 7882b13485562ca4a212b7499920f85f22cf9f294bMarc R. Hoffmann if (classBeingRedefined != null) { 79bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann return null; 80bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann } 81bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann 82bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann // We exclude dynamically created non-bootstrap classes. 83bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann if (loader != null && !hasSourceLocation(protectionDomain)) { 8482b13485562ca4a212b7499920f85f22cf9f294bMarc R. Hoffmann return null; 8582b13485562ca4a212b7499920f85f22cf9f294bMarc R. Hoffmann } 8682b13485562ca4a212b7499920f85f22cf9f294bMarc R. Hoffmann 87e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov if (!filter(loader, classname)) { 88e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov return null; 89e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 90e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 91e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov try { 92e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov classFileDumper.dump(classname, classfileBuffer); 93ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann return instrumenter.instrument(classfileBuffer, classname); 94e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } catch (final Exception ex) { 95e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final IllegalClassFormatException wrapper = new IllegalClassFormatException( 96ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann ex.getMessage()); 97c572b099c3b59e6e03d9bdaa2ba5e27d4845dcdfMarc R. Hoffmann wrapper.initCause(ex); 98e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov // Report this, as the exception is ignored by the JVM: 99e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov logger.logExeption(wrapper); 100c572b099c3b59e6e03d9bdaa2ba5e27d4845dcdfMarc R. Hoffmann throw wrapper; 101e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 102e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 103e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 104e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov /** 105bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann * Checks whether this protection domain is associated with a source 106bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann * location. 107bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann * 108bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann * @param protectionDomain 109bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann * protection domain to check (or <code>null</code>) 110bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann * @return <code>true</code> if a source location is defined 111bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann */ 112bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann boolean hasSourceLocation(final ProtectionDomain protectionDomain) { 113bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann if (protectionDomain == null) { 114bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann return false; 115bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann } 116bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann final CodeSource codeSource = protectionDomain.getCodeSource(); 117bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann if (codeSource == null) { 118bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann return false; 119bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann } 120bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann return codeSource.getLocation() != null; 121bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann } 122bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann 123bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann /** 124e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * Checks whether this class should be instrumented. 125e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * 126e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param loader 127e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * loader for the class 128e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param classname 129e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * VM name of the class to check 130e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @return <code>true</code> if the class should be instrumented 131e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov */ 132bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann boolean filter(final ClassLoader loader, final String classname) { 133bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann if (loader == null) { 134bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann if (!includeBootstrapClasses) { 135310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann return false; 136310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann } 137bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann } else { 138310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann if (exclClassloader.matches(loader.getClass().getName())) { 139310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann return false; 140310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann } 141310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann } 142e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 143310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann return !classname.startsWith(AGENT_PREFIX) && 144e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 145e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov includes.matches(classname) && 146e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 147e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov !excludes.matches(classname); 148e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 149e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 150e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private static String toVMName(final String srcName) { 151e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov return srcName.replace('.', '/'); 152e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 153e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 154e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov} 155