在使用JetBrains CLion
调试OpenJDK
的过程当中,有时候会发现Call Stack
中有一部分是汇编代码,致使没法彻底探究其内部实现。本文主要针对此问题给出了如何在不引入汇编代码(零汇编,Zero-Assembler
)的状况下完成OpenJDK
项目的编译和调试。html
在OpenJDK 编译调试指南一文中,已经详细介绍了编译调试
OpenJDK
的步骤,这里再也不详述具体过程。java
首先咱们来看下为何会存在部分汇编代码,以及这部分汇编代码是什么? 从 HotSpot Runtime Overview - Interpreter 了解到,HotSpot
为了提升性能,使用了基于模板的解释器(template based interpreter
)来解释执行JAVA虚拟机指令
,JVM
在启动时会根据TemplateTable
(与每一个字节码对应的汇编代码)来生成解释器,因此说这个解释器实际上是部分基于汇编代码实现的。除了解释器以外,在OpenJDK
项目中还有一些部分也是基于汇编代码的,例如即时编译器JIT
(C1
编译器,C2
编译器)等,但汇编代码是依赖于硬件架构的,这种作法在提高了性能的同时却下降了可移植性。macos
OpenJDK
社区的IcedTea
项目提供了一种比较通用的移植方法,使得OpenJDK
能够在全部的Linux
系统上构建, 而无需进一步进行移植工做。bash
IcedTea
项目最初的出现是由于Sun
公司在发布JDK
时,类库的一些部分因为产权缘由没有发布其源代码,而是仅以二进制插件的形式提供, 由于这部分源代码属于受版权保护的第三方。IcedTea
的主要目标就是提供这些二进制插件的免费等效替代品,使得彻底使用免费开源软件构建JDK
成为可能。markdown
此外,IdeaTea
还作了不少工做以促进用户更容易地构建和部署JDK
,其中包括将OpenJDK
移植到更多的平台。架构
由于OpenJDK
的代码中除了 C++
代码以外还包含许多汇编代码,而OpenJDK
支持的硬件平台架构有限,远远少于Linux
系统所支持的。因而IcedTea
的子项目Zero
出现了,Zero
项目旨在经过移除OpenJDK
项目代码中的与平台相关的汇编代码,使用纯C++
来替代,从而能够在任何 Linux
系统上构建,而无需进一步进行移植工做。jvm
这也正是标题中"零汇编"的含义所在,就是指不使用汇编代码来构建
OpenJDK
。ide
OpenJDK
的虚拟机在很大程度上依赖 JIT(即时编译器)
编译来提升性能。而Zero
中只包含一个纯C++
解释器,Zero
在相同的硬件上要比原始(vanilla
)的 OpenJDK
慢得多。因而又一个子项目Shark
出现了, Shark
使用 LLVM
来即时编译 Java
代码,从而在不引入系统特定的代码的状况下提升性能。oop
到这里咱们已经明白了,使用zero
来构建OpenJDK
就能够实现"零汇编(Zero-Assembler
)",那么如何使用呢?目前Zero
和Shark
已经集成到了OpenJDK
的主分支中,只要在配置OpenJDK
的时候指定参数--with-jvm-variants=zero
从新编译便可。post
虽然在
OpenJDK 8
中提供了--with-jvm-variants=zeroshark
的配置项,但通过几番尝试发现并不能真正构建成功。根据OpenJDK
社区的一些资料([JDK-8171853] Remove Shark compiler - Java Bug System、jdk-updates/jdk10u: fb290fd1f9d4),Shark
因为长期缺少维护已经在JDK 10
版本中被移除了,也再也不支持配置--with-jvm-variants=zeroshark
参数了。因此下文咱们只考虑Zero
而不设计Shark
相关内容。
这篇文章Sustaining the zero assembler port in OpenJDK: An inside perspective of CPU specific issues比较形象地描述了
模板解释器
和C++解释器
的区别,以及如何构建zero
版的OpenJDK
,推荐阅读。
下面主要记录一下在MacOS
和Ubuntu
平台下编译Zero
版OpenJDK
所遇到的问题。
在Ubuntu 16.04
平台上编译OpenJDK 8
时遇到的一个问题是缺乏 libffi
依赖。运行configure
时出现下面的错误信息:
checking for LIBFFI... configure: error: Package requirements (libffi) were not met:
No package 'libffi' found
复制代码
使用以下命令安装以后从新配置编译便可:
sudo apt-get install libffi-dev
复制代码
在Ubuntu 16.04
平台上编译OpenJDK 11
时没有问题,很顺利就编译完成了。
在MacOS 10.15
平台上编译时遇到较多问题,主要缘由是OpenJDK
项目对MacOS
平台编译zero
版本 支持不够完善,致使有一些代码编译不经过。
configure
出现下面的错误信息:checking for LIBFFI... configure: error: in `/Users/jiajiawang/Workspace/openjdk-jdk8u':
configure: error: The pkg-config script could not be found or is too old. Make sure it
is in your PATH or set the PKG_CONFIG environment variable to the full
path to pkg-config.
复制代码
一样是由于缺乏了libffi
库,除了libffi
以外还缺乏pkg-config
,使用HomeBrew
安装这两个依赖便可:
brew install libffi pkg-config
复制代码
configure
完成,运行make
时出现下面的错误信息:/usr/bin/clang++ ... ... ... openjdk-jdk8u/hotspot/src/share/vm/precompiled/precompiled.hpp -o precompiled.hpp.pch
clang: error: unsupported option '-gstabs'
复制代码
这是由于clang
不支持gcc
的-gstabs
选项,须要对hotspot/make/bsd/makefiles/gcc.make
文件进行修改,修改内容以下:
DEBUG_CFLAGS/ppc = -g
DEBUG_CFLAGS += $(DEBUG_CFLAGS/$(BUILDARCH))
ifeq ($(DEBUG_CFLAGS/$(BUILDARCH)),)
- DEBUG_CFLAGS += -gstabs
+ ifeq ($(USE_CLANG), true)
+ # Clang doesn't understand -gstabs
+ DEBUG_CFLAGS += -g
+ else
+ DEBUG_CFLAGS += -gstabs
+ endif
endif
复制代码
make
时出现下面的错误信息:... ... ... /src/os/bsd/vm/os_perf_bsd.cpp:29:10: fatal error: 'vm_version_ext_x86.hpp' file not found
#include "vm_version_ext_x86.hpp"
^~~~~~~~~~~~~~~~~~~~~~~~
复制代码
这里引入了错误的文件,须要对hotspot/src/os/bsd/vm/os_perf_bsd.cpp
作以下修改,使其引入正确的vm_version_ext_zero.hpp
文件:
#include "memory/resourceArea.hpp"
#include "runtime/os.hpp"
#include "runtime/os_perf.hpp"
-#include "vm_version_ext_x86.hpp"
+
+#ifdef TARGET_ARCH_aarch32
+# include "vm_version_ext_aarch32.hpp"
+#endif
+#ifdef TARGET_ARCH_x86
+# include "vm_version_ext_x86.hpp"
+#endif
+#ifdef TARGET_ARCH_sparc
+# include "vm_version_ext_sparc.hpp"
+#endif
+#ifdef TARGET_ARCH_zero
+# include "vm_version_ext_zero.hpp"
+#endif
+#ifdef TARGET_ARCH_arm
+# include "vm_version_ext_arm.hpp"
+#endif
+#ifdef TARGET_ARCH_ppc
+# include "vm_version_ext_ppc.hpp"
+#endif
复制代码
make
时出现下面的错误信息:make[2]: *** No rule to make target ` ... ... openjdk-jdk8u/jdk/src/macosx/bin/zero/jvm.cfg', needed by ` ... ... openjdk-jdk8u/build/macosx-x86_64-normal-zero-slowdebug/jdk/lib/jvm.cfg'. Stop.
复制代码
缺乏jdk/src/macosx/bin/zero/jvm.cfg
文件,咱们从其余目录拷贝一份过来:
mkdir -p jdk/src/macosx/bin/zero/
cp jdk/src/macosx/bin/x86_64/jvm.cfg jdk/src/macosx/bin/zero
复制代码
configure
出现下面的错误信息:checking for ffi.h... no
configure: error: Could not find libffi!
复制代码
一样是由于缺乏ligffi
依赖,与OpenJDK 8
不一样的是在OpenJDK 11
中,安装 libffi
以后须要经过--with-libffi=<path>
来指定libffi
的地址:
# 安装libffi
brew install libffi
# 配置
sh ./configure ...省略其余参数... --with-jvm-variants=zero --with-libffi=/usr/local/Cellar/libffi/3.3/
复制代码
configure
完成,执行make
出现下面的错误信息:... ... ... /openjdk-jdk11u/src/hotspot/share/interpreter/bytecodeInterpreter.cpp:2926:13: error: 7 enumeration values not handled in switch: 'T_VOID', 'T_ADDRESS', 'T_NARROWOOP'... [-Werror,-Wswitch]
switch (istate->method()->result_type()) {
^
... ... ... /openjdk-jdk11u/src/hotspot/share/interpreter/bytecodeInterpreter.cpp:2926:13: note: add missing switch cases
switch (istate->method()->result_type()) {
^
复制代码
这是由于源代码中的switch
语句没有处理全部的分支状况,全部clang
给出了警告(Diagnostic
),致使编译不经过,咱们能够经过clang
的-w
参数来取消全部的警告(Diagnostic
),须要在配置时添加以下两个参数,从新编译便可:
sh ./configure ...省略其余参数... --with-extra-cflags=-w --with-extra-cxxflags=-w
复制代码
make
时出现下面的错误信息:... ... ... /src/os/bsd/vm/os_perf_bsd.cpp:29:10: fatal error: 'vm_version_ext_x86.hpp' file not found
#include "vm_version_ext_x86.hpp"
^~~~~~~~~~~~~~~~~~~~~~~~
复制代码
这里引入了错误的文件,须要对src/hotspot/os/bsd/os_perf_bsd.cpp
作以下修改,使其引入正确的vm_version_ext_zero.hpp
文件(与OpenJDK 8
中稍有不一样):
#include "runtime/os.hpp"
#include "runtime/os_perf.hpp"
#include "utilities/globalDefinitions.hpp"
-#include "vm_version_ext_x86.hpp"
+
+#include CPU_HEADER(vm_version_ext)
复制代码