LoggerRuntime.java revision 177434c487364833a95015fbfd38b6a58ca56729
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 block 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 void generateDataAccessor(final long classid, 67 final GeneratorAdapter gen) { 68 69 // 1. Create parameter array: 70 71 final int param = gen.newLocal(Type.getObjectType("java/lang/Object")); 72 73 // stack := new Object[1] 74 gen.push(1); 75 gen.newArray(Type.getObjectType("java/lang/Object")); 76 77 // stack[0] = Long.valueOf(classId) 78 gen.dup(); 79 gen.push(0); 80 gen.push(classid); 81 gen.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf", 82 "(J)Ljava/lang/Long;"); 83 gen.arrayStore(Type.getObjectType("java/lang/Object")); 84 85 gen.storeLocal(param); 86 87 // 2. Call Logger: 88 89 // stack := Logger.getLogger(CHANNEL) 90 gen.push(CHANNEL); 91 gen.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/logging/Logger", 92 "getLogger", "(Ljava/lang/String;)Ljava/util/logging/Logger;"); 93 94 // stack = Level.INFO; 95 gen.getStatic(Type.getObjectType("java/util/logging/Level"), "INFO", 96 Type.getObjectType("java/util/logging/Level")); 97 98 // stack := key 99 gen.push(key); 100 101 // stack := param 102 gen.loadLocal(param); 103 104 // stack.log(stack, stack, stack) 105 gen 106 .visitMethodInsn(Opcodes.INVOKEVIRTUAL, 107 "java/util/logging/Logger", "log", 108 "(Ljava/util/logging/Level;Ljava/lang/String;[Ljava/lang/Object;)V"); 109 110 // 3. Load data structure from parameter array: 111 112 gen.loadLocal(param); 113 gen.push(0); 114 gen.arrayLoad(GeneratorConstants.DATAFIELD_TYPE); 115 gen.checkCast(GeneratorConstants.DATAFIELD_TYPE); 116 } 117 118 public void startup() { 119 this.logger.addHandler(handler); 120 } 121 122 public void shutdown() { 123 this.logger.removeHandler(handler); 124 } 125 126 private class RuntimeHandler extends Handler { 127 128 @Override 129 public void publish(final LogRecord record) { 130 if (key.equals(record.getMessage())) { 131 final Object[] params = record.getParameters(); 132 final Long id = (Long) params[0]; 133 synchronized (store) { 134 final boolean[][] blockdata = store.getData(id); 135 if (blockdata == null) { 136 throw new IllegalStateException(String.format( 137 "Unknown class id %x.", id)); 138 } 139 params[0] = blockdata; 140 } 141 } 142 } 143 144 @Override 145 public void flush() { 146 } 147 148 @Override 149 public void close() throws SecurityException { 150 // The Java logging framework removes and closes all handlers on JVM 151 // shutdown. As soon as our handler has been removed, all classes 152 // that might get instrumented during shutdown (e.g. loaded by other 153 // shutdown hooks) will fail to initialize. Therefore we add ourself 154 // again here. 155 // This is a nasty hack that might fail in some Java 156 // implementations. 157 logger.addHandler(handler); 158 } 159 } 160 161} 162