Java虚拟机原理分析之Win7下VS2010编译OpenJDK8与单步调试HotSpot VM过程详细记录

版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LPWSTR/article/details/78840188

上周五偶然翻到本讲JVM原理的书,看着觉得很过瘾,居然因此错过了晚饭,很久没这么酣畅淋漓过了。然而过瘾之余,看不到JVM源码、也无法调试,总觉得未能尽兴。Linux的老爸Linus大牛曾说“Talk is cheap, show me the code”,光看书,总觉得像在吃别人已经吃剩下的饭,远不如自己动手看看源码、调试跟一跟来得实在。遂决定在Windows平台上自己动手编译一个Java虚拟机,并且要求能调试。

本文详细记录了Windows平台下使用VS2010编译x86版本的Java虚拟机客户端的全过程。包括编译前的准备工作、配置与编译过程与错误排除、将Hotspot VM源码导入VS2010 IDE实现图形界面管理,并在其中下断点调试的整个过程。

安装VS2010及SP1补丁

OpenJDK官方使用VS2010构建Windows平台下的JVM和JDK,并且在其配置脚本中也有一些针对VS2010“硬编码”的内容,如捕捉其编译器的版本信息。为了保证兼容性和编译的方便性,我们也入乡随俗,使用VS2010。

值得注意的是,为了顺利编译VS2010,需要为VS2010安装SP1补丁。之后最好再通过Windows的更新系统将所有VS2010相关的补丁打齐,然后开始编译。我安装的是VS2010中文版,在之后的编译过程中要改一些配置(OpenJDK的配置脚本对中文支持不好),因此如果直接安装英文版的VS2010,编译过程会更顺利些。

图中正在给VS2010打补丁。从安装VS2010再到安装VS2010 SP1补丁包,然后在线更新补丁,耗时会比较长,这段时间可以并行下面的操作。

2017-12-18_165619

安装Cygwin

由于OpenJDK在配置阶段使用了bash脚本和一些gnu工具集,要想在Windows系统中完成这些步骤,就需要Cygwin的帮助了。访问https://www.cygwin.com/网站,下载对应的Cygwin版本。由于我的开发环境是Win2k8 R2,于是下载了x64版本的。

Cygwin的安装比较傻瓜化,其中有一步是选择镜像,我选择的是kernel.org的镜像,速度还行。接下来是定制需要安装的工具,在默认的基础上,我们加装如下工具:

Binary Name   Category        Package        Description  
================================================================  
ar.exe        Devel            binutils      The GNU assembler, linker and binary utilities  

make.exe      Devel            make          The GNU version of the 'make' utility built for CYGWIN

m4.exe        Interpreters     m4            GNU implementation of the traditional Unix macro processor  

cpio.exe      Utils            cpio          A program to manage archives of files  

zip.exe       Archive          zip           Package and compress (archive) files  

unzip.exe     Archive          unzip         Extract compressed files in a ZIP archive  

free.exe      System           procps        Display amount of free and used memory in the system  
================================================================  

选定工具后就是漫长的等待下载过程了。

Cygwin

下载OpenJDK8的源码

OpenJDK的源码采用Mercurial进行版本管理 ,为了获取最新版本的源码,我们需要下载TortoiseHg。由于开发机网络不太稳定,这一步我选择在VPS上完成,并将下载好的源码传回本地。

用于下载源码的VPS使用的是CentOS系统,需要先安装TortoiseHg:

yum install mercurial

如果是在Windows环境下,则可以到bitbucket官方进行下载和安装:https://tortoisehg.bitbucket.io/download/index.html

安装好后,将源码下载到本地,并下载好附加的源码:

hg clone http://hg.openjdk.java.net/jdk8u/jdk8u-dev  
cd jdk8u-dev  
sh get_source.sh  

编译freetype

由于OpenJDK中的swing与JConsole需要使用freetype的字体渲染功能,因此需要首先对其进行编译。首先到https://www.freetype.org/download.html#stable-releases下载freetype的源码,最终转到https://download.savannah.gnu.org/releases/freetype/处。目前(17.12.17)最新的版本是freetype-2.8.1.tar.bz2,下载后解压到本地。

