Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@AtLine 应用在 arthas 中,@Binding.LocalVars 不生效 #18

Closed
lotabout opened this issue Jan 8, 2022 · 1 comment
Closed

@AtLine 应用在 arthas 中,@Binding.LocalVars 不生效 #18

lotabout opened this issue Jan 8, 2022 · 1 comment

Comments

@lotabout
Copy link

lotabout commented Jan 8, 2022

背景

在尝试为 arthas 添加 @AtLine 的支持,用于打印出方法运行时,局部变量的信息。
当前有了一个 MVP 的版本(commit: lotabout/arthas@5536242#diff-060c8feafaa130cf1bf5dad5a84703d46db28aa09e78fe2088b8d49288005949R48)

在自测时,发现 @AtLine 返回的 LocalVarsLocalVarName 都是空。

问题复现

测试类如下:

public class Hello {

  public static void main(String[] args) throws InterruptedException {
    Sample sample = new Sample();
    for (int i = 0; i < 10000; i++) {
      System.out.println(sample.hello());
      Thread.sleep(1000);
    }
  }


  private static class Sample {
    private int i = 0;

    public String hello() {
      int x = i * 2;
      String y = String.format("hello %d", x);
      return y;
    }
  }
}

启动测试类:

javac Hello.java
java Hello

随后有添加 AtLine 版本的 arthas 进行 attach

java -jar arthas-boot.jar
# 其中 `-l` 新 patch 后新加的参数
$ watch Hello$Sample hello '{varNames, vars}' -l

在 arthas 的 Enhancer 中已经加了 log 逻辑,打印出 transform 后的代码,如下:

/*
 * Decompiled with CFR.
 *
 * Could not load the following classes:
 *  java.arthas.SpyAPI
 */
import java.arthas.SpyAPI;

private static class Hello.Sample {
    private int i = 0;

    public String hello() {
        Object[] objectArray = new Object[]{};
        String string = "hello|()Ljava/lang/String;";
        Class<Hello.Sample> clazz = Hello.Sample.class;
        Hello.Sample sample = this;
        SpyAPI.atEnter(clazz, (String)string, (Object)sample, (Object[])objectArray);
        try {
            String string2;
            String[] stringArray = new String[]{};
            Object[] objectArray2 = new Object[]{};
            int n = 16;
            Object[] objectArray3 = new Object[]{};
            String string3 = "hello|()Ljava/lang/String;";
            Class<Hello.Sample> clazz2 = Hello.Sample.class;
            Hello.Sample sample2 = this;
            SpyAPI.atLine(clazz2, (String)string3, (Object)sample2, (Object[])objectArray3, (int)n, (String[])stringArray, (Object[])objectArray2);
            int n2 = this.i * 2;
            String[] stringArray2 = new String[]{};
            Object[] objectArray4 = new Object[]{};
            int n3 = 17;
            Object[] objectArray5 = new Object[]{};
            String string4 = "hello|()Ljava/lang/String;";
            Class<Hello.Sample> clazz3 = Hello.Sample.class;
            Hello.Sample sample3 = this;
            SpyAPI.atLine(clazz3, (String)string4, (Object)sample3, (Object[])objectArray5, (int)n3, (String[])stringArray2, (Object[])objectArray4);
            String string5 = String.format("hello %d", n2);
            String[] stringArray3 = new String[]{};
            Object[] objectArray6 = new Object[]{};
            int n4 = 18;
            Object[] objectArray7 = new Object[]{};
            String string6 = "hello|()Ljava/lang/String;";
            Class<Hello.Sample> clazz4 = Hello.Sample.class;
            Hello.Sample sample4 = this;
            SpyAPI.atLine(clazz4, (String)string6, (Object)sample4, (Object[])objectArray7, (int)n4, (String[])stringArray3, (Object[])objectArray6);
            String string7 = string2 = string5;
            Object[] objectArray8 = new Object[]{};
            String string8 = "hello|()Ljava/lang/String;";
            Class<Hello.Sample> clazz5 = Hello.Sample.class;
            Hello.Sample sample5 = this;
            SpyAPI.atExit(clazz5, (String)string8, (Object)sample5, (Object[])objectArray8, (Object)string7);
            return string2;
        }
        catch (Throwable throwable) {
            Throwable throwable2 = throwable;
            Object[] objectArray9 = new Object[]{};
            String string9 = "hello|()Ljava/lang/String;";
            Class<Hello.Sample> clazz6 = Hello.Sample.class;
            Hello.Sample sample6 = this;
            SpyAPI.atExceptionExit(clazz6, (String)string9, (Object)sample6, (Object[])objectArray9, (Throwable)throwable2);
            throw throwable;
        }
    }

    private Hello.Sample() {
    }
}

发现其中 atLine 的最后两个参数 varNamevars 都是空数组。

环境信息

分别使用 JDK 1.8 和 JDK 11 进行测试,都有这个问题。

另外,单独运行 bytekit 的 unit test,是能正确 transform 的,下面是 UT 的一些中间输出

vars: [com.alibaba.bytekit.asm.interceptor.AtLineTest$Sample@5b3c56e0, 101, 100, 100100]
varNames: [this, i, s, abc]
method: methodtestLine|(I)I
atLine: thiscom.alibaba.bytekit.asm.interceptor.AtLineTest$Sample@5b3c56e0
line: 29
args: [10202]
argNames: [i]
vars: [com.alibaba.bytekit.asm.interceptor.AtLineTest$Sample@5b3c56e0, 10202, 100, 100100]
varNames: [this, i, s, abc]
method: methodtestLine|(I)I
atLine: thiscom.alibaba.bytekit.asm.interceptor.AtLineTest$Sample@5b3c56e0
line: 30
args: [10211]
argNames: [i]
vars: [com.alibaba.bytekit.asm.interceptor.AtLineTest$Sample@5b3c56e0, 10211, 100]
varNames: [this, i, s]
method: methodtestLine|(I)I
atLine: thiscom.alibaba.bytekit.asm.interceptor.AtLineTest$Sample@5b3c56e0
line: 41
args: [10211]
argNames: [i]
vars: [com.alibaba.bytekit.asm.interceptor.AtLineTest$Sample@5b3c56e0, 10211, 100]
varNames: [this, i, s]
@lotabout
Copy link
Author

lotabout commented Jan 9, 2022

定位到是 AsmUtils.loadClassAsmUtils.toClassNode 的差异,loadClass 生成的 ClassNode 缺少 localVariables 信息。
并发现 javac 后的 bytecode 就缺少了 localVariableTable。

@lotabout lotabout closed this as completed Jan 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant