【问题标题】:Java Agent using Byte-Buddy doesn't work使用 Byte-Buddy 的 Java 代理不起作用
【发布时间】:2016-07-27 08:45:46
【问题描述】:

我需要您的帮助来找出我的实施有什么问题...

我正在尝试使用 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 类中的“enter”和“exit”方法根本没有被调用。

我做错了什么?

谢谢, 埃拉德。

【问题讨论】:

  • 你的代理怎么样?您是否注册了AgentBuilder.Listener 以查看仪器是否有任何问题?
  • @RafaelWinterhalter,我不确定我是否理解你的问题......“你的经纪人看起来怎么样”是什么意思?你能解释一下你的问题吗....我的代理类如上所示...加载正常,但建议机制不起作用。
  • 我想知道您的 premain / agentmain 方法是什么样的。您需要使用AgentBuilder 在某处收集建议。
  • 我把整个Agent代码附在帖子后面,你可以看premain方法。
  • 为了方便您搜索: 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);

标签: java profiler byte-buddy


【解决方案1】:

我尝试了您的示例,并在将退出建议减少到打印命令和您的匹配器以仅匹配某些类 foo.Bar 之后:

package foo;
public class Bar {
  public long qux() { return 0L; }
}

仪器工作没有问题。我发现您的 Advice 匹配器指定 any() 而您的 ProfilingAdvice 需要 long 返回类型有点可疑。您是否尝试只打印到控制台而不在您的建议中添加任何注释?

您可以通过设置调试此类问题:

new AgentBuilder.Default()
   .with(AgentBuilder.Listener.StreamWriting.toSystemOut());

检测期间的潜在错误将打印到控制台。

【讨论】:

  • 在附加监听器时,我注意到通知方法实际上被调用了,但返回类型确实存在问题。从退出方法中删除返回参数后,大多数检测工作正常,但在特定情况下,我得到以下异常:java.lang.IllegalStateException:操作数堆栈上的意外余数:net.bytebuddy.utility 处的 -1 .StackAwareMethodVisitor.drainStack(StackAwareMethodVisitor.java:120)....我将发布一个单独的问题。
  • 请做。如果你能提供重建,那就太好了。也请更新到 1.4.16,我最近修复了一个相关问题。
  • 虽然更新到 1.4.16 解决了之前的问题,但似乎当我更改“ElementMatchers”组合时,再次导致此异常。
  • 那时我可能错过了一些东西。您能否更新您的项目以重现此错误?
  • 有没有办法通过电子邮件联系您?
【解决方案2】:

@Advice.Return代表监听方法的返回值。

@Advice.Return 不是enter(@Advice.Origin("#t.#m") String signature) 方法的返回值。

所以,下面的代码就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();
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-02
    • 1970-01-01
    相关资源
    最近更新 更多