I am trying to get the parameter values ββof a Java program method. I use ASM to control byte code and get these values. However, I ran into some problems.
Here is the visitCode () method used to process the code. What does he do:
- Create an empty array to store the collected parameters.
- For each parameter, load its value into an array.
- Send this array to my agent OnMethodEntry method (where the values ββwill be used).
.
@Override public void visitCode() { int paramLength = paramTypes.length; // Create array with length equal to number of parameters mv.visitIntInsn(Opcodes.BIPUSH, paramLength); mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); mv.visitVarInsn(Opcodes.ASTORE, paramLength); // Fill the created array with method parameters int i = 0; for (Type tp : paramTypes) { mv.visitVarInsn(Opcodes.ALOAD, paramLength); mv.visitIntInsn(Opcodes.BIPUSH, i); if (tp.equals(Type.BOOLEAN_TYPE) || tp.equals(Type.BYTE_TYPE) || tp.equals(Type.CHAR_TYPE) || tp.equals(Type.SHORT_TYPE) || tp.equals(Type.INT_TYPE)) mv.visitVarInsn(Opcodes.ILOAD, i); else if (tp.equals(Type.LONG_TYPE)) { mv.visitVarInsn(Opcodes.LLOAD, i); i++; } else if (tp.equals(Type.FLOAT_TYPE)) mv.visitVarInsn(Opcodes.FLOAD, i); else if (tp.equals(Type.DOUBLE_TYPE)) { mv.visitVarInsn(Opcodes.DLOAD, i); i++; } else mv.visitVarInsn(Opcodes.ALOAD, i); mv.visitInsn(Opcodes.AASTORE); i++; } // Load id, class name and method name this.visitLdcInsn(new Integer(this.methodID)); this.visitLdcInsn(this.className); this.visitLdcInsn(this.methodName); // Load the array of parameters that we created this.visitVarInsn(Opcodes.ALOAD, paramLength); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "jalen/MethodStats", "onMethodEntry", "(ILjava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V"); super.visitCode(); }
However, this does not work when, apparently, the method has more than one parameter.
The resulting class file shows these things:
static void moveDisk(char arg0, char arg1, PrintStream arg2) { Object[] arrayOfObject = new Object[3]; arrayOfObject[0] = ???; arrayOfObject[1] = ???; Object localObject; arrayOfObject[2] = localObject; MethodStats.onMethodEntry(5, "hanoi/TowersOfHanoi", "moveDisk", arrayOfObject);
If instead of loading parameters 2 local objects are created.
The bytecode shows nothing strange:
static void moveDisk(char, char, java.io.PrintStream); Code: 0: bipush 3 2: anewarray #4
And finally, the error shown (when using -noverify):
param: [Ljava.lang.String;@420e54f3 Exception in thread "Jalen Agent" java.lang.NullPointerException at hanoi.TowersOfHanoi.solveHanoi(TowersOfHanoi.java) at hanoi.TowersOfHanoi.main(TowersOfHanoi.java:29)
Otherwise it is:
Exception in thread "Jalen Agent" java.lang.VerifyError: (class: hanoi/TowersOfHanoi, method: moveDisk signature: (CCLjava/io/PrintStream;)V) Expecting to find object/array on stack at java.lang.Class.getDeclaredMethods0(Native Method) at java.lang.Class.privateGetDeclaredMethods(Class.java:2442) at java.lang.Class.getMethod0(Class.java:2685) at java.lang.Class.getMethod(Class.java:1620) at sun.launcher.LauncherHelper.getMainMethod(LauncherHelper.java:492) at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:484)
This should usually work, as I just load the information from the stack frame. I also tried to test static and non-static methods (as from the stack given here: http://www.artima.com/insidejvm/ed2/jvm8.html ), but still without success.
Any idea of ββwhy this is happening, or perhaps an idea of ββa solution?
Thanks:)
EDIT:
Now it works when boxing primitive types (thanks to the sentences from int3 below :)). Here is the working code of the visitCode () method:
@Override public void visitCode() { int paramLength = paramTypes.length; // Create array with length equal to number of parameters mv.visitIntInsn(Opcodes.BIPUSH, paramLength); mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); mv.visitVarInsn(Opcodes.ASTORE, paramLength); // Fill the created array with method parameters int i = 0; for (Type tp : paramTypes) { mv.visitVarInsn(Opcodes.ALOAD, paramLength); mv.visitIntInsn(Opcodes.BIPUSH, i); if (tp.equals(Type.BOOLEAN_TYPE)) { mv.visitVarInsn(Opcodes.ILOAD, i); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;"); } else if (tp.equals(Type.BYTE_TYPE)) { mv.visitVarInsn(Opcodes.ILOAD, i); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;"); } else if (tp.equals(Type.CHAR_TYPE)) { mv.visitVarInsn(Opcodes.ILOAD, i); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;"); } else if (tp.equals(Type.SHORT_TYPE)) { mv.visitVarInsn(Opcodes.ILOAD, i); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;"); } else if (tp.equals(Type.INT_TYPE)) { mv.visitVarInsn(Opcodes.ILOAD, i); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;"); } else if (tp.equals(Type.LONG_TYPE)) { mv.visitVarInsn(Opcodes.LLOAD, i); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;"); i++; } else if (tp.equals(Type.FLOAT_TYPE)) { mv.visitVarInsn(Opcodes.FLOAD, i); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;"); } else if (tp.equals(Type.DOUBLE_TYPE)) { mv.visitVarInsn(Opcodes.DLOAD, i); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;"); i++; } else mv.visitVarInsn(Opcodes.ALOAD, i); mv.visitInsn(Opcodes.AASTORE); i++; } // Load id, class name and method name this.visitLdcInsn(new Integer(this.methodID)); this.visitLdcInsn(this.className); this.visitLdcInsn(this.methodName); // Load the array of parameters that we created this.visitVarInsn(Opcodes.ALOAD, paramLength); mv.visitMethodInsn(Opcodes.INVOKESTATIC, "jalen/MethodStats", "onMethodEntry", "(ILjava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V"); super.visitCode(); }