在使用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)
複製代碼