6 Replies Latest reply on Nov 18, 2016 6:13 AM by gaurav6281

    Dynamic Byteman Rule Injection

    gaurav6281

      Hi,

       

      I am using JDK 6.  Tried both Byteman versions 2.1.2  and 3.0.6.    Both Java program and agent / rule installation and submission of rule is happening on my local Windows machine  only.

       

      I want to inject my agent and rule dynamically inside already running java process.  But I am not getting expected result and also no errors.

       

      My Java process which is running with ProcessId :- 15356

       

      public class TestByteMan {

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

              System.out.println("Inside Main");

              int x=1;

              Thread.sleep(120000);

              System.out.println("x is "+x);

              int y =add();

              System.out.println("Y is "+y);

              int z=x+y;

              System.out.println("Final Addition result is "+z);

          }

                     

          private static int add(){

                return 3;

      }

      }

       

       

      My btm file (rule file is)

       

      RULE Testing Going On

      CLASS TestByteMan

      METHOD main

      AFTER WRITE $y

      IF $y==3

      DO traceln("hello World")

      ENDRULE

       

       

      (a) I am installing agent dynamically as below:-

       

      C:\Users\gaurav>java -classpath "C:\Users\gaurav\BYTEMAN_TESTING\install\*" org.jboss.byteman.agent.install.Install -h localhost -p 12345 -Dorg.jboss.byteman.verbose 15356      ---(where C:\Users\gaurav\BYTEMAN_TESTING\install contains both byteman.jar , jdk 6 tools.jar and byteman-install.jar)

       

      When above command is fired in another command prompt , I can see below getting printing in the first command prompt where Java process is running.

       

      C:\Users\gaurav>java TestByteMan

      Inside Main

      Setting org.jboss.byteman.verbose=

      TransformListener() : accepting requests on localhost:12345

       

      (b) I am submitting the rules dynamically as below:-

       

      C:\Users\N662245>java -classpath "C:\Users\gaurav\BYTEMAN_TESTING\submit\*" org.jboss.byteman.agent.submit.Submit -h localhost -p 12345 -l test.btm ---(where C:\Users\gaurav\BYTEMAN_TESTING\submit   contains tools.jar, byteman.jar and byyteman-submit.jar)

      install rule Testing Going On

       

      When above command is fired in another command prompt , I can see below getting printing in the first command prompt where Java process is running.

       

      TransformListener() : handling connection on port 12345

      retransforming TestByteMan

      org.jboss.byteman.agent.Transformer : possible trigger for rule Testing Going On in class TestByteMan

      org.jboss.byteman.agent.Transformer : inserted trigger for Testing Going On in class TestByteMan

       

      My Expectations:- After int y=add() statement in my above java class, my logger statement of "hello World"  should get printed and then Y is 3  should be printed.   I am submitting and injecting rule well within 2 mins of sleep which I have given in my main java class.

       

      But actual result is :-(without my rule getting worked)

       

       

      C:\Users\N662245>java TestByteMan

      Inside Main

      Setting org.jboss.byteman.verbose=

      TransformListener() : accepting requests on localhost:12345

      TransformListener() : handling connection on port 12345

      retransforming TestByteMan

      org.jboss.byteman.agent.Transformer : possible trigger for rule Testing Going On in class TestByteMan

      org.jboss.byteman.agent.Transformer : inserted trigger for Testing Going On in class TestByteMan

      x is 1

      Y is 3

      Final Addition result is 4

       

       

       

      Can someone please guide me why it is not working ?

        • 1. Re: Dynamic Byteman Rule Injection
          adinn

          Hi Gaurav,

           

          Can someone please guide me why it is not working ?

           

          Yes, I can :-)

           

          You are injecting changes into method main after your thread has started executing the method. Byteman has clearly injected your rule into the code and modified the bytecode successfully (otherwise you would be seeing errors). However, your thread is still executing the old version of the bytecode. It cannot suddenly switch into the new bytecode for a method in the middle of a call to that method. There is no way to make that work.

           

          To understand that just consider that a bytecode transformer can do anythgin to redefine the bytecode. Let's assume it redefined method main as follows.

           

          public static void main(String[] args) throws Exception{
            System.out.println("Inside Main");
            int x=1;
            int x1 = Integer.valueOf(args[0]);
            Thread.sleep(120000);
            if (x1 == 3) {
              return;
            }
            System.out.println("x is "+x);
            int y = add();
            System.out.println("Y is "+y);
            int z=x+y;
            System.out.println("Final Addition result is "+z);
          }
          

           

          If that happened when your thread was in the middle of the Thread.sleep() call how would you get the thread to continue when it returned from the sleep() call? There is an obvious place where you might decide to continue (after the call to sleep()) but at that point you have no value for variable x1.

           

          So, the way the JVM works is that threads will only execute the updated version of a method at their next call to that method (and since main only gets called once ...).

           

          If you change your main method so it calls out to another method after the wait and you change your rule so it redefines the called method then the call will use the new version.Here's something that ought to work.

           

          public class TestByteMan {
            public static void main(String[] args) throws Exception{
              System.out.println("Inside Main");
              Thread.sleep(120000);
              submain();
            }
            public static void submain() {
              int x = 1;
              System.out.println("x is "+x);
              int y = add();
              System.out.println("Y is "+y);
              int z=x+y;
              System.out.println("Final Addition result is "+z);
            }
                          
            private static int add(){
              return 3;
            }
          }
          

           

          RULE Testing Going On
          CLASS TestByteMan
          METHOD submain
          AFTER WRITE $y
          IF $y==3
          DO traceln("hello World")
          ENDRULE
          

           

          regards,

           

           

          Andrew Dinn

          • 2. Re: Dynamic Byteman Rule Injection
            gaurav6281

            Hi Andrew,

             

            I changed the java and rule file as your above method. Recompiled java, started Java, installed agent into the new java process id and submitted rule. But still the end result is same.(no Hello world printed)

             

            C:\Users\gaurav>java TestByteMan

            Inside Main

            Setting org.jboss.byteman.verbose=

            TransformListener() : accepting requests on localhost:12345

            TransformListener() : handling connection on port 12345

            retransforming TestByteMan

            org.jboss.byteman.agent.Transformer : possible trigger for rule Testing Going On in class TestByteMan

            org.jboss.byteman.agent.Transformer : inserted trigger for Testing Going On in class TestByteMan

            x is 1

            Y is 3

            Final Addition result is 4

            • 3. Re: Dynamic Byteman Rule Injection
              adinn

              GAURAV BHATNAGAR wrote:

               

              Hi Andrew,

               

              I changed the java and rule file as your above method. Recompiled java, started Java, installed agent into the new java process id and submitted rule. But still the end result is same.(no Hello world printed)

               

              If you run this command (assuming your rule file is called myrule.btm)

               

                $ %BYTEMAN_HOME%\bin\bmcheck -cp . myrule.btm
              

               

              it will tell you that your rule fails to match the method in your class. The output will be something like this:

               

              Checking rule Testing Going On against class TestByteMan
              WARNING : Problem type checking rule "Testing Going On" loaded from test.btm line 5 against method submain() void
              org.jboss.byteman.rule.exception.TypeWarningException: no matching injection point for method submain() void
              WARNING : Problem type checking rule "Testing Going On" loaded from test.btm line 5
              org.jboss.byteman.rule.exception.TypeWarningException: failed to find any matching trigger method in class TestByteMan
              
              TestScript: 2 total warnings
                          2 type warnings
              

               

              That's happening because it does not know what $y is referring to. You need to compile your class with flag -g.

               

              The verbose trace output is actually misleading. Byteman tried to inject the rule into TestByteMan.submain but it didn't match because the class it looked at didn't have a variable y in that method. That's not an error because the rule might be intended for another class/method where y is defined. It causes a warning to be generated which the verbose trace doesn't mention.

               

              You can see details of the warning if you run command bmsubmit a second time (with no arguments) after you have uploaded the rule while the thread is still sleeping. The output will be somethubg like this:

               

              $ %BYTEMAN_HOME%\bin\bmsubmit
              # File test.btm line 5
              RULE Testing Going On
              CLASS TestByteMan
              METHOD submain
              NOCOMPILE
              AFTER WRITE $y
              IF $y==3
              DO traceln("hello World")
              ENDRULE
              Transformed in:
              loader: sun.misc.Launcher$AppClassLoader@18b4aac2
              trigger method: TestByteMan.submain() void
              threw org.jboss.byteman.rule.exception.TypeWarningException: no matching injection point for method submain() void
              org.jboss.byteman.rule.exception.TypeWarningException: no matching injection point for method submain() void
              at org.jboss.byteman.agent.TransformContext.warn(TransformContext.java:235)
              at org.jboss.byteman.agent.adapter.RuleCheckMethodAdapter.checkBindings(RuleCheckMethodAdapter.java:72)
              at org.jboss.byteman.agent.adapter.RuleCheckMethodAdapter.visitEnd(RuleCheckMethodAdapter.java:205)
              at org.jboss.byteman.objectweb.asm.tree.MethodNode.accept(Unknown Source)
              at org.jboss.byteman.agent.adapter.BMLocalScopeMethodAdapter.visitEnd(BMLocalScopeMethodAdapter.java:62)
              at org.jboss.byteman.objectweb.asm.ClassReader.b(Unknown Source)
              at org.jboss.byteman.objectweb.asm.ClassReader.accept(Unknown Source)
              at org.jboss.byteman.objectweb.asm.ClassReader.accept(Unknown Source)
              at org.jboss.byteman.agent.TransformContext.transform(TransformContext.java:117)
              at org.jboss.byteman.agent.Transformer.transform(Transformer.java:745)
              at org.jboss.byteman.agent.Transformer.tryTransform(Transformer.java:812)
              at org.jboss.byteman.agent.Transformer.tryTransform(Transformer.java:784)
              at org.jboss.byteman.agent.Transformer.transform(Transformer.java:257)
              at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
              at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
              at java.lang.ClassLoader.defineClass1(Native Method)
              at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
              at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
              at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
              at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
              at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
              at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
              at java.security.AccessController.doPrivileged(Native Method)
              at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
              at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
              at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
              at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
              at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)
              Transformed in:
              loader: sun.misc.Launcher$AppClassLoader@18b4aac2
              trigger class: TestByteMan
              threw org.jboss.byteman.rule.exception.TypeWarningException: failed to find any matching trigger method in class TestByteMan
              org.jboss.byteman.rule.exception.TypeWarningException: failed to find any matching trigger method in class TestByteMan
              at org.jboss.byteman.agent.TransformContext.notifyRules(TransformContext.java:318)
              at org.jboss.byteman.agent.TransformContext.transform(TransformContext.java:161)
              at org.jboss.byteman.agent.Transformer.transform(Transformer.java:745)
              at org.jboss.byteman.agent.Transformer.tryTransform(Transformer.java:812)
              at org.jboss.byteman.agent.Transformer.tryTransform(Transformer.java:784)
              at org.jboss.byteman.agent.Transformer.transform(Transformer.java:257)
              at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
              at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
              at java.lang.ClassLoader.defineClass1(Native Method)
              at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
              at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
              at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
              at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
              at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
              at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
              at java.security.AccessController.doPrivileged(Native Method)
              at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
              at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
              at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
              at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
              at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)
              

               

              You should always use script bmcheck.bat to check that your rules have no errors and that they can actually be injected into your target class.

               

              regards,

               

               

              Andrew Dinn

              • 4. Re: Dynamic Byteman Rule Injection
                gaurav6281

                Thanks Andrew for the trick of submitting twice to see the failure. I did  the same and have got the same error as you mentioned. But I have doubly checked submain is the method name in java class as well as in my byteman rule file(with exact same case and spelling) so not sure what to change. I have even tried compiling with javac -g option but difference.

                 

                Kindly suggest how to make it work.

                • 5. Re: Dynamic Byteman Rule Injection
                  adinn

                  GAURAV BHATNAGAR wrote:

                   

                  Thanks Andrew for the trick of submitting twice to see the failure. I did the same and have got the same error as you mentioned. But I have doubly checked submain is the method name in java class as well as in my byteman rule file(with exact same case and spelling) so not sure what to change. I have even tried compiling with javac -g option but difference.

                   

                  Kindly suggest how to make it work.

                   

                  Have you checked the rule using the bmcheck script as I suggested?

                   

                  I am afraid I really don't know what more I can suggest.

                   

                  If you saw the warning from submit then it indicates that the rule can be parsed and that the class and method name have been matched to your class. Which means also that Byteman has tried to inject the rule into the method.  If you see a type warning in the listing from bmsubmit then that can only mean that the rule does not actually match the  code: either because the location or one of the variable names specified in the rule is not found in the method (it it was found but had the wrong type you would get a type error).

                   

                  Please check that your bytecode really has been compiled with 'java -g'. I am suggesting this because I  recall that you had a problem earlier when you thought it was compiled with -g and it turned out that it was not.

                   

                  One way to check your bytecode is to decompile it  using the javap program. Pass flags -c and -v to request decompilation of method bytecode and verbose output. If you have compiled with -g then the output will contain details of the LocalVariableTable attribute for each method which will show each known local var and the bytecode positions at which it is valid. If you have not compiled with -g then no LocalVariableTable ouptut will be displayed.

                  • 6. Re: Dynamic Byteman Rule Injection
                    gaurav6281

                    Thanks a lot Andrew for the pointers.  I did run javap -c -v TestByteMan   and saw my local variables were not getting printed (even though I compiled my class using javac -g TestByteMan.java...this I don't know yet..)

                     

                    Then I changed my local variable names , deleted old .class file , reran with javac -g and then java  and it worked.

                     

                    I also checked with bmsubmit as you suggested and its really great.

                     

                    Many thanks.