2 cents contribution for #ClassFilesAsResources
Andrew Dinn
adinn at redhat.com
Wed Mar 2 10:15:13 UTC 2016
Hi all,
I just wanted to note that the #ClassFilesAsResources feature is very
important for Byteman and, potentially, other JVMTI Java agent
transformers. So, I'd prefer this capability to be retained, at least
for agent transformer code.
For anyone interested in the detail an explanation of why this is
important to 'coherent' operation of an agent transformer is provided below.
regards,
Andrew Dinn
-----------
Senior Principal Software Engineer
Red Hat UK Ltd
Registered in UK and Wales under Company Registration No. 3798903
Directors: Michael Cunningham (US), Michael O'Neill (Ireland), Paul
Argiry (US)
Rationale
---------
The problem scenario:
An agent transformer may potentially be presented with a class X for
transformation before its superclasses S[X] = {X', X'', ...} or its
(directly or indirectly) implemented interfaces I[X] = {I', I'' , ...}
have been loaded. In some cases the applicability of a transformation
can only be determined by knowing something about these two sets such as
i) whether some specific class or interface belongs to S[X] or I[X]
e.g. does S[X] contain a class called Foo
ii) whether one one of these classes or interfaces satisfies certain
structural constraints e.g. does some class in S[X] implement finalize()
n.b. in the case of Byteman the requirement is of the first type.
Byteman rules may request the agent to inject into any subclass of
MyClass or any class which implements MyInterface (possibly directly or
maybe indirectly). So, given a class X Byteman will in some cases need
to know if S[X] contains a class called MyClass or O[X] contains an
interface called MyInterface
Why this is a problem:
The obvious thing to do here would be to use reflection to traverse S[X]
and I[X] but that is not an acceptable solution.
The transformer can indeed call getSuper() to obtain X', and so on for
X'' etc, without caring whether those classes have already been loaded.
The JVM will simply load the necessary classes and hand over a Class<?>
instance. (similarly it can call getInterfaces() to obtain Class<?>
instances for I, I', etc).
However, for good reasons transformation is not re-entrant. So, if the
calls to getSuper or getInterfaces are made from within a
ClassTransfromer's transform() method they will indeed cause X', X'',
..., I, I', ... to be loaded but will also prejudice its opportunity to
apply transformations to those classes.
This would mean that agent transformers could not guarantee to
coherently transform all applicable cases -- in particular, it means
that which classes get transformed would vary according to whether the
flow of execution ends up loading a superclass before or after one of
it's subclasses. Partial application of transformations is bad enough
but variable outcomes according to runtime conditions is even more
difficult to live with.
The solution Byteman adopts when it needs to search the super or
interface hierarchy relies on resource loading of .class files. When
traversing S[X] or I[X] looking for a superclass or interface with a
given name Byteman first scans the class list returned by
Instrumentation.allInstances() for potential matches.
If a matching class or interface is found then it will have been fully
resolved. So, it can be queried using reflection to find the next
elements in the super or interface chain.
If no match can be found then Byteman resorts to loading the .class
file as a resource. Loaded bytecode is analyzed to identify the names of
the next super or interface elements in the relevant parent set.
The recursive search through S[X] or I[X] may then continue using
reflection or further bytecode loading + analysis as required.
n.b. This workaround is not foolproof since for certain classloader
configurations (in particular for certain configurations which arise
when using OSGi or JBoss modules) a super class may not be visible from
the class loader of a subclass /whether/ as a loadable class /or/ as a
resource. However, for the vast majority of cases using resource loading
resolves the problem of non-re-entrant transformation.
n.b.b. This may sound like it's not a significant problem but it cropped
up quite early on when we first tried to use Byteman to inject code into
the pre-JBoss Modules release of JBoss EAP. It also turned up very early
on when injecting code into JDK runtime classes. I have heard
anecdotally from other developers of agent transformers that this is a
real problem for them too.
More information about the jpms-spec-comments
mailing list