Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Forum / General / June 2006

Tip: Looking for answers? Try searching our database.

Instrumentation + BCEL | ASM

Thread view: 
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 Magazines

Get 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 ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.