Advanced variables manipulation with Byteman

In this basic tutorial Introduction to Byteman we have learnt how to use Byteman tool to inject some bytecode in your Java classes, without modifying the actual code but by coding some rules files which can be further enhanced by Byteman Helper classes. In this tutorial we will go more in deep in this awesome tool to learn how to vary the application flow by changing a method variable.

So here’s a simple Java example class:

public class Test   
{  
  public void action(Pojo test) throws Exception  
  {  
    if (!test.isOk()) {  
      throw new Exception("Error in Test " + test);  
    }  
  }  
}

In this simple class we’re checking for the isSuccess method in the action method. In case the isSuccess returns false a new Exception is raised. Hacking the value of the function called in the method is quite simple. Here is a Byteman rule which will prevent the exception to get thrown:

RULE hack rule
CLASS Test  
METHOD action  
AT INVOKE isOk  
IF TRUE  
$test.ok = true  
ENDRULE  

What is worth mentioning is that the rule is injected into the Java code before method isOk is called. If you want to be more precise you could make it clearer by writing

AT INVOKE Test.isOk()

The assignment in the rule action ($test.ok = true ) uses $test to refer to the first parameter of method action by name. This way you can set the value of the Pojo’s ok class member to true. By the way, yuo can also refer to it by index using the syntax $1.

The list of location (AT) clauses is restricted to positions in the source code which clearly identify locations in the corresponding bytecode. For example

AT ENTRY
AT EXIT
AT INVOKE calledMethod
AT WRITE fieldName =
AT READ $localVarName
AT LINE nnn

Here’s one more example which detects a change in local variable using AFTER WRITE:

RULE Check Variable Write
CLASS Test
METHOD main
AFTER WRITE $y
IF $y==2
DO traceln("y is now set to 2")
ENDRULE

Finally, it’s worth mentioning that you can insert more than one expression in a DO clause to make your Rules more complex. The only twist is that you need to separate them using a ‘;’ — for example:

RULE  
...    
DO traceln("y is now set to 2");
traceStack()  
ENDRULE 

Publish server metrics via JMX using a Byteman Helper

In this tutorial we will learn how to unleash the power of Byteman by registering an Helper class which can trace some server attributes and publish them via JMX.

If you are new to Byteman I’d suggest reading the Base Documentation and the following tutorials we have published:

So we know the basics of Byteman. We will create an Helper class which will monitor a basic WebListener:

package com.sample;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionListener;
import javax.servlet.http.HttpSessionEvent;

@WebListener
public class SessionCounter implements HttpSessionListener {

    private int activeSessions = 0;

    public void sessionCreated(HttpSessionEvent se) {
        activeSessions++;

    }

    public void sessionDestroyed(HttpSessionEvent se) {
        if (activeSessions > 0) {
            activeSessions--;
        }
    }

    public int getActiveSessions() {
        return activeSessions;
    }

}

Now suppose we want to publish the count of activeSession without adding any line of code to the Web application. We will code an Helper class named JMXHelper that extends the org.jboss.byteman.rule.helper.Helper class. The base Helper class is responsible for handling a Byteman Rule file, providing the base syntax of it.

In our case, we will need additional functionalities as we want to publish an MBean, with the activeSessions, via JMX. Here is our JMXHelper class:

package org.jboss.byteman.sample.helper;

import org.jboss.byteman.rule.Rule;
import org.jboss.byteman.rule.helper.Helper;

import javax.management.MBeanServer;
import javax.management.ObjectName;

import java.lang.management.ManagementFactory;

import javax.management.StandardMBean;

public class JMXHelper extends Helper {
    static Data user = new Data("activeCount", 1);
  

    private static boolean DEBUG;

    public static void activated() {
        DEBUG = Boolean.getBoolean("org.jboss.byteman.sample.helper.debug");
        if (DEBUG) {
            System.err.println("JMXHelper  activated");
        }
    }

    public static void installed(Rule rule) {
        if (DEBUG) {
            System.err.println("JMXHelper installed, " + rule);
        }
    }

    protected JMXHelper(Rule rule) {
        super(rule);

    }

