1 /******************************************************************************* 2 * Copyright (c) 2009, 2015 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 *******************************************************************************/ 12 package org.jacoco.core.runtime; 13 14 import org.jacoco.core.data.ExecutionData; 15 import org.jacoco.core.data.ExecutionDataStore; 16 import org.jacoco.core.data.IExecutionDataVisitor; 17 import org.jacoco.core.data.ISessionInfoVisitor; 18 import org.jacoco.core.data.SessionInfo; 19 import org.jacoco.core.internal.instr.InstrSupport; 20 import org.objectweb.asm.MethodVisitor; 21 import org.objectweb.asm.Opcodes; 22 23 /** 24 * Container for runtime execution and meta data. All access to the runtime data 25 * is thread safe. 26 */ 27 public class RuntimeData { 28 29 /** store for execution data */ 30 protected final ExecutionDataStore store; 31 32 private long startTimeStamp; 33 34 private String sessionId; 35 36 /** 37 * Creates a new runtime. 38 */ RuntimeData()39 public RuntimeData() { 40 store = new ExecutionDataStore(); 41 sessionId = "<none>"; 42 startTimeStamp = System.currentTimeMillis(); 43 } 44 45 /** 46 * Sets a session identifier for this runtime. The identifier is used when 47 * execution data is collected. If no identifier is explicitly set a 48 * identifier is generated from the host name and a random number. This 49 * method can be called at any time. 50 * 51 * @see #collect(IExecutionDataVisitor, ISessionInfoVisitor, boolean) 52 * @param id 53 * new session identifier 54 */ setSessionId(final String id)55 public void setSessionId(final String id) { 56 sessionId = id; 57 } 58 59 /** 60 * Get the current a session identifier for this runtime. 61 * 62 * @see #setSessionId(String) 63 * @return current session identifier 64 */ getSessionId()65 public String getSessionId() { 66 return sessionId; 67 } 68 69 /** 70 * Collects the current execution data and writes it to the given 71 * {@link IExecutionDataVisitor} object. 72 * 73 * @param executionDataVisitor 74 * handler to write coverage data to 75 * @param sessionInfoVisitor 76 * handler to write session information to 77 * @param reset 78 * if <code>true</code> the current coverage information is also 79 * cleared 80 */ collect(final IExecutionDataVisitor executionDataVisitor, final ISessionInfoVisitor sessionInfoVisitor, final boolean reset)81 public final void collect(final IExecutionDataVisitor executionDataVisitor, 82 final ISessionInfoVisitor sessionInfoVisitor, final boolean reset) { 83 synchronized (store) { 84 final SessionInfo info = new SessionInfo(sessionId, startTimeStamp, 85 System.currentTimeMillis()); 86 sessionInfoVisitor.visitSessionInfo(info); 87 store.accept(executionDataVisitor); 88 if (reset) { 89 reset(); 90 } 91 } 92 } 93 94 /** 95 * Resets all coverage information. 96 */ reset()97 public final void reset() { 98 synchronized (store) { 99 store.reset(); 100 startTimeStamp = System.currentTimeMillis(); 101 } 102 } 103 104 /** 105 * Returns the coverage data for the class with the given identifier. If 106 * there is no data available under the given id a new entry is created. 107 * This is a synchronized access to the underlying store. 108 * 109 * @param id 110 * class identifier 111 * @param name 112 * VM name of the class 113 * @param probecount 114 * probe data length 115 * @return execution data 116 */ getExecutionData(final Long id, final String name, final int probecount)117 public ExecutionData getExecutionData(final Long id, final String name, 118 final int probecount) { 119 synchronized (store) { 120 return store.get(id, name, probecount); 121 } 122 } 123 124 /** 125 * Retrieves the execution probe array for a given class. The passed 126 * {@link Object} array instance is used for parameters and the return value 127 * as follows. Call parameters: 128 * 129 * <ul> 130 * <li>args[0]: class id ({@link Long}) 131 * <li>args[1]: vm class name ({@link String}) 132 * <li>args[2]: probe count ({@link Integer}) 133 * </ul> 134 * 135 * Return value: 136 * 137 * <ul> 138 * <li>args[0]: probe array (<code>boolean[]</code>) 139 * </ul> 140 * 141 * @param args 142 * parameter array of length 3 143 */ getProbes(final Object[] args)144 public void getProbes(final Object[] args) { 145 final Long classid = (Long) args[0]; 146 final String name = (String) args[1]; 147 final int probecount = ((Integer) args[2]).intValue(); 148 args[0] = getExecutionData(classid, name, probecount).getProbes(); 149 } 150 151 /** 152 * In violation of the regular semantic of {@link Object#equals(Object)} 153 * this implementation is used as the interface to the execution data store. 154 * 155 * @param args 156 * the arguments as an {@link Object} array 157 * @return has no meaning 158 */ 159 @Override equals(final Object args)160 public boolean equals(final Object args) { 161 if (args instanceof Object[]) { 162 getProbes((Object[]) args); 163 } 164 return super.equals(args); 165 } 166 167 /** 168 * Generates code that creates the argument array for the 169 * {@link #getProbes(Object[])} method. The array instance is left on the 170 * operand stack. The generated code requires a stack size of 5. 171 * 172 * @param classid 173 * class identifier 174 * @param classname 175 * VM class name 176 * @param probecount 177 * probe count for this class 178 * @param mv 179 * visitor to emit generated code 180 */ generateArgumentArray(final long classid, final String classname, final int probecount, final MethodVisitor mv)181 public static void generateArgumentArray(final long classid, 182 final String classname, final int probecount, final MethodVisitor mv) { 183 mv.visitInsn(Opcodes.ICONST_3); 184 mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); 185 186 // Class Id: 187 mv.visitInsn(Opcodes.DUP); 188 mv.visitInsn(Opcodes.ICONST_0); 189 mv.visitLdcInsn(Long.valueOf(classid)); 190 mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf", 191 "(J)Ljava/lang/Long;", false); 192 mv.visitInsn(Opcodes.AASTORE); 193 194 // Class Name: 195 mv.visitInsn(Opcodes.DUP); 196 mv.visitInsn(Opcodes.ICONST_1); 197 mv.visitLdcInsn(classname); 198 mv.visitInsn(Opcodes.AASTORE); 199 200 // Probe Count: 201 mv.visitInsn(Opcodes.DUP); 202 mv.visitInsn(Opcodes.ICONST_2); 203 InstrSupport.push(mv, probecount); 204 mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Integer", 205 "valueOf", "(I)Ljava/lang/Integer;", false); 206 mv.visitInsn(Opcodes.AASTORE); 207 } 208 209 /** 210 * Generates the code that calls a {@link RuntimeData} instance through the 211 * JRE API method {@link Object#equals(Object)}. The code pops a 212 * {@link Object} instance from the stack and pushes the probe array of type 213 * <code>boolean[]</code> on the operand stack. The generated code requires 214 * a stack size of 6. 215 * 216 * @param classid 217 * class identifier 218 * @param classname 219 * VM class name 220 * @param probecount 221 * probe count for this class 222 * @param mv 223 * visitor to emit generated code 224 */ generateAccessCall(final long classid, final String classname, final int probecount, final MethodVisitor mv)225 public static void generateAccessCall(final long classid, 226 final String classname, final int probecount, final MethodVisitor mv) { 227 // stack[0]: Ljava/lang/Object; 228 229 generateArgumentArray(classid, classname, probecount, mv); 230 231 // stack[1]: [Ljava/lang/Object; 232 // stack[0]: Ljava/lang/Object; 233 234 mv.visitInsn(Opcodes.DUP_X1); 235 236 // stack[2]: [Ljava/lang/Object; 237 // stack[1]: Ljava/lang/Object; 238 // stack[0]: [Ljava/lang/Object; 239 240 mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "equals", 241 "(Ljava/lang/Object;)Z", false); 242 mv.visitInsn(Opcodes.POP); 243 244 // stack[0]: [Ljava/lang/Object; 245 246 mv.visitInsn(Opcodes.ICONST_0); 247 mv.visitInsn(Opcodes.AALOAD); 248 249 // stack[0]: [Z 250 251 mv.visitTypeInsn(Opcodes.CHECKCAST, InstrSupport.DATAFIELD_DESC); 252 } 253 254 } 255