jdk中gamma啟動器指令碼詳解
解析
本文是基於jdk1.6進行解析的,在HotSpot中存在兩種啟動器,一種是通用啟動器(java/javaw),另一種是除錯版啟動器(gamma).
在對openjdk編譯後,會在jvmg 目錄下生成hotSpot指令碼,這個啟動器入口位於hotspot/src/share/tools/luncher/java.c. 本文就來看一下該指令碼
#解析
該指令碼中首先是設定gdb,dbx,valgrind,emacs等引數.如下:
# # User changeable parameters ------------------------------------------------ # # This is the name of the gdb binary to use if [ ! "$GDB" ] then GDB=gdb fi # This is the name of the gdb binary to use if [ ! "$DBX" ] then DBX=dbx fi # This is the name of the Valgrind binary to use if [ ! "$VALGRIND" ] then VALGRIND=valgrind fi # This is the name of Emacs for running GUD EMACS=emacs
其中,gdb是linux環境中的除錯工具,可通過官方文件進行了解,dbx是uninx中的除錯工具,可通過該文件進行了解, valgrind是一款用於記憶體除錯、記憶體洩漏檢測以及效能分析的軟體開發工具。emacs則是著名的整合開發環境和文字編輯器。Emacs被公認為是最受專業程式設計師喜愛的程式碼編輯器之一,另外一個是vim。
接下來時獲得當前指令碼中和當前指令碼所在的目錄:
# Make sure the paths are fully specified, i.e. they must begin with /. SCRIPT=$(cd $(dirname $0) && pwd)/$(basename $0) RUNDIR=$(pwd)
這裡針對$dirname $0等做一些說明:
變數 | 含義 |
---|---|
$0 | 當前指令碼的檔名 |
$n | 傳遞給指令碼或函式的引數。n 是一個數字,表示第幾個引數。例如,第一個引數是$1,第二個引數是$2。 |
$# | 傳遞給指令碼或函式的引數個數。 |
$* | 傳遞給指令碼或函式的所有引數。 |
[email protected] | 傳遞給指令碼或函式的所有引數。被雙引號(" ")包含時,與 $* 稍有不同,下面將會講到。 |
$? | 上個命令的退出狀態,或函式的返回值。 |
$$ | 當前Shell程序ID。對於 Shell 指令碼,就是這些指令碼所在的程序ID。 |
$dirname | 取指定路徑所在的目錄 |
$basename | 去除目錄後剩下的名字 |
關於這部分的內容可以參考如下連結:
接下來,匹配執行模式,預設情況下是run,如下:
# Look whether the user wants to run inside gdb
case "$1" in
-gdb)
MODE=gdb
shift
;;
-gud)
MODE=gud
shift
;;
-dbx)
MODE=dbx
shift
;;
-valgrind)
MODE=valgrind
shift
;;
*)
MODE=run
;;
esac
接下來,是獲得該指令碼的目錄的絕對路徑
#Find out the absolute path to this script
MYDIR=$(cd $(dirname $SCRIPT) && pwd)
接下來時設定jdk:
JDK=
if [ "${ALT_JAVA_HOME}" = "" ]; then
source ${MYDIR}/jdkpath.sh
else
JDK=${ALT_JAVA_HOME%%/jre};
fi
if [ "${JDK}" = "" ]; then
echo Failed to find JDK. ALT_JAVA_HOME is not set or ./jdkpath.sh is empty or not found.
exit 1
fi
如果沒有設定ALT_JAVA_HOME環境變數的話,則執行${MYDIR}/jdkpath.sh 設定環境變數,否則,jdk指向ALT_JAVA_HOME/jre. 其中,jdkpath.sh的內容如下:
# Generated by /usr/local/openjdk-6/hotspot/make/linux/makefiles/buildtree.make
JDK=/usr/java/jdk1.6.0_45
該檔案中的內容依賴於你本機中編譯的情況
接下來設定JRE,JAVA,ARCH,MYDIR,SBP等變數:
# We will set the LD_LIBRARY_PATH as follows:
# o $JVMPATH (directory portion only)
# o $JRE/lib/$ARCH
# followed by the user's previous effective LD_LIBRARY_PATH, if
# any.
JRE=$JDK/jre
JAVA_HOME=$JDK
[email protected]@[email protected]@
# Find out the absolute path to this script
MYDIR=$(cd $(dirname $SCRIPT) && pwd)
SBP=${MYDIR}:${JRE}/lib/${ARCH}
設定LD_LIBRARY_PATH:
# Set up a suitable LD_LIBRARY_PATH
if [ -z "$LD_LIBRARY_PATH" ]
then
LD_LIBRARY_PATH="$SBP"
else
LD_LIBRARY_PATH="$SBP:$LD_LIBRARY_PATH"
fi
接下來,設定JAVA_HOME, LD_LIBRARY_PATH等環境變數:
export LD_LIBRARY_PATH
export JAVA_HOME
設定引數:
JPARMS="[email protected] $JAVA_ARGS";
因此,我們可以通過JAVA_ARGS環境變數來進行設定引數
獲取gamma偵錯程式:
# Locate the gamma development launcher
LAUNCHER=${MYDIR}/gamma
if [ ! -x $LAUNCHER ] ; then
echo Error: Cannot find the gamma development launcher \"$LAUNCHER\"
exit 1
fi
設定GDBSRCDIR, BASEDIR等變數:
GDBSRCDIR=$MYDIR
BASEDIR=$(cd $MYDIR/../../.. && pwd)
聲明瞭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
}
這裡做些解釋:
-
cat >>$GDBSCR <<EOF --> 是建立/tmp/hsl.當前Shell程序ID 的檔案,其內容是EOF之前的內容.
-
handle --> 在GDB中定義一個訊號處理。訊號可以以SIG開頭或不以 SIG開頭,可以用定義一個要處理訊號的範圍(如:SIGIO-SIGKILL,表示處理從SIGIO訊號到SIGKILL的訊號,其中包括SIGIO, SIGIOT,SIGKILL三個訊號),也可以使用關鍵字all來標明要處理所有的訊號。一旦被除錯的程式接收到訊號,執行程式馬上會被GDB停住,以 供除錯。
-
nostop --> 當被除錯的程式收到訊號時,GDB不會停住程式的執行,但會打出訊息告訴你收到這種訊號。
-
noprint -->當被除錯的程式收到訊號時,GDB不會告訴你收到訊號的資訊。
-
set args --> 指定執行時引數。
-
file --> 設定要執行的程式
-
directory --> 設定原始檔的目錄
-
break InitializeJVM --> 在InitializeJVM函式入口處設定斷點
-
delete 1 --> 刪除斷點1
關於gdb的使用可以引數如下連結:
接下來,就會針對不同的模式進行處理:
case "$MODE" in
gdb)
init_gdb
$GDB -x $GDBSCR
rm -f $GDBSCR
;;
gud)
init_gdb
# First find out what emacs version we're using, so that we can
# use the new pretty GDB mode if emacs -version >= 22.1
case $($EMACS -version 2> /dev/null) in
*GNU\ Emacs\ 2[23]*)
emacs_gud_cmd="gdba"
emacs_gud_args="--annotate=3"
;;
*)
emacs_gud_cmd="gdb"
emacs_gud_args=
;;
esac
$EMACS --eval "($emacs_gud_cmd \"$GDB $emacs_gud_args -x $GDBSCR\")";
rm -f $GDBSCR
;;
dbx)
$DBX -s $MYDIR/.dbxrc $LAUNCHER $JPARAMS
;;
valgrind)
echo Warning: Defaulting to 16Mb heap to make Valgrind run faster, use -Xmx for larger heap
echo
$VALGRIND --tool=memcheck --leak-check=yes --num-callers=50 $LAUNCHER -Xmx16m $JPARMS
;;
run)
LD_PRELOAD=$PRELOADING exec $LAUNCHER $JPARMS
;;
*)
echo Error: Internal error, unknown launch mode \"$MODE\"
exit 1
;;
esac
RETVAL=$?
exit $RETVAL
如果是gdb模式的話,則會建立/tmp/hsl.當前Shell程序ID 的檔案,然後執行 gdb -x $GDBSCR 命令進行除錯,除錯結束後,刪除臨時檔案.
這裡對–annotate=3(emacs中的gdb引數)做如下解釋:
- annotate = 0是最基本的模式和在命令列使用gdb完全一樣
- annotate = 1是單步除錯模式,出現上下兩個視窗,上面是gdb執行的buffer,下面是你程式碼的buffer,會在程式碼 buffer中,同步指示當前執行的語句的位置.
- annotate = 2是產生註解的模式。
- annotate = 3是資訊最完整的模式。此時的 Emacs 分5個 buffer,從上到下、從左到右依次是:gdb 除錯視窗、變數實時變化顯示視窗、原始碼視窗、棧視窗、斷點資訊.
關於這部分內容,可以參考: