#ReflectiveAccessByInstrumentationAgents
Peter Levart
peter.levart at gmail.com
Thu Apr 21 11:09:52 UTC 2016
Hi Andrew,
On 04/21/2016 09:31 AM, Andrew Dinn wrote:
>
> On 20/04/16 12:40, Alan Bateman wrote:
>> On 19/04/2016 18:00, Andrew Dinn wrote:
>>> :
>>> I have been building and testing on JDK9 Byteman using the EA program
>>> releases and have not seen anything break so far. However, that may just
>>> be because i) Byteman and the Byteman tests are only using runtime
>>> classes from the java base module and ii) app code used in tests is not
>>> modularized -- well, at least not using Jigsaw (see below).
>> It's good to hear that you are testing with the EA builds.
>>
>> I think the main thing for Byteman, and this will be true at least some
>> other agents too, is that there will likely need to be updated to
>> support instrumentation with modules. We have a reasonable compatibility
>> story for existing JVM TI and java agents but as soon as you get into
>> instrumenting code to statically or reflectively access code in other
>> modules then it may require the agent to arrange for this access to be
>> allowed.
> Having to explicitly manage module dependencies to retain compatibiity
> with previous behaviour is at the very least a /nuisance/ because it now
> means I -- and, no doubt, others in the agent business -- will have to
> implement a dual-source agent, one for JDK9(+) and another to continue
> to operate in JDK8/7/6 (yes, people can and do still use the latest
> Byteman release in all those JDK releases). A solution internal to the
> JVM which preserved existing behaviour and so did not require the
> insertion of JDK-specific jumps through JDK-specific hoops would be much
> preferred. Still, I suppose the releavant implementors are a relatively
> small crowd and ought to know what we are doing and how to fix it.
>
> That minor rant aside (sorry, I feel better for that but I don't suppose
> anyone else on this list does :-) I will happily investigate the
> available dynamic module-management APIs and see what options exist to
> remedy the changes to the status quo and retain as much backwards
> compatibility as I can. At that point I will come back and comment on
> the very useful advice you have provided below. Thanks very much for
> your help so far.
>
> regards,
>
>
> Andrew Dinn
The situation is not so complicated, I think. If instrumented code calls
some code in your Byteman module(s) then you should 1st make this code
public and reside in exported packages. All you have to do then is add
read edges from modules of instrumented classes to modules of Byteman
classes that are called by instrumented code as they are being
instrumented. For example, having the following utility class compiled
with JDK 8 (not using any JDK 9 API):
public class ClassFileTransformerWrapper {
public ClassFileTransformer wrap(Instrumentation instrumentation,
ClassFileTransformer transformerImpl,
Class<?>... readableClasses) {
try {
Class.forName("java.lang.reflect.Module");
} catch (ClassNotFoundException e) {
// don't need to wrap if on JDK 8 or less
return transformerImpl;
}
// wrap it
return new ReadEdgeAddingClassFileTransformer(instrumentation,
transformerImpl,
readableClasses);
}
}
...which you call with your ClassFileTransformer implementation and the
Class(es) where your Byteman code resides that contains methods called
from instrumented code. The following delegating ClassFileTransformer,
compiled with -target 9, will add read edges then:
public class ReadEdgeAddingClassFileTransformer implements
ClassFileTransformer {
private final Instrumentation instrumentation;
private final ClassFileTransformer transformerImpl;
private final Set<Module> readableModules;
ReadEdgeAddingClassFileTransformer(Instrumentation instrumentation,
ClassFileTransformer
transformerImpl,
Class<?>[] readableClasses) {
this.instrumentation = instrumentation;
this.transformerImpl = transformerImpl;
this.readableModules = new HashSet<>();
for (Class<?> c : readableClasses) {
this.readableModules.add(c.getModule());
}
}
@Override
public byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws
IllegalClassFormatException {
return transformerImpl.transform(loader,
className,
classBeingRedefined,
protectionDomain,
classfileBuffer);
}
@Override
public byte[] transform(Module module,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws
IllegalClassFormatException {
byte[] transformedClassFile = transformerImpl.transform(module,
className,
classBeingRedefined,
protectionDomain,
classfileBuffer);
// only need to add read edges if there was a transformation
performed
if (transformedClassFile != null) {
for (Module target : readableModules) {
if (!module.canRead(target)) {
instrumentation.addModuleReads(module, target);
}
}
}
return transformedClassFile;
}
}
This class is the only class that needs to be compiled with JDK 9 actually.
Regards, Peter
More information about the jigsaw-dev
mailing list