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 *******************************************************************************/ 12e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovpackage org.jacoco.core.runtime; 13e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 14e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport static java.lang.String.format; 15e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 16e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.lang.instrument.ClassFileTransformer; 17e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.lang.instrument.IllegalClassFormatException; 18e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.lang.instrument.Instrumentation; 19e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.lang.reflect.Field; 20e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport java.security.ProtectionDomain; 21e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 229f4ef8dcffb3acbcd066ec073d87de65eeb06b48Marc R. Hoffmannimport org.jacoco.core.JaCoCo; 23e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.objectweb.asm.ClassReader; 24e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.objectweb.asm.ClassVisitor; 25e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.objectweb.asm.ClassWriter; 26e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.objectweb.asm.MethodVisitor; 27e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovimport org.objectweb.asm.Opcodes; 28e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 29e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov/** 30e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * This {@link IRuntime} implementation works with a modified system class. A 31e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * new static method is added to a bootstrap class that will be used by 32e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * instrumented classes. As the system class itself needs to be instrumented 33e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * this runtime requires a Java agent. 34e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov */ 35e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikovpublic class ModifiedSystemClassRuntime extends AbstractRuntime { 36e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 37e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private static final String ACCESS_FIELD_TYPE = "Ljava/lang/Object;"; 38e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 39e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private final Class<?> systemClass; 40e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 41e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private final String systemClassName; 42e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 43e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private final String accessFieldName; 44e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 45e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov /** 46e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * Creates a new runtime based on the given class and members. 47e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * 48e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param systemClass 49e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * system class that contains the execution data 50e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param accessFieldName 51e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * name of the public static runtime access field 52e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * 53e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov */ 54e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov public ModifiedSystemClassRuntime(final Class<?> systemClass, 55e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final String accessFieldName) { 56e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov super(); 57e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov this.systemClass = systemClass; 58e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov this.systemClassName = systemClass.getName().replace('.', '/'); 59e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov this.accessFieldName = accessFieldName; 60e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 61e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 6227e32e433ba0ac1fdec9290d3454d5b369c969ccMarc R. Hoffmann @Override 6327e32e433ba0ac1fdec9290d3454d5b369c969ccMarc R. Hoffmann public void startup(final RuntimeData data) throws Exception { 6427e32e433ba0ac1fdec9290d3454d5b369c969ccMarc R. Hoffmann super.startup(data); 65e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final Field field = systemClass.getField(accessFieldName); 6627e32e433ba0ac1fdec9290d3454d5b369c969ccMarc R. Hoffmann field.set(null, data); 67e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 68e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 69e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov public void shutdown() { 70e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov // nothing to do 71e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 72e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 73e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov public int generateDataAccessor(final long classid, final String classname, 74e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final int probecount, final MethodVisitor mv) { 75e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 76e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov mv.visitFieldInsn(Opcodes.GETSTATIC, systemClassName, accessFieldName, 77e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov ACCESS_FIELD_TYPE); 78e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 7927e32e433ba0ac1fdec9290d3454d5b369c969ccMarc R. Hoffmann RuntimeData.generateAccessCall(classid, classname, probecount, mv); 80e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 81e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov return 6; 82e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 83e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 84e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov /** 85e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * Creates a new {@link ModifiedSystemClassRuntime} using the given class as 86e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * the data container. Members are creates with internal default names. The 87e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * given class must not have been loaded before by the agent. 88e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * 89e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param inst 90e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * instrumentation interface 91e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param className 92e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * VM name of the class to use 93e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @return new runtime instance 94e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * 95e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @throws ClassNotFoundException 96e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * id the given class can not be found 97e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov */ 98e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov public static IRuntime createFor(final Instrumentation inst, 99e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final String className) throws ClassNotFoundException { 100e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov return createFor(inst, className, "$jacocoAccess"); 101e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 102e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 103e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov /** 104e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * Creates a new {@link ModifiedSystemClassRuntime} using the given class as 105e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * the data container. The given class must not have been loaded before by 106e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * the agent. 107e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * 108e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param inst 109e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * instrumentation interface 110e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param className 111e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * VM name of the class to use 112e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param accessFieldName 113e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * name of the added runtime access field 114e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @return new runtime instance 115e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * 116e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @throws ClassNotFoundException 117e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * id the given class can not be found 118e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov */ 119e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov public static IRuntime createFor(final Instrumentation inst, 120e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final String className, final String accessFieldName) 121e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov throws ClassNotFoundException { 122e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final ClassFileTransformer transformer = new ClassFileTransformer() { 123e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov public byte[] transform(final ClassLoader loader, 124e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final String name, final Class<?> classBeingRedefined, 125e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final ProtectionDomain protectionDomain, final byte[] source) 126e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov throws IllegalClassFormatException { 127e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov if (name.equals(className)) { 128e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov return instrument(source, accessFieldName); 129e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 130e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov return null; 131e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 132e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov }; 133e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov inst.addTransformer(transformer); 134e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final Class<?> clazz = Class.forName(className.replace('/', '.')); 135e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov inst.removeTransformer(transformer); 136e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov try { 137e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov clazz.getField(accessFieldName); 138e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } catch (final NoSuchFieldException e) { 139e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov throw new RuntimeException(format( 140e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov "Class %s could not be instrumented.", className), e); 141e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 142e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov return new ModifiedSystemClassRuntime(clazz, accessFieldName); 143e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 144e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 145e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov /** 146e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * Adds the static access method and data field to the given class 147e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * definition. 148e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * 149e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param source 150e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * class definition source 151e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @param accessFieldName 152e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * name of the runtime access field 153e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov * @return instrumented version with added members 154e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov */ 155e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov public static byte[] instrument(final byte[] source, 156e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final String accessFieldName) { 157e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final ClassReader reader = new ClassReader(source); 158e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final ClassWriter writer = new ClassWriter(reader, 0); 1599f4ef8dcffb3acbcd066ec073d87de65eeb06b48Marc R. Hoffmann reader.accept(new ClassVisitor(JaCoCo.ASM_API_VERSION, writer) { 160e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 161e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov @Override 162e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov public void visitEnd() { 163e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov createDataField(cv, accessFieldName); 164e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov super.visitEnd(); 165e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 166e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 167e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov }, ClassReader.EXPAND_FRAMES); 168e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov return writer.toByteArray(); 169e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 170e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 171e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov private static void createDataField(final ClassVisitor visitor, 172e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov final String dataField) { 173e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov visitor.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC 174e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_TRANSIENT, dataField, 175e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov ACCESS_FIELD_TYPE, null, null); 176e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov } 177e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov 178e69ba4dbb015949c5d84ba7bbb0b53efac28bb23Evgeny Mandrikov} 179