Byteman 2.0.0: Bytecode Manipulation, Testing, Fault Injection, Logging
JBoss has released Byteman 2.0.0, an open source Java bytecode manipulation tool licensed under GNU LGPL 2.1. Byteman is a Java agent which helps testing, tracing, and monitoring code. It allows developers to change the operation of Java applications, either as it is loaded or during runtime. It works without the need to rewrite or recompile the application, and can even modify Java Platform classes like String, Thread, etc.
Here are what's new with Byteman 2.0.0.
- File and Line Debug Information. Compiled Byteman rules now contain file and line number information. This is one step in providing full debugger support.
- TestNG Import of BMunit using @Listener. TestNG classes can now import BMUnit behavior using @Listener annotation. Previous versions required TestNG classes to subclass BMNGRunner, which causes conflicts when test classes already extend another test class.
- Throw Error Rule. Byteman now allows for a throw Error rule even if it is undeclared by the target class.
- Bug Fixes. The latest release has fixed eight bugs found in the previous 1.6.0 release.
Byteman uses a simple Event Condition Action (ECA) scripting language to specify where, when and how the target Java code should be transformed. Code execution continues once the injected code has been executed, although the injected code may throw an exception or force an early return. Here's an example of a simple Byteman script.
# script.btm RULE trace main entry CLASS App METHOD main AT ENTRY IF true DO traceln("entering main") ENDRULE
Here we have a script that tells Byteman to print "entering main" at the start of the App.main() method. To enable Byteman in your application, and specify the script, add the Java agent JVM argument.
Note you can also add Byteman on already running application using bminstall.sh. For more complex rules and code injection, you can use the built-in Byteman Rule Helpers, or you can create your own POJO as a plug-in.
Similar to Byteman, AOP is capable of instrumenting classes and inject code. With Byteman however, there is no need to create classes or compile code. You don't need 100% hindsight when you write your code, and can decide what code to inject at a later time. Byteman is also simpler to use and easier to change, especially for testing and ad hoc logging purposes.
To get started, you can download the Byteman binaries and specify it as a Java agent to your application. Note that Byteman requires JDK 6 or higher. Read the Byteman Programmer's Guide for in depth details. More information about this release can be found in the Byteman 2.0.0 release notes. You can also visit the official Byteman documentation and forum. Source code is available at GitHub.
The example involved 2 threads T1 and T2; T2 would be blocked and T1 would run first. T1 executed a part of the code, then it would be blocked, then T2 would execute, be blocked again, and T1 would continue.
I used sleeps to mimic this before, but the issue was that the JGroups test suite might run up to 20 tests in parallel, and thus a simple GC, or one of the threads getting enough CPU could get the timings off.
What I haven't used yet, but probably will is the capability to inject rules at runtime and remove them again later. This will be very handy when trying to dignose a running system.
Kudos to the Byteman team !
Byteman does indeed include file and line number info into injected rule code but this is only one step in providing full debugger support. A couple of contributors are working on extending an IDE/debugger (specifically, Eclipse and IntelliJ Idea) to support both Byteman development and execution but neither of them is yet complete.
Byteman rules can be used to throw unexpected exceptions and errors in both application and JVM runtime code -- a very useful feature in both unit and integration tests. Indeed it can also be used to bypass method execution and return synthetic return values. However, code injected by Byteman is type-checked and type-safe, hence it will not break method contracts. So, you can only throw an exception declared by the method (or, of course, a RuntimeException or Error) and you can only return a value whose type matches the method return type.