2 Replies Latest reply on Feb 7, 2017 2:08 PM by Alexandre Porcelli

    Is it possible to use Byteman to force a test wait for a method be executed Nth times in a background thread that I don't have control over?

    Alexandre Porcelli Newbie

      Hi,

      I have a background thread that starts other threads, and I don't have control over it. At some point, one of those threads will execute a particular method of a certain type that I need to check if it was executed a X number of times.

        Today I'm using Thread.sleep on my test, to make sure the threads are propagated and have enough time to get all execution done. I also had to change my types to add an observer just for testing purposed.

      I'm wondering if Byteman could help me force the test thread to wait for the target method to be executed an X number of times (with some reasonable timeout) and also inject the number of times that method was executed. Is this possible?

      Thank you in advance!

        • 1. Re: Is it possible to use Byteman to force a test wait for a method be executed Nth times in a background thread that I don't have control over?
          Andrew Dinn Master

          Hi Alexandre,

           

          Good question!

           

          I can think of  quite a few ways to achieve what you need. Here is a sketch of one way:

           

          First you need to find a way of tracking calls to the method you are interested in -- lets assume it is MyClass.myMethod. The Byteman CountDown API is quite a good way to do this sort of thing. CountDowns are objects managed by the Byteman default Helper class which allow rules to count down from some initial value and take an action only when the value reaches 0. To show how you use them let's start off by assuming we have a CountDown identified using the label "mycountdown" intiialized to the desired count (we'll come to how you create a CountDown up in a bit). The rule we want will look something like this:

           

          RULE count down calls
          CLASS MyClass
          METHOD myMethod
          AT ENTRY
          IF countDown("mycountdown")
          DO ...
          ENDRULE
          

           

          So, assume the countdown was created with value 9. At each of the next 9 calls to myMethod the rule condition calls builtin method countdown which atomically decrements the value of the countdown and returns false (9, 8, 7, 6, ...). At the 10th call to myMethod the rule condition detects that the countdown has value 0 so it deletes the countdown and returns true. Note that the Byteman Helper method countdown ensures that the operation is thread-safe by serialising operations on the CountDown. Once the 10th call has called builtin method countdown subsequent calls will fail to find a countdown and hence will also return false. In summary, the rule will fire and execute the DO clause only once under the 10th call to MyClass.myMethod.

           

          Ok, we still need to to a bit more work -- not least to compete the DO clause of this rule. The trickiest task left requires making the test thread wait and adding  code in the DO clause of the rule to wake it up. It's not actually too difficult to achieve that but first off let's deal with a simpler issue which will help make it clearer how to resolve the rest of the problem.

           

          How does the test thread ensure that the countdown is created before running the test. The Helper API for creating a CountDown is

           

            boolean createCountdown(Object identifier, int count)
          

           

          However, that method is normally only available to be called from a Byteman rule. So, how do we get the test thread to call it? Well, let's add the following method to the test class

           

          class MyTest extends Test
          {
            @Test
            public void testMyMethod() {
              setupCountDown(9)
              <start test threads>
              <wait for threads to finish>
              <check it worked>
            }
          
            private void setupCountDown(int n)
            {
              // do nothing -- Byteman will inject code here
            }
          }
          

           

          n.b. I have left a few blank spaces for the code that starts the test threads, the code that waits until they are finished and the code that checks they did what was expected. I assume you already have code for the first and third parts. I'll explain how to do the wait in a minute.

           

          We can use the following Byteman rule to inject a call to Helper.createCountDown into the test code

           

          RULE create count down
          CLASS MyTest
          METHOD setupCountDown(int)
          AT ENTRY
          IF true
          DO createCountDown("mycountdown", $1)
          ENDRULE
          

           

          So, when we enter testMyMethod the call to setupCountDown triggers a Byteman rule and it creates a counter with the count set using $1, the int value specified as argument.

           

          That rule gives a clue to how we are going to get the countdown rule to interact with the test thread. We can use a rule triggered by the test thread to rendezvous with the thread which trips the CountDown. A rendezvous can be achieved in a couple of ways. One option is to use Byteman's built-in Rendezvous type which allows 2 or more threads to synchronize. Since this is just a 2-way rendezvous we can use the more simple Waiter API provided by Helper. I'll show you the latter version and leave you to see how you might use the rendezvous API to do something similar.

           

          Let's add another method to the test class to allow the test thread to wait for an event with a timeout.

           

          class MyTest extends Test
          {
            @Test
            public void testMyMethdo() {
              setupCountDown(9);
              <start test threads>
              waitForCountDown(5000);
              <check it worked>
            }
          
            private void setupCountDown(int n)
            {
              // do nothing -- Byteman will inject code here
            }
          
            private void waitForCountDown(int msecs)
            {
              // do nothing -- Byteman will inject code here
            }
          }
          

           

          We use this rule to make the test thread wait

           

          RULE wait for count down
          CLASS MyTest
          METHOD waitForCountDown(int)
          AT ENTRY
          IF true
          DO waitFor("myMethodTest",  $1)
          ENDRULE
          

           

          String "myMethodTest" is used to idenitfy a Waiter object managed by Byteman. The waiter doesn't need to be created in advance. A call to waitFor will create one if it doesn't exist. The caller suspends waiting for some thread to call signalWake("myMethodTest"). This call includes a timeout in millisecs which means the test thread will time out if it does not get woken.

           

          Now we can go back and complete the DO clause for the rule that test the CountDown. When the last call to myMethod is reached and the CountDown trips we need to call signalWake to wake any waiting thread.

           

          RULE count down calls
          CLASS MyClass
          METHOD myMethod
          AT ENTRY
          IF countDown("mycountdown")
          DO signalWake("myMethodTest", true)
          ENDRULE
          

           

          Note that the call to signalWake accepts a second argument true. This is to avoid race condition that might possibly happen. The thread which trips the CountDown might call signalWake before the test thread thread gets to call waitFor. When you pass 2nd argument true to signalWake it checks whether a thread is waiting. If so it just wakes it. If not it suspends the signalling thread until a waiting thread comes along (i.e. converts the waitFor/signalWake pair from an asymmetrical rendezvous into a symmetrical rendezvous). In summary, by passing 2nd argument true you can ensure that the test thread does not miss the signal.

           

          I hope that helps you achieve what you need. If there is anything you don't follow or you think I have made any mistakes in explaining this or in my rules please ask for clarification.

           

          regards,

           

          Andrew Dinn