[英]Java Agent using Byte-Buddy doesn't work
I need your help in finding what's wrong with my implementation... 我需要你的帮助来找出我的实施有什么问题...
I'm trying to implement a simple JVM runtime profiler using byte-buddy. 我正在尝试使用byte-buddy实现一个简单的JVM运行时分析器。 In general, what I need is that every method call will be logged in a stack which I manage in a separate object.
一般来说,我需要的是每个方法调用都将记录在一个堆栈中,我在一个单独的对象中管理。
After reading several posts, I understood that it's better to use the "Advise" approach instead of "MethodDelegation", and here's what I came out with: 在阅读了几篇文章之后,我明白最好使用“Advise”方法而不是“MethodDelegation”,这就是我发布的内容:
Agent.java: 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();
}
}
}
And ProfilingAdvice.java: 和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();
}
}
}
For some reason, the "enter" and "exit" methods in ProfilingAdvice class, aren't invoked at all. 出于某种原因,ProfilingAdvice类中的“enter”和“exit”方法根本不会被调用。
What am I doing wrong ? 我究竟做错了什么 ?
Thanks, Elad. 谢谢,很高兴。
I tried your example and after reducing the exit advice to the print commands and your matcher to only match some class foo.Bar
: 我尝试了你的例子,在减少了对print命令的退出建议后,你的匹配器只匹配了一些类
foo.Bar
:
package foo;
public class Bar {
public long qux() { return 0L; }
}
the instrumentation works without problem. 仪器工作没有问题。 I find it a bit fishy that your
Advice
matcher specifies any()
while your ProfilingAdvice
requires a long
return type. 当你的
ProfilingAdvice
需要一个long
返回类型时,我发现你的Advice
匹配器指定any()
有点可疑。 Did you try only printing to the console without any annotations in your advice? 您是否尝试仅在您的建议中没有任何注释的情况下打印到控制台?
You can debug such issues by settting: 您可以通过以下方式调试此类问题:
new AgentBuilder.Default()
.with(AgentBuilder.Listener.StreamWriting.toSystemOut());
where potential errors during the instrumentation are printed to the console. 将仪器中的潜在错误打印到控制台。
@Advice.Return
stands for the return value of the monitor methord. @Advice.Return
代表监视器方法的返回值。
@Advice.Return
is not the return value of the enter(@Advice.Origin("#t.#m") String signature)
method. @Advice.Return
不是enter(@Advice.Origin("#t.#m") String signature)
方法的返回值。
So, the following code is 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();
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.