Java Forum / General / June 2006
Instrumentation + BCEL | ASM
Boris Gorjan - 06 Jun 2006 09:38 GMT I'm trying to utilize instrumentation to modify bytecode of the loaded classes. Having added MyTransformer to Instrumentation in MyAgent's premain(String, Instrumentation) and using VM comand line argument -javaagent:myjar.jar=myargs I can intercept the bytecode before it is loaded, through the
public byte[] transform( ClassLoader loader, String className, Class redefiningClass, ProtectionDomain domain, byte[] bytes ) throws IllegalClassFormatException { System.out.println("Transforming: " + className); return bytes; }
OK so far.
Now, MyTransformer is doing nothing at the moment, except returning unmodified bytecode. What I'd like to do is this: for a specified method I'd like to wrap the whole body of the method in a try - finally, like this:
try { MyTiming.start(); /* Old method body / code. */ } finally { MyTiming.end(); }
What's the easyest way to do this?
I looked into BCEL; using DecendingVisitor (and MyVisitor which implements Visitor: all the visit*() methods) I can go through the bytecode in a similar way as using a SAX parser for XML. But, I don't know how to modify the code in an above mentioned way letting most of it through unmodified.
Can anyone give me some ideas / guidance, please?
How about using ASM? I'd also be happy using that instead of BCEL. I read it's somewhat faster.
Thanks.
Marcin Wielgus - 06 Jun 2006 09:51 GMT bcel can do it. Dont exacly remember how try - catch works in bytecode, but i remember api is quite simple. if u'r capable of obtaining method object, u should be able to get instruction list, and put some instructions at the beginning, and at the end (look for them in the api docs), but u have to remember to go through all instructions in that method, and correct all jumps (if, catch, while and so on). If u want some sample code i can send it to u.
-- SaSol
Boris Gorjan - 06 Jun 2006 10:22 GMT > bcel can do it. Dont exacly remember how try - catch works in bytecode, > but i remember api is quite simple. if u'r capable of obtaining method > object, As things stand now I can obtain everything in org.apache.bcel.classfile.Visitor (now I see that there's also an org.apache.bcel.generic.Visitor).
What I don't know how to do (yet ;-) ) is how to pack all the data I obtain through that Visitor, into a new bytecode (byte[]). A good exercise would be to pack it unmodified, I know, but that'd take me a long time.
Intuitively I'd go about it this way: make a new JavaClass(), use its set*() methods to fill it with data obtained through a Visitor (an implementation of the classfile one) and finally use some dump(*) or getBytes() method to produce the bytecode. Am I on the right track?
> u should be able to get instruction list, I can get org.apache.bcel.classfile.Method and org.apache.bcel.classfile.Code for that method, but neither of them has a "handle" on InstructionList. I don't see it, to be precise.
I can't seem to "connect" a Visitor, that is instances of classes its visit*() methods take as arguments, and those with their respective InstructionLists.
> and put some > instructions at the beginning, and at the end (look for them in the api > docs), but u have to remember to go through all instructions in that > method, and correct all jumps (if, catch, while and so on). If u want > some sample code i can send it to u. I'd be most grateful. Thanks. Use the boris.gorjan address, please.
Boris Gorjan - 06 Jun 2006 10:39 GMT > I can get org.apache.bcel.classfile.Method and > org.apache.bcel.classfile.Code for that method, but neither of them has [quoted text clipped - 3 lines] > visit*() methods take as arguments, and those with their respective > InstructionLists. Found it! In Visitors visit*() methods, just use .getCode() from an argument object and create a new InstructionList. Like this:
public void visitCode(Code code) { InstructionList instructionList = new InstructionList(code.getCode()); // ... }
Boris Gorjan - 06 Jun 2006 15:32 GMT Nailed it! With a little help from my firends. ;-) [imagine a lengthy oscars-style-thank-you litany here]
Just one more thing. As said in my initial post, MyTransformer (implementing ClassFileTransformer) implements a method
public byte[] transform(ClassLoader classLoader, String className, Class redefiningClass, ProtectionDomain domain, byte[] bytes),
where byte[] bytes are supposed to be "the input byte buffer in class file format - must not be modified" (quoting J2SE 1.5 API docs).
I only know how to obtain an instance of org.apache.bcel.classfile.JavaClass (which I later transform) from org.apache.bcel.Repository like this:
JavaClass clazz = Repository.lookupClass(className.replace('/', '.'));
Is there a (simple) way to obtain that instance using byte[] bytes directly?
Piotr Kobzda - 06 Jun 2006 16:52 GMT Boris Gorjan napisał(a):
> Is there a (simple) way to obtain that instance using byte[] bytes > directly? Based on BCEL Manual and Javadoc, it should work this way:
JavaClass clazz = new ClassParser(new ByteArrayInputStream(bytes), className).parse();
(I've never tried it out)
Regards, piotr
Boris Gorjan - 06 Jun 2006 17:45 GMT > Boris Gorjan napisał(a): > [quoted text clipped - 7 lines] > > (I've never tried it out) It works. Thanks.
Piotr Kobzda - 06 Jun 2006 15:58 GMT > How about using ASM? I'd also be happy using that instead of BCEL. I > read it's somewhat faster. That is quite easy task for ASM, if you know what is exactly the bytecode you want to achieve.
See code provided below for one of the possible solutions. It could be interesting to see, how your already achieved BCEL solution looks like (I have no experience with BCEL), so if it's not a problem for you, please send it here.
Also consider using some APO framework, for example AspectJ, which allows doing similar things (and much more) easier.
Regards, piotr
 Signature import org.objectweb.asm.*; import static org.objectweb.asm.Opcodes.*;
class AddTimingCallsClassAdapter extends ClassAdapter { AddTimingCallsClassAdapter(ClassVisitor cv) { super(cv); }
@Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
return new MethodAdapter(super.visitMethod(access, name, desc, signature, exceptions)) {
Label startLabel = new Label(); Label finallyLabel = new Label(); int retOpcode = RETURN;
private void genStartCode() { super.visitMethodInsn(INVOKESTATIC, "MyTiming", "start", "()V"); }
private void genEndCode() { super.visitMethodInsn(INVOKESTATIC, "MyTiming", "end", "()V"); }
@Override public void visitCode() { super.visitCode();
super.visitLabel(startLabel);
genStartCode();
// ... }
@Override public void visitInsn(int opcode) { if (isRetInsn(opcode)) { retOpcode = opcode; super.visitJumpInsn(GOTO, finallyLabel); } else { super.visitInsn(opcode); } }
@Override public void visitEnd() { // ...
Label endLabel = new Label(); super.visitLabel(endLabel); super.visitVarInsn(ASTORE, 1);
genEndCode();
super.visitVarInsn(ALOAD, 1); super.visitInsn(ATHROW);
super.visitLabel(finallyLabel);
genEndCode();
super.visitInsn(retOpcode);
super.visitTryCatchBlock(startLabel, endLabel, endLabel, null);
super.visitEnd(); }
}; }
static boolean isRetInsn(int opcode) { switch(opcode) { case IRETURN: case LRETURN: case FRETURN: case DRETURN: case ARETURN: case RETURN: return true; default: return false; } }
}
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|