You are viewing a plain text version of this content. The canonical link for it is here.
Posted to bcel-user@jakarta.apache.org by Bil Lewis <bi...@bayarea.net> on 2003/12/27 17:09:07 UTC

Using Classloaders to dynamically instrument anything

What I want to do is instrument users' code as it is loaded --
even if it's loaded by its own classloaders.

I don't care where the code comes from (file, url, rmi, byte stream,
etc.), I just want my chance to instrument it.

Now as I see it, what I *think* I should do is to replace a
low-level definition of defineClass(), which will then see every
class as a byte array as its loaded. But...

This implies that I'll need to hack the core Java libraries to
get to the proper classloader. Or maybe worse? I have to change
the JVM's primordial classloader? Just changing the System
classloader isn't enough, because people can avoid it. 

And BCEL has no interface for reading a byte array and turning
it into a javaClass (does it?). Using the Repository methods
won't work with arbitrary classloaders (will it?).




Others have probably thought about this before. Can you give me
direction on how to do this?

Thanks,


-Bil Lewis




---------------------------------------------------------------------
To unsubscribe, e-mail: bcel-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: bcel-user-help@jakarta.apache.org


Re: Using Classloaders to dynamically instrument anything

Posted by Alex <al...@gnilux.com>.
Bil

For a custom classloader implementing bytecode instrumentation, the BCEL
sample or the AspectWerkz WeavingClassLoader are suitable.
But you cannot rely on them to work with user defined class loaders, and
as you pointed it, to work with scheme where bytecode is somewhere (over
the networrk or whatever).

Since your primary goal was to allow bytecode instrumentation no matter
the classloader implementation - including support for classloader
hierarchy as in J2EE space, you have from what I know the following
options:
- replace the java.lang.ClassLoader with a custom one (legal issue if
-Xbootclasspath/p is use, use HotSwap when possible)
- use JVMPI ClassLoadHook event (a bit limited)
- use JVMTI / Instrumentation under java 1.5
- use BEA JRockit JMAPI
- hack a custom JVM

The first four options are covered by AspectWerkz hooking architecture as
I already said. The "WeavingClassLoader" is only use for simple
applications relying on a single classloader.

I think the BCEL sample classloader allow to start with "class load time
instrumentation", but I don't think it is in the scope of BCEL to provide
a "universal" hooking mechanism.

I suggest you post AspectWerkz related question to AspectWerkz mailing
list. Here are some short answers:
- defineClass is (are) final, you cannot override it by subclassing
java.lang.ClassLoader
- one of AspectWerkz way of doing it is instrumenting the
java.lang.ClassLoader defineClass (defineClass0 is native, so it cannot be
altered).
- it really work - don't mismatch classloader delegation andd OO
inheritance from java.lang.ClassLoader

Alex


>
> Alex,
>
> Thank you for your advice! It makes sense, although I
> don't quite understand what's going on...
>
> My current classloader is based on the BCEL example
> code. It works fine, except for when the user uses
> his own classloader.
>
> Unfortunately, I don't totally understand why they
> wrote it that way. And, I don't quite understand why
> you wrote your WeavingClassLoader the way you did...
>
> In particular, I don't want to affect where the code
> is coming from. In the BCEL example, they write:
>
>
> protected Class findClass(String className, boolean instrument) {
>  JavaClass javaClass = Repository.lookupClass(className);
>  ...
>  javaClass = Debugify.debugifyClass(javaClass, className);    // do
> theinstrumentation
>  byte[] b = javaClass.getBytes();
>  clazz = defineClass(className, b, 0, b.length);
>  return clazz;
> }
>
>
> and you have something very similar:
>
> protected Class findClass(String name) throws ClassNotFoundException {
>  String path = name.replace('.', '/').concat(".class");
>  Resource res = new URLClassPath(getURLs()).getResource(path, false);
>  ...
>  byte[] b = res.getBytes();
>  byte[] transformed = ClassPreProcessorHelper.defineClass0Pre(this, name,
> b, 0, b.length, null);
>  return defineClass(name, transformed, 0, transformed.length);
> }
>
>
>
> The bit I'm confused about is the Repository/Resource. Won't
> that affect where the class is loaded from? So if the user
> writes a weird classloader that just creates a byte array
> "magically", our R/R will miss that. (right?)
>
> So shouldn't we be delegating the findClass() stuff to the
> existing classloaders? They would then come up with the
> byte array and hand that to defineClass(). Our classloaders
> would specialize defineClass() to include the instrumentation
> and pass the transformed byte array to super.defineClass(),
> which would finally turn the byte array into a class.
>
> Does that make sense?
>
> (I've read all I could find on classloaders, but it's still
> less then 100% clear.)
>
> 		================
>
> Looking through your Plug stuff, it looks like you're instrumenting
> java.lang.ClassLoader to do instrumentation in all the
> define0() methods (yes?). And that's equivalent to what I
> said above wrt defineClass()?
>
> 		================
>
> Finally, does this really work? If a user defines a classloader
> which has the bootstrap classloader as parent, will your
> enhanced classloader be used?
>
>
>
>
> Sincerely,
>
>
> -Bil
>
>
>
>