使用VS2010载入其中的\builds\windows\vc2010目录下的sln文件,打开该工程。然后在工程名上右键,进行属性设置。这里有几点要进行修改:

  1. 将Release模式设置为当前活动模式
  2. 我们需要让其生成动态链接库(Dll)和用于动态链接的Lib库(先将配置类型选择为动态库)
  3. 生成的Dll名需要修改,去掉版本号(目标文件名中,去掉末尾的版本号)

2017-12-18_172448

为了能在生成Dll的同时构建好lib文件,需要对freetype的源码进行一点点修改。找到其头文件中的ftconfig.h文件,定位到FT_EXPORT的条件宏部分,将其用如下代码替换:

#ifdef DLL_EXPORT
#undef DLL_EXPORT
#define DLL_EXPORT  __declspec(dllexport)
#else
#define DLL_EXPORT  __declspec(dllimport)
#endif /* !DLL_EXPORT */


#ifndef FT_EXPORT
#ifdef __cplusplus
#define FT_EXPORT( x )  extern "C"  DLL_EXPORT  x
#else
#define FT_EXPORT( x )  extern  DLL_EXPORT  x
#endif

如果有文件提示需要转换编码或者转换代码页,按其提示操作即可。(我这里偷了个懒,没管,编译时有警告,不过没影响到最终的生成)

在freetype的根目录下,创建一个名为lib的文件夹,将编译后得到的freetype.dll和freetype.lib拷贝到其中即可。如下所示:

2017-12-18_172632

配置OpenJDK

到目前为止,我们已经完成了VS2010 with SP1的安装、Cygwin及需要用到的工具的安装、freetype的编译生成、OpenJDK8的源码获取。在本机,上述各组件的为止如下:

  1. VS2010的x86环境配置脚本位置:C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin目录下的vcvars32.bat
  2. freetype库的路径为C:\jvm\freetype-2.8.1,其下有include、lib文件夹
  3. Cygwin安装到C:\cygwin64,其下有Cygwin.bat脚本用于设置环境
  4. 下载的OpenJDK源码路径为C:\jvm\jdk8u-dev

首先进行配置。开启一个Cygwin的bash窗口,进入OpenJDK源码路径(注意,Cygwin中硬盘路径为/cygdrive/盘符/路径),按我的配置,则为:

cd /cygdrive/c/jvm/jdk8u-dev

然后进行配置:

bash ./configure --with-freetype=/cygdrive/c/JVM/freetype-2.8.1 -with-target-bits=32 --with-debug-level=slowdebug --with-jvm-variants=client with_toolsdir="/cygdrive/c/Program Files (x86)/Microsoft Visual Studio 10.0/VC/bin"
  • 由于需要生成的是Win32下的client版本JVM,因此使用-with-target-bits=32和–with-jvm-variants=client
  • 希望虚拟机不要随意对指令进行优化,使用–with-debug-level=slowdebug;如果要进行适度优化,可使用–with-debug-level=fastdebug
  • 而with_toolsdir=”/cygdrive/c/Program Files (x86)/Microsoft Visual Studio 10.0/VC/bin”则指明了编译工具集路径,这里指的是vcvars32.bat的路径。其实如果VS2010安装在默认路径,是不需要传入这个参数的。

很快,我们就收到了报错提示:

解决报错:Target CPU mismatch

2017-12-18_173151

打开jdk8u-dev\common\autoconf下的generated-configure.sh文件,搜索“Target CPU mismatch”关键字,共两处命中。分析其上下文可知,其是通过微软编译器cl.exe的输出获得目标平台和版本号的。由于我们使用的是中文的VS2010,因此和其预设的英文不同,故而无法匹配,进而报错。解决方案很简单,直接将这段判断逻辑干掉:

elif test  "x$OPENJDK_TARGET_OS" = xwindows; then
    # First line typically looks something like:
    # Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    COMPILER_VERSION_TEST="Microsoft C/C++ Compiler version 16.00.40219.01 x80x86"
    COMPILER_VERSION="16.00.40219.01"
    COMPILER_VENDOR="Microsoft CL.EXE"
    COMPILER_CPU_TEST="x80x86"

而几个变量也被我直接赋值了。此时再重新执行配置命令,很快就完成了。

2017-12-18_174700

编译OpenJDK

