-
1. Re: A small question from a starter
adinn Nov 4, 2013 4:57 AM (in response to flyaway_921)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 Nov 5, 2013 5:01 AM (in response to adinn)HI Andrew , Thanks for your answer. I will try it later
-
3. Re: A small question from a starter
flyaway_921 Nov 5, 2013 9:29 PM (in response to adinn)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 Nov 6, 2013 4:42 AM (in response to flyaway_921)1 of 1 people found this helpfulHi 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.