/* * AppInsights-Java * Copyright (c) Microsoft Corporation * All rights reserved. * * MIT License * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the ""Software""), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all copies or * substantial portions of the Software. * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ package com.microsoft.applicationinsights.agent.internal.agent.redis; import com.microsoft.applicationinsights.agent.internal.agent.ByteCodeUtils; import com.microsoft.applicationinsights.agent.internal.agent.ClassToMethodTransformationData; import com.microsoft.applicationinsights.agent.internal.agent.DefaultMethodVisitor; import com.microsoft.applicationinsights.agent.internal.coresync.impl.ImplementationsCoordinator; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; /** * Created by gupele on 8/6/2015. */ final class JedisMethodVisitor extends DefaultMethodVisitor { private final static String FINISH_DETECT_METHOD_NAME = "methodFinished"; private final static String FINISH_METHOD_DEFAULT_SIGNATURE = "(Ljava/lang/String;J[Ljava/lang/Object;Ljava/lang/Throwable;)V"; private Type[] argumentTypes; private boolean isStatic; private int firstEmptyIndexForLocalVariable; public JedisMethodVisitor(int access, String desc, String owner, String methodName, MethodVisitor methodVisitor, ClassToMethodTransformationData additionalData) { super(false, true, 0, access, desc, owner, methodName, methodVisitor, additionalData); argumentTypes = Type.getArgumentTypes(desc); firstEmptyIndexForLocalVariable = argumentTypes.length; for (Type tp : argumentTypes) { if (tp.equals(Type.LONG_TYPE) || tp.equals(Type.DOUBLE_TYPE)) { ++firstEmptyIndexForLocalVariable; } } isStatic = ByteCodeUtils.isStatic(access); if (!isStatic) { ++firstEmptyIndexForLocalVariable; } } private int localStart; private int localFinish; private int localDelta; @Override public void onMethodEnter() { localStart = this.newLocal(Type.getType(Long.class)); localFinish = this.newLocal(Type.getType(Long.class)); localDelta = this.newLocal(Type.getType(Long.class)); mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "nanoTime", "()J", false); mv.visitVarInsn(LSTORE, localStart); } @Override protected void byteCodeForMethodExit(int opcode) { switch (translateExitCode(opcode)) { case EXIT_WITH_EXCEPTION: TempVar throwable = duplicateTopStackToTempVariable(Type.getType(Throwable.class)); finished(throwable); break; case EXIT_WITH_RETURN_VALUE: case EXIT_VOID: finished(null); break; default: break; } } private void finished(TempVar throwable) { mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "nanoTime", "()J", false); mv.visitVarInsn(LSTORE, localFinish); mv.visitVarInsn(LLOAD, localFinish); mv.visitVarInsn(LLOAD, localStart); mv.visitInsn(LSUB); mv.visitVarInsn(LSTORE, localDelta); mv.visitVarInsn(LLOAD, localDelta); mv.visitFieldInsn(GETSTATIC, ImplementationsCoordinator.internalName, "INSTANCE", ImplementationsCoordinator.internalNameAsJavaName); mv.visitMethodInsn(INVOKEVIRTUAL, ImplementationsCoordinator.internalName, "getRedisThresholdInNS", "()J", false); mv.visitInsn(LCMP); Label l0 = new Label(); mv.visitJumpInsn(IFLE, l0); reportFinishWithArgs(throwable); Label l1 = new Label(); mv.visitJumpInsn(GOTO, l1); mv.visitLabel(l0); notifyEndOfMethod(null, throwable); mv.visitLabel(l1); } private void reportFinishWithArgs(TempVar throwable) { int boxedArgumentsIndex = 0; int argumentIndex = isMethodStatic() ? 0 : 1; TempArrayVar arrayOfArgumentsAfterBoxing; arrayOfArgumentsAfterBoxing = createArray(numberOfArguments()); // Fill the array with method parameters after boxing the primitive ones. for (Type argumentType : getArgumentTypes()) { setBoxedValueIntoArray(arrayOfArgumentsAfterBoxing, boxedArgumentsIndex, argumentType, argumentIndex); boxedArgumentsIndex++; if (ByteCodeUtils.isLargeType(argumentType)) { argumentIndex += 2; } else { ++argumentIndex; } } notifyEndOfMethod(arrayOfArgumentsAfterBoxing, throwable); } private void notifyEndOfMethod(TempArrayVar arrayOfArgumentsAfterBoxing, TempVar throwable) { super.visitFieldInsn(GETSTATIC, ImplementationsCoordinator.internalName, "INSTANCE", ImplementationsCoordinator.internalNameAsJavaName); mv.visitLdcInsn(getMethodName()); mv.visitVarInsn(LLOAD, localDelta); if (arrayOfArgumentsAfterBoxing != null) { mv.visitVarInsn(Opcodes.ALOAD, arrayOfArgumentsAfterBoxing.tempVarIndex); } else { mv.visitInsn(ACONST_NULL); } if (throwable != null) { loadLocal(throwable.tempVarIndex); } else { mv.visitInsn(ACONST_NULL); } mv.visitMethodInsn(INVOKEVIRTUAL, ImplementationsCoordinator.internalName, FINISH_DETECT_METHOD_NAME, FINISH_METHOD_DEFAULT_SIGNATURE, false); } private void reportFinishWithNoArgs(TempVar throwable) { super.visitFieldInsn(GETSTATIC, ImplementationsCoordinator.internalName, "INSTANCE", ImplementationsCoordinator.internalNameAsJavaName); mv.visitLdcInsn(getMethodName()); mv.visitVarInsn(LLOAD, localDelta); mv.visitInsn(ACONST_NULL); mv.visitInsn(ACONST_NULL); mv.visitMethodInsn(INVOKEVIRTUAL, ImplementationsCoordinator.internalName, FINISH_DETECT_METHOD_NAME, FINISH_METHOD_DEFAULT_SIGNATURE, false); } private void reportFinishWithArgs1(int opcode) { int resultIndex = this.newLocal(Type.getType(Integer.class)); super.visitFieldInsn(GETSTATIC, ImplementationsCoordinator.internalName, "INSTANCE", ImplementationsCoordinator.internalNameAsJavaName); mv.visitLdcInsn(getMethodName()); mv.visitMethodInsn(INVOKEVIRTUAL, ImplementationsCoordinator.internalName, FINISH_DETECT_METHOD_NAME, FINISH_METHOD_DEFAULT_SIGNATURE, false); mv.visitVarInsn(ISTORE, resultIndex); mv.visitVarInsn(ILOAD, resultIndex); Label ok = new Label(); mv.visitJumpInsn(IFEQ, ok); int boxedArgumentsIndex = 0; int argumentIndex = isMethodStatic() ? 0 : 1; TempArrayVar arrayOfArgumentsAfterBoxing; arrayOfArgumentsAfterBoxing = createArray(numberOfArguments()); // Fill the array with method parameters after boxing the primitive ones. for (Type argumentType : getArgumentTypes()) { setBoxedValueIntoArray(arrayOfArgumentsAfterBoxing, boxedArgumentsIndex, argumentType, argumentIndex); boxedArgumentsIndex++; if (ByteCodeUtils.isLargeType(argumentType)) { argumentIndex += 2; } else { ++argumentIndex; } } super.visitFieldInsn(GETSTATIC, ImplementationsCoordinator.internalName, "INSTANCE", ImplementationsCoordinator.internalNameAsJavaName); mv.visitLdcInsn(getMethodName()); mv.visitVarInsn(Opcodes.ALOAD, arrayOfArgumentsAfterBoxing.tempVarIndex); mv.visitMethodInsn(INVOKEVIRTUAL, ImplementationsCoordinator.internalName, FINISH_DETECT_METHOD_NAME, FINISH_METHOD_DEFAULT_SIGNATURE, false); mv.visitLabel(ok); super.byteCodeForMethodExit(opcode); } protected TempArrayVar createArray(int length, int extraArgs) { firstEmptyIndexForLocalVariable += extraArgs; return createArray(length); } protected TempArrayVar createArray(int length) { mv.visitIntInsn(Opcodes.BIPUSH, length); mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); mv.visitVarInsn(Opcodes.ASTORE, firstEmptyIndexForLocalVariable); return new TempArrayVar(firstEmptyIndexForLocalVariable); } protected void boxVariable(Type argumentType, int argumentIndex) { if (argumentType.equals(Type.BOOLEAN_TYPE)) { mv.visitVarInsn(Opcodes.ILOAD, argumentIndex); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false); } else if (argumentType.equals(Type.BYTE_TYPE)) { mv.visitVarInsn(Opcodes.ILOAD, argumentIndex); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false); } else if (argumentType.equals(Type.CHAR_TYPE)) { mv.visitVarInsn(Opcodes.ILOAD, argumentIndex); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false); } else if (argumentType.equals(Type.SHORT_TYPE)) { mv.visitVarInsn(Opcodes.ILOAD, argumentIndex); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false); } else if (argumentType.equals(Type.INT_TYPE)) { mv.visitVarInsn(Opcodes.ILOAD, argumentIndex); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false); } else if (argumentType.equals(Type.LONG_TYPE)) { mv.visitVarInsn(Opcodes.LLOAD, argumentIndex); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false); } else if (argumentType.equals(Type.FLOAT_TYPE)) { mv.visitVarInsn(Opcodes.FLOAD, argumentIndex); super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Float", "valueOf", "(F)Ljava/lang/Long;", false); } else if (argumentType.equals(Type.DOUBLE_TYPE)) { mv.visitVarInsn(Opcodes.DLOAD, argumentIndex); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false); } else { mv.visitVarInsn(Opcodes.ALOAD, argumentIndex); } } protected void prepareArrayEntry(int arrayIndex, int entryIndex) { mv.visitVarInsn(Opcodes.ALOAD, arrayIndex); mv.visitIntInsn(Opcodes.BIPUSH, entryIndex); } protected void storeTopStackValueIntoArray() { mv.visitInsn(Opcodes.AASTORE); } private void setBoxedValueIntoArray(TempArrayVar arrayOfArgumentsAfterBoxing, int boxedArgumentsIndex, Type argumentType, int argumentIndex) { prepareArrayEntry(arrayOfArgumentsAfterBoxing.tempVarIndex, boxedArgumentsIndex); boxVariable(argumentType, argumentIndex); storeTopStackValueIntoArray(); } protected boolean isMethodStatic() { return isStatic; } protected int numberOfArguments() { return argumentTypes.length; } protected Type[] getArgumentTypes() { return argumentTypes; } }