mac下編譯openjdk1.9及集成clion動(dòng)態(tài)調(diào)試

晚上被小伙伴問道如何使用ide進(jìn)行jvm源碼的調(diào)試,剛好前段時(shí)間花了點(diǎn)時(shí)間折騰了一下,mac最新版本下jvm9順利編譯通過,并且可以完美集成clion進(jìn)行調(diào)試(支持windows),下面記錄一下全過程,如果想看效果的話,可以直接拉到集成到clion進(jìn)行調(diào)試小節(jié)末尾

mac下openjdk源碼編譯過程

準(zhǔn)備編譯環(huán)境準(zhǔn)備

我的mac的版本如下

image.png

由于openjdk1.9之前的版本對(duì)mac下編譯支持得不是很流暢,所以這篇文章選擇openjdk1.9

編譯之前,首先你需要準(zhǔn)備 homebrew,homwbrew是mac下的包管理器,如果你的mac上沒有安裝,可以按照下面的方式來(lái)安裝

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

homwbrew下載完成之后,接下來(lái)準(zhǔn)備編譯環(huán)境

  • 首先安裝openjdk的版本管理工具mercurial
  • 然后安裝ccache和freetype,ccache用來(lái)加速編譯,freetype在編譯過程也會(huì)依賴到

上述準(zhǔn)備編譯環(huán)境的腳本為

brew install mercurial
brew install ccache
brew install freetype

請(qǐng)確保上述三個(gè)依賴安裝成功再進(jìn)行下面的步驟

源碼獲取

環(huán)境準(zhǔn)備好之后,接下來(lái)獲取源碼,我這里工作目錄是 ~/jvm,建議你也建一個(gè)此目錄

cd ~/jvm
hg clone http://hg.openjdk.java.net/jdk9/jdk9 jdk9

這條命令運(yùn)行之后,openjdk的源碼并沒有下載下來(lái),我們隨后進(jìn)入到~/jvm/jdk9目錄,會(huì)發(fā)現(xiàn)有一個(gè) get_source.sh 的文件
調(diào)用這個(gè)腳本下載完整的源碼之前,需要做一下簡(jiǎn)單的修改,不然可能你在下載源碼的過程中會(huì)數(shù)次中斷

get_source.sh文件最后幾行的內(nèi)容如下

# Get clones of all absent nested repositories (harmless if already exist)
sh ./common/bin/hgforest.sh clone "$@" || exit $?

# Update all existing repositories to the latest sources
sh ./common/bin/hgforest.sh pull -u

我們把上面幾行的腳本刪掉,替換成下面的腳本

# Get clones of all absent nested repositories (harmless if already exist)
sh ./common/bin/hgforest.sh clone "$@"

while [ $? -ne 0 ]
do
    sh ./common/bin/hgforest.sh clone "$@"
done

# Update all existing repositories to the latest sources
sh ./common/bin/hgforest.sh pull -u

while [ $? -ne 0 ]
do
    sh ./common/bin/hgforest.sh pull -u
done

然后,愉快地調(diào)用

bash ./get_source.sh

友情提醒:這個(gè)過程可能要持續(xù)1~2小時(shí),請(qǐng)?zhí)崆包c(diǎn)好外賣

開始編譯

源碼下載完之后,我們開始編譯,我們先進(jìn)行編譯前的配置

./configure --with-target-bits=64 --with-freetype=/usr/local/Cellar/freetype/2.8.1 --enable-ccache --with-jvm-variants=server,client --with-boot-jdk-jvmargs="-Xlint:deprecation -Xlint:unchecked" --disable-zip-debug-info --disable-warnings-as-errors --with-debug-level=slowdebug 2>&1 | tee configure_mac_x64.log

注意,上面的freetype,需要替換成本機(jī)實(shí)際安裝的版本

執(zhí)行完之后,記下來(lái)會(huì)進(jìn)行一系列的配置,這個(gè)過程時(shí)間要短很多,最后,如果出現(xiàn)如下的提示,那么恭喜你,接下來(lái)就可以執(zhí)行編譯了

image.png

接下來(lái)調(diào)用下面的腳本進(jìn)行編譯

export LANG=C
make all LOG=debug  2>&1 | tee make_mac_x64.log

編譯完成之后,如果沒有出現(xiàn)錯(cuò)誤提示,那么再次恭喜你,你的第一個(gè)自行編譯的openjdk版本已經(jīng)順利通過了,可以考慮打個(gè)賞來(lái)慶祝一下快樂的心情

最后,驗(yàn)證一下

image.png

編譯過程中遇到的問題

我比較幸運(yùn),在編譯過程中就只遇到過三個(gè)空指針轉(zhuǎn)換的問題

vi src/share/vm/memory/virtualspace.cpp (char *)

image.png

vi src/share/vm/opto/lcm.cpp (unsigned char *)

image.png

vi src/share/vm/opto/loopPredicate.cpp (const TypeInt *)

image.png

然后,google了一把,根據(jù)這篇文章,找到對(duì)應(yīng)的源碼文件的位置,把0強(qiáng)制轉(zhuǎn)換成同一種類型
比如下面這個(gè)

image.png

好在一共只有三個(gè)地方,我都給你列出來(lái)了

#1\. src/hotspot/share/memory/virtualspace.cpp # l585

