openjdk-riscv / jdk11u

Read-only mirror of https://hg.openjdk.java.net/jdk-updates/jdk11u/
GNU General Public License v2.0
11 stars 14 forks source link

通过排除部分基础类库的方法来调试C2 #584

Closed shining1984 closed 10 months ago

shining1984 commented 1 year ago

目前C2的问题出现在编译基础类库并且加载的情况下,导致整个JVM运行不起来,然后无法定位错误。基础类库又很庞大,所以通过部分排除或者完全排除基础类库的情况,来让JVM运行起来,可以在基础类库中锁定错误,或者自己编写测试用例来寻找错误。

整个排除基础类库的情况下,java -version是可以运行的。 整个排除基础类库的方法是:/home/shining/qemu/bin/qemu-riscv32 -L /opt/riscv32/sysroot/ ./java -XX:CompileCommand=exclude,"*.*" -version 使用 -XX:CompileCommand=exclude, "*.*"

image

shining1984 commented 1 year ago

在运行/home/shining/qemu/bin/qemu-riscv32 -L /opt/riscv32/sysroot/ ./javac -XX:CompileCommand=exclude,"*.*"的时候,就会遇到和不排除公共类库同样的问题,说明在javac的这个程序中,也存在会让JIT出错的代码。

javac的源码位于src/jdk.compiler/share/classes/com/sun/tools/javac目录之下,修改这个目录下的Main.java中的main函数,让其直接返回,依然会出现这个错误。

这个问题需要进一步分析跟踪。Javac本身代码量较大,也不容易定位。

lazyparser commented 1 year ago

加油

shining1984 commented 1 year ago

由于javac会遇到JIT的错误,所以只能暂时使用Java 10来编译java文件为class文件,然后用jdk11 rv32g版本的JIT来运行class文件,来测试问题。

采用之前自己编写的测试用例集合(https://github.com/openjdk-riscv/testcase-jdk11u-rv32g )逐个进行测试:

  1. Assert 文件没有main函数,无法运行;
  2. AsertException 文件没有main函数,无法运行;
  3. HelloWorld 正常运行;
  4. TestArrayCopy 正常运行;
  5. TestArrayOverflow 正常运行;
  6. TestByteBuffer 正常运行;
  7. TestConvertIFD 正常运行;
  8. TestConvertLFD 正常运行;
  9. TestJavac 正常运行;
  10. TestLadd 正常运行;
  11. TestLastore 正常运行;
  12. TestLdiv 正常运行;
  13. TestLmul 正常运行;
  14. TestLneg 正常运行;
  15. TestLrem 正常运行;
  16. TestLshl 正常运行;
  17. TestLshr 正常运行;
  18. TestLsub 正常运行;
  19. TestLushr 正常运行;
  20. TestLxor 正常运行;
  21. TestReflectInvoke 出现错误;
  22. TestReflect 正常运行。

综上,Test系列的测试用例除了TestReflectInvoke 之外都可以正常运行通过。TestReflectInvoke 的错误信息为:

47244740255
Exception in thread "main" java.lang.RuntimeException: java.lang.RuntimeException: AssertException: assert result error
        at TestReflectInvoke.main(TestReflectInvoke.java:11)
Caused by: java.lang.RuntimeException: AssertException: assert result error
        at TestReflectInvoke.test(TestReflectInvoke.java:22)
        at TestReflectInvoke.main(TestReflectInvoke.java:9)
Caused by: AssertException: assert result error
        at Assert.isTrue(Assert.java:5)
        at TestReflectInvoke.test(TestReflectInvoke.java:20)
        ... 1 more

需要进一步调试。

shining1984 commented 1 year ago

TestReflectInvoke 出错的原因,经过对TestReflectInvoke 和TestReflect进行调试,发现问题出在 final Long result = (long)pid.invoke(null); 这个转换过程有问题。导致了数据异常以及抛出异常。

shining1984 commented 1 year ago

TestReflectInvoke 测试用例,最初是为了解决 : https://github.com/openjdk-riscv/jdk11u/issues/333#issuecomment-1025085417

代码提交在: https://github.com/openjdk-riscv/jdk11u/pull/334/files

这个提交修改了两部分代码,一部分是关于long数据的处理,一部分是关于object的数据处理。long数据的处理相关代码才是真正解决问题的核心。(long部分的代码现在修改依然能影响运行结果。) object相关部分的代码并不影响该问题的运行结果,它在32位下应该与指针一样是32位,所以object有关的代码有改进的余地。无论object这里的操作按照32位还是64位来做,都不会影响当下的数据,所以这不是问题的根本。改进也是为了代码更简洁。 改进代码提交在:https://github.com/openjdk-riscv/jdk11u/pull/585

真正TestReflectInvoke 出错的原因,在C2里,还有别的地方有问题,有待进一部分析。

shining1984 commented 1 year ago

大致浏览了一遍riscv32目录下的所有T_OBJECT相关代码,没发现问题,但是shareRuntime_riscv32.cpp中的T_OBJECT相关代码最后可能有问题,需要进一步调试。

shining1984 commented 1 year ago

将rv32g_dev_c2分支的代码,编译解释器模式,测试TestReflectInvoke ,和C2模式下有同样的问题。 切换到解释器代码分支rv32g_dev分支,编译解释器模式,测试TestReflectInvoke ,这是完全正确的。 据此分析,推断rv32g_dev_c2分支有关解释器的代码,有问题。应该是有后来的提交影响了解释器。这需要回溯来判断并解决。

经过分析,引发TestReflectInvoke 错误的代码提交在 #465 到 #472 。经过调试,最终确定是 #472 中的修改引发了该错误。将其改回来,就可以让TestReflectInvoke 正常运行。

https://github.com/openjdk-riscv/jdk11u/pull/588 解决了该问题,最终让rv32g_dev_c2分支的代码,无论在解释器模式,还是在C2模式-XX:CompileCommand=exclude,"." 状态下,都可以成功运行TestReflectInvoke测试用例。

shining1984 commented 1 year ago

https://github.com/openjdk-riscv/jdk11u/pull/588 提交之后,正常的C2运行-version,会回到InvalidModuleDescriptorException错误:

Warning:  TraceDependencies results may be inflated by VerifyDependencies
Error occurred during initialization of boot layer
java.lang.module.FindException: Error reading module: /home/shining/jdk11u/build/linux-riscv32-normal-custom-slowdebug/jdk/modules/jdk.localedata
Caused by: java.lang.module.InvalidModuleDescriptorException

这个错误之间简单的解决过:https://github.com/openjdk-riscv/jdk11u/pull/461

如果按照 #461 的方法修改loadL,那么就会又变成byte offset 越界的问题,整体的编译log并没有增加多少。所以这个问题还要仔细考虑,多方验证。

经过仔细的分析问题,该问题的核心点是寻址空间对应的寄存器是否和目标寄存器相同,相同的话,需要先存储高位置数据,否则会覆盖数据,导致剩余数据出错。所以,参考arm 32bit的实现,要判断这两个寄存器是否相同,然后确定数据加载顺序。修改代码提交在: https://github.com/openjdk-riscv/jdk11u/pull/589 。 该PR提交之后,问题就变成了byte_offset的问题。

shining1984 commented 1 year ago

/home/shining/qemu/bin/qemu-riscv32 -L /opt/riscv32/sysroot/ ./java -XX:CompileCommand=exclude,"sun." -XX:CompileCommand=exclude,"java." -XX:CompileCommand=exclude,"jdk.*" -version 来试图去掉这些包来运行,但是还是失败了。显示去掉包了,但是还是报错了。

shining1984 commented 1 year ago

除了exclude方法之外,还有另外一个方法,使用的是.hotspot_compiler。 遇到的文档:

  1. https://forums.oracle.com/ords/apexds/post/what-s-the-hotspot-compiler-file-9190
  2. https://support.hpe.com/hpesc/public/docDisplay?docId=emr_na-c02716055