преобразование класса не имеет никакого эффекта
Основываясь на этом руководстве, я пытаюсь заставить работать java-агент. https://www.baeldung.com/java-instrumentation
Я получаю [Agent] Transforming class TestApplication
У меня нет ошибок, но я не вижу никакого эффекта от преобразования класса.
В конце концов, я хотел бы, чтобы работали как статическая, так и динамическая нагрузка, но пока я сосредоточусь на статическом способе.
public class Static_Agent {
public static void premain(String agentArgs, Instrumentation inst) {
String[] tokens = agentArgs.split(";");
String className = tokens[0];
String methodName = tokens[1];
System.out.println(">> "+className);
System.out.println(">> "+methodName);
transformClass(className, methodName, inst);
}
public static void transformClass(String className, String methodName, Instrumentation instrumentation) {
Class<?> targetCls = null;
ClassLoader targetClassLoader = null;
// see if we can get the class using forName
try {
targetCls = Class.forName(className);
targetClassLoader = targetCls.getClassLoader();
transform(targetCls, methodName, targetClassLoader, instrumentation);
return;
} catch (Exception ex) {
ex.printStackTrace();
}
// otherwise iterate all loaded classes and find what we want
for(Class<?> clazz: instrumentation.getAllLoadedClasses()) {
if(clazz.getName().equals(className)) {
targetCls = clazz;
targetClassLoader = targetCls.getClassLoader();
transform(targetCls, methodName, targetClassLoader, instrumentation);
return;
}
}
throw new RuntimeException("Failed to find class [" + className + "]");
}
public static void transform(Class<?> clazz, String methodName, ClassLoader classLoader, Instrumentation instrumentation) {
Transformer dt = new Transformer(clazz.getName(), methodName, classLoader);
instrumentation.addTransformer(dt, true);
try {
instrumentation.retransformClasses(clazz);
} catch (Exception ex) {
throw new RuntimeException("Transform failed for class: [" + clazz.getName() + "]", ex);
}
}
}
public class Transformer implements ClassFileTransformer {
/** The internal form class name of the class to transform */
private String targetClassName;
/** The class loader of the class we want to transform */
private ClassLoader targetClassLoader;
private String targetMethodName;
public Transformer(String targetClassName, String targetMethodName, ClassLoader targetClassLoader) {
this.targetClassName = targetClassName;
this.targetClassLoader = targetClassLoader;
this.targetMethodName = targetMethodName;
}
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
byte[] byteCode = classfileBuffer;
String finalTargetClassName = this.targetClassName.replaceAll("\\.", "/");
if (!className.equals(finalTargetClassName)) {
return byteCode;
}
if (className.equals(finalTargetClassName) && loader.equals(targetClassLoader)) {
System.out.println("[Agent] Transforming class TestApplication");
try {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get(targetClassName);
CtMethod m = cc.getDeclaredMethod(targetMethodName);
m.addLocalVariable("startTime", CtClass.longType);
m.insertBefore("startTime = System.currentTimeMillis();");
StringBuilder endBlock = new StringBuilder();
m.addLocalVariable("endTime", CtClass.longType);
m.addLocalVariable("opTime", CtClass.longType);
endBlock.append("endTime = System.currentTimeMillis();");
endBlock.append("opTime = (endTime-startTime)/1000;");
endBlock.append("System.out.println(\"[Application] Withdrawal operation completed in:\" + opTime + \" seconds!\");");
m.insertAfter(endBlock.toString());
byteCode = cc.toBytecode();
cc.detach();
} catch (Exception e) {
System.out.println("Exception"+e);
}
}
return byteCode;
}
}
public class TestApplication {
public static void main(String[] args) {
try {
TestApplication.run();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void run() throws Exception {
System.out.println("--- start ---");
while (true) {
test();
Thread.sleep(4_000);
}
}
static int count = 0;
public static void test() {
System.out.println(count++);
}
}
Я запускаю:
java -javaagent:static_agent.jar="doeke.application.TestApplication;test" -jar application.jar
Если это поможет, проект находится здесь:https://github.com/clankill3r/java_agent
Редактировать:
В Transformer.java в конце файла, который я использую e.printStackTrace();
сейчас же.
Я получаю следующую ошибку:
[Агент] Преобразование класса TestApplication javassist.NotFoundException: doeke.application.TestApplication в javassist.ClassPool.get(ClassPool.java:436) в doeke.transformer.Transformer.transform(Transformer.java:48) в java.instrument/java.lang.instrument.ClassFileTransformer.transform(ClassFileTransformer.java:246) в java.instrument/sun.instrument.TransformerManager.transform(TransformerManager.java:188) в java.instrument/sun.instrument.InstrumentationImpl.transform.java:563) в java.instrument/sun.instrument.InstrumentationImpl.retransformClasses0(собственный метод) в java.instrument/sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:167) в doeke.static_agent.Static_Agent.transform.java. 56) в doeke.static_agent.Static_Agent.transformClass(Static_Agent.java:34) в doeke.static_agent.Static_Agent.premain(Static_Agent.java:22) в java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(собственный метод) в java.base.internal. Reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) в java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) в java.base.base () в java.base.java:566) в java.instrument/sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:513) в java.instrument/sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:525)invoke0 (собственный метод) в java.base / jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) в java.base / jdk.internal.reflect.DelegatingMethodAccessorImpl.invokeho.java. base / java.lang.reflect.Method.invoke (Method.java:566) в java.instrument/sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:513) в java.instrument / sun.instrument.InstrumentationImpl.loadClassAndCall InstrumentationImpl.java:525)invoke0 (собственный метод) в java.base / jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) в java.base / jdk.internal.reflect.DelegatingMethodAccessorImpl.invokeho.java. base / java.lang.reflect.Method.invoke (Method.java:566) в java.instrument/sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:513) в java.instrument / sun.instrument.InstrumentationImpl.loadClassAndCall InstrumentationImpl.java:525)instrument / sun.instrument.InstrumentationImpl.loadClassAndStartAgent (InstrumentationImpl.java:513) в java.instrument/sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:525)instrument / sun.instrument.InstrumentationImpl.loadClassAndStartAgent (InstrumentationImpl.java:513) в java.instrument/sun.instrument.InstrumentationImpl.loadClassAndCallPremain(InstrumentationImpl.java:525)
--- Начало ---
0
1
1 ответ
Спасибо, что подняли этот вопрос, чтобы дать мне возможность взглянуть на Java Instrumentation.
Потратив некоторое время на перекрестную проверку образцов кода и предоставленного руководства. Проблема не в кодах программирования, а в способе запуска вашей программы.
Если вы добавите несколько регистраторов к методу transform() в Transformer.java, вы обнаружите, что путь кода нарушен после запуска:
ClassPool cp = ClassPool.getDefault();
И после замены кода перехвата исключений тем же методом из:
} catch (Exception e) {
кому:
} catch (NotFoundException | CannotCompileException | IOException e) {
Это даст вам больше подсказок, как показано ниже:
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(Unknown Source)
at sun.instrument.InstrumentationImpl.loadClassAndCallPremain(Unknown Source)
Caused by: java.lang.NoClassDefFoundError: javassist/NotFoundException
at doeke.static_agent.Static_Agent.transform(Static_Agent.java:60)
at doeke.static_agent.Static_Agent.transformClass(Static_Agent.java:40)
at doeke.static_agent.Static_Agent.premain(Static_Agent.java:28)
... 6 more
Caused by: java.lang.ClassNotFoundException: javassist.NotFoundException
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 9 more
FATAL ERROR in native method: processing of -javaagent failed
До этого момента основная причина более очевидна. Это связано с тем, что при запуске программы эти соответствующие классы javassist (например, ClassPool, CtClass, CtMethod и т. Д.) Не могут ссылаться на соответствующие библиотеки во время выполнения.
Итак, решение:
предполагая, что вы экспортировали static_agent.jar в ту же папку "build", что и application.jar
вся остальная структура папок остается такой же, как показано в предоставленном вами github
давайте "cd" в папку сборки в командной консоли
изменение исходного сценария запуска программы, как показано ниже
ОС Windows:
java -javaagent:static_agent.jar="doeke.application.TestApplication;test" -cp ../libs/javassist-3.12.1.GA.jar;application.jar doeke.application.TestApplication
ОС Unix/Linux:
java -javaagent:static_agent.jar="doeke.application.TestApplication;test" -cp ../libs/javassist-3.12.1.GA.jar:application.jar doeke.application.TestApplication
Наконец-то вы получите ожидаемый результат:
[Agent] In premain method.
>> doeke.application.TestApplication
>> test
[Agent] Transforming class
--- start ---
0
[Application] Withdrawal operation completed in:0 seconds!
1
[Application] Withdrawal operation completed in:0 seconds!
РЕДАКТИРОВАТЬ
Кроме того, позвольте мне вставить несколько кодов о том, как вставлять коды в середину метода через javassist.
В случае, если метод test() в TestApplication.java изменяется как:
line 30 public static void test() {
line 31 System.out.println(count++);
line 32
line 33 System.out.println("Last line of test() method");
line 34 }
Предположим, что мы хотим добавить черту между счетчиком и =========, скажем "Это разделитель строк", и результат будет выглядеть так:
1
-- This is line separator--
Last line of test() method
Затем в методе transform(...) Transformer.java вы можете добавить строку кода, как показано ниже:
m.insertAt(32,"System.out.println(\"-- This is line separator--\");");
что делает это становится:
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
byte[] byteCode = classfileBuffer;
String finalTargetClassName = this.targetClassName.replaceAll("\\.", "/");
if (!className.equals(finalTargetClassName)) {
return byteCode;
}
if (className.equals(finalTargetClassName) && loader.equals(targetClassLoader)) {
System.out.println("[Agent] Transforming class TestApplication");
try {
// Step 1 Preparation
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get(targetClassName);
CtMethod m = cc.getDeclaredMethod(targetMethodName);
// Step 2 Declare variables
m.addLocalVariable("startTime", CtClass.longType);
m.addLocalVariable("endTime", CtClass.longType);
m.addLocalVariable("opTime", CtClass.longType);
// Step 3 Insertion of extra logics/implementation
m.insertBefore("startTime = System.currentTimeMillis();");
m.insertAt(32,"System.out.println(\"-- This is line separator--\");");
StringBuilder endBlock = new StringBuilder();
endBlock.append("endTime = System.currentTimeMillis();");
endBlock.append("opTime = (endTime-startTime)/1000;");
endBlock.append("System.out.println(\"[Application] Withdrawal operation completed in:\" + opTime + \" seconds!\");");
m.insertAfter(endBlock.toString());
// Step 4 Detach from ClassPool and clean up stuff
byteCode = cc.toBytecode();
cc.detach();
} catch (NotFoundException | CannotCompileException | IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
return byteCode;
}
Наконец, будет получен результат, как показано ниже, при печати кода в середине метода:
[Agent] In premain method.
className=doeke.application.TestApplication
methodName=test
>> doeke.application.TestApplication
>> test
[Agent] Transforming class TestApplication
--- start ---
0
-- This is line separator--
=========
[Application] Withdrawal operation completed in:0 seconds!
1
-- This is line separator--
=========
[Application] Withdrawal operation completed in:0 seconds!
2
-- This is line separator--
=========
[Application] Withdrawal operation completed in:0 seconds!