问题
I'm trying to create Java Agent that will intercept FileInputStream/FileOutputStream constructors:
import java.io.*;
import java.lang.instrument.Instrumentation;
import java.util.Arrays;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.agent.builder.AgentBuilder.InitializationStrategy;
import net.bytebuddy.agent.builder.AgentBuilder.Listener;
import net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy;
import net.bytebuddy.agent.builder.AgentBuilder.TypeStrategy;
import net.bytebuddy.asm.Advice;
import static net.bytebuddy.dynamic.ClassFileLocator.CLASS_FILE_EXTENSION;
import static net.bytebuddy.matcher.ElementMatchers.*;
import net.bytebuddy.matcher.StringMatcher;
public class Agent {
private static final List<Class<?>> BOOTSTRAP_CLASSES = Arrays.asList(
Interceptor.class
);
private Agent() {
}
public static void premain(String arg, Instrumentation instrumentation) {
injectBootstrapClasses(instrumentation);
new AgentBuilder.Default()
.with(RedefinitionStrategy.RETRANSFORMATION)
.with(InitializationStrategy.NoOp.INSTANCE)
.with(TypeStrategy.Default.REDEFINE)
.ignore(new AgentBuilder.RawMatcher.ForElementMatchers(nameStartsWith("net.bytebuddy.").or(isSynthetic()), any(), any()))
.with(new Listener.Filtering(
new StringMatcher("java.io.FileInputStream", StringMatcher.Mode.EQUALS_FULLY)
.or(new StringMatcher("java.io.FileOutputStream", StringMatcher.Mode.EQUALS_FULLY)),
Listener.StreamWriting.toSystemOut()))
.type(named("java.io.FileInputStream").or(named("java.io.FileOutputStream")))
.transform((builder, type, classLoader, module) ->
builder
.constructor(any())
.intercept(Advice.to(Interceptor.class))
)
.installOn(instrumentation);
}
private static void injectBootstrapClasses(Instrumentation instrumentation) {
try {
File jarFile = File.createTempFile(Agent.class.getSimpleName(), ".jar");
jarFile.deleteOnExit();
try (JarOutputStream jarOutputStream = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(jarFile)))) {
for (Class<?> bootstrapClass : BOOTSTRAP_CLASSES) {
String klassPath = classFileFullname(bootstrapClass);
jarOutputStream.putNextEntry(new JarEntry(klassPath));
jarOutputStream.write(readFully(bootstrapClass.getClassLoader().getResourceAsStream(klassPath)));
}
}
instrumentation.appendToBootstrapClassLoaderSearch(new JarFile(jarFile));
} catch (IOException exception) {
throw new IllegalStateException("Cannot write jar file to disk", exception);
}
}
private static String classFileFullname(Class<?> bootstrapClass) {
return bootstrapClass.getName().replace('.', '/') + CLASS_FILE_EXTENSION;
}
private static byte[] readFully(InputStream input) throws IOException {
byte[] buffer = new byte[8192];
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
int bytesRead;
while ((bytesRead = input.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
return output.toByteArray();
}
}
public static class Interceptor{
@Advice.OnMethodExit
public static void intercept() {
System.out.println("Exit constructor");
}
}
}
The above code prints:
java.lang.IllegalStateException: Cannot call super (or default) method for public java.io.FileOutputStream(java.lang.String) throws java.io.FileNotFoundException
at net.bytebuddy.implementation.SuperMethodCall$Appender.apply(SuperMethodCall.java:97)
at net.bytebuddy.asm.Advice$Appender$EmulatingMethodVisitor.resolve(Advice.java:7812)
at net.bytebuddy.asm.Advice$Appender.apply(Advice.java:7765)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyCode(TypeWriter.java:620)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyBody(TypeWriter.java:609)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$RedefinitionClassVisitor$CodePreservingMethodVisitor.visitCode(TypeWriter.java:3969)
at net.bytebuddy.jar.asm.ClassReader.b(Unknown Source)
at net.bytebuddy.jar.asm.ClassReader.accept(Unknown Source)
at net.bytebuddy.jar.asm.ClassReader.accept(Unknown Source)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining.create(TypeWriter.java:2941)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1633)
at net.bytebuddy.dynamic.scaffold.inline.RedefinitionDynamicTypeBuilder.make(RedefinitionDynamicTypeBuilder.java:171)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:2676)
at net.bytebuddy.agent.builder.AgentBuilder$Default$Transformation$Simple$Resolution.apply(AgentBuilder.java:8902)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.doTransform(AgentBuilder.java:9303)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:9266)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.access$1300(AgentBuilder.java:9044)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:9622)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:9572)
at java.security.AccessController.doPrivileged(Native Method)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:9191)
at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
at sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
at sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:144)
at net.bytebuddy.agent.builder.AgentBuilder$RedefinitionStrategy$Collector$ForRetransformation.doApply(AgentBuilder.java:6213)
at net.bytebuddy.agent.builder.AgentBuilder$RedefinitionStrategy$Collector.apply(AgentBuilder.java:6071)
at net.bytebuddy.agent.builder.AgentBuilder$RedefinitionStrategy.apply(AgentBuilder.java:4252)
at net.bytebuddy.agent.builder.AgentBuilder$Default.installOn(AgentBuilder.java:8258)
at net.bytebuddy.agent.builder.AgentBuilder$Default$Delegator.installOn(AgentBuilder.java:9957)
at com.github.soldierkam.agent.Agent.premain(Agent.java:46)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:386)
at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:401)
How to solve this problem? It looks like ByteBuddy tries to call OutputStream(String) constructor that does not exists. I'm using Byte Buddy 1.7.1 and JVM 1.8.0_131
回答1:
You need to use the Advice
component as a visitor to enhance the existing constructor; currently, you are implementing a new method.
This should work:
builder.visit(Advice.to(Interceptor.class).on(isConstructor()));
来源:https://stackoverflow.com/questions/44747219/byte-buddy-advice-onmethodexit-constructor-retransformation