6 Replies Latest reply on Nov 16, 2010 12:34 PM by jzillmann

    error accessing field of an inner class

    jzillmann

      Hi folks,

       

      i'm trying out byteman-1.4 on HttpClient-3.1 class (http://www.docjar.com/html/api/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java.html).

       

      This is the full script:

      RULE rule1 trace create
      CLASS org.apache.commons.httpclient.MultiThreadedHttpConnectionManager
      METHOD doGetConnection(HostConfiguration, long)
      AT LINE 454
      IF TRUE
      DO 
          traceStack("*** huhuh " + $0, 15);
          debug("maxHostConnections "+ $maxHostConnections);
          debug("maxHostConnections "+ $hostPool.numConnections)
      ENDRULE
      

       

      traceStack and the first debug are successfull, the second debug fails with

       

      rule1 trace create : org.jboss.byteman.rule.exception.ExecuteException: FieldExpression.interpret : error accessing field numConnections file byteman-1.4.0/sample/scripts/S3Monitor.txt line 59

       

      hostPool is an instance of the inner class HostConnectionPool and numConnections is and public int field.

      I guess the inner class is making problems. I debugged a little bit and saw that the $hostPool.numConnections i translated into the binding $$.

       

      Is there any workarount for this ?

      best regards

      Johannes

        • 1. Re: error accessing field of an inner class
          adinn

          Hi Johannes,

           

          Thanks for reporting this.

          Johannes Zillmann wrote:

          . . .

          traceStack and the first debug are successfull, the second debug fails with

           

          rule1 trace create : org.jboss.byteman.rule.exception.ExecuteException: FieldExpression.interpret : error accessing field numConnections file byteman-1.4.0/sample/scripts/S3Monitor.txt line 59

           

          hostPool is an instance of the inner class HostConnectionPool and numConnections is and public int field.

          I guess the inner class is making problems. I debugged a little bit and saw that the $hostPool.numConnections i translated into the binding $$.

          I am not sure exactly what is going on here. It may relate to the fact that this is an inner class instance or it may be that the reference to $hostPool is being handled erroneously. The error message indicates that the field access (a call to Field.get()) is throwing an IllegalAccessException. If $hostPool does indeed dereference to a HostConnectonPool and the field object idenitfies field numConnections then this ought not to be happening.

           

          I will try to replicate the problem, although that might require reducing it to a simpler case so as to avoid having to write an app to use the Apache class.

           

          When you say you 'saw that the $hostPool.numConnections i translated into the binding $$.' can you explain that a little further? Can you break the code at tyhe point wher ethe error occurs? If so then could you provide me with details of the FieldExpression instance? That might help pin down the problem. In particular, it would help if you could print the values of the following fields

          fieldName

          pathList (the array's size and each String in the array)

          owner (preferably not just the Expression object but also its type and the basic details of its fields)

          ownerType (ownerType.getName() will do)

          field (field.getName() and field.getdeclaringClass.getname() will do)

          indirectStatic (if this is non-null then not just the Expression object but also its type and the basic details of its fields)

           

          I'll see what I can find out in the meantime.

           

          regards,

           

           

          Andrew Dinn

          • 2. Re: error accessing field of an inner class
            adinn

            Hi Johannes,

             

            I think I have found the problem. The code injected at the trigger point is valid, as is the interpreter code executing the field get operation. The problem is actually in the trigger injection code. The code gets injected more than once i.e. it is injected at line 454, 455, 456, etc. Oops!

             

            I appear to have introduced a regression into AT LINE injection at some point in the recent past. The method which injects the code is this

             

                public void visitLineNumber(final int line, final Label start) {
                    if (unlatched && !visitedLine && (targetLine <= line)) {
                        injectTriggerPoint();
                    }
                    super.visitLineNumber(line, start);
                }
            

             

            Of course the correct version should be

             

                public void visitLineNumber(final int line, final Label start) {
                    if (unlatched && !visitedLine && (targetLine <= line)) {

                        injectTriggerPoint();

                        visitedLine = true;
                    }

                    super.visitLineNumber(line, start);
                }

             

            The boolean visitedLine is supposed to stop injection happening more than once.

             

            I will raise a JIRA for this and fix it in release 1.4.1.

             

            Unfortunately, I don't think there is a suitable alternative location you can use to target the point in the code you are interested in.. For example, if you try to use AFTER CALL getHostPool then this will inject the code at roughly the same spot. However, hostPool will not have been declared or assigned at this point so your rule will fail at typecheck.

             

            It would be nice if you could use AFTER WRITE hostPool to specify the location immediately after the local variable write. Unfortunately READ/WRITE locations currently only apply to object fields. I would like to extend this location so that it also detected lcoal variable reads/writes but that is not yet available (I will raise a JIRA for this too).

             

            Another thing I would like to enable is for AFTER CALL rules to refer to the value returned form the call using $!. That would allow you to use location AFTER CALL getHostPool and replace $hostPool with $! in your rule. Unfortunately, this feature is also not yet available (another JIRA needed).

             

            So, I am afraid you will have to wait until I get a patch release out which fixes the AT LINE problem. I'll try to do that in the next week if I can find the time. Thanks once again for reporting this.

             

            regards,

             

             

            Andrew Dinn

            • 3. Re: error accessing field of an inner class
              adinn

              Here is the JIRA for the AT LINE injection problem

               

              https://jira.jboss.org/browse/BYTEMAN-131

              • 4. Re: error accessing field of an inner class
                jzillmann

                Thanks for the reply Andrew!

                 

                In the meantime i fixed my problem so its not an urgent issue for me any more. Byteman was a help, i'm looking forward to use it in other cases, it definitly open some new possiblities in different kind of areas!

                 

                Just i'm listing some feedback about some small things which kind of delayed my "success" with byteman:

                1. the code between DO and ENDRULE: if one has multiple lines, the lines have to be separated with ; but the last line must not end with ;. Took me a while until i figured this out and it is kind of annoying if you are in the experimenting phase and you continously have to add or remove the semicolon if you add or remove a line.
                2. referencing an instance field. In the beginning i tried this statement Sytem.out.println($numConnections) instead of Sytem.out.println($this.numConnections). In the first case everything compiles but the println simply does not occur during execution. I would expect an exception here
                3. Uncomment code: Is there a way to comment code between DO and ENDRULE out ? I tried // and # but i *think* neither of it worked...

                 

                So thanks for the great work!

                Johannes

                • 5. Re: error accessing field of an inner class
                  adinn

                  Hi Johannes,

                   

                  Johannes Zillmann wrote:

                   

                  Thanks for the reply Andrew!

                   

                  In the meantime i fixed my problem so its not an urgent issue for me any more. Byteman was a help, i'm looking forward to use it in other cases, it definitly open some new possiblities in different kind of areas!

                  Excellent. Are you able to say anything more about what you are using Byteman for? I'm keen to identify what people are doing wiht it so I can identify where to put in further development effort.

                   

                  Johannes Zillmann wrote:

                   

                  Just i'm listing some feedback about some small things which kind of delayed my "success" with byteman:
                  1. the code between DO and ENDRULE: if one has multiple lines, the lines have to be separated with ; but the last line must not end with ;. Took me a while until i figured this out and it is kind of annoying if you are in the experimenting phase and you continously have to add or remove the semicolon if you add or remove a line.

                  Yes, according to the current grammar ';' is a separator rather than a terminator. However, I might be able to tweak the grammar so that an extra ';' at the end of the BIND or DO clauses is quietly ignored. If you want this as a feature please raise a JIRA for it.

                  Johannes Zillmann wrote:

                   

                  Thanks for the reply Andrew!

                   

                  I

                  1. ...
                  2. referencing an instance field. In the beginning i tried this statement Sytem.out.println($numConnections) instead of Sytem.out.println($this.numConnections). In the first case everything compiles but the println simply does not occur during execution. I would expect an exception here

                  This is a bit of a grey area because the rule fails to match the code -- there is no local variable called numConnections in the indicated method. So, the agent will have tried to inject the rule but will not have succeeded in doing so. You see no output because the rule never gets executed.

                   

                  In general, a match failure may indicate that there is a problem or may just be somethhing to be ignored because there is some other lcoation which will match. In cases where e.g. you use an unqualified classname such as Foo or a method name without a signature such as myMethod it may be that you get several candiadte matches but only one which actually meets the full specification i.e. it contains the specified line number and has a suitable local var. Printing an error message in such a case might b useful but it might also be considered misleading.

                   

                  Given that your rule specifies the fully qualified classname and the method signature there is little room for ambiguity here. However, even in this case it might be that you load another version of this class in another classloader which does happen to include that local variable (e.g. if you embedded different versions fo the jar in two separate war deployments.

                   

                  So, rather than generate an error message to the console Byteman just records the match failure quietly. If you run the submit.sh script in the bin directory the listing that it prints out will indicate that injection ito the rule was attempted but failed. It will also print the classloader in whcih this injecton was attempted. So, the information is available but it is not pushed at you. If you switch on verbose trace you will see this along with a lot of other info. If you think some more obvious reporting procedure should be configurable then raise a JIRA and I will consider addding it.

                   

                  Johannes Zillmann wrote:


                  1. ...
                  2. ...
                  3. Uncomment code: Is there a way to comment code between DO and ENDRULE out ? I tried // and # but i *think* neither of it worked...

                  The tokenizer should delete any text starting with a # up to end of line and the parser should ignore the resulting white space. So, the following ought to work:

                   

                  RULE foo
                  CLASS Foo
                  METHOD foo
                  # This is a comment
                  AT LINE 999
                  IF TRUE
                  DO traceln("Foo.foo:line 999")
                  # this is another comment
                  ENDRULE
                  

                   

                  If you can provide a rule which definitely does not parse because of an embedded comment please raise a JIRA.

                   

                  By the way I also raised two JIRAs for the other features I mentioned in a previous post:

                   

                  https://jira.jboss.org/browse/BYTEMAN-132

                  https://jira.jboss.org/browse/BYTEMAN-133

                   

                  regards,

                   

                   

                  Andrew Dinn

                  • 6. Re: error accessing field of an inner class
                    jzillmann

                    Andrew Dinn wrote:

                     

                    Excellent. Are you able to say anything more about what you are using Byteman for? I'm keen to identify what people are doing wiht it so I can identify where to put in further development effort.

                     

                    well at the moment its just a list of thoughts which poped up in my mind... But enterprise applications with an extended capability for debugging and patching without re-deploying them is the central thing...

                     


                    1. the code between DO and ENDRULE: if one has multiple lines, the lines have to be separated with ; but the last line must not end with ;. Took me a while until i figured this out and it is kind of annoying if you are in the experimenting phase and you continously have to add or remove the semicolon if you add or remove a line.

                    Yes, according to the current grammar ';' is a separator rather than a terminator. However, I might be able to tweak the grammar so that an extra ';' at the end of the BIND or DO clauses is quietly ignored. If you want this as a feature please raise a JIRA for it.

                     

                    Done that! https://jira.jboss.org/browse/BYTEMAN-134 !

                     


                    This is a bit of a grey area because the rule fails to match the code -- there is no local variable called numConnections in the indicated method. So, the agent will have tried to inject the rule but will not have succeeded in doing so. You see no output because the rule never gets executed.

                     

                    In general, a match failure may indicate that there is a problem or may just be somethhing to be ignored because there is some other lcoation which will match. In cases where e.g. you use an unqualified classname such as Foo or a method name without a signature such as myMethod it may be that you get several candiadte matches but only one which actually meets the full specification i.e. it contains the specified line number and has a suitable local var. Printing an error message in such a case might b useful but it might also be considered misleading.

                     

                    Given that your rule specifies the fully qualified classname and the method signature there is little room for ambiguity here. However, even in this case it might be that you load another version of this class in another classloader which does happen to include that local variable (e.g. if you embedded different versions fo the jar in two separate war deployments.

                     

                    So, rather than generate an error message to the console Byteman just records the match failure quietly. If you run the submit.sh script in the bin directory the listing that it prints out will indicate that injection ito the rule was attempted but failed. It will also print the classloader in whcih this injecton was attempted. So, the information is available but it is not pushed at you. If you switch on verbose trace you will see this along with a lot of other info. If you think some more obvious reporting procedure should be configurable then raise a JIRA and I will consider addding it.

                     

                    I see and i'm too less into it to make some valuable comment on that...

                     

                    The tokenizer should delete any text starting with a # up to end of line and the parser should ignore the resulting white space. So, the following ought to work:

                     

                    RULE foo
                    CLASS Foo
                    METHOD foo
                    # This is a comment
                    AT LINE 999
                    IF TRUE
                    DO traceln("Foo.foo:line 999")
                    # this is another comment
                    ENDRULE
                    

                     

                     

                    Probably i mixed this up with other errors!

                     

                    Thanks for all the insights!

                    Johannes