【问题标题】:How to easily create Java bytecode dependent regression tests?如何轻松创建依赖于 Java 字节码的回归测试?
【发布时间】:2013-05-15 13:26:18
【问题描述】:

我在我的应用程序(使用烟灰处理字节码)中发现了一个仅在特定字节码指令上出现的错误。

我想为该特定案例创建一个测试。但是,我无法可靠地编写测试代码,它将编译为预期的字节码,然后会触发错误。

这是我触发错误的尝试:

public void updateRhsOnIfEq() {
        int x = 15;
        int y = AircraftControl.readSensor(0);
        // FIXME != in bytecode instead of ==
        if (x == y) {
            AircraftControl.readSensor(y);
        }
        else {
            AircraftControl.readSensor(x);
        }
    }

问题在于,编译器通过反转比较和切换两个分支来更改分支逻辑。正如您在下面的字节码中看到的,它进行了!= 比较,而不是==。但是,我正在测试的错误仅由 == 触发。

 public void updateRhsOnIfEq();
     0  bipush 15
     2  istore_1 [x]
     3  iconst_0
     4  invokestatic AircraftControl.readSensor(int) : int [17]
     7  istore_2 [y]
     8  iload_1 [x]
     9  iload_2 [y]
    10  if_icmpne 21 <============================== Should be if_icmpeq
    13  iload_2 [y]
    14  invokestatic AircraftControl.readSensor(int) : int [17]
    17  pop
    18  goto 26
    21  iload_1 [x]
    22  invokestatic AircraftControl.readSensor(int) : int [17]
    25  pop
    26  return

有没有办法编写需要轻松生成可预测字节码的测试用例?鉴于存在不同的 Java 编译器、版本等,这是否可能?

【问题讨论】:

    标签: java javac bytecode soot


    【解决方案1】:

    如果你想要特定的字节码指令,显而易见且最可靠的方法是直接用字节码编写。

    我已经编写了一个可用的开源汇编程序here。对于简单的情况,您可以使用 Jasmin 之类的东西,可能会有更好的文档记录。我也有一个反汇编器,所以如果你只需要微调,你可以编译一个 Java 类,反汇编,进行微调,然后重新组装。

    【讨论】:

      【解决方案2】:

      编译器不会改变分支逻辑。 在这种情况下使用 if_icmpne 是编译器的自然行为(只是我的看法) 要让(eclipse)编译器使用if_icmpeq,只需将代码更改如下:

      if (x != y) {
          AircraftControl.readSensor(x);
      }
      else {
          AircraftControl.readSensor(y);
      }
      

      这段代码:

      public static void main(String[] args) {
          int x = (int) System.currentTimeMillis(), y = (int) System
                  .currentTimeMillis();
          if (x != y) {
              System.out.println("x != y");
          } else {
              System.out.println("x == y");
          }
      }
      

      结果:

         0: invokestatic  #16     // Method java/lang/System.currentTimeMillis:()J
         3: l2i
         4: istore_1
         5: invokestatic  #16     // Method java/lang/System.currentTimeMillis:()J
         8: l2i
         9: istore_2
        10: iload_1
        11: iload_2
        12: if_icmpeq     26
        15: getstatic     #22     // Field java/lang/System.out:Ljava/io/PrintStream;
        18: ldc           #26     // String x != y
        20: invokevirtual #28     // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        23: goto          34
        26: getstatic     #22     // Field java/lang/System.out:Ljava/io/PrintStream;
        29: ldc           #34     // String x == y
        31: invokevirtual #28     // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        34: return
      

      【讨论】:

      • 这很有趣,我可以发誓我试过了,但没有成功。也许那是一个不同的情况。你能详细说明compiler's natural behavior吗?这是否记录在案,我可以依赖它吗,为什么要这样做?
      • 既不指定也不保证。但是条件反转确实很自然,as explained here...
      【解决方案3】:

      解决如何生成测试用例的问题,您是对的。随着时间的推移,不同的 Java 编译器和同一编译器的不同版本可能会产生不同的字节码。

      你的选择是:

      • 如果编译器可以为您生成测试用例,请使用它
      • 或者您可以:
        • 采用现有的编译器生成的类似测试用例
        • 反汇编它以确保您知道字节在文件中的位置
        • 使用十六进制编辑器来调整指令;例如将 if_icmpne 更改为 if_icmpeq
      • 或者您可以使用 Java 汇编语言从头开始编写示例代码。

      生成测试用例后,您需要将它们捕获并存储为类文件。不要依赖于能够在运行中重新生成它们。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-03-31
        • 2012-06-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-20
        • 2014-12-24
        相关资源
        最近更新 更多