if (base() != NULL) {

#2\. src/hotspot/share/opto/lcm.cpp # l42

if (Universe::narrow_oop_base() != NULL) { // Implies UseCompressedOops.

#3\. src/hotspot/share/opto/loopPredicate.cpp # l915

assert(rng->Opcode() == Op_LoadRange || iff->is_RangeCheck() || _igvn.type(rng)->is_int()->_lo >= 0, "must be");

集成到clion進(jìn)行調(diào)試

千呼萬(wàn)喚試出來(lái),終于來(lái)到本小節(jié)的內(nèi)容,相信很多小伙伴更想知道jvm源碼編譯完之后,如何在本機(jī)進(jìn)行流暢地調(diào)試閱讀源碼,在這之前,我發(fā)現(xiàn)網(wǎng)上這方面的資料少得可憐,所以自己慢慢摸索出來(lái),分享給大家

使用clion載入源碼

首先,我們打開clion,選擇 File->ImportProject,選擇到 ~/jvm/jdk9/hotspot作為jvm源碼的根目錄,這里導(dǎo)入的過程無(wú)腦點(diǎn)擊next即可

項(xiàng)目導(dǎo)入之后,clion會(huì)給你默認(rèn)建立一個(gè) CMakeLists.txt文件,這里可以不用管他,讓他建,完成之后,clion會(huì)做大量的索引,索引建立完成之后,項(xiàng)目導(dǎo)入到此結(jié)束,界面如下

image.png

很多小伙伴遇到clion導(dǎo)入源碼之后遇到頭文件找不到的問題,而實(shí)際上這些頭文件在源碼里面是存在的,只不過在某些源文件里面是以相對(duì)路徑的方式來(lái)搜索,可以在CMakeLists.txt里面添加一些根路徑,我添加了如下根路經(jīng)

image.png
include_directories(./src/share/vm)
include_directories(./src/cpu/x86/vm)
include_directories(./src/share/vm/precompiled)
include_directories(./src/share/vm/utilities)

另外,如果某些頭文件依然找不到,可以手工導(dǎo)入,然后把導(dǎo)入的頭文件加到
hotspot/src/share/vm/precompiled/precompiled.hpp里,因?yàn)榇蠖鄶?shù)源文件都會(huì)包含這個(gè)源文件,加到這個(gè)頭文件,可以保證在能夠引入缺失頭文件的同時(shí)在debug的時(shí)候行數(shù)不會(huì)串掉,我在讀源碼的過程中添加了如下幾個(gè)頭文件

image.png

# include <cstdlib>
# include <cstdint>
# include "register_x86.hpp"
# include "assembler_x86.hpp"
# include "globalDefinitions.hpp"
# include "globalDefinitions_x86.hpp"
# include "assembler_x86.hpp"
#include <stubRoutines_x86.hpp>

構(gòu)建調(diào)試環(huán)境

到了這里,源碼的閱讀環(huán)境才剛剛開始,沒有debug,總覺得看起代碼來(lái)不真實(shí),于是,接下來(lái)我們繼續(xù)構(gòu)建自己的調(diào)試環(huán)境

image.png

右上角,我們點(diǎn)擊Edit Configuration,進(jìn)入到下面這個(gè)界面

image.png

我們創(chuàng)建一個(gè)調(diào)試環(huán)境


image.png

Executable選擇編譯好的二進(jìn)制 java文件,然后在Before launch...選項(xiàng)中,干掉Build,就是說我們?cè)赿ebug的時(shí)候不需要再build,再說了,這里你也build不起來(lái)。
到了這里,一個(gè)可調(diào)試的jvm源碼調(diào)試環(huán)境已經(jīng)準(zhǔn)備完畢了,下面我們來(lái)調(diào)試一把看看效果

我們來(lái)到 jni.cpp,在 JNI_CreateJavaVM_inner 這個(gè)方法上打個(gè)斷點(diǎn),點(diǎn)擊右上角的debug,神奇的一幕出現(xiàn)了

image.png

方法調(diào)用棧,當(dāng)前方法上下文環(huán)境,調(diào)試所需要的東西應(yīng)有盡有,如果上面這個(gè)畫面不夠打動(dòng)你的話,那么下面一張實(shí)際源碼閱讀過程中的動(dòng)圖呢?


3.gif

與java程序聯(lián)合調(diào)試

我們想要修改jvm源碼,修改完之后想要通過修改完的源碼來(lái)運(yùn)行我們的指定的java程序,由于clion默認(rèn)的build工具無(wú)法build jvm,我們只能借助于make命令。
clion在debug的時(shí)候可以添加兩個(gè)前置處理器,如下圖

image.png

第一個(gè)前置處理器用于build jvm,第二個(gè)前置處理器用于compile java文件,這樣,當(dāng)你修改了點(diǎn)jvm源碼,并且修改了java文件之后,在點(diǎn)擊右上角的debug文件的時(shí)候,clion就會(huì)默認(rèn)先執(zhí)行jvm的編譯,然后再進(jìn)行java文件的編譯,編譯完之后就在 /Users/yuchao/IdeaProjects/jvm/src(我們?cè)诖四夸浵路胖胘ava源文件) 目錄下生成 一個(gè)Main.class文件,然后就可以使用編譯過后的jvm來(lái)運(yùn)行,下面是構(gòu)建jvm和編譯java文件的兩個(gè)external tool

image.png
image.png
image.png
image.png

最后,我們來(lái)看下,修改了jvm源碼之后的效果


image.png

當(dāng)然,你也可以在你的java ide中,把jdk選擇自己編譯的jdk,再執(zhí)行,也是一樣的效果


image.png

[參考資料]
https://segmentfault.com/a/1190000008346240
https://liuzhengyang.github.io/2017/04/28/buildopenjdk/
http://www.itdecent.cn/p/746963f28245
https://bugs.openjdk.java.net/browse/JDK-8187787

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容