Эффективно получить вызывающего абонента в MethodDelegation байтового приятеля

Я пытаюсь построить дерево вызовов в своем java-агенте с библиотекой byte buddy. Чтобы добавить элементы в дерево, я хочу использовать делегирование метода. Однако, чтобы убедиться, кто является родителем любого листа, мне нужно знать, кто вызвал метод.

Я не хочу использовать:

sun.reflect.Reflection#getCallerClass(int)

Потому что он устарел и недоступен в Java 8+. Кроме того, я пробовал:

public class ThreadUtil {

public static StackTraceElement getCaller() {
    Instant now = Instant.now();
    StackTraceElement ste = Thread.currentThread().getStackTrace()[3];
    String callerClass = ste.getClassName();
    String callerMethod = ste.getMethodName();
    Instant now2= Instant.now();
    System.out.println(Duration.between(now, now2));
    return ste;
}

}

Но это очень медленно (~ 1 мс - слишком много, если у меня тысячи звонков).

Есть ли способ эффективно вызвать вызывающего абонента в этот момент (может быть, какие-то трюки с байтовыми приятелями)?

P.S. Мой агент:

private static void instrument(String agentOps, Instrumentation inst) {
    System.out.println("Agent");
    new AgentBuilder.Default().with(new Eager())
            .ignore(ElementMatchers.nameContains("com.dvelopp.agenttest"))
            .type((ElementMatchers.any()))
            .transform((builder, typeDescription, classLoader, module) -> builder.method(ElementMatchers.any())
                    .intercept(MethodDelegation.to(Interceptor.class))).installOn(inst);
}

public static class Interceptor {

    @RuntimeType
    public static Object intercept(@SuperCall Callable<?> zuper, @Origin Method method,
                                   @AllArguments Object[] args, @This(optional = true) Object me) throws Exception {
        System.out.println("CURRENT: " + method.getDeclaringClass().getName());
        System.out.println("CALLER: " + ThreadUtil.getCaller().getClassName());
        return zuper.call();
    }

}

ENV: Java 8


person dvelopp    schedule 07.01.2019    source источник
comment
sun.reflect.Reflection#getCallerClass(int) не просто устарел — он никогда не был частью официального API. Начиная с Java 9, официальным способом является StackWalker.getCallerClass().   -  person Holger    schedule 08.01.2019
comment
@Holger, я наткнулся на StackWalker. Это выглядит потрясающе. Но в настоящее время я хочу, чтобы мой агент поддерживал приложения Java 8 (я добавил это к вопросу). Благодарю вас!   -  person dvelopp    schedule 08.01.2019
comment
Просто используйте StackWalker, когда вы находитесь в Java 9 JRE, и вернитесь к устаревшему методу в более старых JRE. Официального способа для старых сред не существует, и устаревший метод не исчезнет в этих средах.   -  person Holger    schedule 08.01.2019
comment
Что насчет Java 8? Он был удален в JDK 8, а StackWalker больше нет.   -  person dvelopp    schedule 08.01.2019
comment
В моей установке Java 8 он все еще присутствует.   -  person Holger    schedule 08.01.2019
comment
Когда я пытаюсь использовать его в JDK 8, даже так: @CallerSensitive public static void main(String[] args) { Reflection.getCallerClass(); } Я получаю ошибки. например Исключение в потоке main java.lang.InternalError: аннотация CallerSensitive ожидается в кадре 1. До этого у меня было исключение Class not found   -  person dvelopp    schedule 10.01.2019
comment
Ничто в этом сообщении об ошибке не указывает на то, что Reflection.getCallerClass не существует. Вы не можете найти вызывающий класс метода main, потому что метод main не имеет вызывающего класса. Это также приведет к ошибке при попытке использовать StackWalker.getCallerClass() в методе main.   -  person Holger    schedule 10.01.2019
comment
@Holger, ты абсолютно прав!   -  person dvelopp    schedule 10.01.2019


Ответы (1)


Инструментарий байт-кода позволяет вам генерировать код, который вы также можете написать самостоятельно. В вашем случае вам нужно будет создать довольно навязчивый инструментарий, который я бы не рекомендовал:

  1. Инструментируйте целевой метод для принятия нового параметра типа Class.
  2. Инструментируйте каждый вызывающий объект, чтобы указать свой тип в качестве дополнительного аргумента.

Лучшее решение, безусловно, то, что Хольгер предложил в комментариях. Используйте StackWalker и, если он недоступен, вернитесь к sun.reflect.Reflection (который присутствует во всех известных мне JVM).

person Rafael Winterhalter    schedule 09.01.2019
comment
Это действительно работает для вас на JDK 8? Попробуйте public static void main(String[] args) { Reflection.getCallerClass(); } Он скомпилирован, но выдает ошибку: Exception in thread main java.lang.InternalError: аннотация CallerSensitive ожидается в кадре 1 в sun.reflect.Reflection.getCallerClass(Native Method) в hello.ServiceInstanceRestController.main - person dvelopp; 10.01.2019
comment
Это работает для меня. Какую версию JDK и от какого поставщика вы используете? - person Rafael Winterhalter; 10.01.2019
comment
Проблема была из-за того, что я вызывал ее из основного метода, как сказал Хольгер. - person dvelopp; 10.01.2019