1e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov/******************************************************************************* 2b9d1b54e300318b470d9fedccc69d75187016444Evgeny Mandrikov * Copyright (c) 2009, 2018 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 4826daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann private final boolean inclBootstrapClasses; 4926daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann 5026daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann private final boolean inclNoLocationClasses; 51310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann 52e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov /** 53e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * New transformer with the given delegates. 54e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * 55e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param runtime 56e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * coverage runtime 57e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param options 58e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * configuration options for the generator 59e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param logger 60e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * logger for exceptions during instrumentation 61e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov */ 62e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov public CoverageTransformer(final IRuntime runtime, 63e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final AgentOptions options, final IExceptionLogger logger) { 64e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov this.instrumenter = new Instrumenter(runtime); 65e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov this.logger = logger; 66e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov // Class names will be reported in VM notation: 678bcab03b77d20a4f33ae5aa24e56efb439b4308cMarc R. Hoffmann includes = new WildcardMatcher(toVMName(options.getIncludes())); 688bcab03b77d20a4f33ae5aa24e56efb439b4308cMarc R. Hoffmann excludes = new WildcardMatcher(toVMName(options.getExcludes())); 698bcab03b77d20a4f33ae5aa24e56efb439b4308cMarc R. Hoffmann exclClassloader = new WildcardMatcher(options.getExclClassloader()); 70e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov classFileDumper = new ClassFileDumper(options.getClassDumpDir()); 7126daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann inclBootstrapClasses = options.getInclBootstrapClasses(); 7226daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann inclNoLocationClasses = options.getInclNoLocationClasses(); 73e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 74e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 75e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov public byte[] transform(final ClassLoader loader, final String classname, 76e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final Class<?> classBeingRedefined, 77e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final ProtectionDomain protectionDomain, 78e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final byte[] classfileBuffer) throws IllegalClassFormatException { 79e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 80bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann // We do not support class retransformation: 8182b13485562ca4a212b7499920f85f22cf9f294bMarc R. Hoffmann if (classBeingRedefined != null) { 82bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann return null; 83bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann } 84bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann 8526daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann if (!filter(loader, classname, protectionDomain)) { 86e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov return null; 87e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 88e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 89e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov try { 90e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov classFileDumper.dump(classname, classfileBuffer); 91ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann return instrumenter.instrument(classfileBuffer, classname); 92e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } catch (final Exception ex) { 93e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final IllegalClassFormatException wrapper = new IllegalClassFormatException( 94ac07e252571819685d3f74cb69c90c23abd340a0Marc R. Hoffmann ex.getMessage()); 95c572b099c3b59e6e03d9bdaa2ba5e27d4845dcdfMarc R. Hoffmann wrapper.initCause(ex); 96e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov // Report this, as the exception is ignored by the JVM: 97e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov logger.logExeption(wrapper); 98c572b099c3b59e6e03d9bdaa2ba5e27d4845dcdfMarc R. Hoffmann throw wrapper; 99e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 100e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 101e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 102e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov /** 103e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * Checks whether this class should be instrumented. 104e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * 105e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param loader 106e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * loader for the class 107e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param classname 108e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * VM name of the class to check 10926daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann * @param protectionDomain 11026daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann * protection domain for the class 111e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @return <code>true</code> if the class should be instrumented 112e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov */ 11326daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann boolean filter(final ClassLoader loader, final String classname, 11426daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann final ProtectionDomain protectionDomain) { 115bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann if (loader == null) { 11626daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann if (!inclBootstrapClasses) { 117310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann return false; 118310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann } 119bc47c020906fde9ce162597cf499fb66e73920fbMarc R. Hoffmann } else { 12026daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann if (!inclNoLocationClasses && !hasSourceLocation(protectionDomain)) { 12126daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann return false; 12226daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann } 123310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann if (exclClassloader.matches(loader.getClass().getName())) { 124310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann return false; 125310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann } 126310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann } 127e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 128310b7d14546c8f5e49f5e99475fb6001331ac4caMarc R. Hoffmann return !classname.startsWith(AGENT_PREFIX) && 129e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 130e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov includes.matches(classname) && 131e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 132e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov !excludes.matches(classname); 133e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 134e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 13526daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann /** 13626daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann * Checks whether this protection domain is associated with a source 13726daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann * location. 13826daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann * 13926daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann * @param protectionDomain 14026daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann * protection domain to check (or <code>null</code>) 14126daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann * @return <code>true</code> if a source location is defined 14226daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann */ 14326daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann private boolean hasSourceLocation(final ProtectionDomain protectionDomain) { 14426daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann if (protectionDomain == null) { 14526daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann return false; 14626daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann } 14726daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann final CodeSource codeSource = protectionDomain.getCodeSource(); 14826daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann if (codeSource == null) { 14926daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann return false; 15026daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann } 15126daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann return codeSource.getLocation() != null; 15226daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann } 15326daee44414eeecb0a7a63d7f6784fcf4cfe32faMarc R. Hoffmann 154e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private static String toVMName(final String srcName) { 155e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov return srcName.replace('.', '/'); 156e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 157e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 158e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov} 159