8 Replies Latest reply on Sep 23, 2016 11:46 AM by GAURAV BHATNAGAR

    Rule with Private method not getting invoked

    GAURAV BHATNAGAR Newbie

      Hi,

       

      I am using JDK 8 and 3.0.6 bytemap.jar. I was able to run dsl which prints system.outs at start and end of the method. Not I want to put rule inside some statement of a private static method. How can I do that ?

       

      Not I am trying to print stack trace of the one of the thread execution. My .btm file looks like :-

       

      RULE trace main entry
      CLASS ThreadInteraction
      METHOD main
      AT ENTRY
      IF true
      DO traceln("entering main")
      ENDRULE

       

      RULE trace thread stack
      CLASS ThreadInteraction
      METHOD workingConsumer
      AFTER READ Thread.currentThread().getName()   ( I have even tried AFTER READ $threadName)
      IF true
      DO traceln(formatStack())
      ENDRULE

       

      RULE trace main exit
      CLASS ThreadInteraction
      METHOD main
      AT EXIT
      IF true
      DO traceln("exiting main")
      ENDRULE

       

       

      My Java Class looks like below(I have created 2 private methods and want to print stack trace of the thread running from 1st line of one of the private method. But 2nd rule in above .btm is not running.

      Kindly let me know

      (1) How to call private method?

      (2) How to inject rule say from in between the method after 3rd or 4th line say after some System.out ..line or some variable assignment etc. I mean can we directly put exact same line of code in .btm file as DO ?

      (3) On basis of some condition of 1 thread running , can I pause all other running threads ? If so , what rule is it ?

       

      My java class is asbelow:-

       

      import java.io.BufferedReader;

      import java.io.InputStream;

      import java.io.InputStreamReader;

      import java.util.concurrent.*;

       

      class ThreadInteraction {

       

         static BlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<Integer>();

       

         public static void main(String[] args) throws Exception {

        ExecutorService executorService = Executors.newFixedThreadPool(2);

         final Future<Object> submit = executorService.submit(() -> {

         return workingProducer();

        });

         final Future<Object> submit1 = executorService.submit(() -> {

         return workingConsumer();

        });

        }

       

         private static Object workingConsumer() throws InterruptedException {

        String threadName = Thread.currentThread().getName();

        System.out.println("Submitting Task2..." + threadName);

         while (true) {

        System.out.println("Thread " + threadName + " taking out " + blockingQueue.take() + " from the blocking Queue");

        }

        }

       

         private static Object workingProducer() throws InterruptedException {

        String threadName = Thread.currentThread().getName();

        System.out.println("Submitting Task1..." + threadName);

         int i = 0;

         while (true) {

         blockingQueue.put(i++);

        System.out.println("Thread " + threadName + " inserting " + i + " into the blocking Queue");

        Thread.sleep(1000);

        }

        }

      }

        • 1. Re: Rule with Private method not getting invoked
          GAURAV BHATNAGAR Newbie

          I am trying to produce a scenario where producer thread after sending 6 messages on a queue , pauses and consumer thread triggers then.

           

          Now I am trying to call local variable messageCount from a private method but I am getting  RuleCheckMethodAdapter.checkBindings : invalid local variable binding $messageCount checking method workingProducer()Ljava/lang/Object;   for variable $messageCount

           

          My Java Local method is like :-

           

          private static Object workingProducer() throws InterruptedException {

            String producerThreadName = Thread.currentThread().getName();

            System.out.println("Producer Thread..." + producerThreadName);

             int messageCount = 0;

             while (true) {

             blockingQueue.put(messageCount++);

            System.out.println("Thread " + producerThreadName + " inserting " + messageCount + " into the blocking Queue");

            Thread.sleep(1000);

            }

          }

           

          My .btm file :-

           

          RULE run producer then halt but consumers continue to run

          CLASS ThreadInteraction

          METHOD workingProducer

          AFTER WRITE $producerThreadName

          IF $messageCount = 6

          DO traceln("with message count"+$messageCount)

          waitFor($threadName)    (I want to pause the thread ..don't know how ...)

          ENDRULE

           

           

          Please provide some inputs.

          • 2. Re: Rule with Private method not getting invoked
            Andrew Dinn Master

            GAURAV BHATNAGAR wrote:

             

            Now I am trying to call local variable messageCount from a private method but I am getting RuleCheckMethodAdapter.checkBindings : invalid local variable binding $messageCount checking method workingProducer()Ljava/lang/Object; for variable $messageCount

             

            There is a reason for that :-). Here is your code formatted a bit more clearly (with line numbers):

             

            private static Object workingProducer() throws InterruptedException {
              String producerThreadName = Thread.currentThread().getName();
              System.out.println("Producer Thread..." + producerThreadName);
              int messageCount = 0;
              while (true) {
                blockingQueue.put(messageCount++);
                System.out.println("Thread " + producerThreadName + " inserting " + messageCount + " into the blocking Queue");
                Thread.sleep(1000);
              }
            

             

            and here is your RULE (I have added a missing semicolon and a comment character for your commewnt

             

            RULE run producer then halt but consumers continue to run
            CLASS ThreadInteraction
            METHOD workingProducer
            AFTER WRITE $producerThreadName
            IF $messageCount = 6
            DO traceln("with message count"+$messageCount);
               waitFor($threadName) # (I want to pause the thread ..don't know how ...)
            ENDRULE
            

             

            The rule uses location AFTER WRITE $producerThreadName i.e. the point where local variable producerThreadName is first written, So, that is line 2 of the listing above. It is trying to refer to local variable messageCount. Now that variable is only declared at line 4. So, at the point where you are trying to inject the rule code variable messageCount does not exist.You are asking Byteman to modify the bytecode so it looks like it comes from source code that was something like this:

             

            private static Object workingProducer() throws InterruptedException {
              String producerThreadName = Thread.currentThread().getName();
              if (messageCount == 6) {
                traceln("with message count"+$messageCount);
                waitFor($threadName); // (I want to pause the thread ..don't know how ...)
              }
              System.out.println("Producer Thread..." + producerThreadName);
              int messageCount = 0;
              while (true) {
                blockingQueue.put(messageCount++);
                System.out.println("Thread " + producerThreadName + " inserting " + messageCount + " into the blocking Queue");
                Thread.sleep(1000);
              }
            

             

            That wouldn't get past a java compiler because at line 3 you are referring to variable messageCount before it has been declared. So, Byteman is telling you the same thing.

             

            What you really want to do is inject the rule into the while loop after the 6th message send. So, you need to use a different location

             

            RULE run producer then halt but consumers continue to run
            CLASS ThreadInteraction
            METHOD workingProducer
            AFTER call put
            IF $messageCount = 6
            DO traceln("with message count"+$messageCount);
              waitFor($threadName) # (I want to pause the thread ..don't know how ...)
            ENDRULE
            

             

            This means the injected code will follow the put call inside the loop. After the 6th put call the value of messageCount will indeed be 6 so the rule will fire at that point.

             

            However, that's not finished yet because your rule has another error -- well, two related errors actually. The first one is that you have called waitFor($threadName) but there is no such local variable. I think what you actually meant to use was $producerThreadName. That change will make your rule parse and type check ok but it probably won't allow your producer and consumer to meet up.

             

            I am assuming that a similar rule in your consumer is going to call helper method signalWake(XXX) with some argument XXX. In order for a wait and a notify call to match each other and cause two threads to synchronize the argument passed to these methods has to be the same. Byteman creates a Waiter object when waitFor is called and it labels it using the input argument to waitFor. It then suspends the thread, hanging it off the Waiter object. When another thread calls signalWake it uses the input argument to look up the Waiter and restarts the suspended thread. If you don't use the same argument then the call to signalWake will not find the Waiter. So, if you pass the producer thread's name to the waitFor call in the producer then you will also need to pass the producer thread's name in the signalWake call made by the consumer thread. How is your consumer going to obtain the name of the producer thread?

             

            What you really need is pass some value shared by both threads to the waitFor and signalWake call. The most obvious solution is to use a constant value like a String or integer. So, you could call waitFor("data read") in the producer rule and signalWake("data read") in the consumer rule. This ensures that the two threads are waiting on the same waiter.

             

            That solution is ok so long as you only have one producer and one consumer thread. However, if you have several producer-consumer pairs then that's going to confuse things because both producers will be waiting on the same Waiter and both consumers will be signalling the same Waiter. This may well mean that the signals get mixed up and one of your waiters may end up waiting for ever.

             

            Luckily, you already have what you need to resolve this problem. Each producer-consumer pair is shares a queue used to transfer the data. So, you can use that to label the Waiter. Here is a rule which will work for your producer and should allow you to provide an equivalent rule for your consumer.

             

            RULE run producer then halt but consumers continue to run
            CLASS ThreadInteraction
            METHOD workingProducer
            AFTER call put
            IF $messageCount = 6
            DO traceln("with message count"+$messageCount);
              waitFor($this.blockingQueue) # label waiter with queue
            ENDRULE
            

             

            n.b. I have assumed that blockingQueue is a field of the ThreadInteraction class so I have used $this.blockingQueue to refer to the queue ($this identifies the ThreadInteraction instance that is executing method workingProducer so you can use the normal field accessor syntax to access field blockingQueue) . I hope that is correct. If not you may have to adjust the rule according to whatever definition you have for this value.

             

            I'll leave you to try to write the corresponding rule to inject into the consumer method. Let me know if you get it working.

             

            Also, here's a hint: you may want to look up the documentation of signalWake to check what the optional second argument is for. It's unlikely but you may experience occasional problems if you don't pass value true for this second argument (if you omit it the default is false).

             

            regards,

             

             

            Andrew Dinn

            • 3. Re: Rule with Private method not getting invoked
              GAURAV BHATNAGAR Newbie

              Thanks Andrew for detailed response.   In Intellij I have given compiler option as -g (and even tried with -g --) but still rule related to local variable $messageCount is not getting executed, no error this time as , I have corrected execution line as per your suggestion to AFTER call put.

               

              RULE run producer then halt but consumers continue to run

              CLASS ThreadInteraction

              METHOD workingProducer

              AFTER call put

              IF $messageCount = 6

              DO traceln("message count value is "+$messageCount);

                waitFor($producerThreadName)

              ENDRULE

               

               

              JAVA method is :-

               

              private static Object workingProducer() throws InterruptedException {

                String producerThreadName = Thread.currentThread().getName();

                System.out.println("Producer Thread Name is..." + producerThreadName);

                 int messageCount = 0;

                 while (true) {

                 blockingQueue.put(messageCount++);

                System.out.println("Thread " + producerThreadName + " inserting " + messageCount + " into the blocking Queue");

                 // sleep for 1 seconds so that we can see in console how threads are working
                 Thread.sleep(1000);

                }

              }

              • 4. Re: Rule with Private method not getting invoked
                Andrew Dinn Master

                Oops, just spotted another error in the rule:

                 

                IF $messageCount = 6

                 

                that should be

                 

                IF $messageCount == 6

                 

                n.b. just in case you are not already doing so I recommend that you check your rule is valid using the bmcheck batch script provided in the download's bin directory before you try running with it. In this case it wold have told you that the IF condition does not have type boolean.

                 

                The Programmer's Guide explains how to use bmcheck.

                 

                regards,

                 

                 

                Andrew Dinn

                1 of 1 people found this helpful
                • 5. Re: Rule with Private method not getting invoked
                  GAURAV BHATNAGAR Newbie

                  Thanks Andrew for bmcheck script.

                   

                  Is there any other way than -g compiler option to use local method variables ? Because even if I have compiler options as -g , rule is not getting invoked and no error too.

                   

                  RULE run producer then halt but consumers continue to run

                  CLASS ThreadInteraction

                  METHOD workingProducer

                  AFTER call put

                  IF $messageCount == 6

                  DO traceln("message count value is "+$messageCount);

                    waitFor($producerThreadName)

                  ENDRULE

                   

                  private method is :-

                   

                  private static Object workingProducer() throws InterruptedException {

                    String producerThreadName = Thread.currentThread().getName();

                    System.out.println("Producer Thread Name is..." + producerThreadName);

                     int messageCount = 0;

                     while (true) {

                     blockingQueue.put(messageCount++);

                    System.out.println("Thread " + producerThreadName + " inserting " + messageCount + " into the blocking Queue");

                     // sleep for 1 seconds so that we can see in console how threads are working
                     Thread.sleep(1000);

                    }

                  }

                  compiler option.jpg

                  • 6. Re: Rule with Private method not getting invoked
                    Andrew Dinn Master

                    Hi Gaurav,

                     

                    Ok, I see form the picture oyu have posted that you have ticked the IntelliJ option 'generate debugging info'. That means that the compiler will be passed the '-g' flag when IntelliJ asks it to compile your code. So, you don't need to supply it as an extra option.

                     

                    Is there any other way than -g compiler option to use local method variables ? Because even if I have compiler options as -g , rule is not getting invoked and no error too.

                     

                    You must compile with '-g' in order for Byteman to be able to know that there is a method local variable in your source program. That flag tells the compiler to store details of local variables in the bytecode for your class and Byteman is only able to read the bytecode.  If you don't ask the compiler to add that information to the bytecode then you cannot refer to a local variable in your Byteman rule. It doesn't make sense to ask if there is another way to do this.

                     

                    If you have compiled it using this flag then something else is wrong. I suggest you delete all the existing compiled class files and recompile from the sources (with a tick to say generate debugging info) just to make sure that they have been compiled correctly. If it fails at that point then something else is going wrong -- probably you have not set up IntelliJ so that it passes the correct -javaagent options to the program. How did you get Byteman to work before? Have you changed anything since then?

                    • 7. Re: Rule with Private method not getting invoked
                      Andrew Dinn Master

                      Hi Gaurav,

                       

                      Sorry, I just noticed that the rule specifies location AFTER call put. I am afraid it must be AFTER CALL put i.e. CALL must be upper case.

                      1 of 1 people found this helpful
                      • 8. Re: Rule with Private method not getting invoked
                        GAURAV BHATNAGAR Newbie

                        Thanks a lot Andrew, after using capital CALL, local variable is getting used. Many thanks for the suggestion.