java - Byte Buddy Advice 破坏了 Eclipse 调试器

标签 java eclipse osgi bytecode byte-buddy

我使用 net.bytebuddy.asm.Advice 在适当注释的方法之前和之后添加代码,以启动和停止计时器。修改后的类在引用原始类之前被手动加载到目标类加载器中,从而取代它们。我正在使用 OSGi (Equinox)。

非常好,但是当我在目标方法中的断点处停止 Eclipse (Photon 4.8.0) 调试器时,变量 View 仅显示:

com.sun.jdi.InternalException: Got error code in reply:35 occurred retrieving 'this' from stack frame.

这是不可避免的吗?如果这使得检测到的代码不可调试,就会破坏我的用例:(

(我禁用了“在步骤操作后显示方法结果(如果虚拟机支持;可能会很慢)”选项。)

示例

我相信我可能发现生成的字节码存在一些问题。

要检测的类:

 1 package com.tom.test;
 2
 3 import com.tom.instrument.Instrumented;
 4 import com.tom.instrument.Timed;
 5
 6 @Instrumented(serviceType = "blah")
 7 public class Test {
 8
 9 @Timed
10 public void writeName() {
11   final String myLocal = "Tom";
12      System.out.println(myLocal);  
13   }
14
15 }

“建议”:

package com.tom.instrument;

import net.bytebuddy.asm.Advice.OnMethodEnter;

public class Instrumentation {

    @OnMethodEnter
    public static void onMethodEnter() {
        System.out.println("Enter");
    }

}

调用字节好友:

new ByteBuddy()
    .redefine(type, ClassFileLocator.ForClassLoader.of(this.classLoader))
    .visit(Advice.to(Instrumentation.class)
    .on(isAnnotatedWith(Timed.class)))
    .make().saveIn(new File("instrumented"));

javap 中的结果:

Compiled from "Test.java"
...

  public void writeName();
    Code:
       0: getstatic     #19                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #40                 // String Enter
       5: invokevirtual #25                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: goto          11
      11: ldc           #17                 // String Tom
      13: astore_1
      14: getstatic     #19                 // Field java/lang/System.out:Ljava/io/PrintStream;
      17: ldc           #17                 // String Tom
      19: invokevirtual #25                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      22: return
    LineNumberTable:
      line 11: 0
      line 12: 14
      line 13: 22
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
         11      12     0  this   Lcom/tom/test/Test;
         14       9     1 myLocal   Ljava/lang/String;
}

如果我在 Test.java 的第 11 行设置断点,那么 Eclipse 调试 View 会显示:<unknown receiving type>(Test).writeName() line: 11

变量 View 显示:com.sun.jdi.InternalException: Got error code in reply:35 occurred retrieving 'this' from stack frame.

如果我破解字节码,将 0x2A2 处的 00 更改为 0B,那么行号表如下所示:

LineNumberTable:
  line 11: 11
  line 12: 14
  line 13: 22

那么一切都很好!这对我来说似乎是正确的,但我不是这方面的专家。

如果我也使用@OnMethodExit那就有点复杂了。将以下内容添加到 Instrumentation.class :

@OnMethodExit
public static void onMethodExit() {
    System.out.println("Exit");
}

javap 给出:

Compiled from "Test.java"
...

  public void writeName();
    Code:
       0: getstatic     #19                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #40                 // String Enter
       5: invokevirtual #25                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: goto          11
      11: aload_0
      12: astore_1
      13: ldc           #17                 // String Tom
      15: astore_2
      16: getstatic     #19                 // Field java/lang/System.out:Ljava/io/PrintStream;
      19: ldc           #17                 // String Tom
      21: invokevirtual #25                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      24: goto          27
      27: getstatic     #19                 // Field java/lang/System.out:Ljava/io/PrintStream;
      30: ldc           #42                 // String Exit
      32: invokevirtual #25                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      35: goto          38
      38: return
    LineNumberTable:
      line 11: 0
      line 12: 16
      line 13: 24
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
         13      14     1  this   Lcom/tom/test/Test;
         16      11     2 myLocal   Ljava/lang/String;
}

为了解决这个问题,我必须更新行号表和局部变量表。像这样:

LineNumberTable:
  line 11: 13
  line 12: 16
  line 13: 24
LocalVariableTable:
  Start  Length  Slot  Name   Signature
     13      14     0  this   Lcom/tom/test/Test;
     16      11     1 myLocal   Ljava/lang/String;

差异: enter image description here

也许这是 Eclipse 调试器预期的错误 this总是在槽 0 中?或者也许事情本来就应该如此。不过错误代码 35 来自 JVM。

添加退出建议会更改插槽的原因似乎是因为它会导致 ForInstrumentedMethod.Default.Copying用于代替 Simple 。他们有 variable() 的不同实现.

最佳答案

参见Eclipse bug 531706 :

并非所有类都已检测时,就会出现此问题,请参阅 comment #4 by Tobias Hirning :

...

Now I am also getting a clearer picture: The errors only appeared in method invocations where the methods are in jar files. And I think they were not instrumented.

...

错误发生在 VM 中,而不是 Eclipse。当 Eclipse 通过调试接口(interface)请求变量时,返回错误代码 35 而不是值。由于提到的错误报告而做出的更改是忽略它,请参阅comment #7 by Till Brychcy (who made the change) :

...

I've been able to reproduce the problem and simply ignoring the InternalException in this codepath improves the situation.

You'll sometimes see a message about the error code 35 in the variables view, but in general it seems to work.

要避免此问题,您必须检测所有类。

关于java - Byte Buddy Advice 破坏了 Eclipse 调试器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52067467/

相关文章:

java - 如何将文本添加到 textArea 而不是替换它

java - 使用Java apache POI填充Excel行中的背景颜色

java - 按钮的 setOnClickListener 不起作用

java - 如何查看Eclipse中使用的执行环境?

java - 在 OSGI 应用程序 Mac OS X - Java 1.7 中引用时 ImageIO 挂起

java - 如何在java中为HBase插入数据生成行名称

java - 如何在 Micronaut 中下载(流式传输)大型(生成)文件

eclipse - 如何在Egit中设置远程跟踪分支?

java - 如何使用 Tycho 将 java 项目构建为带有 Maven 的 OSGi 部署包?

apache - bnd 如何决定适用于导入包的版本范围?