美文网首页JVMJVM深度解析
mac下编译openjdk1.9及集成clion动态调试

mac下编译openjdk1.9及集成clion动态调试

作者: 简书闪电侠 | 来源:发表于2017-12-27 13:28 被阅读2745次

    晚上被小伙伴问道如何使用ide进行jvm源码的调试,刚好前段时间花了点时间折腾了一下,mac最新版本下jvm9顺利编译通过,并且可以完美集成clion进行调试(支持windows),下面记录一下全过程,如果想看效果的话,可以直接拉到集成到clion进行调试小节末尾

    mac下openjdk源码编译过程

    准备编译环境准备

    我的mac的版本如下

    image.png

    由于openjdk1.9之前的版本对mac下编译支持得不是很流畅,所以这篇文章选择openjdk1.9

    编译之前,首先你需要准备 homebrew,homwbrew是mac下的包管理器,如果你的mac上没有安装,可以按照下面的方式来安装

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

    homwbrew下载完成之后,接下来准备编译环境

    • 首先安装openjdk的版本管理工具mercurial
    • 然后安装ccache和freetype,ccache用来加速编译,freetype在编译过程也会依赖到

    上述准备编译环境的脚本为

    brew install mercurial
    brew install ccache
    brew install freetype
    

    请确保上述三个依赖安装成功再进行下面的步骤

    源码获取

    环境准备好之后,接下来获取源码,我这里工作目录是 ~/jvm,建议你也建一个此目录

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

    这条命令运行之后,openjdk的源码并没有下载下来,我们随后进入到~/jvm/jdk9目录,会发现有一个 get_source.sh 的文件
    调用这个脚本下载完整的源码之前,需要做一下简单的修改,不然可能你在下载源码的过程中会数次中断

    get_source.sh文件最后几行的内容如下

    # 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
    

    然后,愉快地调用

    bash ./get_source.sh
    

    友情提醒:这个过程可能要持续1~2小时,请提前点好外卖

    开始编译

    源码下载完之后,我们开始编译,我们先进行编译前的配置

    ./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,需要替换成本机实际安装的版本

    执行完之后,记下来会进行一系列的配置,这个过程时间要短很多,最后,如果出现如下的提示,那么恭喜你,接下来就可以执行编译了

    image.png

    接下来调用下面的脚本进行编译

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

    编译完成之后,如果没有出现错误提示,那么再次恭喜你,你的第一个自行编译的openjdk版本已经顺利通过了,可以考虑打个赏来庆祝一下快乐的心情

    最后,验证一下

    image.png

    编译过程中遇到的问题

    我比较幸运,在编译过程中就只遇到过三个空指针转换的问题

    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了一把,根据这篇文章,找到对应的源码文件的位置,把0强制转换成同一种类型
    比如下面这个

    image.png

    好在一共只有三个地方,我都给你列出来了

    #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进行调试

    千呼万唤试出来,终于来到本小节的内容,相信很多小伙伴更想知道jvm源码编译完之后,如何在本机进行流畅地调试阅读源码,在这之前,我发现网上这方面的资料少得可怜,所以自己慢慢摸索出来,分享给大家

    使用clion载入源码

    首先,我们打开clion,选择 File->ImportProject,选择到 ~/jvm/jdk9/hotspot作为jvm源码的根目录,这里导入的过程无脑点击next即可

    项目导入之后,clion会给你默认建立一个 CMakeLists.txt文件,这里可以不用管他,让他建,完成之后,clion会做大量的索引,索引建立完成之后,项目导入到此结束,界面如下

    image.png

    很多小伙伴遇到clion导入源码之后遇到头文件找不到的问题,而实际上这些头文件在源码里面是存在的,只不过在某些源文件里面是以相对路径的方式来搜索,可以在CMakeLists.txt里面添加一些根路径,我添加了如下根路经

    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)
    

    另外,如果某些头文件依然找不到,可以手工导入,然后把导入的头文件加到
    hotspot/src/share/vm/precompiled/precompiled.hpp里,因为大多数源文件都会包含这个源文件,加到这个头文件,可以保证在能够引入缺失头文件的同时在debug的时候行数不会串掉,我在读源码的过程中添加了如下几个头文件

    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>
    

    构建调试环境

    到了这里,源码的阅读环境才刚刚开始,没有debug,总觉得看起代码来不真实,于是,接下来我们继续构建自己的调试环境

    image.png

    右上角,我们点击Edit Configuration,进入到下面这个界面

    image.png

    我们创建一个调试环境


    image.png

    Executable选择编译好的二进制 java文件,然后在Before launch...选项中,干掉Build,就是说我们在debug的时候不需要再build,再说了,这里你也build不起来。
    到了这里,一个可调试的jvm源码调试环境已经准备完毕了,下面我们来调试一把看看效果

    我们来到 jni.cpp,在 JNI_CreateJavaVM_inner 这个方法上打个断点,点击右上角的debug,神奇的一幕出现了

    image.png

    方法调用栈,当前方法上下文环境,调试所需要的东西应有尽有,如果上面这个画面不够打动你的话,那么下面一张实际源码阅读过程中的动图呢?


    3.gif

    与java程序联合调试

    我们想要修改jvm源码,修改完之后想要通过修改完的源码来运行我们的指定的java程序,由于clion默认的build工具无法build jvm,我们只能借助于make命令。
    clion在debug的时候可以添加两个前置处理器,如下图

    image.png
    第一个前置处理器用于build jvm,第二个前置处理器用于compile java文件,这样,当你修改了点jvm源码,并且修改了java文件之后,在点击右上角的debug文件的时候,clion就会默认先执行jvm的编译,然后再进行java文件的编译,编译完之后就在 /Users/yuchao/IdeaProjects/jvm/src(我们在此目录下放置java源文件) 目录下生成 一个Main.class文件,然后就可以使用编译过后的jvm来运行,下面是构建jvm和编译java文件的两个external tool image.png image.png image.png image.png

    最后,我们来看下,修改了jvm源码之后的效果


    image.png

    当然,你也可以在你的java ide中,把jdk选择自己编译的jdk,再执行,也是一样的效果


    image.png

    [参考资料]
    https://segmentfault.com/a/1190000008346240
    https://liuzhengyang.github.io/2017/04/28/buildopenjdk/
    https://www.jianshu.com/p/746963f28245
    https://bugs.openjdk.java.net/browse/JDK-8187787

    相关文章

      网友评论

        本文标题:mac下编译openjdk1.9及集成clion动态调试

        本文链接:https://www.haomeiwen.com/subject/zkhugxtx.html