【发布时间】:2019-02-03 15:41:30
【问题描述】:
我使用 net.bytebuddy.asm.Advice 在经过适当注释的方法之前和之后添加代码,以启动和停止计时器。修改后的类在引用它们的原始类之前手动加载到目标类加载器中,从而取代它们。我正在使用 OSGi (Equinox)。
相当不错,但是当我在目标方法的断点处停止 Eclipse (Photon 4.8.0) 调试器时,变量视图仅显示:
com.sun.jdi.InternalException:得到错误代码回复:35 发生 从堆栈帧中检索“this”。
这是不可避免的和不可避免的吗?如果这会使检测代码无法调试,那有点毁了我的用例:(
(我已禁用选项“在步骤操作后显示方法结果(如果 VM 支持;可能会很慢”。)
示例
我相信我可能发现生成的字节码存在一些问题。
要检测的类:
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 调试视图会显示:<unknown receiving type>(Test).writeName() line: 11
变量视图显示: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;
也许这是 Eclipse 调试器期望 this 始终位于插槽 0 中的错误?或者也许这就是它应该的方式。错误代码 35 来自 JVM。
添加退出建议更改槽的原因似乎是因为它导致使用ForInstrumentedMethod.Default.Copying 而不是Simple。他们有不同的variable() 实现。
【问题讨论】:
-
谢谢。我已经看到的不是那个错误,而是它链接到的帖子。我已禁用选项“在步骤操作后显示方法结果(如果 VM 支持;可能会很慢”以防万一它有帮助但没有区别。另外,我有 Eclipse 4.8.0,它应该包含似乎由您链接的错误导致。
-
在构建期间使用 Maven 或 Gradle 插件应用建议时,是否也会出现此问题?如果可以执行字节码,我看不出这是由 Byte Buddy 引起的问题,而在我使用的 IntelliJ 中,我没有观察到类似的情况。鉴于这些观察,我认为这是由 Eclipse 调试器引起的问题。 Byte Buddy 可能会弄乱一些用于调试的元数据,如果您可以构建复制品,我可以看看,您也可以将其作为错误提交给 Eclipse。
标签: java eclipse osgi bytecode byte-buddy