When debugging applications, the usual approach is that of using logging or breakpoints to step through the code. When logging, it’s up to you where you introduce this in your code, but not so when it comes to 3rd party code, where the logging you get is the logging there is… unless you change the byte-code that is. This is where tools like Byteman come into play. Byteman allows you to inject code, either at JVM startup or dynamically while the application is running, giving you the ability to modify bytecode inside any 3rd party libraries your application may be using. Primarily, Byteman was developed in order to carry out testing using a technique known as fault injection, however, it’s useful for other purposes like debugging and tracing. Apart from that, it’s a very handy tool to know about.
Just for the sake of illustrating the basic use of Byteman, lets simply log a message before the execution of the method shouldAggregateEvents in org.mule.routing.correlation.CollectionCorrelatorCallback which can be found in the mule-core jar in the mule distribution. The method shouldAggregateEvents determines whether or not all messages that should be aggregated have been aggregated, before returning the result for the initial message received by Mule. The following .btm file can be used to accomplish this:
RULE log shouldAggregateEvents
DO System.out.println(“This message was injected: about to execute shouldAggregateEvents”)
Rules are used to determine how to change the bytecode. In this case, we are simply specifying that in the class CollectionCorrelatorCallback, the method shouldAggregateEvents should always (because of IF TRUE) be preceded by the action of printing to standard out. This action happens before the invocation of shouldAggregateEvents because if no detailed location is specified, the default location where the bytecode performing the action is added, is upon entry of the target method (the action in this case is that of printing to standard out).
Now lets say we’ve started Mule and have deployed an application making use of the aggregation feature supported by Mule. We would now like to change the running code according to the rule/s specified in our script file:
- Start by setting the BYTEMAN_HOME environment variable to the directory where you extracted Byteman and exporting it from your startup scripts such as .bashrc.
- Next, you can use the ps or the jps (with argument -l) tool to get the process id of the JVM running Mule or the name of the application (which will be org.mule.module.reboot.MuleContainerBootstrap in our case) needed to install the Byteman agent into the JVM. Simply execute jps and take note of the id corresponding to your running Mule instance.
- Go to Byteman’s bin directory and execute ./bminstall.sh -b <pid-of-mule>. This will install the Byteman agent on the JVM running Mule, enabling you to install, update and uninstall rules which will dynamically alter the running code (the -b argument is used to add the byteman.jar to the bootstrap class path). Of course, make sure you change the script’s permissions to be able to execute it if you need to.
- Next, simply install the rule above by executing ./bmsubmit.sh <path-to-btm-file>. That’s it. After installing, whenever your application is about to execute shouldAggregateEvents, it will log a message to standard out.
- You can install, redefine, and uninstall rules to change the running code by using the bmsubmit script. Refer to Byteman’s documentation for more detail.
Below is a snippet of the logs generated by Mule, interposed by the logging generated from our bytecode modifications:
... [09-13 09:36:27] INFO JmsReplyToHandler [[mule-tests-cluster-manual-0.0.1-SNAPSHOT].processorFlowCustomAggregator.stage1.05]: Reply Message sent to: queue://reply.flow.aggregator with correlationID:14160b17-dddb-11e0-af67-b39ced60ae27 [09-13 09:36:27] INFO JmsReplyToHandler [[mule-tests-cluster-manual-0.0.1-SNAPSHOT].processorFlowCustomAggregator.stage1.09]: Reply Message sent to: queue://reply.flow.aggregator with correlationID:14140f45-dddb-11e0-af67-b39ced60ae27 This message was injected: about to execute shouldAggregateEvents [09-13 09:36:27] WARN AbstractCorrelationAggregator$DelegateCorrelatorCallback [ActiveMQ Session Task-2]: Correlation Group Size not set, but correlation aggregator is being used. Message is being forwarded as is This message was injected: about to execute shouldAggregateEvents [09-13 09:36:27] WARN AbstractCorrelationAggregator$DelegateCorrelatorCallback [ActiveMQ Session Task-8]: Correlation Group Size not set, but correlation aggregator is being used. Message is being forwarded as is ...
Of course, we have only scratched the surface of what’s possible with tools like Byteman. This small example has hopefully given you a taste of how simple it can be to manipulate bytecode.