Я хочу изменить то, что происходит, когда 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")
- ничего не делает.
Любая помощь очень важна оценили.