Eclipse遠端偵錯Tomcat

來源:互聯網
上載者:User

http://blog.csdn.net/jarfield/article/details/5250915

最近,一直在研究Tomcat的工作內幕,主要的方法就是參考《How Tomcat Works》 這本書和Tomcat 5.5.26的原始碼。

 

Tomcat的代碼結構還是比較清晰的,注釋也比較全。但是代碼畢竟是靜態,難以徹底弄清類與類之間的協作關係,以及運行時對象的互動關係。

 

如果能對Tomcat的啟動、處理請求和停止的過程進行斷點調試,看清Tomcat的每一步行蹤,那麼就能解決上面的問題了。

 

於是,又一個問題出來了:如何使用Eclipse遠端偵錯Tomcat ?

 

上網查了一些資料,相關的文章還是很多的。我簡單梳理了一下解決方案及原理,順便熟悉了Tomcat的啟動指令碼。

 

如何遠端偵錯JVM?

遠端偵錯Tomcat,本質上就是遠端偵錯JVM。倒不是需要瞭解JVM自身的運行細節,而是要瞭解JVM上應用程式的運行細節。

無論如何,我們都要擷取JVM運行時的內部資訊(比如查看調試資訊),並對JVM的運行流程進行控制(比如逐步執行),才能達到調試的目的。

 

這個事情光靠調試器本身,肯定是做不到的。不然,JVM的安全性就大打折扣了。除非JVM提供某種“後門”,供調試器查詢一些運行時資訊,並允許調試器發送一些控制命令。

 

不得不感慨,JVM的強大。從J2SE 1.4.2開始,就已經提出並實現了JavaTM Platform Debugger Architecture ,簡稱JPDA。

 

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繼續執行,我們可以看到,控制台輸出了啟動資訊。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.