1 /******************************************************************************* 2 * Copyright (c) 2009, 2021 Mountainminds GmbH & Co. KG and Contributors 3 * This program and the accompanying materials are made available under 4 * the terms of the Eclipse Public License 2.0 which is available at 5 * http://www.eclipse.org/legal/epl-2.0 6 * 7 * SPDX-License-Identifier: EPL-2.0 8 * 9 * Contributors: 10 * Marc R. Hoffmann - initial API and implementation 11 * 12 *******************************************************************************/ 13 package org.jacoco.agent.rt.internal; 14 15 import java.lang.instrument.Instrumentation; 16 import java.util.Collections; 17 import java.util.Map; 18 import java.util.Set; 19 20 import org.jacoco.core.runtime.AgentOptions; 21 import org.jacoco.core.runtime.IRuntime; 22 import org.jacoco.core.runtime.InjectedClassRuntime; 23 import org.jacoco.core.runtime.ModifiedSystemClassRuntime; 24 25 /** 26 * The agent which is referred as the <code>Premain-Class</code>. The agent 27 * configuration is provided with the agent parameters in the command line. 28 */ 29 public final class PreMain { 30 PreMain()31 private PreMain() { 32 // no instances 33 } 34 35 /** 36 * This method is called by the JVM to initialize Java agents. 37 * 38 * @param options 39 * agent options 40 * @param inst 41 * instrumentation callback provided by the JVM 42 * @throws Exception 43 * in case initialization fails 44 */ premain(final String options, final Instrumentation inst)45 public static void premain(final String options, final Instrumentation inst) 46 throws Exception { 47 48 final AgentOptions agentOptions = new AgentOptions(options); 49 50 final Agent agent = Agent.getInstance(agentOptions); 51 52 final IRuntime runtime = createRuntime(inst); 53 runtime.startup(agent.getData()); 54 inst.addTransformer(new CoverageTransformer(runtime, agentOptions, 55 IExceptionLogger.SYSTEM_ERR)); 56 } 57 createRuntime(final Instrumentation inst)58 private static IRuntime createRuntime(final Instrumentation inst) 59 throws Exception { 60 61 if (redefineJavaBaseModule(inst)) { 62 return new InjectedClassRuntime(Object.class, "$JaCoCo"); 63 } 64 65 return ModifiedSystemClassRuntime.createFor(inst, 66 "java/lang/UnknownError"); 67 } 68 69 /** 70 * Opens {@code java.base} module for {@link InjectedClassRuntime} when 71 * executed on Java 9 JREs or higher. 72 * 73 * @return <code>true</code> when running on Java 9 or higher, 74 * <code>false</code> otherwise 75 * @throws Exception 76 * if unable to open 77 */ redefineJavaBaseModule( final Instrumentation instrumentation)78 private static boolean redefineJavaBaseModule( 79 final Instrumentation instrumentation) throws Exception { 80 try { 81 Class.forName("java.lang.Module"); 82 } catch (final ClassNotFoundException e) { 83 return false; 84 } 85 86 Instrumentation.class.getMethod("redefineModule", // 87 Class.forName("java.lang.Module"), // 88 Set.class, // 89 Map.class, // 90 Map.class, // 91 Set.class, // 92 Map.class // 93 ).invoke(instrumentation, // instance 94 getModule(Object.class), // module 95 Collections.emptySet(), // extraReads 96 Collections.emptyMap(), // extraExports 97 Collections.singletonMap("java.lang", 98 Collections.singleton( 99 getModule(InjectedClassRuntime.class))), // extraOpens 100 Collections.emptySet(), // extraUses 101 Collections.emptyMap() // extraProvides 102 ); 103 return true; 104 } 105 106 /** 107 * @return {@code cls.getModule()} 108 */ getModule(final Class<?> cls)109 private static Object getModule(final Class<?> cls) throws Exception { 110 return Class.class // 111 .getMethod("getModule") // 112 .invoke(cls); 113 } 114 115 } 116