如何调试内部错误?
how to debug an internal error?
所以我有一个 class Foo
应该最终调整并重新加载 classes。它也有一个方法:
private void redefineClass(String classname, byte[] bytecode) {
ClassFileLocator cfl = ClassFileLocator.Simple.of(classname,bytecode);
Class clazz;
try{
clazz = Class.forName(classname);
}catch(ClassNotFoundException e){
throw new RuntimeException(e);
}
Debug._print("REDEFINING %s",clazz.getName());
new ByteBuddy()
.redefine(clazz,cfl)
.make()
.load(clazz.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent())
;
}
为了测试它,我只是将 classes 从 .class
文件加载到 byte[]
(使用 ASM)
private byte[] getBytecode(String classname){
try {
Path p = Paths.get(LayoutConstants.SRC_DIR).resolve(classname.replace(".","/") + ".class");
File f = p.toFile();
InputStream is = new FileInputStream(f);
ClassReader cr = new ClassReader(is);
ClassWriter cw = new ClassWriter(cr,0);
cr.accept(cw,0);
return cw.toByteArray();
}catch(IOException e){
throw new RuntimeException(e);
}
}
并将其传递给上面的redefineClass
。
似乎适用于相当多的 classes ...但并非适用于所有人:
REDEFINING parc.util.Vector
Exception in thread "Thread-0" java.lang.InternalError: Enclosing method not found
at java.lang.Class.getEnclosingMethod(Class.java:952)
at sun.reflect.generics.scope.ClassScope.computeEnclosingScope(ClassScope.java:50)
at sun.reflect.generics.scope.AbstractScope.getEnclosingScope(AbstractScope.java:74)
at sun.reflect.generics.scope.AbstractScope.lookup(AbstractScope.java:90)
at sun.reflect.generics.factory.CoreReflectionFactory.findTypeVariable(CoreReflectionFactory.java:110)
at sun.reflect.generics.visitor.Reifier.visitTypeVariableSignature(Reifier.java:165)
at sun.reflect.generics.tree.TypeVariableSignature.accept(TypeVariableSignature.java:43)
at sun.reflect.generics.visitor.Reifier.reifyTypeArguments(Reifier.java:68)
at sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:138)
at sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
at sun.reflect.generics.repository.ClassRepository.getSuperInterfaces(ClassRepository.java:100)
at java.lang.Class.getGenericInterfaces(Class.java:814)
at net.bytebuddy.description.type.TypeList$Generic$OfLoadedInterfaceTypes$TypeProjection.resolve(TypeList.java:722)
at net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection.accept(TypeDescription.java:5308)
at net.bytebuddy.description.type.TypeList$Generic$AbstractBase.accept(TypeList.java:249)
at net.bytebuddy.dynamic.scaffold.InstrumentedType$Factory$Default.represent(InstrumentedType.java:221)
at net.bytebuddy.ByteBuddy.redefine(ByteBuddy.java:698)
at net.bytebuddy.ByteBuddy.redefine(ByteBuddy.java:676)
at parc.Foo.redefineClass(Foo.java:137)
反汇编 Vector
给我 class Vector implements java/util/Enumeration
,所以这表明它是这个 class:
/**
* Returns an enumeration of the components of this vector. The
* returned {@code Enumeration} object will generate all items in
* this vector. The first item generated is the item at index {@code 0},
* then the item at index {@code 1}, and so on.
*
* @return an enumeration of the components of this vector
* @see Iterator
*/
public Enumeration<E> elements() {
return new Enumeration<E>() {
int count = 0;
public boolean hasMoreElements() {
return count < elementCount;
}
public E nextElement() {
synchronized (Vector.this) {
if (count < elementCount) {
return elementData(count++);
}
}
throw new NoSuchElementException("Vector Enumeration");
}
};
}
除了我仍然不知道如何处理这些信息。
出于某种原因,可以加载和使用保存到文件中的检测代码,但无法重新加载。
我如何找出原因?
编辑:我应该提到我正在从事的项目需要 Java 7.
查看byte-buddy代码,我假设ClassReloadingStrategy.fromInstalledAgent()将return配置为Strategy.REDEFINITION的ClassReloadingStrategy,不支持匿名类.请改用 Strategy.RETRANSFORMATION。
ClassReloadingStrategy strat = new ClassReloadingStrategy(
(Instrumentation) ClassLoader.getSystemClassLoader()
.loadClass("net.bytebuddy.agent.Installer")
.getMethod("getInstrumentation")
.invoke(null),
Strategy.RETRANSFORMATION);
您可以考虑写一个错误报告,默认行为与评论说默认行为不匹配Strategy.RETRANSFORMATION。
我测试了几个 Java 版本,并没有发现 Class.getEnclosingMethod
和 Class.getGenericInterfaces
对于本地 class 实现通用接口的任何问题,例如 Vector.elements()/Enumeration<E>
案例。也许,问题出现了,因为 class 文件已经被操作过。
但似乎 ByteBuddy
前端在涉及 Class.getGenericInterfaces
的幕后所做的一切对您的用例来说都太过分了,因为您已经有了预期的结果字节代码。
我建议降低一级并使用
ClassReloadingStrategy s = ClassReloadingStrategy.fromInstalledAgent();
s.load(clazz.getClassLoader(),
Collections.singletonMap(new TypeDescription.ForLoadedType(clazz), bytecode));
跳过这些操作,只激活你的字节码。
当class加载策略基于ClassReloadingStrategy.Strategy.REDEFINITION
时你也可以使用
ClassReloadingStrategy s = ClassReloadingStrategy.fromInstalledAgent();
s.reset(ClassFileLocator.Simple.of(classname, bytecode), clazz);
因为它将使用通过 ClassFileLocator
检索到的字节码作为基础。
所以我有一个 class Foo
应该最终调整并重新加载 classes。它也有一个方法:
private void redefineClass(String classname, byte[] bytecode) {
ClassFileLocator cfl = ClassFileLocator.Simple.of(classname,bytecode);
Class clazz;
try{
clazz = Class.forName(classname);
}catch(ClassNotFoundException e){
throw new RuntimeException(e);
}
Debug._print("REDEFINING %s",clazz.getName());
new ByteBuddy()
.redefine(clazz,cfl)
.make()
.load(clazz.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent())
;
}
为了测试它,我只是将 classes 从 .class
文件加载到 byte[]
(使用 ASM)
private byte[] getBytecode(String classname){
try {
Path p = Paths.get(LayoutConstants.SRC_DIR).resolve(classname.replace(".","/") + ".class");
File f = p.toFile();
InputStream is = new FileInputStream(f);
ClassReader cr = new ClassReader(is);
ClassWriter cw = new ClassWriter(cr,0);
cr.accept(cw,0);
return cw.toByteArray();
}catch(IOException e){
throw new RuntimeException(e);
}
}
并将其传递给上面的redefineClass
。
似乎适用于相当多的 classes ...但并非适用于所有人:
REDEFINING parc.util.Vector
Exception in thread "Thread-0" java.lang.InternalError: Enclosing method not found
at java.lang.Class.getEnclosingMethod(Class.java:952)
at sun.reflect.generics.scope.ClassScope.computeEnclosingScope(ClassScope.java:50)
at sun.reflect.generics.scope.AbstractScope.getEnclosingScope(AbstractScope.java:74)
at sun.reflect.generics.scope.AbstractScope.lookup(AbstractScope.java:90)
at sun.reflect.generics.factory.CoreReflectionFactory.findTypeVariable(CoreReflectionFactory.java:110)
at sun.reflect.generics.visitor.Reifier.visitTypeVariableSignature(Reifier.java:165)
at sun.reflect.generics.tree.TypeVariableSignature.accept(TypeVariableSignature.java:43)
at sun.reflect.generics.visitor.Reifier.reifyTypeArguments(Reifier.java:68)
at sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:138)
at sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
at sun.reflect.generics.repository.ClassRepository.getSuperInterfaces(ClassRepository.java:100)
at java.lang.Class.getGenericInterfaces(Class.java:814)
at net.bytebuddy.description.type.TypeList$Generic$OfLoadedInterfaceTypes$TypeProjection.resolve(TypeList.java:722)
at net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection.accept(TypeDescription.java:5308)
at net.bytebuddy.description.type.TypeList$Generic$AbstractBase.accept(TypeList.java:249)
at net.bytebuddy.dynamic.scaffold.InstrumentedType$Factory$Default.represent(InstrumentedType.java:221)
at net.bytebuddy.ByteBuddy.redefine(ByteBuddy.java:698)
at net.bytebuddy.ByteBuddy.redefine(ByteBuddy.java:676)
at parc.Foo.redefineClass(Foo.java:137)
反汇编 Vector
给我 class Vector implements java/util/Enumeration
,所以这表明它是这个 class:
/**
* Returns an enumeration of the components of this vector. The
* returned {@code Enumeration} object will generate all items in
* this vector. The first item generated is the item at index {@code 0},
* then the item at index {@code 1}, and so on.
*
* @return an enumeration of the components of this vector
* @see Iterator
*/
public Enumeration<E> elements() {
return new Enumeration<E>() {
int count = 0;
public boolean hasMoreElements() {
return count < elementCount;
}
public E nextElement() {
synchronized (Vector.this) {
if (count < elementCount) {
return elementData(count++);
}
}
throw new NoSuchElementException("Vector Enumeration");
}
};
}
除了我仍然不知道如何处理这些信息。
出于某种原因,可以加载和使用保存到文件中的检测代码,但无法重新加载。
我如何找出原因?
编辑:我应该提到我正在从事的项目需要 Java 7.
查看byte-buddy代码,我假设ClassReloadingStrategy.fromInstalledAgent()将return配置为Strategy.REDEFINITION的ClassReloadingStrategy,不支持匿名类.请改用 Strategy.RETRANSFORMATION。
ClassReloadingStrategy strat = new ClassReloadingStrategy(
(Instrumentation) ClassLoader.getSystemClassLoader()
.loadClass("net.bytebuddy.agent.Installer")
.getMethod("getInstrumentation")
.invoke(null),
Strategy.RETRANSFORMATION);
您可以考虑写一个错误报告,默认行为与评论说默认行为不匹配Strategy.RETRANSFORMATION。
我测试了几个 Java 版本,并没有发现 Class.getEnclosingMethod
和 Class.getGenericInterfaces
对于本地 class 实现通用接口的任何问题,例如 Vector.elements()/Enumeration<E>
案例。也许,问题出现了,因为 class 文件已经被操作过。
但似乎 ByteBuddy
前端在涉及 Class.getGenericInterfaces
的幕后所做的一切对您的用例来说都太过分了,因为您已经有了预期的结果字节代码。
我建议降低一级并使用
ClassReloadingStrategy s = ClassReloadingStrategy.fromInstalledAgent();
s.load(clazz.getClassLoader(),
Collections.singletonMap(new TypeDescription.ForLoadedType(clazz), bytecode));
跳过这些操作,只激活你的字节码。
当class加载策略基于ClassReloadingStrategy.Strategy.REDEFINITION
时你也可以使用
ClassReloadingStrategy s = ClassReloadingStrategy.fromInstalledAgent();
s.reset(ClassFileLocator.Simple.of(classname, bytecode), clazz);
因为它将使用通过 ClassFileLocator
检索到的字节码作为基础。