/* * 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 static fr.pingtimeout.tyrion.transformation.SynchronizedMethodTransformer.isStatic; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.commons.AdviceAdapter; import fr.pingtimeout.tyrion.agent.StaticAccessor; import fr.pingtimeout.tyrion.util.SimpleLogger; class SynchronizedMethodProbesInjector extends AdviceAdapter { private final String methodName; private final String className; private final Label startFinally; private final Label endFinally; protected SynchronizedMethodProbesInjector(int api, MethodVisitor mv, int access, String name, String desc, String className) { super(Opcodes.ASM4, mv, access, name, desc); this.methodName = name; this.className = className.replaceAll("/", "."); this.startFinally = new Label(); this.endFinally = new Label(); } @Override protected void onMethodEnter() { SimpleLogger.debug("Entering synchronized method %s", methodName); pushTargetOnStack(); mv.visitInsn(DUP); mv.visitInsn(DUP); popTargetAndRecordTryEnter(); popTargetAndEnterSynchronizedBlock(); popTargetAndRecordEnter(); } @Override protected void onMethodExit(int opcode) { // Let visitMaxs handle exceptions if (opcode != ATHROW) { onFinally(opcode); } } @Override public void visitMaxs(int maxStack, int maxLocals) { mv.visitTryCatchBlock(this.startFinally, this.endFinally, this.endFinally, null); mv.visitLabel(this.endFinally); // Program execution may reach visitMaxs in case of exception onFinally(ATHROW); super.visitMaxs(maxStack, maxLocals); } private void onFinally(int opcode) { SimpleLogger.debug("Leaving synchronized method %s", methodName); pushTargetOnStack(); mv.visitInsn(DUP); popTargetAndRecordExit(); popTargetAndExitSynchronizedBlock(); if (opcode == ATHROW) { mv.visitInsn(opcode); } } private void pushTargetOnStack() { if (isStatic(methodAccess)) { pushCurrentClassOnStack(); } else { pushThisOnStack(); } } private void pushCurrentClassOnStack() { mv.visitLdcInsn(className); mv.visitMethodInsn(INVOKESTATIC, StaticAccessor.CLASS_FQN, StaticAccessor.RETRIEVE_CLASS_BY_NAME.getMethodName(), StaticAccessor.RETRIEVE_CLASS_BY_NAME.getSignature()); } private void pushThisOnStack() { mv.visitVarInsn(ALOAD, 0); } private void popTargetAndRecordTryEnter() { mv.visitMethodInsn(INVOKESTATIC, StaticAccessor.CLASS_FQN, StaticAccessor.BEFORE_MONITORENTER_ON_OBJECT.getMethodName(), StaticAccessor.BEFORE_MONITORENTER_ON_OBJECT.getSignature()); } private void popTargetAndEnterSynchronizedBlock() { mv.visitInsn(MONITORENTER); mv.visitLabel(startFinally); } private void popTargetAndRecordEnter() { mv.visitMethodInsn(INVOKESTATIC, StaticAccessor.CLASS_FQN, StaticAccessor.AFTER_MONITORENTER_ON_OBJECT.getMethodName(), StaticAccessor.AFTER_MONITORENTER_ON_OBJECT.getSignature()); } private void popTargetAndExitSynchronizedBlock() { mv.visitInsn(MONITOREXIT); } private void popTargetAndRecordExit() { mv.visitMethodInsn(INVOKESTATIC, StaticAccessor.CLASS_FQN, StaticAccessor.BEFORE_MONITOREXIT_ON_OBJECT.getMethodName(), StaticAccessor.BEFORE_MONITOREXIT_ON_OBJECT.getSignature()); } }