4 Replies Latest reply on Nov 6, 2013 4:42 AM by adinn

    A small question from a starter

    flyaway_921

      Hi All,

           I am confused how to get a local variable within the target method scope.

      for example:

      public class LocalVar {
          public static void main(String[] args){
                System.out.println("before method");
      
                boolean b = new LocalVar().method();
      
                System.out.println("after method");
                System.out.println("the value of b is: "+b);
          }
          
          public boolean method(){
              boolean b = true;
              return b;
          }
      }
      
        • 1. Re: A small question from a starter
          adinn

          Hi Wei Cao,

           

          Create a file call localtest.btm containing the following Byteman rule

           

          RULE test and reassign local variable

          CLASS LocalVar

          METHOD method

          AT EXIT

          IF true

          DO traceln("value of b is " + $b);

             $b = false

          ENDRULE

           

          The rule prints the value of b just before executing the return statement in method() and then reassigns b to have value false. As you can see in the rule you refer to a local variable in the trigger method by adding the prefix $ in front of the local variable name.

           

          To make this work you need to compile your program argument -g. That makes sure that the bytecode contains debug information which includes the names and extents of local variables. So, here is how you would compile and run your test on Linux

           

          $ javac -g LocalVar.java

          $ $BYTEMAN_HOME/bin/bmjava.sh -l localtest.btm LocalVar

           

          bmjava.sh is a script which passes the agent arguments to the java command (there is a similar batch script for Windows call bmjava.bat). BYTEMAN_HOME is a shell variable identifying the directory where you unpacked your byteman release.

           

          If you want to call java directly you would execute the following command

           

          $ java -javaagemt:${BYTEMAN_HOME}/lib/byteman.jar=script:localtest.btm


          (on Windows you would need to use %BYTEMAN_HOME% instead of ${BYTEMAN_HOME})

          • 2. Re: A small question from a starter
            flyaway_921

            HI Andrew , Thanks for your answer. I will try it later

            • 3. Re: A small question from a starter
              flyaway_921

              Hi Andrew,

                   I think the rule should make a change as bellow:

               

              RULE test and reassign local variable

              CLASS LocalVar

              METHOD method

              AT EXIT

              IF true

              DO traceln("value of b is " + $b);

                 $b = false;

                 return $b;

              ENDRULE


              I think we should return the value again at the exit of the method. I tested it, and it works. If not, it will cause an exception bellow, There may be the something wrong when the jvm run the bytecode.

              value of b is 1   -->this is printed from rule

                     Exception in thread "main" java.lang.ClassCastException: java.lang.Boolean cannot be cast to java.lang.Number

                     

              And in addition , I have done some research that in JAVA the "return" statement is not always executed before the method exit. In another word , the running sequence maybe: method exit then the return executed. So,in this case,we'd better retrun the value again to make sure the value is actually changed by the bytecode.


              BTW, I think we'd better use AT WRITE condition to change the local variable. Please have a check if the exception is a bug or not.


              Best Regards

              Wei Cao


              • 4. Re: Re: A small question from a starter
                adinn

                Hi Wei Cao,

                 

                Wei Cao wrote:

                 

                Hi Andrew,

                    I think the rule should make a change as bellow:

                 

                RULE test and reassign local variable

                CLASS LocalVar

                METHOD method

                AT EXIT

                IF true

                DO traceln("value of b is " + $b);

                  $b = false;

                  return $b;

                ENDRULE


                I think we should return the value again at the exit of the method. I tested it, and it works. If not, it will cause an exception bellow, There may be the something wrong when the jvm run the bytecode.

                value of b is 1  -->this is printed from rule

                      Exception in thread "main" java.lang.ClassCastException: java.lang.Boolean cannot be cast to java.lang.Number

                 

                First of all, thanks for reporting the problem. The exception is an error in Byteman which has already been reported and should be fixed in the next release (see BYTEMAN-252 the rule engine thinks the local variable is an int when it is actually a boolean).

                 

                You are right that the rule does not do what I expected . With the original rule the rule engine reassigns local variable b to false when it executes the rule then continues to execute the target method (LocalVar.method()). The point where the rule code gets injected into the method bytecode is just before the return bytecode. So, at this point the value of local variable b is now false. The problem that the old value of variable b was already loaded onto the Java stack before the rule was executed. The original bytecode will look like:

                 

                  iload 1                              # loads local var b (== true) onto stack

                  return                               # returns top of stack (== true)

                 

                When Byteman changes the bytecode it will look like

                 

                  iload 1                              # loads local var b (== true) onto stack

                  <injected bytecode for rule>         # assigns local var b (== false)

                  return                               # returns top of stack (== true)

                 

                So, the value of b is changed but this does not alter the returned result.

                 

                Your version of the rule does what is wanted by using a Byteman return expression. However, it does not work in exactly the way you suggested. What happens is that the rule code throws a special type of exception defined by the Byteman code. Class ReturnExeption includes a field returnValue of type Object. The method initialises this field with Boolean.FALSE, an instance of class Boolean. The exception is caught by handler code which is also injected into the trigger method by Byteman and this code unwraps the exception and returns the value false. The transformed code looks like this:

                 

                  iload 1                              # loads local var b (== true) onto stack

                  <injected bytecode for rule>         # rule throws Byteman ReturnException

                  return                               # returns top of stack (== true)


                handler: ReturnException               # try block is the injected rule code above

                  getfield                             # returnValue : Ljava/lang/Object;

                  checkcast boolean                    # cast return value to boolean

                  return

                 

                You are also right to say that we can use an assignment if we change the rule's location clause:

                 

                RULE test and reassign local variable

                CLASS LocalVar

                METHOD method

                AFTER WRITE $b

                IF true

                DO traceln("value of b is " + $b);

                  $b = false;

                  return $b;

                ENDRULE

                 

                With this definition the rule will be triggered just after b is initialised to true. The rule will then reassign it to false and continue to execute method(). This time the rule code gets injected into the bytecode one instruction earlier

                 

                  ldc 1                                # load boolean true (represented by 1)

                  istore 1                             # initialise local var b (== true)

                  <injected bytecode for rule>         # assigns local var b (== false)

                  iload 1                              # loads local var b (== true) onto stack

                  return                               # returns top of stack (== true)

                 

                I am expecting to fix the bug and release a new Byteman version very soon.

                1 of 1 people found this helpful