/* * Copyright (c) 2013-2014, Pierre Laporte * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this work; if not, see <http://www.gnu.org/licenses/>. */ package fr.pingtimeout.tyrion.transformation; import fr.pingtimeout.tyrion.agent.StaticAccessor; import fr.pingtimeout.tyrion.util.SimpleLogger; import lombok.RequiredArgsConstructor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.*; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.ListIterator; @RequiredArgsConstructor class SynchronizedBlockTransformer { private final ClassNode classNode; void interceptAllSynchronizedBlocks() { @SuppressWarnings("unchecked") List<MethodNode> methods = classNode.methods; int blocksIntercepted = 0; for (MethodNode methodNode : methods) { blocksIntercepted += interceptAllSynchronizedBlocks(classNode, methodNode); } if (blocksIntercepted != 0) { SimpleLogger.debug("Intercepted %s synchronized blocks in %s", blocksIntercepted, classNode.name); } } private int interceptAllSynchronizedBlocks(ClassNode classNode, MethodNode methodNode) { int numberOfBlocksIntercepted = 0; if (SynchronizedMethodTransformer.isSynchronized(methodNode.access)) { SimpleLogger.debug("%s::%s is synchronized, nothing to do here", classNode.name, methodNode.name); } else { SimpleLogger.debug("Intercepting all synchronized blocks of %s::%s", classNode.name, methodNode.name); numberOfBlocksIntercepted += interceptSynchronizedBlocks(methodNode); } return numberOfBlocksIntercepted; } private int interceptSynchronizedBlocks(MethodNode methodNode) { int numberOfBlocksIntercepted = interceptMonitorEnter(methodNode); interceptMonitorExit(methodNode); return numberOfBlocksIntercepted; } private int interceptMonitorEnter(MethodNode methodNode) { Collection<AbstractInsnNode> monitorEnterInsn = extractMonitorEnterInsn(methodNode); for (AbstractInsnNode monitorEnterInsnNode : monitorEnterInsn) { // Duplicate lock SimpleLogger.debug("Inserting DUP before %s", monitorEnterInsnNode); methodNode.instructions.insertBefore(monitorEnterInsnNode, new InsnNode(Opcodes.DUP)); methodNode.instructions.insertBefore(monitorEnterInsnNode, new InsnNode(Opcodes.DUP)); // Add invokestatic just before critical section SimpleLogger.debug("Inserting call to enteringSynchronizedBlock before %s", monitorEnterInsnNode); methodNode.instructions.insertBefore(monitorEnterInsnNode, new MethodInsnNode(Opcodes.INVOKESTATIC, StaticAccessor.CLASS_FQN, StaticAccessor.BEFORE_MONITORENTER_ON_OBJECT.getMethodName(), StaticAccessor.BEFORE_MONITORENTER_ON_OBJECT.getSignature())); // Add invokestatic as first instruction of critical section AbstractInsnNode nodeAfterInterception = monitorEnterInsnNode.getNext(); SimpleLogger.debug("Inserting call to enteredSynchronizedBlock before %s", nodeAfterInterception); methodNode.instructions.insertBefore(nodeAfterInterception, new MethodInsnNode(Opcodes.INVOKESTATIC, StaticAccessor.CLASS_FQN, StaticAccessor.AFTER_MONITORENTER_ON_OBJECT.getMethodName(), StaticAccessor.AFTER_MONITORENTER_ON_OBJECT.getSignature())); } return monitorEnterInsn.size(); } private void interceptMonitorExit(MethodNode methodNode) { Collection<AbstractInsnNode> monitorExitInsn = extractMonitorExitInsn(methodNode); for (AbstractInsnNode monitorExitInsnNode : monitorExitInsn) { // Duplicate lock SimpleLogger.debug("Inserting DUP before %s", monitorExitInsnNode); methodNode.instructions.insertBefore(monitorExitInsnNode, new InsnNode(Opcodes.DUP)); // Add invokestatic as last instruction of critical section methodNode.instructions.insertBefore(monitorExitInsnNode, new MethodInsnNode(Opcodes.INVOKESTATIC, StaticAccessor.CLASS_FQN, StaticAccessor.BEFORE_MONITOREXIT_ON_OBJECT.getMethodName(), StaticAccessor.BEFORE_MONITOREXIT_ON_OBJECT.getSignature())); } } private Collection<AbstractInsnNode> extractMonitorEnterInsn(MethodNode methodNode) { return extractInstructions(methodNode, Opcodes.MONITORENTER); } private Collection<AbstractInsnNode> extractMonitorExitInsn(MethodNode methodNode) { return extractInstructions(methodNode, Opcodes.MONITOREXIT); } @SuppressWarnings("unchecked") private Collection<AbstractInsnNode> extractInstructions(MethodNode methodNode, int instructionToExtract) { Collection<AbstractInsnNode> monitorEnterInsn = new ArrayList<>(); ListIterator<AbstractInsnNode> iterator = methodNode.instructions.iterator(); while (iterator.hasNext()) { AbstractInsnNode insnNode = iterator.next(); if (insnNode.getOpcode() == instructionToExtract) { monitorEnterInsn.add(insnNode); } } return monitorEnterInsn; } }