CoverageTransformer.java
1/*******************************************************************************
2 * Copyright (c) 2009, 2010 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;
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.CRC64;
21import org.jacoco.core.instr.Instrumenter;
22import org.jacoco.core.runtime.AgentOptions;
23import org.jacoco.core.runtime.IRuntime;
24import org.jacoco.core.runtime.WildcardMatcher;
25
26/**
27 * Class file transformer to instrument classes for code coverage analysis.
28 *
29 * @author Marc R. Hoffmann
30 * @version 0.4.1.20101007204400
31 */
32public class CoverageTransformer implements ClassFileTransformer {
33
34 private static final String AGENT_PREFIX;
35
36 static {
37 final String name = CoverageTransformer.class.getName();
38 AGENT_PREFIX = toVMName(name.substring(0, name.lastIndexOf('.')));
39 }
40
41 private final IExceptionLogger logger;
42
43 private final Instrumenter instrumenter;
44
45 private final WildcardMatcher includes;
46
47 private final WildcardMatcher excludes;
48
49 private final WildcardMatcher exclClassloader;
50
51 public CoverageTransformer(IRuntime runtime, AgentOptions options,
52 final IExceptionLogger logger) {
53 this.instrumenter = new Instrumenter(runtime);
54 this.logger = logger;
55 // Class names will be reported in VM notation:
56 includes = new WildcardMatcher(toVMName(options.getIncludes()));
57 excludes = new WildcardMatcher(toVMName(options.getExcludes()));
58 exclClassloader = new WildcardMatcher(options.getExclClassloader());
59 }
60
61 public byte[] transform(ClassLoader loader, String classname,
62 Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
63 byte[] classfileBuffer) throws IllegalClassFormatException {
64
65 if (!filter(loader, classname)) {
66 return null;
67 }
68
69 try {
70 return instrumenter.instrument(classfileBuffer);
71 } catch (Throwable t) {
72 final Long id = Long.valueOf(CRC64.checksum(classfileBuffer));
73 final String msg = "Error while instrumenting class %s (id=%016x).";
74 final IllegalClassFormatException ex = new IllegalClassFormatException(
75 format(msg, classname, id));
76 ex.initCause(t);
77 // Report this, as the exception is ignored by the JVM:
78 logger.logExeption(ex);
79 throw ex;
80 }
81 }
82
83 /**
84 * Checks whether this class should be instrumented.
85 *
86 * @param loader
87 * loader for the class
88 * @return <code>true</code> if the class should be instrumented
89 */
90 protected boolean filter(ClassLoader loader, String classname) {
91 // Don't instrument classes of the bootstrap loader:
92 return loader != null &&
93
94 !classname.startsWith(AGENT_PREFIX) &&
95
96 !exclClassloader.matches(loader.getClass().getName()) &&
97
98 includes.matches(classname) &&
99
100 !excludes.matches(classname);
101 }
102
103 private static String toVMName(String srcName) {
104 return srcName.replace('.', '/');
105 }
106
107}