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