在完成配置后,就可以进行构建了。由于我们只需要Win32版本的JVM客户端,因此继续在完成配置的bash窗口中执行生成命令:

make images

解决报错:get_msc_ver.sh:需要整数表达式

执行后,很快又发生了一个错误:

2017-12-18_174949

首先按照提示路径,定位并打开get_msc_ver.sh文件第61行:

MSC_VER_RAW="16.00.40219.01"
  MSC_VER_MAJOR="16"
  MSC_VER_MINOR="0"
  MSC_VER_MICRO="40219"
  if [ "${MSC_VER_MAJOR}" -eq 14 -a "${MSC_VER_MINOR}" -eq 0 -a "${MSC_VER_MICRO}" -eq 30701 ] ;     then
    MSC_VER=1399
  else
    MSC_VER="1600"

关键就是那个MSC_VER=”1600”,这便是VS2010中编译器cl的版本号。

解决报错:CreateJars.gmk中的错误

执行完make clean后,重新执行make images,过了一会儿又报错了:

2017-12-18_205615

这个错误比较坑,在Google上搜索得到的结果又是换行符不同造成的血案。为此,需要修改两处class文件的换行标识。

用vi打开jdk\make目录下的CreateJars.gmk,定位到268行,相距不远处有两个$$换行符,将其转换为Windows下的换行符。方法是将光标定位到两个$$之前,按i切换到insert模式后,按Ctrl+C和Ctrl+M即可(感谢fh759208669兄指出,此处命令应该为Ctrl+V 与 Ctrl+M,并且经过他的实验,是ctrl+v 然后不用松开ctrl按m)。完成后按esc退出编辑模式,然后按:进入命令模式,输入wq保存并退出。

2017-12-18_235505

改完后,重新生成。稍作等待(去食堂吃了个饭回来)即完成了编译。

2017-12-18_205547

此时,进入jdk8u-dev\build\windows-x86-normal-client-slowdebug\images\j2sdk-image目录下即可看到生成的JVM文件。

2017-12-19_000200

在其bin下执行java -version结果如下:

2017-12-19_000303

使用VS2010生成HotSpot VM项目

  1. 将Cygwin的bin路径加入环境变量Path中,在我这里即C:\Cygwin\bin。

  2. 在dk8u-dev\hotspot\make\windows目录下开启一个新的命令提示符窗口,将vcvars32.bat拖到该窗口中,然后按回车键完成环境变量的设置。

  3. 然后执行set HOTSPOTMKSHOME=C:\cygwin64\bin 设置好HOTSPOTMKSHOME环境变量。

  4. 在该命令提示符窗口输入grep进行测试,如果该命令被执行了则可进行下面的操作了。】

上述过程如图所示:

2017-12-19_001029

接着执行:

create.bat C:\jvm\jdk8u-dev\build\windows-x86-normal-client-slowdebug\images\j2sdk-image  

其中后面的路径即是我们之前测试jvm时访问的路径。执行完毕后即会在jdk8u-dev\hotspot\build\vs-i486中生成jvm.vcxproj工程文件。用VS2010打开该工程文件即可。使用F7快捷键即可重新生成jvm.dll。

修改工程属性中的启动参数,传入要加载的类名,如下图传入的即为C盘下,名为CHello的类。

2017-12-19_001624

然后到jdk8u-dev\build\windows-x86-normal-client-slowdebug\images\j2sdk-image\jre\bin\client下,将java.diz解压到当前目录,其中包含了java.exe对应的符号表和映射文件,这样才能进行源码级调试。
20171221注:在VS的调试命令参数中,加入-XXaltjvm=$(TargetDir)参数,则默认使用VS2010编译得到的jvm.dll,由于jvm.pdb也在该目录下,所以可以直接调试,无需再进行任何解压操作了。另外加入的类路径最好放在最后。

在share\vm\runtime\stubRoutines.hpp中的call_stub函数处下断:

  static CallStub call_stub()                              
  { 
    return CAST_TO_FN_PTR(CallStub, _call_stub_entry); 
  }

F5运行后,程序被断下:

2017-12-19_003034

在Visual Studio强大的源码浏览与动态调试功能的帮助下,我们终于可以亲自一窥JVM的全貌了。

展开阅读全文

没有更多推荐了,返回首页