Review Request JDK-8200559: Java agents doing instrumentation need a means to define auxiliary classes
mandy chung
mandy.chung at oracle.com
Tue Apr 17 07:28:17 UTC 2018
Hi Rafael,
I see that mocking/proxying/testing framework should be looked at
separately since its requirements and approaches can be different than
tool agents.
On 4/17/18 5:06 AM, Rafael Winterhalter wrote:
> Hei Mandy,
>
> I have looked into several Java agents that I have worked on and for
> many of them, this API does unfortunately not supply sufficient
> access. I would therefore still prefer a method
> Instrumentation::defineClass.
>
> The problem is that some agents need to define classes in other
> packages then in that of the instrumented class. For example, I might
> need to enhance a library that defines a set of callback classes in
> package A. All these classes share a common super class with a
> package-private constructor. I want to instrument some class in
> package B to use a callback that the library does not supply and need
> to add a new callback class to A. This is not possible using the
> current API.
>
Are these callback classes made available statically? or just
dynamically defining additional class as needed? Is Lookup::defineClass
an alternative if you get a hold of common super class in A?
> I could however achieve do so by calling Instrumentation::retransform
> on one of the classes in A after registering a class file transformer.
> Once the retransformation is triggered, I can now define a class in A.
> Of course this is inefficient and I would rather open the
> jdk.internal.misc module and use the "old" API instead.
>
> For this reason, I argue that this rather restrained API is not
> convenient while it does not add anything to security. Also, for the
> use case of Mockito, this would neither be sufficient as Mockito
> sometimes redefines classes and sometimes adds a subclass without
> retransforming. We would rather have direct access to class definition
> once we are already running with the privileges of a Java agent.
>
> I would therefore suggest to add a method:
>
> interface Instrumentation {
> Class<?> defineClass(byte[] bytes, ProtectionDomain pd);
> }
>
> which can be implemented simply by delegating to jdk.internal.misc.Unsafe.
>
> On a side note. Does JavaLangAccess::defineClass work with the
> bootstrap class loader? I have not tried it but I always thought it
> was just an access layer for the class loader API that cannot access
> the null value.
>
The JVM entry point does allow null loader.
Mandy
> Thanks for considering this use case!
> Best regards, Rafael
>
> 2018-04-15 8:23 GMT+02:00 mandy chung <mandy.chung at oracle.com
> <mailto:mandy.chung at oracle.com>>:
>
> Background:
>
> Java agents support both load time and dynamic instrumentation.
> At load time,
> the agent's ClassFileTransformer is invoked to transform class
> bytes. There is
> no Class objects at this time. Dynamic instrumentation is when
> redefineClasses
> or retransformClasses is used to redefine an existing loaded
> class. The
> ClassFileTransformer is invoked with class bytes where the Class
> object is present.
>
> Java agent doing instrumentation needs a means to define auxiliary
> classes
> that are visible and accessible to the instrumented class.
> Existing agents
> have been using sun.misc.Unsafe::defineClass to define aux classes
> directly
> or accessing protected ClassLoader::defineClass method with
> setAccessible to
> suppress the language access check (see [1] where this issue was
> brought up).
>
> Instrumentation::appendToBootstrapClassLoaderSearch and
> appendToSystemClassLoaderSearch
> APIs are existing means to supply additional classes. It's too
> limited
> for example it can't inject a class in the same runtime package as
> the class
> being transformed.
>
> Proposal:
>
> This proposes to add a new ClassFileTransformer.transform method
> taking additional ClassDefiner parameter. A transformer can
> define additional
> classes during the transformation process, i.e.
> when ClassFileTransformer::transform is invoked. Some details:
>
> 1. ClassDefiner::defineClass defines a class in the same runtime
> package
> as the class being transformed.
> 2. The class is defined in the same thread as the transformers are
> being
> invoked. ClassDefiner::defineClass returns Class object directly
> before the transformed class is defined.
> 3. No transformation is applied to classes defined by
> ClassDefiner::defineClass.
>
> The first prototype we did is to collect the auxiliary classes and
> define
> them until all transformers are invoked and have these aux
> classes to go
> through the transformation pipeline. Several complicated issues
> would
> need to be resolved for example timing whether the auxiliary
> classes should
> be defined before the transformed class (otherwise a potential
> race where
> some other thread references the transformed class and cause the
> code to
> execute that in turn reference the auxiliary classes. The current
> implementation has a native reentrancy check that ensure one class
> is being
> transformed to avoid potential circularity issues. This may need
> JVM TI
> support to be reliable.
>
> This proposal would allow java agents to migrate from internal API
> and ClassDefiner to be enhanced in the future.
>
> Webrev:
> http://cr.openjdk.java.net/~mchung/jdk11/webrevs/8200559/webrev.00/
> <http://cr.openjdk.java.net/%7Emchung/jdk11/webrevs/8200559/webrev.00/>
>
> Mandy
> [1]
> http://mail.openjdk.java.net/pipermail/jdk-dev/2018-January/000405.html
> <http://mail.openjdk.java.net/pipermail/jdk-dev/2018-January/000405.html>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/serviceability-dev/attachments/20180417/32745e1a/attachment.html>
More information about the serviceability-dev
mailing list