---------------------------------------------------------------------
To unsubscribe, e-mail: bcel-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: bcel-user-help@jakarta.apache.org


Re: Using Classloaders to dynamically instrument anything

Posted by Bil Lewis <bi...@bayarea.net>.
Alex,

Thank you for your advice! It makes sense, although I
don't quite understand what's going on...

My current classloader is based on the BCEL example
code. It works fine, except for when the user uses
his own classloader. 

Unfortunately, I don't totally understand why they
wrote it that way. And, I don't quite understand why
you wrote your WeavingClassLoader the way you did...

In particular, I don't want to affect where the code
is coming from. In the BCEL example, they write:


protected Class findClass(String className, boolean instrument) {
 JavaClass javaClass = Repository.lookupClass(className);
 ...
 javaClass = Debugify.debugifyClass(javaClass, className);    // do theinstrumentation
 byte[] b = javaClass.getBytes();
 clazz = defineClass(className, b, 0, b.length);
 return clazz;
}


and you have something very similar:

protected Class findClass(String name) throws ClassNotFoundException {
 String path = name.replace('.', '/').concat(".class");
 Resource res = new URLClassPath(getURLs()).getResource(path, false);
 ...
 byte[] b = res.getBytes();
 byte[] transformed = ClassPreProcessorHelper.defineClass0Pre(this, name,
b, 0, b.length, null);
 return defineClass(name, transformed, 0, transformed.length);
}



The bit I'm confused about is the Repository/Resource. Won't 
that affect where the class is loaded from? So if the user 
writes a weird classloader that just creates a byte array
"magically", our R/R will miss that. (right?)

So shouldn't we be delegating the findClass() stuff to the
existing classloaders? They would then come up with the
byte array and hand that to defineClass(). Our classloaders
would specialize defineClass() to include the instrumentation
and pass the transformed byte array to super.defineClass(),
which would finally turn the byte array into a class.

Does that make sense?

(I've read all I could find on classloaders, but it's still
less then 100% clear.)

		================

Looking through your Plug stuff, it looks like you're instrumenting
java.lang.ClassLoader to do instrumentation in all the
define0() methods (yes?). And that's equivalent to what I
said above wrt defineClass()?

		================

Finally, does this really work? If a user defines a classloader
which has the bootstrap classloader as parent, will your
enhanced classloader be used?




Sincerely,


-Bil




---------------------------------------------------------------------
To unsubscribe, e-mail: bcel-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: bcel-user-help@jakarta.apache.org


Re: Using Classloaders to dynamically instrument anything

Posted by Bil Lewis <bi...@bayarea.net>.
Alex,

It works! Yee-ha!

Thank you, thank you, thank you!

-Bil




---------------------------------------------------------------------
To unsubscribe, e-mail: bcel-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: bcel-user-help@jakarta.apache.org


Re: Using Classloaders to dynamically instrument anything

Posted by Alex <al...@gnilux.com>.
Hi Bil

Check how we do that in AspectWerkz by replacing the core
java.lang.ClassLoader when possible, and providing many option to feet users
needs and environment, since hotswapping the java.lang.ClassLoader is not
always possible.
http://aspectwerkz.codehaus.org/online.html
http://aspectwerkz.codehaus.org/downloads/papers/AspectWerkz-online-architec
ture.pdf.zip

If you don't need to support custom classloader, but the system classloader
is enough for you, there are many easier ways to do class load time
instrumentation (java 1.4 has an option to substitute the system class
loader, and a simple "main" wrapper class using Thread.setContextClassLoader
can be also be enough).

Also note that the JSR 163 will hoppefully bring a standard solution to this
in java 1.5, just as the AspectWerkz BEA JRockit module does right now.

Alex
-
http://blogs.codehaus.org/people/avasseur/




----- Original Message -----
From: "Bil Lewis" <bi...@bayarea.net>
To: <bc...@jakarta.apache.org>
Sent: Saturday, December 27, 2003 5:09 PM
Subject: Using Classloaders to dynamically instrument anything


>
> What I want to do is instrument users' code as it is loaded --
> even if it's loaded by its own classloaders.
>
> I don't care where the code comes from (file, url, rmi, byte stream,
> etc.), I just want my chance to instrument it.
>
> Now as I see it, what I *think* I should do is to replace a
> low-level definition of defineClass(), which will then see every
> class as a byte array as its loaded. But...
>
> This implies that I'll need to hack the core Java libraries to
> get to the proper classloader. Or maybe worse? I have to change
> the JVM's primordial classloader? Just changing the System
> classloader isn't enough, because people can avoid it.
>
> And BCEL has no interface for reading a byte array and turning
> it into a javaClass (does it?). Using the Repository methods
> won't work with arbitrary classloaders (will it?).
>
>
>
>
> Others have probably thought about this before. Can you give me
> direction on how to do this?
>
> Thanks,
>
>
> -Bil Lewis
>
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: bcel-user-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: bcel-user-help@jakarta.apache.org
>
>


---------------------------------------------------------------------
To unsubscribe, e-mail: bcel-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: bcel-user-help@jakarta.apache.org