3

I am using ASM 5.0.3 byte code library with Tomcat 8 and JDK 8. Also I am using

ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_FRAMES);

and

classReader.accept(myClassVisitor, ClassReader.SKIP_FRAMES);

Below exception is thrown by ASM

  Caused by: java.lang.VerifyError: Operand stack overflow
Exception Details:
  Location:      org/apache/tomcat/websocket/server/WsServerContainer.addEndpoint(Ljavax/websocket/server/ServerEndpointConfig;)V @0: aload_0
   Reason:
    Exceeded max stack size.
  Current Frame:
    bci: @0
    flags: { }
    locals: { 'org/apache/tomcat/websocket/server/WsServerContainer', 'javax/websocket/server/ServerEndpointConfig' }
    stack: { }
  Bytecode:
    0x0000000: 2ab4 000a 9900 1a2a b400 0b9a 0013 bb01
    0x0000010: 3d59 b200 4412 45b6 0046 b700 47bf 2ab4
    0x0000020: 000e c700 13bb 0043 59b2 0044 1248 b600
    0x0000030: 46b7 0047 bf2b b900 4901 004d bb00 4a59
    0x0000040: 2bb9 004b 0100 2bb9 004c 0100 2cb7 004d
    0x0000050: 4e2d b600 4ec7 0018 2db6 004f c700 112d
    0x0000060: b600 50c7 000a 2db6 0051 9900 122b b900
    0x0000070: 5201 0012 532d b900 5403 0057 bb00 5559
    0x0000080: 2cb7 0056 3a04 1904 b600 5799 0087 1904
    0x0000090: b600 58b8 0059 3a05 2ab4 0008 1905 b600
    0x00000a0: 5ac0 005b 3a06 1906 c700 29bb 005c 59b8
    0x00000b0: 005d b700 5e3a 062a b400 0819 0519 06b6
    0x00000c0: 005f 572a b400 0819 05b6 005a c000 5b3a
    0x00000d0: 0619 06bb 0060 592b 1904 b700 61b9 0062
    0x00000e0: 0200 9a00 2dbb 0043 59b2 0044 1263 06bd
    0x00000f0: 0064 5903 2c53 5904 2bb9 004b 0100 5359
    0x0000100: 052b b900 4b01 0053 b600 65b7 0047 bfa7
    0x0000110: 0043 2ab4 0007 2c2b b900 5403 00c0 0066
    0x0000120: 3a05 1905 c600 2ebb 0043 59b2 0044 1263
    0x0000130: 06bd 0064 5903 2c53 5904 1905 b900 4b01
    0x0000140: 0053 5905 2bb9 004b 0100 53b6 0065 b700
    0x0000150: 47bf 2a04 b500 0db1                    

    at org.apache.tomcat.websocket.server.WsSci.init(WsSci.java:131)
    at org.apache.tomcat.websocket.server.WsSci.onStartup(WsSci.java:47)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5244)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)

How to resolve this issue without using -noverify option?

Edit #1: I also tried using simply COMPUTE_FRAMES as in:

ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_FRAMES);

and I omitted the below SKIP_FRAMES statement:

//classReader.accept(myClassVisitor, ClassReader.SKIP_FRAMES);

I continue to get the same "Caused by: java.lang.VerifyError: Operand stack overflow" error.

Edit #2: I have tried all of the following combinations and am getting the same error: java.lang.VerifyError: Operand stack overflow Reason:Exceeded max stack size.

  1. ClassWriter.COMPUTE_FRAMES with ClassReader.EXPAND_FRAMES and mv.visitMaxs(maxStack, maxLocals);
  2. ClassWriter.COMPUTE_FRAMES with ClassReader.EXPAND_FRAMES and mv.visitMaxs(-1, -1);
  3. ClassWriter.COMPUTE_FRAMES with ClassReader.SKIP_FRAMES and mv.visitMaxs(maxStack, maxLocals);
  4. ClassWriter.COMPUTE_FRAMES with ClassReader.SKIP_FRAMES and mv.visitMaxs(-1, -1);

Edit #3. Here is my Advice adapter code

public class PojoMethodAdviceAdapter extends AdviceAdapter  {
private String methodName;
private String className;
private String description;
private boolean excludeCheckFlag = false;
private int okFlag =  newLocal(Type.getType("Z")); //newLocal(Type.BOOLEAN_TYPE); 
private int classFileVersion;

Label startFinally = new Label();

private static Hashtable excludeMethods = new  Hashtable();
private static final String yesString = "Yes";
private boolean isValid;

static{
    excludeMethods.put("<clinit>", yesString);
    excludeMethods.put("<init>", yesString);
}

public PojoMethodAdviceAdapter(int access , MethodVisitor mv , String methodName, String description, String className, int classFileVersion){
    super(Opcodes.ASM5 , mv, access, methodName, description);
    this.className = className;
    this.methodName = methodName;
    this.description = description;
    this.excludeCheckFlag = true;
    this.isValid = true;
    this.classFileVersion = classFileVersion;

    String yesStr = (String)excludeMethods.get(this.methodName);
    if(yesStr!=null && yesStr.equals(yesString))
        isValid = false;
    if(this.methodName.indexOf("$") > 0)
        isValid = false;
    if(isValid){
        System.out.println(" [POJO MethodAdviceAdapter] :"+className+" \t "+methodName +" \t  "+description);
    }
}

public void visitCode() {
    super.visitCode(); 
    mv.visitLabel(startFinally);
}

protected void onMethodEnter(){
    if(isValid) {
        mv.visitInsn(Opcodes.ICONST_0);
        mv.visitVarInsn(ISTORE, okFlag);

        mv.visitLdcInsn(className);
        mv.visitLdcInsn(methodName);
        mv.visitLdcInsn(description);
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/mini/agent/trace/RootTracer", "pojoMethodBegin", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z", false);
        mv.visitVarInsn(ISTORE, okFlag);
    }
}

