1 2 Previous Next 18 Replies Latest reply: Feb 29, 2012 11:50 AM by Andrew Dinn RSS

Does rule condition support null checking?

Anton Ryabtsev Newbie

Hi,

 

Is it possible to create rule that should does actions only if some variable is not null?

So the rule like this:

 

RULE rule

CLASS Foo

METHOD bar

AT EXIT

IF $! != null

DO traceln("yes, it is possible!")

ENDRULE

  • 1. Re: Does rule condition support null checking?
    Andrew Dinn Master

    Hi Anton,

    Anton Ryabtsev wrote:


    Is it possible to create rule that should does actions only if some variable is not null?

    So the rule like this:

     

    RULE rule

    CLASS Foo

    METHOD bar

    AT EXIT

    IF $! != null

    DO traceln("yes, it is possible!")

    ENDRULE

     

    Yes, it is possible. In fact, the example above probably ought to work. However, you may find that in some cases when you supply null as a literal in your rule code the type checker is unable to identify a type for the literal at the point of mention and so rejects your rule with a type error.

     

    Initially this was very broken. Now it should only be rare cases where the type checker cannto handle a mention of null. There is still an outstanding JIRA for these remaining cases.

     

    Do you have a specific reason for asking? For example, have you found a case which Byteman cannot handle? If so I'll be happy to look at it and see if it is possible to fix Byteman to deal with it or else to rewrite your rule to work round this limitation.

  • 2. Re: Does rule condition support null checking?
    Anton Ryabtsev Newbie

    Hi Andrew,

     

    If I try to apply the above rule to the next class

     

     

    public class Foo {
    
        public String bar(int input) {
            if ( input == 0 ) {
                return null;
            }
    
            return "bar";
        }
    
    }
    

     

     

     

    I get this exception:

    org.jboss.byteman.rule.exception.TypeException: NullLiteral.typeCheck : unable to derive type for null from context

        at org.jboss.byteman.rule.expression.NullLiteral.typeCheck(NullLiteral.java:68)

        at org.jboss.byteman.rule.expression.ComparisonExpression.typeCheck(ComparisonExpression.java:53)

  • 3. Re: Does rule condition support null checking?
    Anton Ryabtsev Newbie

    I believe it is a different issue, but I can't use the above rule if my class looks like as the following:

     

     

    public class Foo {
    
        public void bar() {
            synchronized ( this ) {
                for ( int i = 0; i < 10; i++ ) {
                    System.out.println(i);
                }
            }
        }
    
    }
    

     

     

     

    In this example the exception is like this:

     

     

    java.lang.NullPointerException

        at org.jboss.byteman.agent.adapter.cfg.CFG.computeContainment(CFG.java:1108)

        at org.jboss.byteman.agent.adapter.cfg.CFG.carryForward(CFG.java:962)

        at org.jboss.byteman.agent.adapter.cfg.CFG.split(CFG.java:1240)

        at org.jboss.byteman.agent.adapter.RuleTriggerMethodAdapter.visitJumpInsn(RuleTriggerMethodAdapter.java:776)

        at org.jboss.byteman.agent.adapter.EntryTriggerAdapter$EntryTriggerMethodAdapter.visitJumpInsn(EntryTriggerAdapter.java:176)

        at org.jboss.byteman.objectweb.asm.tree.JumpInsnNode.accept(Unknown Source)

        at org.jboss.byteman.objectweb.asm.tree.InsnList.accept(Unknown Source)

        at org.jboss.byteman.objectweb.asm.tree.MethodNode.accept(Unknown Source)

        at org.jboss.byteman.objectweb.asm.commons.JSRInlinerAdapter.visitEnd(Unknown Source)

        at org.jboss.byteman.objectweb.asm.ClassReader.accept(Unknown Source)

        at org.jboss.byteman.objectweb.asm.ClassReader.accept(Unknown Source)

        at org.jboss.byteman.agent.Transformer.transform(Transformer.java:746)

        at org.jboss.byteman.test.TestScript.checkRules(TestScript.java:266)

        at org.jboss.byteman.test.TestScript.testScript(TestScript.java:139)

        at org.jboss.byteman.test.TestScript.main(TestScript.java:98)

  • 4. Re: Does rule condition support null checking?
    Andrew Dinn Master

    HI Anton,

     

    Your rule does indeed fail to typecheck which is not what I expected. I thought the changes I made to partially resolve BYTEMAN-86 had dealt with this case but clearly there is more work needed. Anyway, this fairly simple variant of your rule works ok.

     

    RULE rule

    CLASS Bar

    METHOD bar

    AT EXIT

    BIND nullStr : String = null

    IF $! != nullStr

    DO traceln("yes, it is possible!")

    ENDRULE

     

    In this version the declaration of local variable nullStr with type String provides the type checker with an expected type for the null literal used to initialize the variable.

  • 5. Re: Does rule condition support null checking?
    Andrew Dinn Master

    Anton Ryabtsev wrote:

     

    java.lang.NullPointerException

        at org.jboss.byteman.agent.adapter.cfg.CFG.computeContainment(CFG.java:1108)

        at org.jboss.byteman.agent.adapter.cfg.CFG.carryForward(CFG.java:962)

        at org.jboss.byteman.agent.adapter.cfg.CFG.split(CFG.java:1240)

        at org.jboss.byteman.agent.adapter.RuleTriggerMethodAdapter.visitJumpInsn(RuleTriggerMethodAdapter.java:776)

        at org.jboss.byteman.agent.adapter.EntryTriggerAdapter$EntryTriggerMethodAdapter.visitJumpInsn(EntryTriggerAdapter.java:176)

        at org.jboss.byteman.objectweb.asm.tree.JumpInsnNode.accept(Unknown Source)

        at org.jboss.byteman.objectweb.asm.tree.InsnList.accept(Unknown Source)

        at org.jboss.byteman.objectweb.asm.tree.MethodNode.accept(Unknown Source)

        at org.jboss.byteman.objectweb.asm.commons.JSRInlinerAdapter.visitEnd(Unknown Source)

        at org.jboss.byteman.objectweb.asm.ClassReader.accept(Unknown Source)

        at org.jboss.byteman.objectweb.asm.ClassReader.accept(Unknown Source)

        at org.jboss.byteman.agent.Transformer.transform(Transformer.java:746)

        at org.jboss.byteman.test.TestScript.checkRules(TestScript.java:266)

        at org.jboss.byteman.test.TestScript.testScript(TestScript.java:139)

        at org.jboss.byteman.test.TestScript.main(TestScript.java:98)

     

    Ooh, that's exciting! I'll take a look at this and see if I can find out what is causing the exception. Can you confirm what version of Byteman you are using?

  • 6. Re: Does rule condition support null checking?
    Anton Ryabtsev Newbie

    Thanks for your help, Andrew.

     

    Byteman version: 2.0.0

  • 7. Re: Does rule condition support null checking?
    Andrew Dinn Master

    Hi Anton,

     

    Anton Ryabtsev wrote:

     

    Thanks for your help, Andrew.

     

    Byteman version: 2.0.0

     

    That's rather strange. When I ran bmcheck on your class and rule I did not get this null pointer exception. I just got the typecheck error you posted earlier.

     

    I would very much like to find out what is going on here as there is obviously something wrong but I don't really know what

     

    Are the code and rule script which gave this exception exactly as you specified in the earlier notes?

     

    Can you provide the sequence of commands you ran to generate the error?

     

    Also, what is the OS and JVM you are using?

     

    Thanks for any info you can provide

  • 8. Re: Does rule condition support null checking?
    Andrew Dinn Master

    Ah,ok, I think that was my mistake. I am now getting an exception but not the one you are seeing. I get this

     

    [adinn@sputstik byteman]$ bmcheck -cp src foo.btm

    checking rule rule

    ERROR : Unexpected exception transforming class Foo using  rule "rule" loaded from foo.btm line 5

     

    java.lang.NullPointerException

        at org.jboss.byteman.objectweb.asm.Frame.a(Unknown Source)

        at org.jboss.byteman.objectweb.asm.MethodWriter.visitVarInsn(Unknown Source)

        at org.jboss.byteman.objectweb.asm.MethodAdapter.visitVarInsn(Unknown Source)

        at org.jboss.byteman.agent.adapter.RuleGeneratorAdapter.visitVarInsn(RuleGeneratorAdapter.java:1425)

        at org.jboss.byteman.agent.adapter.RuleTriggerMethodAdapter.visitVarInsn(RuleTriggerMethodAdapter.java:713)

        at org.jboss.byteman.agent.adapter.RuleGeneratorAdapter.storeLocal(RuleGeneratorAdapter.java:1493)

        at org.jboss.byteman.agent.adapter.RuleTriggerMethodAdapter.doReturnOrThrowSave(RuleTriggerMethodAdapter.java:297)

        at org.jboss.byteman.agent.adapter.RuleTriggerMethodAdapter.injectTriggerPoint(RuleTriggerMethodAdapter.java:1102)

        at org.jboss.byteman.agent.adapter.ExitTriggerAdapter$ExitTriggerMethodAdapter.visitInsn(ExitTriggerAdapter.java:79)

        at org.jboss.byteman.objectweb.asm.tree.InsnNode.accept(Unknown Source)

        at org.jboss.byteman.objectweb.asm.tree.InsnList.accept(Unknown Source)

        at org.jboss.byteman.objectweb.asm.tree.MethodNode.accept(Unknown Source)

        at org.jboss.byteman.objectweb.asm.commons.JSRInlinerAdapter.visitEnd(Unknown Source)

        at org.jboss.byteman.objectweb.asm.ClassReader.accept(Unknown Source)

        at org.jboss.byteman.objectweb.asm.ClassReader.accept(Unknown Source)

        at org.jboss.byteman.agent.Transformer.transform(Transformer.java:746)

        at org.jboss.byteman.test.TestScript.checkRules(TestScript.java:266)

        at org.jboss.byteman.test.TestScript.testScript(TestScript.java:139)

        at org.jboss.byteman.test.TestScript.main(TestScript.java:98)

     

    TestScript: 1 total errors

                0 total warnings

                0 parse errors

                0 type errors

                0 type warnings

     

     

    The error above is happening because your rule mentions variable $! but the method you are injecting into has return type void. What should happen is that the initial check to see whether the candidate method is a valid target for the rule should notice that you are using $! and realise that this is invalid. Insteda it goes on to inject the trigger code into the target method and tries to create a local var slot to save the return value on the stack. This breaks because i) there is no return value on the stack and ii) it is trying to create a local var slot with type void. So, I need to fix the trigger injecton code to inlcude this preliminary check. I will raise a JIRA for this and psot a reference on this thread  (I also probably need to do the same for AFTER CALL rules where $! refers to the return value form the called method).

     

    That said, I don't know why you are seeing a different exception trace. So, details of how you got there would be very welcome.

  • 9. Re: Does rule condition support null checking?
    Anton Ryabtsev Newbie

    Well, I use next rule to produce this exciting thing(sorry for misleading you):

     

    RULE rule

    CLASS Foo

    METHOD bar

    AT ENTRY

    IF true

    DO traceln("yes, it works!")

    ENDRULE

     

    OS: WinXP

    JVM: Sun jdk_1.6.0_21

  • 10. Re: Does rule condition support null checking?
    Andrew Dinn Master

    Hi Anton,

    Anton Ryabtsev wrote:

     

    Well, I use next rule to produce this exciting thing(sorry for misleading you):

     

    RULE rule

    CLASS Foo

    METHOD bar

    AT ENTRY

    IF true

    DO traceln("yes, it works!")

    ENDRULE

     

    OS: WinXP

    JVM: Sun jdk_1.6.0_21

     

    Hmm, that's interestingh. When I run this on Linux it works without any exception. I will try it on XP when I get a chance (my XP machine is at home) and let you know what happens.

  • 11. Re: Does rule condition support null checking?
    Andrew Dinn Master

    Could you help me by posting the output when you run the following command

     

    javap -c -verbose -private -classpath XXX Foo

     

    where you need to substitute the path to Foo.class in place of XXX (or omit -classpath XXX if your class file is in the working directory)

     

    It looks like the problem is happening during scanning of the bytecode when a jump instruction is reached so seeing the bytecode might be very useful

  • 13. Re: Does rule condition support null checking?
    Andrew Dinn Master

    Oooh, it gets even more exciting!! Can you tell me what bytecode compiler (i.e. javac program) was used to generate your class file? It looks (maybe?) like the code the Jikes compiler generates. Anyway, Byteman is going to barf on this sort of code because of the way the loop code is generated. There is nothing wrong with your file as bytecode -- it is perfectly legitimate. The problem belongs to Byteman's code analyzer (class CFG whihc you can see in the stacktrace) .

     

    If you (or anyone else reading this thread) want to understand the details then here goes. Otherwise close your eyes now.

     

    Byteman expects that the first entry to every basic block of code occurs either via a drop through or a forward jump in other words primary control flow follows bytecode order. Of course, if there is a for or while loop in a method then the bytecode will have to (re-)enter a block via a backward jump but Byteman expects the first instructions in the loop condition and loop body to be arrived at via a forward transfer of control. This assumption may be invalid for your javac compiler but it is necessary because Byteman needs to push constraints through the code as it traverses the instruction sequence.

     

    In this specific case the constraint is that there is a monitorenter before the loop body (this is the opening half of how synchronized is implemented) and it means that any path out of the loop needs to include a monitorexit (the other half of how syncrhonized is implemented) which Byteman can pair up with its corresponding monitorenter.

     

    Now, with the code generated by OpenJDK's javac the loop condition comes at the top of the loop and skips over the body if it is false. The body directly follows after the condition and there is an unconditional jump backwards at the end of the loop to retest the condition.

     

    public void bar();

      Code:

       Stack=2, Locals=4, Args_size=1

       0:    aload_0

       1:    dup

       2:    astore_1

       3:    monitorenter

       4:    iconst_0

       5:    istore_2

       6:    iload_2

       7:    bipush    10

       9:    if_icmpge    25

       12:    getstatic    #2; //Field java/lang/System.out:Ljava/io/PrintStream;

       15:    iload_2

       16:    invokevirtual    #3; //Method java/io/PrintStream.println:(I)V

       19:    iinc    2, 1

       22:    goto    6

       25:    aload_1

       26:    monitorexit

       27:    goto    35

       30:    astore_3

       31:    aload_1

       32:    monitorexit

       33:    aload_3

       34:    athrow

       35:    return

     

    You can see the loop condition being entered at offest 6 (iload_2 loads the loop counter which is compared against constant value 10) and the loop body entered at offset 12.

     

    Unfortunately with the bytecode you provided the loop condition is at the bottom of the loop starting at offset 19. The loop body starts at offset 9 and is skipped over at loop start by a forward jump at offset 6. The loop condition ends with a conditional jump back to the start of the loop in place of the unconditional jump we saw before.

     

    public void bar();

      Code:

       Stack=2, Locals=3, Args_size=1

       0:    aload_0

       1:    dup

       2:    astore_1

       3:    monitorenter

       4:    iconst_0

       5:    istore_2

       6:    goto    19

       9:    getstatic    #15; //Field java/lang/System.out:Ljava/io/PrintStream;

       12:    iload_2

       13:    invokevirtual    #21; //Method java/io/PrintStream.println:(I)V

       16:    iinc    2, 1

       19:    iload_2

       20:    bipush    10

       22:    if_icmplt    9

       25:    aload_1

       26:    monitorexit

       27:    goto    33

       30:    aload_1

       31:    monitorexit

       32:    athrow

       33:    return

     

    So, when Byteman sees the goto at instruction 6 it can propagate the monitorenter constraint to the end of the loop body but but it does not yet know that the loop body code starting at offset 9 is also reachable from the monitorenter. So, when Byteman analyzes this body code it does not know that a  monitorenter will have happened before this block is reached. So, when it gets to the monitorexit in the block starting at instruction 25 it has lost track of the corresponding monitorenter. The null pointer is supposed to be a reference to instruction 3 only there is no such reference.

     

    Ok, you can open your eyes now :-)

     

    I'll raise another JIRA for this but I cannot see it getting fixed very soon. This assumption is deep in the heart of the Byteman code generation strategy and is key to why Byteman can transform code relatively quickly. Anyway, thanks for reporting the problem and providing all the info needed to identify where things are going wrong.

     

    regards,

     

     

    Andrew Dinn

  • 14. Re: Does rule condition support null checking?
    Anton Ryabtsev Newbie

    I use Eclipse to generate it. So guess, it is ECJ.

     

    Thanks for quick reply.

1 2 Previous Next