JVM 源碼分析(二):搭建 JDK 8 源碼調試環境(Windows 上使用 CLion)


前言

上一篇文章介紹了幾種 JVM,接下來,我將以 OpenJDK 8 中的 HotSpot VM 為例,通過分析其源碼,探索 JVM 的實現。本篇主要記錄調試環境的搭建過程。

由於在 Windows 下編譯 JVM 必須使用 Visual Studio,然而本人用慣了 JetBrains 家的 CLion,不想更換 IDE,所以選擇在 Linux (CentOS7) 上編譯,在 Windows 上使用 CLion 遠程調試。

這里需要注意,由於整個操作過程需要安裝很多工具,並且編譯時還將產生大量的臨時文件,因此,在開始編譯前必須確保有足夠的磁盤空間(最好大於20G)。

一、准備源碼

CentOS 中執行如下命令:

# 下載源碼包
wget https://download.java.net/openjdk/jdk8u41/ri/openjdk-8u41-src-b04-14_jan_2020.zip

#
 如果沒有安裝 unzip,先安裝
yum install -y unzip

#
 解壓
unzip openjdk-8u41-src-b04-14_jan_2020.zip

二、安裝 "Bootstrap JDK"

OpenJDK 的編譯除了依賴 C/C++ 編譯器之外,還依賴一個 Java 編譯器。這是因為 OpenJDK 的很多模塊都是用 Java 寫的,編譯這部分代碼就需要用到另一個 JDK。官方稱這個 JDK 為 “Bootstrap JDK”, 它的版本應當低於需要編譯的目標 JDK 的版本。

編譯 OpenJDK 8 需要使用 Update 7 或更高版本的 JDK 7 版本。參考源碼根目錄下的 “README-builds.html” 文件。

CentOS 中執行如下命令:

# 卸載已安裝的JDK
yum list installed | grep jdk
rpm -qa | grep jdk
yum remove -y xxxx

#
 確保卸載成功
java -version

#
 下載jdk-7u80,這里選擇從華為鏡像站下載
wget https://repo.huaweicloud.com/java/jdk/7u80-b15/jdk-7u80-linux-x64.tar.gz

#
 解壓到指定目錄
tar -zxf jdk-7u80-linux-x64.tar.gz -C /usr/local/java/

#
 配置環境變量
vim /etc/profile
 # 追加如下內容
    export JAVA_HOME=/usr/local/java/jdk1.7.0_80
    export CLASSPATH=$CLASSPATH:$JAVA_HOME/lib/
    export PATH=$PATH:$JAVA_HOME/bin
source /etc/profile

#
 確保安裝成功
java -version

完成后如圖所示:

三、配置編譯環境

CentOS 中執行如下命令:

# 安裝編譯所需工具
yum install -y gcc gcc-c++ make libXtst-devel libXt-devel libXrender-devel cups-devel freetype-devel alsa-lib-devel fontconfig-devel

#
 進入源碼目錄
cd openjdk/

#
 確保configure腳本擁有可執行權限
chmod +x configure

#
 執行configure腳本,看看缺少什么依賴項,根據錯誤提示安裝即可,然后重復執行直到提示成功
./configure --with-debug-level=slowdebug --enable-debug-symbols --disable-zip-debug-info

#
 參數說明
# --with-debug-level=slowdebug 設置編譯級別為slowdebug,將會輸出較多的調試信息
# --enable-debug-symbols 啟用調試符號,將會生成調試信息文件
# --disable-zip-debug-info 禁用調試信息壓縮,否則,調試信息默認會被壓縮成"libjvm.diz"文件,調試時只能看到匯編代碼,不能跟進源碼

完成后如圖所示:

四、編譯與測試

CentOS 中執行如下命令:

# 編譯(這里啟動6條編譯線程以加快編譯速度)
make JOBS=6

#
 測試
./build/linux-x86_64-normal-server-release/jdk/bin/java -version

#
 確保"libjvm.debuginfo"文件存在,否則調試時將不能跟進源碼
ls ./build/linux-x86_64-normal-server-release/jdk/lib/amd64/server/

如果 Linux 內核版本為 4+,編譯時將出現 “This OS is not supported” 的報錯。解決辦法是修改源碼目錄下的 “./hotspot/make/linux/MakeFile” 文件,找到 SUPPORTED_OS_VERSION 變量定義的地方,在后面追加 “4%”,如下圖所示。

如果一切順利,將會看到如圖所示信息:

至此,我們已經完成了 JDK 的編譯。

五、安裝 CMake 和 GDB

