Java 使用 Byte-Buddy 的代理不起作用
Java Agent using Byte-Buddy doesn't work
我需要你的帮助来找出我的实现有什么问题...
我正在尝试使用 byte-buddy 实现一个简单的 JVM 运行时分析器。
一般来说,我需要的是每个方法调用都将记录在一个堆栈中,我在一个单独的对象中管理它。
阅读几篇文章后,我了解到使用 "Advise" 方法比 "MethodDelegation" 更好,我得出的结论是:
Agent.java:
package com.panaya.java.agent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
public class Agent {
private static List<Pattern> pkgIncl = new ArrayList<Pattern>();
private static List<Pattern> pkgExcl = new ArrayList<Pattern>();
private static void initMatcherPatterns(String argument) {
if (argument == null) {
System.out.println("Missing configuration argument");
return;
}
System.out.println("Argument is: " + argument);
String[] tokens = argument.split(";");
if (tokens.length < 1) {
System.out.println("Missing delimeter ;");
return;
}
for (String token : tokens) {
String[] args = token.split("=");
if (args.length < 2) {
System.out.println("Missing argument delimeter =:" + token);
return;
}
String argtype = args[0];
if (!argtype.equals("incl") && !argtype.equals("excl")) {
System.out.println("Wrong argument: " + argtype);
return;
}
String[] patterns = args[1].split(",");
for (String pattern : patterns) {
Pattern p = null;
System.out.println("Compiling " + argtype + " pattern:" + pattern + "$");
try {
p = Pattern.compile(pattern + "$");
} catch (PatternSyntaxException pse) {
System.out.println("pattern: " + pattern + " not valid, ignoring");
}
if (argtype.equals("incl"))
pkgIncl.add(p);
else
pkgExcl.add(p);
}
}
}
private static boolean isShouldInstrumentClass(String className) {
System.out.println("Testing " + className + " for match.");
boolean match = false;
String name = className.replace("/", ".");
for (Pattern p : pkgIncl) {
Matcher m = p.matcher(name);
if (m.matches()) {
match = true;
break;
}
}
for (Pattern p : pkgExcl) {
Matcher m = p.matcher(name);
if (m.matches()) {
match = false;
break;
}
}
if (match) {
System.out.println("Class " + name + "should be instrumented.");
} else {
System.out.println("Skipping class: " + name);
}
return match;
}
public static void premain(String agentArgument, Instrumentation instrumentation) {
System.out.println("Premain started");
try {
initMatcherPatterns(agentArgument);
new AgentBuilder.Default()
.with(AgentBuilder.TypeStrategy.Default.REBASE)
.type(new AgentBuilder.RawMatcher() {
public boolean matches(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, Class<?> aClass, ProtectionDomain protectionDomain) {
return isShouldInstrumentClass(typeDescription.getActualName());
}
})
.transform((builder, typeDescription, classLoader) -> builder
.visit(Advice.to(ProfilingAdvice.class)
.on(ElementMatchers.any()))).installOn(instrumentation);
} catch (RuntimeException e) {
System.out.println("Exception instrumenting code : " + e);
e.printStackTrace();
}
}
}
和ProfilingAdvice.java:
package com.panaya.java.agent;
import com.panaya.java.profiler.MethodStackCollector;
import net.bytebuddy.asm.Advice;
public class ProfilingAdvice {
@Advice.OnMethodEnter
public static void enter(@Advice.Origin("#t.#m") String signature) {
System.out.println("OnEnter :" + signature);
try {
MethodStackCollector.getInstance().push(signature);
} catch (Exception e) {
e.printStackTrace();
}
}
@Advice.OnMethodExit
public static void exit(@Advice.Return long value) {
System.out.println("OnExit - value = " + value);
try {
MethodStackCollector.getInstance().pop();
} catch (Exception e) {
e.printStackTrace();
}
}
}
出于某种原因,根本没有调用 ProfilingAdvice class 中的 "enter" 和 "exit" 方法。
我做错了什么?
谢谢,
埃拉德.
我试过你的例子,在减少打印命令的退出建议和你的匹配器后只匹配一些 class foo.Bar
:
package foo;
public class Bar {
public long qux() { return 0L; }
}
检测工作没有问题。我发现您的 Advice
匹配器指定 any()
而您的 ProfilingAdvice
需要 long
return 类型有点可疑。您是否尝试过只打印到控制台而没有在您的建议中添加任何注释?
您可以通过设置来调试此类问题:
new AgentBuilder.Default()
.with(AgentBuilder.Listener.StreamWriting.toSystemOut());
将检测过程中的潜在错误打印到控制台。
@Advice.Return
代表监控方法的return值。
@Advice.Return
不是 enter(@Advice.Origin("#t.#m") String signature)
方法的 return 值。
所以,下面的代码就OK了:
public class ProfilerInterceptor {
public static ThreadLocal<Long> threadLocal = new ThreadLocal();
@Advice.OnMethodEnter
public static void enter(@Advice.Origin("#t.#m") String signature) {
System.out.printf("Enter: %s\n", signature);
long start = System.currentTimeMillis();
threadLocal.set(start);
try {
MethodStackCollector.getInstance().push(signature);
} catch (Exception e) {
e.printStackTrace();
}
}
@Advice.OnMethodExit
public static void exit(@Advice.Origin("#t.#m") String signature) {
long value = System.currentTimeMillis() - threadLocal.get();
System.out.printf("Exit: %s\nTime: %d\n\n", signature, value);
System.out.println("OnExit - value = " + value);
try {
MethodStackCollector.getInstance().pop();
} catch (Exception e) {
e.printStackTrace();
}
}
}
我需要你的帮助来找出我的实现有什么问题...
我正在尝试使用 byte-buddy 实现一个简单的 JVM 运行时分析器。 一般来说,我需要的是每个方法调用都将记录在一个堆栈中,我在一个单独的对象中管理它。
阅读几篇文章后,我了解到使用 "Advise" 方法比 "MethodDelegation" 更好,我得出的结论是:
Agent.java:
package com.panaya.java.agent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
public class Agent {
private static List<Pattern> pkgIncl = new ArrayList<Pattern>();
private static List<Pattern> pkgExcl = new ArrayList<Pattern>();
private static void initMatcherPatterns(String argument) {
if (argument == null) {
System.out.println("Missing configuration argument");
return;
}
System.out.println("Argument is: " + argument);
String[] tokens = argument.split(";");
if (tokens.length < 1) {
System.out.println("Missing delimeter ;");
return;
}
for (String token : tokens) {
String[] args = token.split("=");
if (args.length < 2) {
System.out.println("Missing argument delimeter =:" + token);
return;
}
String argtype = args[0];
if (!argtype.equals("incl") && !argtype.equals("excl")) {
System.out.println("Wrong argument: " + argtype);
return;
}
String[] patterns = args[1].split(",");
for (String pattern : patterns) {
Pattern p = null;
System.out.println("Compiling " + argtype + " pattern:" + pattern + "$");
try {
p = Pattern.compile(pattern + "$");
} catch (PatternSyntaxException pse) {
System.out.println("pattern: " + pattern + " not valid, ignoring");
}
if (argtype.equals("incl"))
pkgIncl.add(p);
else
pkgExcl.add(p);
}
}
}
private static boolean isShouldInstrumentClass(String className) {
System.out.println("Testing " + className + " for match.");
boolean match = false;
String name = className.replace("/", ".");
for (Pattern p : pkgIncl) {
Matcher m = p.matcher(name);
if (m.matches()) {
match = true;
break;
}
}
for (Pattern p : pkgExcl) {
Matcher m = p.matcher(name);
if (m.matches()) {
match = false;
break;
}
}
if (match) {
System.out.println("Class " + name + "should be instrumented.");
} else {
System.out.println("Skipping class: " + name);
}
return match;
}
public static void premain(String agentArgument, Instrumentation instrumentation) {
System.out.println("Premain started");
try {
initMatcherPatterns(agentArgument);
new AgentBuilder.Default()
.with(AgentBuilder.TypeStrategy.Default.REBASE)
.type(new AgentBuilder.RawMatcher() {
public boolean matches(TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule, Class<?> aClass, ProtectionDomain protectionDomain) {
return isShouldInstrumentClass(typeDescription.getActualName());
}
})
.transform((builder, typeDescription, classLoader) -> builder
.visit(Advice.to(ProfilingAdvice.class)
.on(ElementMatchers.any()))).installOn(instrumentation);
} catch (RuntimeException e) {
System.out.println("Exception instrumenting code : " + e);
e.printStackTrace();
}
}
}
和ProfilingAdvice.java:
package com.panaya.java.agent;
import com.panaya.java.profiler.MethodStackCollector;
import net.bytebuddy.asm.Advice;
public class ProfilingAdvice {
@Advice.OnMethodEnter
public static void enter(@Advice.Origin("#t.#m") String signature) {
System.out.println("OnEnter :" + signature);
try {
MethodStackCollector.getInstance().push(signature);
} catch (Exception e) {
e.printStackTrace();
}
}
@Advice.OnMethodExit
public static void exit(@Advice.Return long value) {
System.out.println("OnExit - value = " + value);
try {
MethodStackCollector.getInstance().pop();
} catch (Exception e) {
e.printStackTrace();
}
}
}
出于某种原因,根本没有调用 ProfilingAdvice class 中的 "enter" 和 "exit" 方法。
我做错了什么?
谢谢, 埃拉德.
我试过你的例子,在减少打印命令的退出建议和你的匹配器后只匹配一些 class foo.Bar
:
package foo;
public class Bar {
public long qux() { return 0L; }
}
检测工作没有问题。我发现您的 Advice
匹配器指定 any()
而您的 ProfilingAdvice
需要 long
return 类型有点可疑。您是否尝试过只打印到控制台而没有在您的建议中添加任何注释?
您可以通过设置来调试此类问题:
new AgentBuilder.Default()
.with(AgentBuilder.Listener.StreamWriting.toSystemOut());
将检测过程中的潜在错误打印到控制台。
@Advice.Return
代表监控方法的return值。
@Advice.Return
不是 enter(@Advice.Origin("#t.#m") String signature)
方法的 return 值。
所以,下面的代码就OK了:
public class ProfilerInterceptor {
public static ThreadLocal<Long> threadLocal = new ThreadLocal();
@Advice.OnMethodEnter
public static void enter(@Advice.Origin("#t.#m") String signature) {
System.out.printf("Enter: %s\n", signature);
long start = System.currentTimeMillis();
threadLocal.set(start);
try {
MethodStackCollector.getInstance().push(signature);
} catch (Exception e) {
e.printStackTrace();
}
}
@Advice.OnMethodExit
public static void exit(@Advice.Origin("#t.#m") String signature) {
long value = System.currentTimeMillis() - threadLocal.get();
System.out.printf("Exit: %s\nTime: %d\n\n", signature, value);
System.out.println("OnExit - value = " + value);
try {
MethodStackCollector.getInstance().pop();
} catch (Exception e) {
e.printStackTrace();
}
}
}