org.jacoco.core.runtime.LoggerRuntime.java Source code

Java tutorial

Introduction

Here is the source code for org.jacoco.core.runtime.LoggerRuntime.java

Source

/*******************************************************************************
 * Copyright (c) 2009, 2013 Mountainminds GmbH & Co. KG and Contributors
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Marc R. Hoffmann - initial API and implementation
 *    
 *******************************************************************************/
package org.jacoco.core.runtime;

import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

import org.jacoco.core.internal.instr.InstrSupport;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

/**
 * This {@link IRuntime} implementation uses the Java logging API to report
 * coverage data.
 * <p>
 * 
 * The implementation uses a dedicated log channel. Instrumented classes call
 * {@link Logger#log(Level, String, Object[])} with the class identifier in the
 * first slot of the parameter array. The runtime implements a {@link Handler}
 * for this channel that puts the probe data structure into the first slot of
 * the parameter array.
 */
public class LoggerRuntime extends AbstractRuntime {

    private static final String CHANNEL = "jacoco-runtime";

    private final String key;

    private final Logger logger;

    private final Handler handler;

    /**
     * Creates a new runtime.
     */
    public LoggerRuntime() {
        super();
        this.key = Integer.toHexString(hashCode());
        this.logger = configureLogger();
        this.handler = new RuntimeHandler();
    }

    private Logger configureLogger() {
        final Logger l = Logger.getLogger(CHANNEL);
        l.setUseParentHandlers(false);
        l.setLevel(Level.ALL);
        return l;
    }

    public int generateDataAccessor(final long classid, final String classname, final int probecount,
            final MethodVisitor mv) {

        // The data accessor performs the following steps:
        //
        // final Object[] args = new Object[3];
        // args[0] = Long.valueOf(classid);
        // args[1] = classname;
        // args[2] = Integer.valueOf(probecount);
        // Logger.getLogger(CHANNEL).log(Level.INFO, key, args);
        // final byte[] probedata = (byte[]) args[0];
        //
        // Note that local variable 'args' is used at two places. As were not
        // allowed to allocate local variables we have to keep this value with
        // DUP and SWAP operations on the operand stack.

        // 1. Create parameter array:

        RuntimeData.generateArgumentArray(classid, classname, probecount, mv);

        // Stack[0]: [Ljava/lang/Object;

        mv.visitInsn(Opcodes.DUP);

        // Stack[1]: [Ljava/lang/Object;
        // Stack[0]: [Ljava/lang/Object;

        // 2. Call Logger:

        mv.visitLdcInsn(CHANNEL);
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/logging/Logger", "getLogger",
                "(Ljava/lang/String;)Ljava/util/logging/Logger;");

        // Stack[2]: Ljava/util/logging/Logger;
        // Stack[1]: [Ljava/lang/Object;
        // Stack[0]: [Ljava/lang/Object;

        mv.visitInsn(Opcodes.SWAP);

        // Stack[2]: [Ljava/lang/Object;
        // Stack[1]: Ljava/util/logging/Logger;
        // Stack[0]: [Ljava/lang/Object;

        mv.visitFieldInsn(Opcodes.GETSTATIC, "java/util/logging/Level", "INFO", "Ljava/util/logging/Level;");

        // Stack[3]: Ljava/util/logging/Level;
        // Stack[2]: [Ljava/lang/Object;
        // Stack[1]: Ljava/util/logging/Logger;
        // Stack[0]: [Ljava/lang/Object;

        mv.visitInsn(Opcodes.SWAP);

        // Stack[3]: [Ljava/lang/Object;
        // Stack[2]: Ljava/util/logging/Level;
        // Stack[1]: Ljava/util/logging/Logger;
        // Stack[0]: [Ljava/lang/Object;

        mv.visitLdcInsn(key);

        // Stack[4]: Ljava/lang/String;
        // Stack[3]: [Ljava/lang/Object;
        // Stack[2]: Ljava/util/logging/Level;
        // Stack[1]: Ljava/util/logging/Logger;
        // Stack[0]: [Ljava/lang/Object;

        mv.visitInsn(Opcodes.SWAP);

        // Stack[4]: [Ljava/lang/Object;
        // Stack[3]: Ljava/lang/String;
        // Stack[2]: Ljava/util/logging/Level;
        // Stack[1]: Ljava/util/logging/Logger;
        // Stack[0]: [Ljava/lang/Object;

        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/util/logging/Logger", "log",
                "(Ljava/util/logging/Level;Ljava/lang/String;[Ljava/lang/Object;)V");

        // Stack[0]: [Ljava/lang/Object;

        // 3. Load data structure from parameter array:

        mv.visitInsn(Opcodes.ICONST_0);
        mv.visitInsn(Opcodes.AALOAD);
        mv.visitTypeInsn(Opcodes.CHECKCAST, InstrSupport.DATAFIELD_DESC);

        // Stack[0]: [Z

        return 5; // Maximum local stack size is 5
    }

    @Override
    public void startup(final RuntimeData data) throws Exception {
        super.startup(data);
        this.logger.addHandler(handler);
    }

    public void shutdown() {
        this.logger.removeHandler(handler);
    }

    private class RuntimeHandler extends Handler {

        @Override
        public void publish(final LogRecord record) {
            if (key.equals(record.getMessage())) {
                data.getProbes(record.getParameters());
            }
        }

        @Override
        public void flush() {
            // nothing to do
        }

        @Override
        public void close() throws SecurityException {
            // The Java logging framework removes and closes all handlers on JVM
            // shutdown. As soon as our handler has been removed, all classes
            // that might get instrumented during shutdown (e.g. loaded by other
            // shutdown hooks) will fail to initialize. Therefore we add ourself
            // again here. This is a nasty hack that might fail in some Java
            // implementations.
            logger.addHandler(handler);
        }
    }

}