Tomcat debug模式啟動
SET JAVA_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n
SET CATALINA_OPTS=-server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8787
啟動tomcat後,在控制檯顯示以下內容表示配置成功:
Listening for transport dt_socket at address: 8787
JPDA簡介
顧名思義,JPDA為Java平臺上的偵錯程式定義了一個標準的體系結構。該體系結構包括3個主要組成部分:JVM TI、JDI和JDWP。
JVM TI的全稱是Java Virtual Machine Tool Interface,它定義了JVM為了支援除錯而必須提供的功能及相應的訪問介面。這些訪問介面是以本地語言的形式提供的,由JVM(比如Sun公司的HotSpot VM)負責實現。
不過,JVM TI只是JVM提供的一系列函式,偵錯程式(特別是遠端的偵錯程式)如何呼叫呢?其實啊,JVM TI的直接客戶端並不是偵錯程式,而是一個稱為“JPDA back-end”的東東。這個東東應該是屬於JVM的一部分,在SUN JRE的bin目錄下可以找到jdwp.dll(jdwp.so)的庫檔案,這就是JPDA back-end的實現。按我理解,JPDA back-end提供了各種訪問方式(共享記憶體,Socket),通過這些方式接收偵錯程式的請求,然後呼叫JVM TI介面。
JDI的全稱是Java Debug Interface,它定義了訪問JVM TI介面的高層API,以純Java語言提供,由JDK實現(在Sun JDK的tools.jar可以找到)。偵錯程式直接使用JDI來實現除錯的功能。與JPDA back-end相對應,JDI實現的角色就是JPDA front-end。
JDWP的全稱是Java Debug Wire Protocol,它定義了JPDA front-end和JPDA back-end之間通訊資訊的二進位制格式。這裡的通訊資訊主要包括兩種:偵錯程式傳送給JVM的請求資訊和JVM傳送給偵錯程式的除錯資訊。
總結一下,偵錯程式 呼叫JDK提供的JDI實現 (JPDA front-end),經由JDWP協議 ,和JVM自帶的JPDA back-end(jdwp.dll, jdwp.so, ...)進行通訊。JPDA back-end 通過呼叫JVM TI介面 ,從而獲知除錯資訊,或傳送控制命令。然後,JPDA back-end 將除錯資訊或命令執行結果,通過JDWP協議 ,返回給偵錯程式 。
如何啟用JPDA
預設情況下,JVM並沒有啟用JPDA back-end。需要在啟動JVM的命令列載入以下引數:
-Xdebug
-Xrunjdwp:transport=dt_socket, address=8000,server=y,suspend=y
-Xdebug
啟用除錯特性
-Xrunjdwp
啟用JDWP實現,它包含若干子選項:
transport=dt_socket
JPDA front-end和back-end之間的傳輸方法。dt_socket表示使用套接字傳輸。
address=8000
JVM在8000埠上監聽請求。
server=y
y表示啟動的JVM是被除錯者。如果為n,則表示啟動的JVM是偵錯程式。
suspend=y
y表示啟動的JVM會暫停等待,直到偵錯程式連線上。
suspend=y這個選項很重要。如果你想從Tomcat啟動的一開始就進行除錯,那麼就必須設定suspend=y。
Tomcat的啟動指令碼
只要Tomcat啟動時,啟用了JPDA,那麼就可以被除錯。而Tomcat預設是不啟用JPDA的,需要我們手動開啟。
開啟JPDA的方法也很簡單,對Tomcat的啟動指令碼做點修改,把啟動JPDA的命令列引數新增進去,就可以了。
不過Tomcat啟動指令碼還是有點複雜的,並不是簡單的”java ...“。我們先簡單的梳理一下。
在Tomcat 5.5.26發行版的bin目錄下,有N多指令碼,其中與Tomcat啟停有關的指令碼是(以Windows為例):
startup.bat //啟動Tomcat
shutdown.bat //停止Tomcat
catalina.bat //包含啟動/停止Tomcat的核心邏輯
startup.bat和shutdown.bat都是通過呼叫catalina.bat來實現啟動和停止的功能,因此他倆的程式碼都很少。主要的邏輯都在catalina.bat中。
catalina.bat是個強大的指令碼,通過引數,可以執行各種動作,包括debug run start stop version等。
如果我們直接執行catalina.bat,就可以看到它的用法說明:
startup.bat,其實就是執行catalina.bat start;shutdown.bat,其實就是執行catalina.bat stop。
不難猜到,我們可以寫一個jdpa.bat,直接呼叫catalina.bat jpda start,應該就可以啟用JPDA。我們拷貝一份startup.bat,把下面這行
call "%EXECUTABLE%" start %CMD_LINE_ARGS%
修改成
call "%EXECUTABLE%" jpda start %CMD_LINE_ARGS%
不過,-Xdebug -Xrunjdwp:transport=dt_socket, address=8000,server=y,suspend=y,這些選項引數怎麼傳遞給catalina.bat呢?
看看catalina.bat前面的註釋,server的值預設就是y,transport的值是變數JPDA_TRANSPORT,address的值是變數JPDA_ADDRESS,suspend的值是變數JPDA_SUSPEND 。如果沒有顯式地複製,這些變數的值預設是dt_shmem jdbconn n(預設值表示使用共享記憶體作為傳輸方式)。這些預設值不是我們需要的,Eclisep的遠端除錯目前只支援套接字傳輸。在呼叫catalina.bat jpda start之前,我們給這些變數設定恰當的值:
set JPDA_TRANSPORT=dt_socket
set JPDA_ADDRESS=8000
set JPDA_SUSPEND=y
call "%EXECUTABLE%" jpda start %CMD_LINE_ARGS%
然後,直接執行jpda.bat,Tomcat就執行在JPDA可調式模式下:
從上圖可以看出,Tomcat的JPDA使用套接字傳輸,監聽在8000埠。Tomcat的啟動已經暫停,只有偵錯程式連線上來,才會繼續啟動。
我把jpda.bat的完整內容列在下面,其中黑體部分,在startup.bat基礎上新增的程式碼:
@echo off
if "%OS%" == "Windows_NT" setlocal
rem ---------------------------------------------------------------------------
rem Jpda script for the CATALINA Server
rem
rem $Id: jpda.bat 302918 2004-05-27 18:25:11Z yoavs $
rem ---------------------------------------------------------------------------
rem Guess CATALINA_HOME if not defined
set CURRENT_DIR=%cd%
if not "%CATALINA_HOME%" == "" goto gotHome
set CATALINA_HOME=%CURRENT_DIR%
if exist "%CATALINA_HOME%/bin/catalina.bat" goto okHome
cd ..
set CATALINA_HOME=%cd%
cd %CURRENT_DIR%
:gotHome
if exist "%CATALINA_HOME%/bin/catalina.bat" goto okHome
echo The CATALINA_HOME environment variable is not defined correctly
echo This environment variable is needed to run this program
goto end
:okHome
set EXECUTABLE=%CATALINA_HOME%/bin/catalina.bat
rem Check that target executable exists
if exist "%EXECUTABLE%" goto okExec
echo Cannot find %EXECUTABLE%
echo This file is needed to run this program
goto end
:okExec
rem Get remaining unshifted command line arguments and save them in the
set CMD_LINE_ARGS=
:setArgs
if ""%1""=="""" goto doneSetArgs
set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
shift
goto setArgs
:doneSetArgs
set JPDA_TRANSPORT=dt_socket
set JPDA_ADDRESS=8000
set JPDA_SUSPEND=y
call "%EXECUTABLE%" jpda start %CMD_LINE_ARGS%
:end
在Eclipse中遠端除錯Tomcat
首先將Tomcat 5.5.26的原始碼分為container connectors jasper servletapi build五個專案,匯入到Eclipse中。啟動相關的程式碼主要在container中,就以它為當前專案,開啟”Debug Configurations“對話方塊。
然後建立一個”Remote Java Application“,Connection Type選擇”Standard (Socket Attach)“,Host填寫localhost(Tomcat所在的主機地址),Port填寫8000。最後點選”Apply“儲存。
首先確保已經執行了jpda.bat,Tomcat正在等待偵錯程式連線;然後執行上述的Debug Configuration,Eclipse就可以連上Tomcat。
Tomcat的啟動是從Bootstrap的main方法開始,我在第一行程式碼處設定了斷點,Tomcat的啟動就停在了這一行:
接著,讓Tomcat繼續執行,我們可以看到,控制檯輸出了啟動資訊。