• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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