-
1. Re: RULE execution based on triggering method field
adinn May 27, 2014 5:48 AM (in response to abroszni)Hi Aurelian,
This is not working for several reasons. I think you have somehow acquired a rather confused idea of how Bytreman works. Rather than try to explain everything that is wrong here I will show you several ways to do whhat I think you want to do and recommend you try to look at some other examples of working Byteman rules and then go read the Programmer's Guide very carefully from start to finish. That ought to make things a bit clearer.
Let's look at what your rule say. The CLASS and METHOD clause say that Byteman should inject some code into method MyInstrumentedClass.doOps(). Well, if that is the method you are injecting code into then it's no good showing me the code for method StopTest.testStop(). It is not very easy for me to explain how Byteman is going to rewrite a method if you don't provide the actual code for the method you have asked it to rewrite.
It seems that what you want Byteman to do is add a call to Thread.sleep at the start of the bytecode for method doOps(). However, it also looks like are trying to make your rule refer to local variable stopped which is defined in the completely different method testStop(). Now Byteman cannot change the bytecode for one method so that it refers to a local variable in another method. The Java virtual machine does not make it possible for methods to access local variables of their caller methods. So Byteman cannot make that possible either.
If stopped was a static field defined by class testStop then the injected code could refer to it:
@BMScript(value = "byteman/stop.btm")
public class StopTest extends BMNGRunner {
public static boolean stopped = false;
@Test
public void testStop() throws InterruptedException {
MyInstrumentedClass myClass = new MyInstrumentedClass();
for (int i = 0; i < 30; i++) {
if (i == 5)
stopped = true;
if (i == 10)
stopped = false;
myClass.doOps(i);
}
}
}
With this definition for your class you could use this rule:
RULE stop method
CLASS MyInstrumentedClass
METHOD doOps
AT ENTRY
BIND stopped:boolean = StopTest.stopped
IF stopped
DO Thread.sleep(10000)
ENDRULE
Or to make it more simple
RULE stop method
CLASS MyInstrumentedClass
METHOD doOps
AT ENTRY
IF StopTest.stopped
DO Thread.sleep(10000)
ENDRULE
n.b. it doesn't actually matter that the static field is public or private (if necessary, Byteman will get round the privacy restriction by injecting bytecode which uses reflection to read the static field).
Another way to do what you want is to inject code into your test code which saves the value of stopped and then inject code into the target method which uses that saved value. You can use the Byteman flag builtins to do this. Let's assume we are using the original code:
@BMScript(value = "byteman/stop.btm")
public class StopTest extends BMNGRunner {
@Test
public void testStop() throws InterruptedException {
boolean stopped = false;
MyInstrumentedClass myClass = new MyInstrumentedClass();
for (int i = 0; i < 30; i++) {
if (i == 5)
stopped = true;
if (i == 10)
stopped = false;
myClass.doOps(i);
}
}
}
You need two rules injected into the test method to record the value of local variable stopped
RULE record stopped is set
CLASS StopTest
METHOD testStop
AT CALL doOps
IF $stopped
DO flag($myClass)
ENDRULE
and
RULE record stopped is clear
CLASS StopTest
METHOD testStop
AT CALL doOps
IF ! $stopped
DO clear($myClass)
ENDRULE
The location says that these two rules get executed just before the call to doOps() in your test method testStop(). The condition in the first rule tests whether local variable stopped is true (n.b. $varName refers to the value of local variable varName in the method Byteman is injecting code into). Note that the injected bytecode uses the value of stopped at the injection point i.e. just at the point where the call is about to be made. The DO clause for the first rule calls builtin method flag(Object) to set a flag associated with the target object for the call the i.e. object referenced by local variable myClass. The second rule calls builtin clear(Object) to clear the same flag if local variable stopped is false. Both rules get fired just before the call but only one will have a valid condition. So, the flag associated with object myClass will be set or cleared depending upon the value of stopped.
Now you can refer to the same flag setting in the rule which injects the sleep call
RULE stop method
CLASS MyInstrumentedClass
METHOD doOps
AT ENTRY
IF flagged($this)
DO Thread.sleep(10000)
ENDRULE
The condition calls builtin flagged(Object) to test whether the flag associated with the target object for the call to doOps() is set. Note that $this inside the call to doOps() is the same object as $myClass in the calling method so the same flag is tested inside the call as was set outside the call.
You might like to try running these rules to see what happens and then I suggest you look for some other examples and go read more about Byteman in the Programmer's Guide.
regards,
Andrew Dinn
-
2. Re: Re: RULE execution based on triggering method field
abroszni Jun 24, 2014 4:35 AM (in response to adinn)Hi Andrew,
thanks a lot for your detailed answer, I am sorry that I wasn't clear enough about my problem,
Actually I had read the doc in length and had already found the solution you are proposing, using a static variable.
But it gives me an exception.
Let me explain briefly what I am trying to achieve, I am using an external API, so I don't have access at all to the code, I just want to be able to add a sleep() when calling this API under certain condition.
This is why I use the boolean 'stopped'. In my test, when my conditions are fulfilled, the call to the API's method should be paused for a while with the sleep() injected in the API's method.
I have opened a jira:
https://issues.jboss.org/browse/BYTEMAN-265
thanks,
Aurelien
-
3. Re: RULE execution based on triggering method field
abroszni Jun 25, 2014 7:09 AM (in response to abroszni)For future readers, here is the working solution and explanation from Andrew:
Try changing your rule as follows and I believe it will work:
RULE stop method
CLASS MyInstrumentedClass
METHOD doOps
AT ENTRY BIND
stopped:boolean = com.mycompany.app.StopTest.stopped
IF stopped
DO Thread.sleep(10000)
ENDRULE
Byteman allows you to omit a package qualifier when you mention a class so long as the unqualified class name is known by virtue of appearing in other rule code e.g. as the target class, as an argument type for the target method, as the result type of an expression in the rule body, etc. In this case the type StopTest is not referenced via any of the types mentioned in the rule's event specification (i.e. CLASS/METHOD/LOCATION clauses). Nor does it appear in the condition or action which follow the BIND clause. So, with your original rule Byteman has no way of knowing that there is a class called StopTest in package org.mycompany.app. If you provide the package qualified class name in the static field reference then it should typecheck the reference correctly.