1. 程式人生 > >jdk中gamma啟動器指令碼詳解

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
}

這裡做些解釋:

  1. cat >>$GDBSCR <<EOF --> 是建立/tmp/hsl.當前Shell程序ID 的檔案,其內容是EOF之前的內容.

  2. handle --> 在GDB中定義一個訊號處理。訊號可以以SIG開頭或不以 SIG開頭,可以用定義一個要處理訊號的範圍(如:SIGIO-SIGKILL,表示處理從SIGIO訊號到SIGKILL的訊號,其中包括SIGIO, SIGIOT,SIGKILL三個訊號),也可以使用關鍵字all來標明要處理所有的訊號。一旦被除錯的程式接收到訊號,執行程式馬上會被GDB停住,以 供除錯。

  3. nostop --> 當被除錯的程式收到訊號時,GDB不會停住程式的執行,但會打出訊息告訴你收到這種訊號。

  4. noprint -->當被除錯的程式收到訊號時,GDB不會告訴你收到訊號的資訊。

  5. set args --> 指定執行時引數。

  6. file --> 設定要執行的程式

  7. directory --> 設定原始檔的目錄

  8. break InitializeJVM --> 在InitializeJVM函式入口處設定斷點

  9. 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 除錯視窗、變數實時變化顯示視窗、原始碼視窗、棧視窗、斷點資訊.

關於這部分內容,可以參考: