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 // BEGIN android-change 16 import java.io.File; 17 import java.io.IOException; 18 import java.io.RandomAccessFile; 19 import java.nio.channels.FileChannel; 20 import java.nio.ByteBuffer; 21 import java.util.HashMap; 22 import java.util.Map; 23 // END android-change 24 import java.util.Properties; 25 26 // BEGIN android-change 27 import org.jacoco.core.data.ExecutionData; 28 import org.jacoco.core.data.ExecutionDataWriter; 29 import org.jacoco.core.data.ExecutionDataDelegate; 30 import org.jacoco.core.data.IExecutionData; 31 import org.jacoco.core.data.ExecutionDataStore; 32 import org.jacoco.core.data.MappedExecutionData; 33 // END android-change 34 import org.jacoco.core.runtime.AgentOptions; 35 import org.jacoco.core.runtime.RuntimeData; 36 37 /** 38 * The API for classes instrumented in "offline" mode. The agent configuration 39 * is provided through system properties prefixed with <code>jacoco.</code>. 40 */ 41 public final class Offline { 42 43 // BEGIN android-change 44 private static final Map<Long, ExecutionDataDelegate> DATA = new HashMap<Long, ExecutionDataDelegate>(); 45 private static FileChannel CHANNEL; 46 // END android-change 47 private static final String CONFIG_RESOURCE = "/jacoco-agent.properties"; 48 Offline()49 private Offline() { 50 // no instances 51 } 52 53 // BEGIN android-change 54 //private static RuntimeData data; 55 /* 56 private static synchronized RuntimeData getRuntimeData() { 57 if (data == null) { 58 final Properties config = ConfigLoader.load(CONFIG_RESOURCE, 59 System.getProperties()); 60 try { 61 data = Agent.getInstance(new AgentOptions(config)).getData(); 62 } catch (final Exception e) { 63 throw new RuntimeException("Failed to initialize JaCoCo.", e); 64 } 65 } 66 return data; 67 } 68 */ 69 70 /** 71 * API for offline instrumented classes. 72 * 73 * @param classid 74 * class identifier 75 * @param classname 76 * VM class name 77 * @param probecount 78 * probe count for this class 79 * @return IExecutionData instance for this class 80 */ getExecutionData(final long classid, final String classname, final int probecount)81 public static IExecutionData getExecutionData(final long classid, 82 final String classname, final int probecount) { 83 //return getRuntimeData() 84 // .getExecutionData(Long.valueOf(classid), classname, probecount) 85 // .getProbes(); 86 synchronized (DATA) { 87 ExecutionDataDelegate entry = DATA.get(classid); 88 if (entry == null) { 89 entry = new ExecutionDataDelegate( 90 classid, classname, probecount, CHANNEL); 91 DATA.put(classid, entry); 92 } else { 93 entry.assertCompatibility(classid, classname, probecount); 94 } 95 return entry; 96 } 97 } 98 99 /** 100 * Enables memory-mapped execution data and converts existing 101 * {@link ExecutionDataDelegate}s. 102 */ enableMemoryMappedData()103 public static void enableMemoryMappedData() { 104 try { 105 prepareFile(getPid()); 106 for (ExecutionDataDelegate data : DATA.values()) { 107 data.convert(CHANNEL); 108 } 109 } catch (IOException e) { 110 // TODO(olivernguyen): Add logging to debug issues more easily. 111 } 112 } 113 114 /** 115 * Creates the output file that will be mapped for probe data. 116 */ prepareFile(int pid)117 private static void prepareFile(int pid) throws IOException { 118 // Write header information to the file. 119 ByteBuffer headerBuffer = ByteBuffer.allocate(5); 120 headerBuffer.put(ExecutionDataWriter.BLOCK_HEADER); 121 headerBuffer.putChar(ExecutionDataWriter.MAGIC_NUMBER); 122 headerBuffer.putChar(ExecutionDataWriter.FORMAT_VERSION); 123 headerBuffer.flip(); 124 125 // If this file already exists (due to pid re-usage), the previous coverage data 126 // will be lost when the file is overwritten. 127 File outputFile = new File("/data/misc/trace/jacoco-" + pid + ".mm.ec"); 128 CHANNEL = new RandomAccessFile(outputFile, "rw").getChannel(); 129 synchronized (CHANNEL) { 130 CHANNEL.write(headerBuffer); 131 } 132 } 133 134 /** 135 * Helper function to determine the pid of this process. 136 */ getPid()137 private static int getPid() throws IOException { 138 // Read /proc/self and resolve it to obtain its pid. 139 return Integer.parseInt(new File("/proc/self").getCanonicalFile().getName()); 140 } 141 142 /** 143 * Creates a default agent, using config loaded from the classpath resource and the system 144 * properties, and a runtime data instance populated with the execution data accumulated by 145 * the probes up until this call is made (subsequent probe updates will not be reflected in 146 * this agent). 147 * 148 * @return the new agent 149 */ createAgent()150 static Agent createAgent() { 151 final Properties config = ConfigLoader.load(CONFIG_RESOURCE, 152 System.getProperties()); 153 synchronized (DATA) { 154 ExecutionDataStore store = new ExecutionDataStore(); 155 for (IExecutionData data : DATA.values()) { 156 store.put(data); 157 } 158 try { 159 return Agent.getInstance(new AgentOptions(config), new RuntimeData(store)); 160 } catch (final Exception e) { 161 throw new RuntimeException("Failed to initialize JaCoCo.", e); 162 } 163 } 164 } 165 // END android-change 166 } 167