JVM wide preprocessor hook
This document explains the detail of the JVM wide preprocessor hook of beSee. This mechanism allows to plug
any class preprocessor component in the whole hierarchy of java class loaders, thus allowing runtime instrumentation
of any component.
Supported environments
beSee mechanism is optimally based on java 1.4 HotSwap features, but provides a transparent support for java 1.3 and non HotSwap enabled JVM.
An important feature of beSee is that it is completely transparent when plugged in: you can turn on remote debugging in the launched application with JDWP even if beSee makes itself uses of it. OverviewClass loader preprocessor
beSee provides a standard way to instrument the java.lang.ClassLoader.
public MyClassLoaderPatcher implements com.gnilux.besee.hook.ClassLoaderPreProcessor { public byte[] preProcess(byte[] classLoaderBytecode) { // transform classLoaderBytecode using your favorite bytecode API // could be BCEL or Javassist, or ... } }
beSee comes with a default
javassit
implementation which hooks in the java class loading mechanims a class preprocessor component.
beSee provides the same mechanism with a BCEL implementation.
Note for java 1.3 and JVM not supporting HotSwap:
In such case, beSee still generates at runtime a modified java.lang.ClassLoader thru the class loader preprocessor implementation,
but add it transparently in the
-Xbootclasspath
option of the target JVM running the instrumented application.
Class preprocessor
The most natural way to provide runtime class instrumentation is the class preprocessor mechanism: the class preprocessor
component is responsible for modifying bytecode
just before
any class is loaded in the JVM (except classes in the bootclasspath like Object, String, ...).
A standard class preprocessor is something like public class StdoutPreProcessor implements com.gnilux.besee.hook.ClassPreProcessor { public byte[] preProcess(String klass, byte bytecode[], ClassLoader caller) { log("preprocess " + klass + " for ClassLoader " + caller); // modify bytecode as needed return bytecode; } } When the beSee default class loader preprocessor implementation is used (BCEL or Javassist based), the effective class preprocessor is specified using its full qualified name with beSee option (not JVM option, see Usage section) -Dbesee.classloader.preprocessor
beSee distribution comes with a very basic class preprocessor which just prints out name of the class loaded without doing instrumentation.
Usage
beSee
is
a regular java main class. It can be considered as a "java" command replacement.
java [target jvm option] [target classpath] targetMainClass [targetMainClass args] java [jvm option] [classpath] com.gnilux.besee.hook.ProcessStarter ... ... [target jvm option] [target classpath] targetMainClass [targetMainClass args] java [target jvm option] [target classpath] -jar targetExecutable.jar [targetMainClass args] java [jvm option] [classpath] com.gnilux.besee.hook.ProcessStarter ... ... [target jvm option] [target classpath] -jar targetExecutable.jar [targetMainClass args]
For the beSee BCEL/javassist class preprocessor standard implementation consider also:
SamplesThe following runs a HelloWorld sample with the standard implementation and a simple class preprocessor that prints out some information. You can reproduce this samples by calling the ant targets demo.hook.nohotswap , demo.hook.hotswap . For a more realistic sample, refer to the WebLogic extension .
// regular java usage java -classpath besee.test.jar com.gnilux.besee.hook.HelloWorldMain // output HelloWorld ! // disable HotSwap and put modified ClassLoader in "demo-boot" directory java -classpath bcel-5.1.jar:besee.jar;%JAVA_HOME%/tools.jar -Dbesee.classloader.clbootclasspath=demo-boot \ com.gnilux.besee.hook.ProcessStarter -Xbootclasspath/a:besee.jar \ -Dbesee.classloader.preprocessor=com.gnilux.besee.hook.impl.StdoutPreProcessor \ -classpath besee.test.jar com.gnilux.besee.hook.HelloWorldMain // output HotSwap deactivated, using bootclasspath: demo-boot com.gnilux.besee.hook.impl.StdoutPreProcessor: initialize besee - INFO - Pre-processor com.gnilux.besee.hook.impl.StdoutPreProcessor loaded and initialized com.gnilux.besee.hook.impl.StdoutPreProcessor: preprocess com.gnilux.besee.hook.HelloWorldMain \ for CL sun.misc.Launcher$AppClassLoader@7d8483 HelloWorld ! // enable HotSwap if available for java version used java -classpath bcel-5.1.jar:besee.jar;%JAVA_HOME%/tools.jar \ com.gnilux.besee.hook.ProcessStarter -Xbootclasspath/a:besee.jar \ -Dbesee.classloader.preprocessor=com.gnilux.besee.hook.impl.StdoutPreProcessor \ -classpath besee.test.jar com.gnilux.besee.hook.HelloWorldMain // output for java 1.3 HotSwap not supported by this java version, using bootclasspath: .\boot com.gnilux.besee.hook.impl.StdoutPreProcessor: initialize com.gnilux.besee.hook.impl.StdoutPreProcessor: loaded by null besee - INFO - Pre-processor com.gnilux.besee.hook.impl.StdoutPreProcessor loaded and initialized com.gnilux.besee.hook.impl.StdoutPreProcessor: preprocess com.gnilux.besee.hook.HelloWorldMain \ for CL sun.misc.Launcher$AppClassLoader@7d8483 HelloWorld ! // Note: autodetection and adaptive behavior for jvm not supporting HotSwap |