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