在無法訪問運行中的執行個體時,調試一個Java程式可能相當麻煩;當應用程式在遠程環境下運行,並且不會在控制台或記錄檔中輸出任何結果時,調試工作變得更加困難。如果你需要對一個運行中的Java應用程式進行全方位調試,Sun的Java平台調試構架(JPDA)可為您提供協助。
JPDA是一組API集合,旨在協助你調試Java代碼。J2SE自1.2.2版開始推出JPDA工具集,並在1.3.x版中將它直接包含在J2SE軟體包中。
JPDA並非一個應用程式或調試工具,而是一組精心設計的介面與協議,瞭解這點很重要。Sun設計這一標準的目的是提供一個基礎構架,以便第三方工具和調整器能夠高效利用它。還有許多利用JPDA的優秀調試器和IDE,包括一些獲得廣泛認可的工具,如Borland JBuilder、Oracle JDeveloper、IntelliJ IDEA、Sun NetBeans、ibm Eclipse等等。不過,Sun在它的傳統命令列式調試器jdb中提供了一個參考執行個體。Java 1.3重新編寫了jdb以支援JPDA。在本文中,我將討論JPDA技術及它的一些實際應用。
工作原理
JPDA由三個介面構成,這些介面為案頭系統的開發環境而設計。Java虛擬機器工具介面(JVMTI)定義虛擬機器(VM)在調試時必須提供的服務。(在Java 5.0中,JVMTI替代已被刪除的Java虛擬機器調試介面)。Java調試線協議(JDWP)定義在調試過程和調試器前端之間傳輸的資訊和請求的格式。它執行Java調試介面(JDI)。JDI定義使用者代碼級資訊和請求。
JPDA概念將調試過程分為兩部分:被調試的程式(被調試者-debuggee)和JDI。JDI一般為一個調試應用程式的使用者介面(或Java IDE的一部分)。被調試的應用程式在後端運行,而JDI在前端運行。在前端與後端之間有一個通訊通道運行JDWP協議;因此,被偵錯工具與調試器可以位於同一個系統內,也可位於不同的系統中。
從開發人員的角度,一個調試應用程式可進入任何JPDA層面。因為JDI是最高層,也最容易使用,我們推薦使用這個介面。假設一家公司用JDI開發了一個調試器。公司能夠把它用於參考執行個體中,它將自動與VM和Sun支援的平台協同工作,因此大多數IDE供應商採用這種方式。還可以這樣,例如,參考執行個體在前端運行,被調試者運行另一家公司執行JDWP(它可能運行或忽略JVMTI)的VM。
一些調試器可能建立在較低層面之上,如JDWP(例如,如果Java沒有編寫前端)或JVMTI(針對需要低級功能的專用調試器)。
調試器的後端負責由調試器前端向被調試者VM傳輸請求,如“告訴我變數X的值”;它還負責向前端傳輸對這些請求(包括像到達斷點之類的預計事件)的響應。後端與前端利用JDWP通過一個通訊通道進行通訊。後端與被調試者VM利用JVMTI進行通訊。
通訊通道串連調試器的前端與後端。可以認為它由兩個裝置組成:一個連接器和一個傳送器。連接器是一個JDI對象,它在前端與後端建立串連;可能有三種類型的連接器:
- 收聽型:前端從後端收聽一個進入的串連。
- 依附型:前端依附到一個已啟動並執行後端上。
- 發布型:前端發布運行被調試者代碼和後端的Java過程。
傳送器是在前端和後端傳輸資訊的基本裝置。在JPDA規範中沒有指定必須使用的傳送器裝置。可能的裝置包括:通訊端、串列線路和共用記憶體。但是,JDWP指定了流經通道的連續化位流的格式與語義。許多IDE和調試器都支援兩種類型的傳送器(Sun的參考執行個體就是如此):共用記憶體(如果被調試者和調試器位於同一系統)和通訊端(被調試者和調試器可位於任何地方,包括同一系統)。
從J2SE 5.0開始,JPDA包括了服務提供器介面,允許對連接器與傳送器執行個體進行開發與配置。這些服務提供器服務介面允許調試器和其它工具供應商開發新的連接器執行個體,並提供除Sun的通訊端和共用記憶體以外的其它傳送器裝置。
被調試者與調試器之間的通訊以串連為導向。因此,一方必須作為伺服器,收聽一個串連;另一方作為一個用戶端串連到伺服器。JPDA允許以調試應用程式和目標VM為伺服器。
JPDA實際應用
如果你需要使用通訊端傳送器,在對應的JVM中以dt_socket為名確定自變數的類型。如果被調試者和調試器位於同一機器之中,且啟動並執行是Windows系統,你可以使用名為dt_schmem的共用記憶體連接器。如果你希望用一個與JPDA相容的調試器調試應用程式,你應在偵錯模式下運行調試器,並提交其它參數,如傳送器類型、主機名稱、連接埠號碼及其它資訊。所有JPDA和調試參數必須在啟動應用程式時作為自變數提交。
要進行調試,你必須將調試JDWP代理載入到應用程式的JVM中。從Java 5.0開始,你可以用-agentlib:jdwp選項來完成載入。5.0以前版本則使用-Xdebug和-Xrunjdwp選項(5.0也支援-Xdebug和-Xrunjdwp選項,不過新的-agentlib:jdwp選項更加好用。因為5.0中的JDWP代理使用JVMTI介面串連VM,而非舊的JVMDI介面)。你應該向-agentlib:jdwp(Java 5.0中)或-Xrunjdwp(Java 5.0以前版本) 參數提供子選項;兩組可能的子選項相同。
以下列方式指定子選項:
-agentlib:jdwp=<name1>[=<value1>],<name2>[=<value2>]...
或
-Xrunjdwp:<name1>[=<value1>],<name2>[=<value2>]...
你可以使用這些選項:
- help:列印如何應用它的簡單資訊,並退出VM。
- server:(是”y”或否”n”):如“server=y”,收到一個要依附的調試應用程式;如“server=n”,依附到指定地址的調試應用程式。
- address:串連傳送地址。如果server=n,嘗試依附到這個地址的調試應用程式;如server=y,收到這個地址的串連。
- timeout:如果server=y,它以毫秒為單位指定等待調試器依附的時間;如server=n,它以毫秒為單位指定依附到調試器所用的時間。
- suspend:如“是”,JVM延緩執行,直到調試器與被調試JVM建立串連。
以下是命令列執行個體:
-agentlib:jdwp=transport=dt_socket,server=y,address=8000
在連接埠8000收聽一個通訊端串連。在主類載入前延緩這個VM(預設suspend=y)。一旦串連上調試應用程式,它發送一個JDWP命令恢複VM。
-agentlib:jdwp=transport=dt_shmem,server=y,suspend=n
選擇一個有效共用記憶體傳輸地址並將它列印出來。在那個地址收聽一個共用記憶體串連。在調試應用程式依附之前,允許VM開始執行。
-agentlib:jdwp=transport=dt_socket,address=myhost:8000
通過myhost主機連接埠8000的通訊端依附到一個啟動並執行調試應用程式。在主類載入前延緩這個VM。