protected void onMethodExit(int opcode){
    if(opcode!=ATHROW) {
        onFinally(opcode);
    }
}

public void visitMaxs(int maxStack, int maxLocals){
    Label endFinally = new Label();
    mv.visitTryCatchBlock(startFinally, endFinally, endFinally, null);
    mv.visitLabel(endFinally);
    onFinally(ATHROW);
    mv.visitInsn(ATHROW);
    if(classFileVersion <= 50){
        mv.visitMaxs(maxStack, maxLocals);
    }
    else{
        mv.visitMaxs(0, 0);
    }
}

private void onFinally(int opcode){
    if(isValid){
        if(opcode == ATHROW){
            mv.visitInsn(Opcodes.DUP);
            mv.visitLdcInsn(className);
            mv.visitLdcInsn(methodName);
            mv.visitLdcInsn(description);
            mv.visitVarInsn(ILOAD, okFlag);
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/mini/agent/trace/RootTracer", "recordPOJOException", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V", false);
        }

        mv.visitLdcInsn(className);
        mv.visitLdcInsn(methodName);
        mv.visitLdcInsn(description);
        mv.visitVarInsn(ILOAD, okFlag);
        mv.visitLdcInsn(opcode);
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/mini/agent/trace/RootTracer", "pojoMethodEnd", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZI)V", false);
    }
}

}

Ramesh Subramanian
  • 944
  • 1
  • 12
  • 28
  • Just looking at the docs of the flags of `ClassWriter` and `ClassReader`, you should be using `COMPUTE_FRAMES` for the writer and `SKIP_FRAMES` for the reader and then `visitMaxs` will be ignored. The exception shows the stack is empty (`stack: { }`), so I don't really know why that would exceed any max stack size... – Adam Michalik Dec 29 '15 at 14:59

1 Answers1

1

Try using the TraceClassVisitor to see where it is getting messed up. In my experience this happens when a Label is written incorrectly, forcing it to write jsr/ret instructions (the JVM should simply reject the class in that case though along with asm crashing when calculating frames), and most often, forgetting to call visitMaxs in the methodVisitor before calling visitEnd. In my code I'm using

visitMaxs(mv.getMaxLocals(),mv.getMaxLocals());

I don't remember what my reasoning for doing that was but my ClassWriter works when I do, but I hope this helps.

  • Hi James, Thank you for your reply.. In my case am using advice adapter, in that I do call visitMaxs before calling visitEnd. Will share my advice adapter..it may help to resolve the issue – Ramesh Subramanian Jan 06 '16 at 09:39
  • I would be so happy to post my adapter, but I need to get permission before I can do so, so keep an eye out for it. – James Ratzlaff Jan 06 '16 at 10:12
  • Aside from that, I am seeing a possible issue coming from your try-catch-finally labels. I'll take a look at my code to see the order in which I'm visiting them. In my visitor I resolve all of the instruction and label offsets by getting calculating each instruction's byte length, then I order each label by visit priority and do a secondary sort by offset and visiting them in that order. I'll see if I can find a simpler solution for your issue. I'm 99% positive that is where your issue is coming from because I going crazy trying to figure out why my code would break from try catches. – James Ratzlaff Jan 06 '16 at 10:21
  • Hi James, based on the tutorial http://wiki.jvmlangsummit.com/pdf/23_Kuleshov_asm.pdf - i use the try-catch-finally labels..it is working for me without any issues – Ramesh Subramanian Jan 06 '16 at 10:34
  • Hey Ramesh, could you post the javap output of the code you generated? From what I can tell of the output: It's failing on the first instruction, which is aload_0 the next instruction is getField at index 0 somewhere down the line stuff is not being popped off of the stack by the time we return or throw. The javap will help with seeing what maxstack value it added to the bytecode. – James Ratzlaff Jan 06 '16 at 11:21
  • Hi James, Posted the tomcat log error and output of bytecode. Thanks for your help. – Ramesh Subramanian Jan 06 '16 at 13:58
  • So running this class through javap I'm seeing some discrepencies. The access flags seem to be missing (which shouldn't affect the stack). But here are what your locals and stack should be: `flags: ACC_PUBLIC` `Code: stack=8, locals=7, args_size=2` – James Ratzlaff Jan 06 '16 at 14:38
  • Hi James, If you could shed some pointers on what I should change in the adapter code to match the correct locals and stack, that would be very helpful. – Ramesh Subramanian Jan 07 '16 at 06:32