Перехват и изменение возвращаемого значения встроенного метода stati c с помощью byte-buddy (System.nanoTime ()) - PullRequest
2 голосов
/ 05 февраля 2020

Я хочу изменить то, что происходит, когда System.nanoTime() вызывается для произвольных Java программ (я хочу перенести время назад, чтобы помочь контейнеру использовать контрольную точку / восстановить варианты использования). Всякий раз, когда вызывается System.nanoTime(), я хочу запустить исходный System.nanoTime(), а затем, возможно, вернуть другое значение. Я пытаюсь использовать byte-buddy и агент инструментария JVM для этого (согласно здесь и здесь ).

Вот моя попытка:

public final class TimeShifter {
    private TimeShifter() {}

    private static final Class<?> INTERCEPTOR_CLASS = TimeShiftedSystem.class;

    public static void premain(String _arg, Instrumentation instrumentation) throws Exception {
        injectBootstrapClasses(instrumentation);
        new AgentBuilder.Default()
                .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
                .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
                .with(AgentBuilder.TypeStrategy.Default.REDEFINE)
                .ignore(
                        new AgentBuilder.RawMatcher.ForElementMatchers(
                                ElementMatchers.nameStartsWith("net.bytebuddy.")
                                        .or(ElementMatchers.isSynthetic())
                                        .or(ElementMatchers.nameStartsWith("my.package.name.")),
                                ElementMatchers.any(),
                                ElementMatchers.any()))
                .with(
                        new AgentBuilder.Listener.Filtering(
                                new StringMatcher("java.lang.System", StringMatcher.Mode.EQUALS_FULLY),
                                AgentBuilder.Listener.StreamWriting.toSystemOut()))
                .type(ElementMatchers.named("java.lang.System"))
                .transform((builder, type, classLoader, module) ->
                        // https://stackoverflow.com/questions/49487148/redefine-rebase-native-method
                        builder.method(ElementMatchers.named("nanoTime"))
                        .intercept(Advice.to(TimeShiftedSystem.class)))
                .installOn(instrumentation);
    }

    private static void injectBootstrapClasses(Instrumentation instrumentation) throws IOException {
        File temp = Files.createTempDirectory("tmp").toFile();
        temp.deleteOnExit();

        ClassInjector.UsingInstrumentation.of(
                        temp, ClassInjector.UsingInstrumentation.Target.BOOTSTRAP, instrumentation)
                .inject(Collections.singletonMap(
                        new TypeDescription.ForLoadedType(INTERCEPTOR_CLASS),
                        ClassFileLocator.ForClassLoader.read(INTERCEPTOR_CLASS)));
    }
}

и

public final class TimeShiftedSystem {
    private TimeShiftedSystem() {}

    @Advice.OnMethodExit
    public static long nanoTime(@Advice.Return long originalReturnValue) {
        System.out.println("originalReturnValue = " + originalReturnValue);

        return originalReturnValue - 1000;
    }
}

но это не с:

[Byte Buddy] DISCOVERY java.lang.System [null, module java.base, loaded=true]
[Byte Buddy] ERROR java.lang.System [null, module java.base, loaded=true]
java.lang.IllegalStateException: Cannot call super (or default) method for public static native long java.lang.System.nanoTime()
    at net.bytebuddy.implementation.SuperMethodCall$Appender.apply(SuperMethodCall.java:133)

Есть ли способ достичь этого? Другие вещи, которые я пробовал:

  • Использование MethodDelegation.to вместо Advice.to с последующим вызовом System.nanoTime() внутри TimeShiftedSystem#nanoTime() - кажется, это вызывает оригинальный nanoTime в первый раз, но сразу переходит в переполнение стека.
  • Использование MethodDelegation.to вместо Advice.to и @Origin Method method в качестве параметра. Кажется, не вызывается вообще (не вызывается, если также используется аннотация @RuntimeType).
  • Использование enabledNativeMethodPrefix("native") - ничего не делает.

Любая помощь очень важна оценили.

1 Ответ

1 голос
/ 05 февраля 2020

Решением было установить

    .with(AgentBuilder.TypeStrategy.Default.REBASE)
    .enableNativeMethodPrefix("native")

на AgentBuilder вместо agentBuilder.TypeStrategy.Default.REDEFINE (и добавить 'Can-Set-Native-Method-Prefix': true в мой манифест банку). Это позволило мне заставить TimeShiftedSystem работать с этим Советом:

@Advice.OnMethodExit
public static void nanoTime(@Advice.Return(readOnly = false) long returnValue) {
    System.out.println("Called with " + returnValue);
    returnValue = 42;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...