    public void registerHelperMBean(String name) {
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
        synchronized (this) {

            try {
                ObjectName oname = new ObjectName(name);

                if (mbs.isRegistered(oname) == false) {

                    StandardMBean mbean = new StandardMBean(user, DataMBean.class);

                    mbs.registerMBean(mbean, oname);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void traceToJMX(int active) {

        user.setValue(active);
        System.out.println("Number of Active Sessions: " + active);
    }

}

The method which is responsible for registering the MBean is registerHelperMBean which requires to be invoked at Server start. The other method is traceToJMX which merely sets the MBean attribute which will be passed from our Rule. Within the Helper class we referenced a Java Bean named DataMBean which is an ordinary Java Bean, fit to be used as StandardMBean:

package org.jboss.byteman.sample.helper;

 public interface DataMBean {
       
    public int getValue();
    
    public void setValue(int value);

    public String getAttribute();

    public void setAttribute(String attribute);
}

Now we will code our rule file- we will name it session.btm:

RULE Register Helper mbean
CLASS org.jboss.modules.Main
METHOD main
HELPER org.jboss.byteman.sample.helper.JMXHelper
AT EXIT
IF TRUE
DO registerHelperMBean("org.jboss.byteman:helper=JMXHelper")
ENDRULE


RULE Count Active Sessions
CLASS com.sample.SessionCounter
HELPER org.jboss.byteman.sample.helper.JMXHelper

METHOD sessionCreated(HttpSessionEvent)
AT EXIT

BIND count:int = $0.getActiveSessions();

IF true
DO traceln("Session count "+count);
traceToJMX(count);
 

ENDRULE

The first rule is used to register our MBean class through the JMXHelper. It fires when the application server starts.

The second rule is activated when the method sessionCreated of the SessionCounter class exits. At this point the number of activeSession is stored in the Rule variable named count and passed to the JMXHelper which will eventually update the MBean.

In order to run this Rule, you have to provide to the JAVA_OPTS the path to the Rule file and to the Helper class, which was packaged in a JAR file named Helper.jar and placed in /home/jboss/dist/ .

JAVA_OPTS="$JAVA_OPTS -Dorg.jboss.byteman.transform.all -javaagent:/home/jboss/byteman-download-3.0.3/lib/byteman.jar=script:/home/jboss/byteman-download-3.0.3/sample/scripts/session.btm,boot:/home/jboss/dist/ByteHelper.jar,boot:/home/jboss/byteman-download-3.0.3/lib/byteman.jar"

Update the above shell with the path to your files. Now start the application server and deploy the Web application which contains the SessionCounter class. You should be able to see on the Console, as soon as you invoke some JSP files:

INFO  [stdout] (default task-49) Number of Active HTTP Sessions: 2

Troubleshooting Byteman

Hint: If you are experiencing issues in firing your rules, include as well the -Dorg.jboss.byteman.verbose in the JAVA_OPTS

Now open JConsole (or any other tool which is able to inspect the MBeans on the application server) and verify that the attribute is correctly updated in the MBean tree:

Byteman advanced tutorial

After we have learnt the basics of ByteMan we can now dive into a more advanced tutorial which shows how to use method positional parameters and advanced Byteman rules to intercept JMS messages.

In this example we will show how we can interecept a JMS message received by an MDB so that we can perform some extra actions on it, without changing the actual MDB code.

Here’s a basic rule named “JMS Message Rule” which is bound to the javax.jms.MessageListener interface ( by the way you can bind rules both to Classes and Interfaces, do you remember?).

The rule is bound on the onMessage Method and uses an Helper class (com.sample.Utility) passing two parameters (the MessageListener and the Message)


RULE JMS Message Rule

INTERFACE javax.jms.MessageListener

METHOD void onMessage(javax.jms.Message)
HELPER com.sample.Utility
BIND

          messageListener:MessageListener = $0;
          message:Message = $1

IF true

DO readMessage ($0,$1)

ENDRULE

 Notice the usage of the positional parameters ($0, $1) which are used to bind the actual Objects (MessageListener and Message) so that they can be used into the DO readMessage call, although for the purpose of this example, we will just need using the Message parameter.

The Utility class can be used to execute any kind of processing on the JMS message, for example check the content and send a warning message to the system.


public class Utility   {

public void readMessage(MessageListener listener, Message message) throws Throwable {
       
       TextMessage txt = (TextMessage)message;
         try {

            String strMessage = txt.getText();

            // Execute actions
            System.out.println("Message received "+strMessage);
        } catch (JMSException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
   }
}

So far we have seen how to use the Utility’s readMessage helper class. One way to improve helper class is extending org.jboss.byteman.rule.helper.Helper class so that we can use also all other available utility methods in the base class. For example:


public class Utility extends  org.jboss.byteman.rule.helper.Helper {  
   protected Utility(Rule rule) {
        super(rule);
 
    }

public void readMessage(MessageListener listener, Message message) throws Throwable {
       setTriggering(false);
       
       TextMessage txt = (TextMessage)message;
         try {
            System.out.println(txt.getText());
        } catch (JMSException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
   }

}

 

 Notice that we have to provide a constructor for our helper which calls the constructor for the default helper. This is because, whenever a rule is triggered a helper instance is created to handle the rule trigger and, if necessary, to fill field calls with builtin methods. The instance is thrown away after the rule has been executed but it used for all operations during execution. So, the helper provides a context for each firing.

Once that we are extending Byteman rules, we can create more complex constructs like this one, which checks that the MDB that is firing the rule belongs to the com.sample.MyMDB class:

 


RULE JMS Message Rule

INTERFACE javax.jms.MessageListener

METHOD void onMessage(javax.jms.Message)
HELPER com.sample.Utility
BIND

          messageListener:MessageListener = $0;

          message:Message = $1

IF callerCheck("com.sample.MyMDB.*.onMessage", true, true, true, 0, 1)

DO readMessage ($0,$1)

ENDRULE

In this example, we are using the callerCheck method of the Helper class which can be used to check whether the name of any of the selected methods in the stack which called the trigger method matches the supplied regular expression:


public boolean   callerMatches(String regExp, boolean includeClass, boolean includePackage, int startFrame, int frameCount)

 

regExp: matches an expression which will be matched against the name of the method which called the trigger method
includeClass: true if the match should be against the class qualified method name
includePackage: true if the match should be against the package and class qualified method name.
startFrame: identifies the first frame which frame which should be considered.
frameCount: counts the frames which should be checked starting from the first caller.

Byteman administration

In the first tutorial we have shown some basic rule examples. We have however not still shown how to load/unload dynamically new rules. This tutorial shows how to manage Byteman rules.

Byteman does not require to be configured statically, you can as well inject rules and start the agent dynamically on a running server.

Starting the Byteman agent  (bminstall script)

If you have not added any JVM startup option, the first thing you need to do is starting the agent and connecting it to the running JVM. Pickup the PID of your JBoss AS (or any other JVM process) with any tool like JConsole:

Now navigate to the BYTEMAN_HOME and move to the “bin” folder

C:\byteman\bin>bminstall 11776						

Loading new Rules at runtime (bmsubmit -l rule)

Once that the Agent is running, you can start loading rules to the JVM. In order to load/unload rules you can use the bmsubmit shell script. Here’s how to load a rule named example.btm

C:\byteman\bin>bmsubmit.bat -l C:\jboss-as-7.1.1.Final\bin\example.btm

Unloading Rules at runtime (bmsubmit -u rule)

Rules can be as well unloaded using the bmsubmit -u shell command as in this example:

C:\byteman\bin>bmsubmit.bat -u C:\jboss-as-7.1.1.Final\bin\example.btm
uninstall RULE trace Servlet

By providing no arguments to the bmsubmit -u shell you will unload all rules

Listing Rules   (bmsubmit -l)

The bmsubmit -l shell command provides a list of all rules which are loaded

C:\byteman\bin>bmsubmit.bat -l
# File example.btm line 2
RULE trace Servlet
CLASS com.sample.ByteServlet
METHOD doGet
AT ENTRY

BIND request = $request;

IF true

DO traceln ("Query String " + request.getQueryString())

ENDRULE

Introduction to Byteman

This tutorial introduces the Byteman framework which is part of the JBoss Ecosystem, showing how it can be used to increase your productivity.

Byteman is addressed into the documentation as a tracing, debugging and testing tool which can be used on any Java application.

You might wonder why do we need to learn another tool for tracing and debugging applications ?

Let me tell you with a simple example why ByteMan can be a needful resource for your applications. Supposing that you are hitting a NPE in your Web application. The first and most obvious fix would be either starting a remote debugging session (which may not be always possible) or add some logs in the critical points. In most cases you will resort to modify your code, dedeploy you app and, once fixed the issue, disable or remove the logs.

Here’s what you can do with Byteman: inject a rule into your code –without any change in your code– in which you can directly refer to app/runtime data & types. In other words you can write a simple rule which says:

WHEN YOU ENTER THE Dummy SERVLET
THEN DUMP THE HTTPREQUEST

The rule will inject dynamically the code in your application (in this case into the application server) and execute the rule. Once you don’t need it any more, just tell to the JVM that you don’t need anymore firing ByteMan rules.

Ok. Now let’s get our hands dirty. Download Byteman from the source.

In order to start Byteman you need to provide the path to Byteman libs to the -javaagent option, specifying as well the path to the rule file.
For example on windows you would code (You need to define BYTEMAN_HOME as system environment variable):

java -javaagent:%BYTEMAN_HOME%\lib\byteman.jar=script:samplerule.btm com.sample.ByteManTest                        

On Linux

java -javaagent:${BYTEMAN_HOME}/lib/byteman.jar=script:samplerule.btm com.sample.ByteManTest    

A rule file is built-on the following skeleton:

RULE <rule name>
CLASS <class name>
METHOD <method name>
BIND <bindings>
IF   <condition>
DO   <actions>
ENDRULE

The RULE keyword identifies the rule name (Rule names do not have to be unique but it obviously helps when debugging rule scripts if they clearly identify the rule).
The CLASS can identify a class either with or without the package qualification.
The METHOD name can identify a method with or without an argument list or return type.
The BIND specification ca be used to bind application variable into rule variables which can subsequently be referenced in the rule body.
The IF section can be ovbiously used to check Rule conditions
The DO section is the Rule action which can be a rule expression, return values or throw action

Let’s try an HelloWorld example:

package com.sample;

public class SimpleTest  
    {

         public static void main(String[] args)

         {         
                 System.out.println("Entering main");
         
         }

    }

And here’s a simple rule, name it example.btm:

 RULE trace main entry

CLASS com.sample.SimpleTest

METHOD main

AT ENTRY
IF true

DO traceln(“Byteman detected you are entering main”)

ENDRULE

As you might guess, here we are defining a rule named “trace main entry” which is fired on main method of the class com.sample.SimpleTest, as soon as the method is entered. In all conditions (IF true) a trace line is printed on the console.

Here’s the Eclipse launch configuration for it.

By running this example you would just see a dummy message on the Console which prints
Byteman detected you are entering main


Now let’s see how you can run a Byteman rule when JBoss As triggers the execution of some classes. At first you need to activate Byteman on the JVM startup options (for example on JBoss AS 7 just add this to standalone.conf.bat)


set "JAVA_OPTS=%JAVA_OPTS% -Dorg.jboss.byteman.transform.all -javaagent:C:\byteman\lib\byteman.jar=script:example.btm,boot:C:\byteman\lib\byteman.jar,listener:true"

Now let’s write an example.btm rule file (as stated in the startup options) which will dump on the console the com.sample.ByteServlet’s query String:

 RULE trace Servlet

CLASS com.sample.ByteServlet

METHOD doGet

AT ENTRY
BIND request = $request;

IF true

DO traceln (“Query String ” + request.getQueryString())

ENDRULE

As you can see, this simple rule does one more step compared with the first rule: it binds the doGet request parameter into the Rule’s request variable. In the rule action the request’s query string is printed.

Define any com.sample.ByteServlet Servlet and invoke it by passing some parameters (/ByteServlet?param1=boo&param2=foo )
As you can see, as soon as the Servlet is invoked you should see the Query String printed on the Console.

Now, let’s write a slightly more complex rule:

 RULE trace Servlet

CLASS com.sample.ByteServlet

METHOD doGet
HELPER com.sample.Utility
AT ENTRY
BIND request = $request;

IF true

DO trace ($request)

ENDRULE

This rule includes an HELPER class which means that all the rules in the script will use this class in the Rule actions.
A rule’s helper class is used to resolve built-in calls like the trace() method of the com.sample.Utility class, which merely does some request snooping:

package com.sample;

import java.util.Enumeration;
import javax.servlet.http.HttpServletRequest;


public class Utility {
    public void trace(HttpServletRequest request) {
        System.out.println("Called trace!");

        Enumeration e = request.getSession().getAttributeNames();
        while (e.hasMoreElements()) {
            String name = (String) e.nextElement();
            System.out.println(name + ": " + request.getSession().getAttribute(name));
        }

        System.out.println("Protocol: " + request.getProtocol());
        System.out.println("Scheme: " + request.getScheme());

        System.out.println("Server Port: " + request.getServerPort());

        System.out.println("Remote Addr: " + request.getRemoteAddr());
        System.out.println("Remote Host: " + request.getRemoteHost());

        System.out.println("Content Length: " + request.getContentLength());
        System.out.close();
    }
}

In this example, as soon as the doGet of the ByteServlet is invoked, the Uility’s trace method is invoked, which receives as parameter the HttpServletRerquest. All this, has been done without one single change into the ByteServlet (!). Cool isn’t it ?