編繹調試HotSpot JVM及在Eclipse裏調試
編繹整個OpenJDK要很久,而且有很多東西是不需要的。研究HotSpot的話,其實隻要下HotSpot部分的代碼就可以了。
下麵簡單記錄下編繹調試HotSpot一些步驟。
一、編繹
進入hotsopt的make目錄下:
cd code/cpp/openjdk/hotspot/make/
用make help可以看到有很多有用的信息。當然查看Makefile文件,裏麵也有很多有用的注釋。
make help會輸出當前的一些環境變量的設置,如果不對,自然編繹不過去。
設置環境變量:
unset JAVA_HOME export ARCH_DATA_MODEL=64 export JDK_IMPORT_PATH=/usr/lib/jvm/java-7-oracle export ALT_BOOTDIR=/usr/lib/jvm/java-7-oracle export ZIP_DEBUGINFO_FILES=0 //這個貌似不起作用。。這些變量的定義貌似都在def.make文件裏。還有一個這樣的參數:FULL_DEBUG_SYMBOLS用make all_beug來編繹。
編繹後好,到目錄下openjdk/hotspot/build/linux/linux_amd64_compiler2/jvmg,執行 unzip libjvm.diz,解壓得到調試信息文件。
這個算是個坑,默認情況下,會壓縮調試信息文件,這樣用gdb調試時,就會出現下麵的提示:
no debugging symbols found
從編繹的輸出信息來看,是有一個ZIP_DEBUGINFO_FILES的參數,但是設置了環境變量卻不起效。
二、調試
在openjdk/hotspot/build/linux/linux_amd64_compiler2/jvmg目錄下,有一個hotspot的腳本,隻要執行這個腳本,就可以啟動Java進程了。
用 ./hotspot -gdb 就會自動進入gdb調試,並停在main函數入口。
後麵還可以加一些jvm的啟動參數等。
這個./hotsopt 腳本是怎麼工作的?
用 sh -x ./hotspot 來查看這個腳本的執行過程,可以發現實際上是設置了 LD_LIBRARY_PATH的環境變量,再調用了一個./gamma 的程序。
+ LD_LIBRARY_PATH=/home/hengyunabc/code/cpp/openjdk/hotspot/build/linux/linux_amd64_compiler2/jvmg:/usr/lib/jvm/java-7-oracle/jre/lib/amd64 + export LD_LIBRARY_PATH + JPARMS= + LAUNCHER=/home/hengyunabc/code/cpp/openjdk/hotspot/build/linux/linux_amd64_compiler2/jvmg/gamma + [ ! -x /home/hengyunabc/code/cpp/openjdk/hotspot/build/linux/linux_amd64_compiler2/jvmg/gamma ] + GDBSRCDIR=/home/hengyunabc/code/cpp/openjdk/hotspot/build/linux/linux_amd64_compiler2/jvmg + cd /home/hengyunabc/code/cpp/openjdk/hotspot/build/linux/linux_amd64_compiler2/jvmg/../../.. + pwd + BASEDIR=/home/hengyunabc/code/cpp/openjdk/hotspot/build + LD_PRELOAD= exec /home/hengyunabc/code/cpp/openjdk/hotspot/build/linux/linux_amd64_compiler2/jvmg/gamma
實際上是通過設置LD_LIBRARY_PATH 環境變量,去優先加載編繹好的libjvm.so。hotsopt jvm的代碼都編繹鏈接在libjvm.so這個文件裏。
查看./hotspot裏的這個函數init_gdb,就知道它是怎麼啟動並設置好gdb的了:
init_gdb() { # Create a gdb script in case we should run inside gdb GDBSCR=/tmp/hsl.$$ rm -f $GDBSCR cat >>$GDBSCR <<EOF cd `pwd` handle SIGUSR1 nostop noprint handle SIGUSR2 nostop noprint set args $JPARMS file $LAUNCHER directory $GDBSRCDIR # 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 EOF }
所以,其實也可以這樣開始調試:
export LD_LIBRARY_PATH=/home/hengyunabc/code/cpp/openjdk/hotspot/build/linux/linux_amd64_compiler2/jvmg:/usr/lib/jvm/java-7-oracle/jre/lib/amd64
gdb
在gdb裏執行file ./gamma,然後就可以調試了。
三、使用Eclipse來調試
盡管gdb功能強大,命令豐富,但是在查看調試的變量時,十分的不方便。特別是hotsopt裏,很多東西都是用指針來存放的,有時要跳轉好幾層才能查看到想要的信息。
下載Eclipse的CDT版,或者安裝CDT的插件。
導入Eclipse工程:
"File", "Import", "C/C++", "Existing Code as Makefile Project":
先擇Linux GCC:
然後,就可以把項目導到Eclipse裏了。會有很多錯誤提示,但是不影響我們的調試。
在Eclipse裏調試:
首先,要設置要調試的文件的路徑:
設置LD_LIBRARY_PATH:
然後就可以調試了。還有一個地方比較重要:
想在運行時輸入gdb指令,可以在console view,在右邊的下拉裏,可以發現有一個gbd的console,還有一個gdb trace的console。
四、一些有用的東東
jvmg1目錄下是O1優化下的,fastdebug目錄下是O3優化的。
java 進程的main入口在:openjdk/hotspot/src/share/tools/launcher/java.c 文件裏。
在gdb裏,用info sharedlibrary 命令查看實際使用到的是哪些so文件。
用file命令來判斷一個可執行文件,so是32位的還是64位的。
查看一個so文件是否包含調試信息,可以用readelf -S xxx.so 命令來查看是否有debug相關的段。這個方法不一定準確,因為調試信息有可能放在外部文件裏。
最後更新:2017-04-03 12:53:42