為了在本地使用 CLion 進行遠程調試,需要在服務端安裝與本地版本相兼容的 CMake 和 GDB。

由於從 yum 源安裝的版本較低,因此這里選擇編譯安裝。

CentOS 中執行如下命令:

# 卸載已有的cmake和gdb
yum remove -y cmake gdb

#
 下載cmake3.14.5(使用華為鏡像站)
wget https://mirrors4.tuna.tsinghua.edu.cn/pkgsrc/distfiles/cmake-3.14.5.tar.gz

#
 解壓
tar -zxf cmake-3.14.5.tar.gz

#
 進入cmake目錄,執行編譯安裝
cd cmake-3.14.5
./bootstrap && make && make install

#
 為cmake命令創建軟鏈接
ln -s /usr/local/bin/cmake /usr/bin/cmake

#
 驗證是否安裝成功
cmake -version

#
 回到上一級目錄,准備安裝gdb
cd ..

#
 下載gdb8.1(使用華為鏡像站)
wget https://mirrors4.tuna.tsinghua.edu.cn/pkgsrc/distfiles/gdb-8.1.tar.gz

#
 解壓
tar -zxf gdb-8.1.tar.gz

#
 安裝編譯所需工具
yum install -y texinfo

#
 進入gdb目錄,執行編譯安裝
cd gdb-8.1
./configure && make && make install

#
 為gdb命令創建軟鏈接
ln -s /usr/local/bin/gdb /usr/bin/gdb

#
 驗證是否安裝成功
gdb -ver

五、准備遠程調試

在 CLion 中創建一個空項目,推薦 Language standard 選擇 C++11。

配置工具鏈,如圖所示。

配置 SFTP 連接,用於連接到遠程主機。

配置路徑映射,用於同步兩端的代碼。

配置排除路徑,排除本地的 cmake 輸出路徑。

將遠程主機的代碼同步到本地。在 Project 面板中點右鍵,在彈出的菜單中選擇 Deployment -> Download from,然后點擊目標 Server,等待下載。如圖所示。

同步的過程比較耗時,主要是因為 CLion 需要給每一個文件建立映射關系。同步完成后的效果如下。

接下來需要在 CMakeLists.txt 文件中完成項目模型的配置。

CMakeLists.txt 中的一些基本配置可以通過 “New CMake Project from Sources” 的方式讓 CLion 自動完成。不過 CLion 僅僅是把項目文件注冊進來,並沒有正確配置依賴關系和宏定義,當我們打開代碼時會發現處處爆紅。

為了解決這些爆紅,我花了兩個晚上,經反復嘗試,終於得到了一個看似使代碼不再爆紅的 CMakeLists.txt 文件。

附上 CMakeLists.txt 文件的鏈接:https://github.com/zhangyongheng/jdkbuild/blob/master/openjdk8/CMakeLists.txt

項目模型配置好了之后,接下來就可以開始遠程調試了。

六、開始遠程調試

首先在遠程主機上開啟 GDB 服務,命令如下:

# 開啟GDB服務,這里指定端口號為8899,后面跟上要調試的java命令
gdbserver :8899 /root/openjdk/build/linux-x86_64-normal-server-release/jdk/bin/java -version

#
 如果沒有安裝gdbserver,先安裝
yum install -y gdb-gdbserver

打開 CLion 的 "Run/Debug Configuration" 對話框,添加一個 "GDB Remote Debug",按照如圖所示進行配置。其中,"target remote args" 是遠程 GDB 服務的 IP 地址和端口號,"Path mappings" 是遠程項目根路徑與本地項目根路徑間的映射關系。

打開 "jdk/src/share/bin/java.c" 文件,找到 JavaMain() 函數,它是 Hotspot 虛擬機的執行入口,我們可以在這個方法內打上斷點。

點擊 "Debug" 開始調試,在 ”GDB“ 標簽頁中可以查看到 GDB 日志,我們還可以在這里通過輸入 GDB 命令,以命令行的方式執行調試。

從 GDB 日志中可以看到, "libjvm.debuginfo" 文件的載入通常比較耗時,我這里大約需要等待 3 分鍾,然后程序啟動后會停在斷點的位置。

在跟蹤調試之前,還需要設置 GDB 對 SIGSEGV 信號的處理方式,忽略調試時的 SIGSEGV 信號。

在 ”GDB“ 標簽頁下的 "(gdb)" 命令行中輸入 "handle SIGSEGV nostop noprint pass",如圖所示。

接下來就可以繼續跟蹤調試了。

調試完成后,遠程 GDB 服務將會退出,再次調試需要重新開啟 GDB 服務。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM