Java Bytecode Instrumentation and Transaction Tracing

Imagine you want to debug your code, or better yet, profile your code during run time. Bytecode instrumentation (BCI) (see my last post) is a perfect solution, since by using BCI an external tool can add code to every beginning and ending of every method call within every class….thus allowing you to measure performance and gather method-related data (variable info and such). However, these performance metrics have to be stored somewhere, and have to be sent somewhere as well so the developer can actually look at the output. Applying this kind of procedure to too many method calls will eventually cause the code to execute very slowly since for every method called, you need to gather and send the performance data. This slowness is called “overhead.” In other words, bytecode instrumentation adds overhead by definition.

In order to create a “transaction trace” for a transaction arriving into a Java Virtual Machine (JVM), one can intercept using BCI all the method calls that are serving HTTP calls to create a unique key (based on session or not). Now this key can be passed (different techniques can do this) to every method that is being called by the “parent” method. Each method metric is reported with this key to a repository, thus creating a potential “method trace” of the specific incoming HTTP request. This basic concept is what is used by the different Java-oriented products for transaction management.

In order to improve the above and adapt more to a production environment, you’ll want to reduce the full method trace to just incoming/outgoing calls and maybe a few in between. This cannot be accomplished generically, since if you are not passing the “trace key” between every method, you may have a problem following the trace uniquely. The solution is to create bytecode which is specific to the Java application you are trying to create a trace for.

Java application servers such as WebSphere or WebLogic (Oracle today) are at the end of the day just another Java application execute by a JVM. This is the exact reason a custom solution for transaction tracing has to be created for every version of a Java application server since classes and methods are changing their names and internal structure. Of course the same goes to a standalone JVM that is executing a Java application of some sort.

The bottom line is that you can either use a generic trace that will intercept all the classes and method calls and eliminate the ability to use it in a production environment, or use a more tailored implementation for a specific application version and try to create a partial trace that will reduce the overhead. Of course, keep in mind that for every new type of application, you will need to create a new version of your BCI as well.

Next post: More bytecode instrumentation limitations