Чтобы проанализировать исходный код JVM, это способ прочитать его непосредственно с данными, но если вы столкнулись с какими-то непонятными сценариями, вы должны совместить отладку, проверить путь выполнения и конкретные значения параметров, чтобы чтобы понять. Итак, давайте сначала скомпилируем исходный код JVM и сможем использовать GDB для отладки.
Среда компилятора
Версия JDK, используемая в этой статье: OpenJDK7, страница загрузки ветки b147: https://download.java.net/openjdk/jdk7 Адрес загрузки: http://download.java.net/openjdk/jdk7/promoted/b147/openjdk- 7 -fcs-src-b147-27jun2011.zipMD5: c284c89a104f64a95afde3a96138ef0f
Другие экологические примечания:
- CentOS 7.4 64-разрядная версия
- gcc version 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC)
- GNU Make 3.82
Установить зависимости
yum -y install gcc gcc-c++ make
yum -y install alsa-lib-devel
yum -y install cups-devel
yum -y install libX*
yum -y install gcc gcc-c++
yum -y install libstdc++-static
wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum.repos.d/epel-apache-maven.repo
yum -y install ant
Для компиляции JVM необходимо использовать более раннюю версию JDK, например, если мы компилируем 7, необходимо установить OracleJDK6: Адрес для скачивания: https://www.oracle.com/java/technologies/javase-java- архив-javase6 -downloads.htmlhttp://gcdncs.101.com/v0.1/static/test_mzb/jdk-6u38-linux-x64-rpm.binЗагрузить: jdk-6u38-linux-x64-rpm.bin
$ sh jdk-6u38-linux-x64-rpm.bin
$ sudo rpm -ivh jdk-6u38-linux-amd64.rpm
писать скрипты компиляции
Разархивируйте openjdk:
unzip openjdk-7-fcs-src-b147-27_jun_2011.zip
Добавьте скрипт build.sh в каталог openjdk:
#!/bin/bash
export LANG=C
#将一下两项设置为你的BootstrapJDK安装目录
export ALT_BOOTDIR=/usr/java/jdk1.6.0_38
export ALT_JDK_IMPORT_PATH=/usr/java/jdk1.6.0_38
#允许自动下载依赖包
export ALLOW_DOWNLOADS=true
#使用预编译头文件,以提升便以速度
export USE_PRECOMPILED_HEADER=true
#要编译的内容,我只选择了LANGTOOLS、HOTSPOT以及JDK
export BUILD_LANGTOOLS=true
export BUILD_JAXP=false
export BUILD_JAXWS=false
export BUILD_CORBA=false
export BUILD_HOSTPOT=true
export BUILD_JDK=true
#要编译的版本
export SKIP_DEBUG_BUILD=false
export SKIP_FASTDEBUG_BUILD=true
export DEBUG_NAME=debug
#避免javaws和浏览器Java插件等的build
BUILD_DEPLOY=false
#不build安装包
BUILD_INSTALL=false
#包含全部的调试信息
export ENABLE_FULL_DEBUG_SYMBOLS=1
#调试信息是否压缩,如果配置为1,libjvm.debuginfo会被压缩成libjvm.diz,将不能被debug。
export ZIP_DEBUGINFO_FILES=0
#用于编译线程数
export HOTSPOT_BUILD_JOBS=3
#设置存放编译结果的目录
#export ALT_OUTPUTDIR=/root/jvm/output
unset CLASSPATH
unset JAVA_HOME
make sanity
DEBUG_BINARIES=true make 2>&1
Затем выполните sh build.sh для компиляции. Если вы столкнулись с проблемами в процессе компиляции, вы можете обратиться к разделу ниже, чтобы решить проблемы компиляции.
Запуск точки доступа
После успешной компиляции скомпилированный вывод по умолчанию имеет видopenjdk/buildПод содержанием. Результат компиляции HotSpot находится вopenjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmgПод содержанием.
Выполните тест, используя команду, предоставленную HotSpot:
cd build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg
# test_gamma是HotSpot提供的一个测试程序,可以成功执行说明编译成功
./test_gamma
# 使用hotspot脚本进行GDB调试
./hotspot -gdb HelloWorld
./hotspotЭто скрипт.Глядя на код, вы можете увидеть, что он делает несколько простых вещей, в основном устанавливая переменные среды:
JAVA_HOME=/usr/java/jdk1.6.0_38
LD_LIBRARY_PATH=/root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg:/usr/java/jdk1.6.0_38/jre/lib/amd64
Затем сгенерируйте сценарий параметров GDB и запустите команду GDB:
gdb -x /tmp/hsl.26037
/tmp/hsl.26037 — это параметр gdb, сгенерированный скриптом, мы можем видеть, что установлено:
cd /root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg
handle SIGUSR1 nostop noprint
handle SIGUSR2 nostop noprint
set args HelloWorld
file /root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg/gamma
directory /root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg
# Get us to a point where we can set breakpoints in libjvm.so
break InitializeJVM
run
# Stop in InitializeJVM
delete 1
# We can now set breakpoints wherever we like
Видно, что задан каталог исходного кода и установлена точка останова по умолчанию. Проанализировав сценарий хотспота, мы можем запустить хотспота и gdb самым примитивным способом, чтобы выполнить более сложные требования по отладке, такие как удаленная отладка.
Метод прямого исполнения:
JAVA_HOME=/usr/java/jdk1.6.0_38 LD_LIBRARY_PATH="/root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg:/usr/java/jdk1.6.0_38/jre/lib/amd64" ./gamma HelloWorld
Отладка ГБД:
JAVA_HOME=/usr/java/jdk1.6.0_38 LD_LIBRARY_PATH="/root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg:/usr/java/jdk1.6.0_38/jre/lib/amd64" gdb ./gamma HelloWorld
Удаленная отладка GDB:
JAVA_HOME=/usr/java/jdk1.6.0_38 LD_LIBRARY_PATH="/root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg:/usr/java/jdk1.6.0_38/jre/lib/amd64" gdbserver :8011 ./gamma HelloWorld
Советы по отладке кода точки доступа
Использование и навыки GDB здесь обсуждаться не будут.Я сам столкнулся с проблемами и теперь проверяю информацию.Вот несколько часто используемых навыков отладки, связанных с HotSpot.
Как напечатать строку, соответствующую объекту внутреннего символа HotSpot Symbol?
Символ — очень распространенный класс.Все строки, соответствующие ссылкам на символы, будут представлены символом, например, имя класса, имя метода, сигнатура метода и т. д. Вы можете использовать следующий метод для вывода соответствующей строки символа:
p *name._body@name._length
Как напечатать имя класса, соответствующее KlassHandle?
p Klass::cast(current_klass.obj())->external_name()
Добавить точку останова при загрузке определенного класса
break ClassFileParser::parseClassFile if strncmp(class_name._body, "XXX", 3) == 0
break SystemDictionary::load_instance_class if strncmp(class_name._body, "XXX", 3) == 0
возникшие проблемы
BootstrapJDK一开始设置为JDK8会失败,要改为JDK6
#错误
echo "*** This OS is not supported:" `uname -a`; exit 1;
#解决
sudo vim openjdk/hotspot/make/linux/Makefile
注释掉以下三行
238 #ifeq ($(DISABLE_HOTSPOT_OS_VERSION_CHECK)$(EMPTY_IF_NOT_SUPPORTED),)
239 # $(QUIETLY) >&2 echo "*** This OS is not supported:" `uname -a`; exit 1;
240 #endif
#错误
error:"__LEAF"redefined [-Werror]
#解决
ubuntu12的glibc比较新,在linux的头文件cdefs.h里,有个__LEAF的宏,
这个和hotspot/src/share/vm/runtime/interfaceSupport.hpp
这个头文件中的宏定义有冲突,我们在428行下面增加一个#undef __LEAF如下:
428 // LEAF routines do not lock, GC or throw exceptions
#ifdef __LEAF
#undef __LEAF
#define __LEAF(result_type, header) \
TRACE_CALL(result_type, header) \
debug_only(NoHandleMark __hm;) \
/* begin of body */
#endif
#错误
Error:/openjdk/hotspot/src/share/vm/oops/constantPoolOop.cpp:272:39:
error: converting 'false' to pointer type 'methodOop' [-Werror=conversion-null]
#解决
vi hotspot/src/share/vm/oops/constantPoolOop.cpp
将272行 return false 改为 return NULL
#错误
/usr/openjdk/hotspot/src/share/vm/opto/loopnode.cpp:896:49:
error: converting 'false' to pointer type 'Node*' [-Werror=conversion-null]
#解决
vi hotspot/src/share/vm/opto/loopnode.cpp
将896行 return false 改为 return NULL
#错误
Using java runtime at: /usr/lib/jvm/java-1.6.0/jre
./gamma: relocation error: /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.41.x86_64/jre/lib/amd64/libjava.so: symbol JVM_FindClassFromCaller, version SUNWprivate_1.1 not defined in file libjvm.so with link time reference
#解决
这里是有一个坑的,为了避免大家踩坑,请提前安装好Oracle JDK 1.6。
# ALT_BOOTDIR 用到的是 OpenJDK 1.6.0 会有此报错, OpenJDK 的bug,需要使用 Oracle JDK
# 见到类似下方的报错了,恭喜童鞋您入坑了
# ./gamma: relocation error: /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.41.x86_64/jre/lib/amd64/libjava.so: symbol JVM_FindClassFromCaller, version SUNWprivate_1.1 not defined in file libjvm.so with link time reference
下载传送门可能需要登陆Oracle,没有帐号的童鞋请注册一下。笔者下载安装的是jdk-6u38-linux-x64-rpm.bin。
如果上一个传送门失效,请继续传送!找到此页面上的Java SE 6进入传送哦~如果这个传送也失效了(T_T),那接着传送,拉到页面最下方,找到Java Archive栏,点击右侧DOWNLOAD按钮自行传送。再不行就只能找baidu了~~~
# 对下载到的bin动动手脚(不要想多,释放里面的rpm包而已)
$ sh jdk-6u38-linux-x64-rpm.bin
# 查看下得到的rpm包
$ ll *.rpm
# 安装Oracle JDK
$ sudo rpm -ivh jdk-6u38-linux-amd64.rpm
# OK 至此已完成Oracle JDK安装
# 查找安装的Oracle JDK目录
# 查找jdk安装名称
$ rpm -qa | grep ^jdk-1.6.0
jdk-1.6.0_38-fcs.x86_64
# 根据安装名称查找安装到本地的文件列表
$ rpm -ql jdk-1.6.0_38-fcs.x86_64
...
/usr/java/jdk1.6.0_38 # Oracle JDK HOME
...
# 以上查找到的目录后面会用到
错误:
gcc: error: unrecognized command line option '-mimpure-text'
解决:
vi jdk/make/common/shared/Compiler-gcc.gmk
在70行remove the command "-mimpure-text" in the code:
错误:
Error: time is more than 10 years from present: 1136059200000
解决:
# 修改以下文件,将日期改为十年以内,JDK的Bug。
vi jdk/src/share/classes/java/util/CurrencyData.properties
# line: 108 377 439 529 555
错误:
../../../src/share/classes/sun/management/jmxremote/ConnectorBootstrap.java:661: error: no suitable constructor found for SslRMIServerSocketFactory(SSLContext,String[],String[],boolean)
解决:
vi ./jdk/src/share/classes/sun/management/jmxremote/ConnectorBootstrap.java
注释掉662行的参数
материал
- "Глубокое понимание виртуальной машины Java"
- Компиляция и отладка openjdk7 | YDDMAX https://yddmax.github.io/2017/06/11/openjdk7%E4%B9%8B%E7%BC%96%E8%AF%91%E5%92%8Cdebug/
- Centos7 compile openjdk7 FAQ - clover 灬 - OSCHINA https://my.oschina.net/zhangdq/blog/2250314
- How to build and package OpenJDK 7 on Linux · hgomez/obuildfactory Wiki https://github.com/hgomez/obuildfactory/wiki/How-to-build-and-package-OpenJDK-7-on-Linux
- Ubuntu16.04 компилирует JDK7 - краткая книга https://www.jianshu.com/p/32dc1a850e23
- Сборка OpenJDK7 с CentOS7 — Rtfsc8 http://blog.rtfsc8.top/2018/07/07/building-openjdk7-with-centos7/
- Компиляция исходного кода OpenJDK8 в CentOS и отладка исходного кода виртуальной машины HotSpot в eclipse — блог tjiyu — блог CSDN
Независимый адрес блога этой статьи:Анализ исходного кода JVM — компиляция и отладка исходного кода JVM | Блог Mushan