LoggerRuntime.java revision 804d83f360c024826dff8c37eb392237cc94240c
1/******************************************************************************* 2 * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others 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 * $Id: $ 12 *******************************************************************************/ 13package org.jacoco.core.runtime; 14 15import java.util.logging.Handler; 16import java.util.logging.Level; 17import java.util.logging.LogRecord; 18import java.util.logging.Logger; 19 20import org.jacoco.core.instr.GeneratorConstants; 21import org.objectweb.asm.Opcodes; 22import org.objectweb.asm.Type; 23import org.objectweb.asm.commons.GeneratorAdapter; 24 25/** 26 * This {@link IRuntime} implementation uses the Java logging API to report 27 * coverage data. The advantage is, that the instrumented classes do not get 28 * dependencies to other classes than the JRE library itself. 29 * <p> 30 * 31 * The implementation uses a dedicated log channel. Instrumented classes call 32 * {@link Logger#log(Level, String, Object[])} with the class identifier in the 33 * first slot of the parameter array. The runtime implements a {@link Handler} 34 * for this channel that puts the probe data structure into the first slot of 35 * the parameter array. 36 * 37 * @author Marc R. Hoffmann 38 * @version $Revision: $ 39 */ 40public class LoggerRuntime extends AbstractRuntime { 41 42 private static final String CHANNEL = "jacoco-runtime"; 43 44 private final String key; 45 46 private final Logger logger; 47 48 private final Handler handler; 49 50 /** 51 * Creates a new runtime. 52 */ 53 public LoggerRuntime() { 54 this.key = Integer.toHexString(hashCode()); 55 this.logger = configureLogger(); 56 this.handler = new RuntimeHandler(); 57 } 58 59 private Logger configureLogger() { 60 final Logger l = Logger.getLogger(CHANNEL); 61 l.setUseParentHandlers(false); 62 l.setLevel(Level.ALL); 63 return l; 64 } 65 66 public int generateDataAccessor(final long classid, 67 final GeneratorAdapter gen) { 68 69 // 1. Create parameter array: 70 71 gen.push(1); 72 gen.newArray(Type.getObjectType("java/lang/Object")); 73 74 // Stack[0]: [Ljava/lang/Object; 75 76 gen.dup(); 77 78 // Stack[1]: [Ljava/lang/Object; 79 // Stack[0]: [Ljava/lang/Object; 80 81 gen.push(0); 82 gen.push(classid); 83 84 // Stack[4]: J 85 // Stack[3]: . 86 // Stack[2]: I 87 // Stack[1]: [Ljava/lang/Object; 88 // Stack[0]: [Ljava/lang/Object; 89 90 gen.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf", 91 "(J)Ljava/lang/Long;"); 92 93 // Stack[3]: Ljava/lang/Long; 94 // Stack[2]: I 95 // Stack[1]: [Ljava/lang/Object; 96 // Stack[0]: [Ljava/lang/Object; 97 98 gen.arrayStore(Type.getObjectType("java/lang/Object")); 99 100 // Stack[0]: [Ljava/lang/Object; 101 102 final int param = gen.newLocal(Type.getObjectType("java/lang/Object")); 103 gen.storeLocal(param); 104 105 // 2. Call Logger: 106 107 gen.push(CHANNEL); 108 gen.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/logging/Logger", 109 "getLogger", "(Ljava/lang/String;)Ljava/util/logging/Logger;"); 110 111 // Stack[0]: Ljava/util/logging/Logger; 112 113 gen.getStatic(Type.getObjectType("java/util/logging/Level"), "INFO", 114 Type.getObjectType("java/util/logging/Level")); 115 116 // Stack[1]: Ljava/util/logging/Level; 117 // Stack[0]: Ljava/util/logging/Logger; 118 119 gen.push(key); 120 121 // Stack[2]: Ljava/lang/String; 122 // Stack[1]: Ljava/util/logging/Level; 123 // Stack[0]: Ljava/util/logging/Logger; 124 125 gen.loadLocal(param); 126 127 // Stack[3]: [Ljava/lang/Object; 128 // Stack[2]: Ljava/lang/String; 129 // Stack[1]: Ljava/util/logging/Level; 130 // Stack[0]: Ljava/util/logging/Logger; 131 132 gen 133 .visitMethodInsn(Opcodes.INVOKEVIRTUAL, 134 "java/util/logging/Logger", "log", 135 "(Ljava/util/logging/Level;Ljava/lang/String;[Ljava/lang/Object;)V"); 136 137 // 3. Load data structure from parameter array: 138 139 gen.loadLocal(param); 140 gen.push(0); 141 142 // Stack[1]: I 143 // Stack[0]: [Ljava/lang/Object; 144 145 gen.arrayLoad(GeneratorConstants.PROBEDATA_TYPE); 146 gen.checkCast(GeneratorConstants.PROBEDATA_TYPE); 147 148 // Stack[0]: [Z 149 150 return 5; // Maximum local stack size is 5 151 } 152 153 public void startup() { 154 this.logger.addHandler(handler); 155 } 156 157 public void shutdown() { 158 this.logger.removeHandler(handler); 159 } 160 161 private class RuntimeHandler extends Handler { 162 163 @Override 164 public void publish(final LogRecord record) { 165 if (key.equals(record.getMessage())) { 166 final Object[] params = record.getParameters(); 167 final Long id = (Long) params[0]; 168 synchronized (store) { 169 final boolean[] data = store.getData(id); 170 if (data == null) { 171 throw new IllegalStateException(String.format( 172 "Unknown class id %x.", id)); 173 } 174 params[0] = data; 175 } 176 } 177 } 178 179 @Override 180 public void flush() { 181 } 182 183 @Override 184 public void close() throws SecurityException { 185 // The Java logging framework removes and closes all handlers on JVM 186 // shutdown. As soon as our handler has been removed, all classes 187 // that might get instrumented during shutdown (e.g. loaded by other 188 // shutdown hooks) will fail to initialize. Therefore we add ourself 189 // again here. 190 // This is a nasty hack that might fail in some Java 191 // implementations. 192 logger.addHandler(handler); 193 } 194 } 195 196} 197