openjdk9u源码分析一:搭建环境
搭建环境可分两步,
1. 搭建jdk9u编译环境
2. 搭建jdk9u调试环境
实验环境
1. Mac OS X: 10.13.3.
2. Xcode: 9.2.
3. boot jdk: 1.8.0_162.
4. freetype: 2.9.
5. XQuartz-2.7.11.
6. GDB-8.0.1.
1.搭建jdk9u编译环境
- 目标:从官网下载openjdk9u源码,正确编译打包出jdk9u可执行文件,能够编译、运行helloworld程序。
- 搭建过程:参考https://www.yanshuo.me/p/156958,按照这篇文章能够正确编译出可运行的jdk9u。我的环境和参考链接中的基本上一致,除了使用的boot jdk,我使用的1.8.0_162,而参考文章使用的1.8.0_161。
- 结果验证:
-
编译后的结果位于源码根目录/build下:
目前阶段只关注hotspot、images和jdk子目录,其中hotspot存放jvm的编译结果,images存放jdk、jrem,doc等产品文件,jdk存放jdk编译结果。
-
验证build出来的jdk版本:
-
Run一个helloworld类:
-
问题总结:
- 网上资料以编译jdk7、8为主,总结比较全面的文章请参考https://github.com/ydcun/Java/blob/master/java/src/main/java/com/ydcun/openjdk/jdk8/MAC%E7%BC%96%E8%AF%91OpenJDK8.md,折腾了半天jdk8的编译,把hotspot8u40给捣腾出来了,但在运行hotspot时提示找不到libjvm.dylib:
unable to load native library:dlopen(/Library/Java/JavaVirtualMachines jdk1.7.0_10.jdk/Contents/Home/jre/lib/libjava.dylib, 1):Symbol not found: \_JVM\_SetProtectionDomain Referenced from: /Library/Java/JavaVirtualMachines/jdk1.7.0\_10.jdk/Contents/Home/jre/lib/libjava.dylib Expected in: /Users/Desktop/hotspot-68577993c7db/build/bsd/ bsd\_amd64\_compiler2/fastdebug/libjvm.dylib
这个错误应该是没有编译jdk8u40的源码,只编译了hotspot的源码,我用boot jdk7代替了jdk8造成的,但并没有实际去验证。看了jdk9u的文章后,里面提到“因为编译8需要Xcode 4现在Xcode版本已远高于4了”,就放弃了jdk8的编译,但现在看,未必是Xcode的版本影响了8的编译。
- 在编译jdk8时,要求安装XQuartz,我安装了XQuartz-2.7.11,但并未能创建这两个链接:
sudo ln -s /usr/X11/include/X11 /usr/include/X11
or
sudo ln -s /usr/local/X11/include/X11 /usr/include/X11
后面编译成功说明这个问题可以忽略。 - 源码推荐从http://hg.openjdk.java.net/jdk-updates下载。首先下载根目录,然后再分别下载各个子目录,下载完后解压到根目录下,如下图所示:
其中,hotspot是JVM的源码目录,jdk存放jdk源码,强烈建议在编译之前先读common/doc/下的build文件。其他目录的功能说明大家可网上了解即可,对于编译过程,基本没影响,如果要运行jdk自带的测试代码,需要关注test目录。 其他两种下载源码的方式:使用Mercurial的hg,openjdk的源码通过mercurial进行管理。但正如网友@since1986在博文中所述,每次都是下载到一定大小的内容后,程序自动退出;还有网友修改了get_source.sh脚步,使之自动重连下载,修改的脚步比较多,而且不保证能成功,放弃。另外,git上有上传的代码,参见https://github.com/luodaxu/blog-1/blob/master/Java/hotspot-debug-under-osx.md,但并未验证。
- 设置环境变量。
- 设置boot jdk path。方便起见,下载了jdk7,jdk8两个版本,jdk7用于实验,8用于工作,如果想直接编译jdk9,jdk7也不需要额外安装。
export JAVA\_8\_HOME=$(/usr/libexec/java\_home \-v1.8)
export JAVA\_7\_HOME=$(/usr/libexec/java\_home \-v1.7)
alias java7='export JAVA\_HOME=$JAVA\_7\_HOME'
alias java8='export JAVA\_HOME=$JAVA\_8\_HOME'
\# default java8
export JAVA_HOME=$JAVA\_8\_HOME
- 设置编译参数。每个参数上都有说明,大家自行查阅,并可以根据实验机器的配置进行调整。本文仅对build和compile的job数进行了修改,编译一遍通过。
\# 设定语言选项,必须设置
export LANG=C
\# Mac平台,C编译器不再是GCC,是clang
export CC=clang
\# 跳过clang的一些严格的语法检查,不然会将N多的警告作为Error
export COMPILER\_WARNINGS\_FATAL=false
\# 链接时使用的参数
export LFLAGS='\-Xlinker \-lstdc++'
\# 是否使用clang
export USE\_CLANG=true
\# 使用64位数据模型
export LP64=1
\# 告诉编译平台是64位,不然会按32位来编译
export ARCH\_DATA\_MODEL=64
\# 允许自动下载依赖
export ALLOW\_DOWNLOADS=true
\# 并行编译的线程数,编译时间长
export HOTSPOT\_BUILD\_JOBS=8
ALT\_BOOTDIR=$JAVA\_HOME
export ALT\_PARALLEL\_COMPILE\_JOBS=2
\# 是否跳过与先前版本的比较
export SKIP\_COMPARE\_IMAGES=true
\# 是否使用预编译头文件,加快编译速度
export USE\_PRECOMPILED\_HEADER=true
\# 是否使用增量编译
export INCREMENTAL\_BUILD=true
\# 编译内容
export BUILD\_LANGTOOLS=true
export BUILD\_JAXP=true
export BUILD\_JAXWS=true
export BUILD\_CORBA=true
export BUILD\_HOTSPOT=true
export BUILD\_JDK=true
\# 编译版本
export SKIP\_DEBUG\_BUILD=true
export SKIP\_FASTDEBUG\_BUILD=false
export DEBUG\_NAME=debug
\# 避开javaws和浏览器Java插件之类的部分的build
export BUILD\_DEPLOY=false
export BUILD\_INSTALL=false
- configure时添加--disable-warnings-as-errors。因为在build hotspot时,如果出现warnings,编译过程会认为失败,实际这些warnings不影响编译的正确性;也可以在环境变量中配置:export COMPILER_WARNINGS_FATAL=false,上述配置已包括。
有些jvm源码需要修改,详情参见上述参考的链接。
jdk9的模块化等一些新特性不会影响编译过程,正如jdk的一位负责人所说,从老版本迁移到jdk9要比之前的升级过程还简单,至少从编译jdk源码的过程来讲,jdk9u并未增加额外的工作量。
-
mac下软件安装时,可能会遇到verify stuck的情况,参考http://osxdaily.com/2016/07/26/fix-stuck-pkg-verifying-installer-mac-os-x/解决。
至此,openjdk9u已经编译成功,并得到一个可用于调试的jvm和jdk。接下来,使用已构建的程序包,调试一个helloworld的程序。该过程网上信息比较少,已有的资料要么jdk版本不一致,要么依赖的软件包版本不同,要么操作系统不同,对于之前未debug jvm的dev,可能是个痛苦的过程,但参考下面的内容后,将会使大家的调试过程变得轻松许多。
搭建jdk9u调试环境
- 目标:利用已生成的jvm,debug一个java helloworld程序,且能够debug到jvm的源码。
- 搭建过程:针对jdk8的调试环境搭建,请参考http://blog.csdn.net/tjiyu/article/details/53725247。整个过程可概况为使用GDB(全平台通用)或LLDB(windows版本正在开发,其他平台都支持)debug java这个可执行文件(GDB、LLDB是两种C、C++程序调试工具,由于hotspot主要采用C++和C实现,因此要调试hotspot必须要使用C、C++的调试工具,这些工具可以通过命令行单条执行命令,也可以通过脚步批量执行),入参为待执行的java class文件名,本文中为Test。java可执行文件启动一个hotspot实例来执行Test class。下面分步骤说明环境搭建过程。
- 安装GDB。 macOsX high sierra下,Xcode9.2默认使用LLDB,因此需要安装GDB。从GDB官网下载了最新的8.1版本,并安装网上的说明进行sign,否则debug时,eclipse的进度条会一直卡在99%不动。参考https://stackoverflow.com/questions/18423124/please-check-gdb-is-codesigned-see-taskgated8-how-to-get-gdb-installed-w对GDB进行签名,需要注意:证书类型需要先选择login方式,然后再拖拽到system类型下,否则创建证书会报错。如下图所示:
后来验证的过程说明,GDB相关的问题是调试环境安装过程中最大的一个坑。
* 添加"set startup-with-shell off" >> ~/.gdbinit,.gdbinit是gdb的初始化文件,也可以在执行gdb之前执行set startup-with-shell off。
* 执行csrutil status,查看Debugging Restrictions配置项的值,如果是enabled,需要更改为disabled。参考https://stackoverflow.com/questions/39702871/gdb-kind-of-doesnt-work-on-macos-sierra/40437725#40437725,需要reboot,并在安全模式下执行。
* 回退到8.0.1版本,事实证明8.1.0与OS X 10.13.3兼容性不好。参见https://gist.github.com/gravitylow/fb595186ce6068537a6e9da6d8b5b96d。在因为GDB抛出"During startup program terminated with signal SIGTRAP, Trace/breakpoint trap."问题时,曾尝试使用LLDB进行debug,但LLDB虽然能跑完测试程序,但看不到java程序的输出,最终还是使用GDB进行调试时看到了java的输出。LLDB调试参见https://github.com/luodaxu/blog-1/blob/master/Java/hotspot-debug-under-osx.md,但通过LLDB与GDB的对比,也找到了GDB不能输出的原因,即在gdb捕获segment invalid异常时,继续执行,在hotspot脚步中添加handle SIGSEGV nostop
这一行指令。后面在介绍hotspot脚步时还会详细介绍。通过一个C程序验证GDB已成功安装。
- 修改Hotspot文件。该脚步位于./jdk9u-1b1226687b89/build/macosx-x86_64-normal-server-slowdebug/hotspot/variant-server/libjvm/下,它是一个可执行的脚步文件,该文件配置了JDK路径、GDB调试器默认的一些参数,关于该文件的内容可参考《HotSpot实战》这本书第一章的内容或https://yq.aliyun.com/articles/93753?spm=a2c4e.11153940.blogcont93737.16.6da64d09stvTm2。需要说明的有三点:
* build源码后,该脚步中已经添加了已build的jdk路径,大家可查看hotspot文件中的这一行: JDK=/Users/Desktop/jdk9u-1b1226687b89/build/macosx-x86_64-normal-server-slowdebug/jdk。
* 脚步中init_gdb()函数用于配置gdb参数,默认生成的内容中有handle SIGUSR1 nostop noprint,handle SIGUSR2 nostop noprint这两行,handle命令的意思是debug过程中,gdb如何处理系统信号,在后面添加handle SIGSEGV nostop这一行。 - 复制libjvm.dylib文件到hotspot脚步相同目录下。最初通过“./jdk9u-1b1226687b89/build/macosx-x86_64-normal-server-slowdebug/hotspot/variant-server/libjvm/hotspot -gdb Test”测试时,总是得到一个ERROR“Error: missing
/Users/.../Desktop/jdk9u-1b1226687b89/build/macosx-x86_64-normal-server-slowdebug/hotspot/variant-server/libjvm' JVM at
/Users/Desktop/jdk9u-1b1226687b89/build/macosx-x86_64-normal-server-slowdebug/hotspot/variant-server/libjvm/libjvm.dylib'.
Please install or use the JRE or JDK that contains these missing components.”,该错误耽误了有半天的时间,最初依赖搜索引擎,但网上在mac high sierra下调试hotspot9的资料基本上没有,只是了解到libjvm就是hotspot的动态库,当时真有些山穷水尽的感觉,于是只能中断一些时间,休息时,libjvm.dylib missing的错误一直在头脑中浮现,某个时刻突然想到libjvm.dylib文件是不是不在hotspot相同目录下,于是查找hotspot/variant-server/libjvm/目录,发现该文件在./libjvm/gtest下,copy一份到hotspot/variant-server/libjvm,即与hotspot文件同目录下,测试Test,得到了期待的结果:
图中,绿色方框中的为java程序的输出,红色框中即为GDB收到的segment fault的信号,如果不修改hotspot脚步,GDB会执行不下去。
- 问题总结:
- 使用当前比较新的软件版本做一些实验时,网上可参考资料很少,如果对一些问题领域不太熟悉,可能会花费较多的时间和精力;
- 实验遇到瓶颈时,抓住错误日志不放,休息后换个思路再试;
- 多尝试不同的思路才可能解决问题;
- 遇到问题请教网上的专家得到响应的情况几乎为零:(。
本文知识产权归创造者所有,复制转发发行需经作者同意。