【问题标题】:how to debug an internal error?如何调试内部错误?
【发布时间】:2018-02-19 15:54:15
【问题描述】:

所以我有一个类Foo 最终应该调整和重新加载类。它也有一个方法:

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())
            ;
}

为了测试它,我只需将 .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。 似乎适用于相当多的课程......但并非适用于所有课程:

REDEFINING parc.util.Vector$1
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$1.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$1给我class Vector$1 implements java/util/Enumeration,这表明它是这个类:

/**
 * 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。

【问题讨论】:

  • stackoverflow.com/a/33912156/1319284 这个问题好像是重复的。
  • @kutchkern 很有趣,谢谢。但是我正在做的项目需要 Java7 并且没有 lambda 表达式。
  • @User1291 使用 ASM 从文件加载 byte[] 有什么意义?为什么你不只是在java.nio.file.Files.readAllBytes(p) 的帮助下加载它?
  • @User1291 lambda 表达式只是匿名内部类,问题在于匿名内部类。
  • @User1291 没关系,您是对的,引用的答案仅涉及不一定适用于匿名类的 lambda 表达式的细节。

标签: java bytecode instrumentation java-bytecode-asm byte-buddy


【解决方案1】:

我测试了几个 Java 版本,在 Class.getEnclosingMethodClass.getGenericInterfaces 中找不到任何问题,因为本地类实现了一个通用接口,就像在 Vector.elements()/Enumeration&lt;E&gt; 案例中一样。也许,问题出现了,因为类文件已经被操纵了。

但似乎无论ByteBuddy 前端在后台做什么涉及Class.getGenericInterfaces 对您的用例来说都是多余的,因为您已经有了预期的结果字节码。

我建议下一层使用

ClassReloadingStrategy s = ClassReloadingStrategy.fromInstalledAgent();
s.load(clazz.getClassLoader(),
    Collections.singletonMap(new TypeDescription.ForLoadedType(clazz), bytecode));

跳过这些操作,只需激活您的字节码。

当类加载策略基于ClassReloadingStrategy.Strategy.REDEFINITION时也可以使用

ClassReloadingStrategy s = ClassReloadingStrategy.fromInstalledAgent();
s.reset(ClassFileLocator.Simple.of(classname, bytecode), clazz);

因为它将使用通过ClassFileLocator 检索到的字节码作为基础。

【讨论】:

  • 谢谢!我没想到reset 可以这样使用。文档的“将所有类重置为其原始定义”让我灰心。
  • 这确实是一个bug,隐含的选择应该是重新转换,因为它更通用。我还添加了一种将策略显式设置为参数的方法。这些修复将成为 Byte Buddy 1.7.11 的一部分。同时,您也可以使用new ClassReloadingStrategy(ByteBuddyAgent.getInstrumentation(), Strategy.RETRANSFORMATION)
【解决方案2】:

查看字节伙伴代码,我假设 ClassReloadingStrategy.fromInstalledAgent() 将返回一个配置了 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 的注释不匹配。

【讨论】:

  • 非常感谢,但恐怕对于这个特定的错误并没有太大变化,它保持不变。
  • @User1291 在这种情况下,我建议您去 github 并为您的问题提交错误报告。看起来它应该可以工作,但没有。
猜你喜欢
  • 2023-03-10
  • 1970-01-01
  • 2011-09-09
  • 2021-11-12
  • 1970-01-01
  • 2012-10-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多