本文主要基于一篇英文原作翻譯而成,刪減部分無用文字,添加了必要的注解和補充。
*英文原文是一篇對遠程調(diào)試講解很通俗的博文: *
http://blog.trifork.com/2014/07/14/how-to-remotely-debug-application-running-on-tomcat-from-within-intellij-idea/
前言
這篇文章將研究如何處理和調(diào)試那些只發(fā)生在生產(chǎn)環(huán)境(或其他遠程環(huán)境)而本地開發(fā)環(huán)境可能沒辦法重現(xiàn)的“問題”。任何碰到過這種情況的人都不得不承認,試圖定位這種“問題”原因的過程,很大可能性是以一堆胡亂猜測而告終:一個非常耗時且低效的過程。
還有一種場景, 你得到了一個可以部署的war/jar包,只有class沒有java源代碼,而應(yīng)用部署在本地/遠程后,是否可以調(diào)試? 讀完本文就明白怎么做了。
配置
遠程調(diào)試包括兩個步驟:
- 啟動Tomcat啟用遠程調(diào)試
- 用 IDE (這里用IntelliJ IDEA)要能夠調(diào)試遠程Tomcat應(yīng)用
Tomcat啟用遠程調(diào)試
這里有多種方法可以做到,根據(jù)tomcat所運行的操作系統(tǒng)而有些微的不同。但是不管用哪種方法,這些配置的背后都做了同一件事:傳遞特定的啟動參數(shù)給 JVM,讓它啟用遠程調(diào)試(remote debugging)。
JVM 激活遠程調(diào)試的啟動參數(shù)有 JPDA_OPTS, CATALINA_OPTS 和 JAVA_OPTS。其中 JAVA_OPTS 是通常不建議使用的, 因為基于 JAVA_OPTS 的參數(shù)設(shè)定會暴露給所有的 JVM 應(yīng)用, 而 CATALINA_OPTS 定義的設(shè)定值限制在Tomcat 內(nèi)。
1 使用JPDA_OPTS
在 CATALINA_HOME/bin 目錄下創(chuàng)建可執(zhí)行腳本文件 setenv.sh ( Windows 創(chuàng)建 setenv.bat ),加入內(nèi)容:
Linux setenv.sh
export JPDA_OPTS="-agentlib:jdwp=transport=dt_socket,address=1043,server=y,suspend=n"
Windows setenv.bat
set JPDA_OPTS="-agentlib:jdwp=transport=dt_socket,address=1043,server=y,suspend=n"
這些參數(shù)要做的事情就是啟用遠程調(diào)試和配置有效的選項:
- 指定運行的被調(diào)試應(yīng)用和調(diào)試者之間的通信協(xié)議,(ie: transport=dt_socket)
- 遠程被調(diào)試應(yīng)用開通的端口,(ie: address=1043), 可定義其他端口,比如9999
- server=y 表示這個 JVM 即將被調(diào)試
- suspend=n 用來告知 JVM 立即執(zhí)行,不要等待未來將要附著上/連上(attached)的調(diào)試者。如果設(shè)成 y, 則應(yīng)用將暫停不運行,直到有調(diào)試者連接上
suspend=y的一個比較適用的場景是,當debug一個會阻止應(yīng)用成功啟動的問題時, 通過suspend=y可以確保調(diào)試者連上來之后再啟動應(yīng)用,否則應(yīng)用已經(jīng)啟動報錯了再調(diào)試也沒意義了。*
當然上面的設(shè)置也可以直接放到 catalina.sh (catalina.bat )內(nèi),但是有個 setevn.* 額外的配置文件一直是最佳選擇, tomcat會自動讀取。
要注意的是, 有些人會碰到過用另一個配置方法來啟用遠程調(diào)試:
-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=1043,suspend=n
-Xdebug and -Xrunjdw 與我們上面推薦的設(shè)置不同之處在于, 它是一種舊方式,適用于JVM 小于 JAVA 5.0 的版本(包括5.0), 而 agentlib:jdwp適用于 JAVA 5.0 和以后版本。
最后通過下面的命令行啟動tomcat,即可完成tomcat啟用遠程調(diào)試啦。
$CATALINA_HOME/bin/catalina.sh jpda start
2 使用 JAVA_OPTS / CATALINA_OPTS
如果你是在 Windows 系統(tǒng)把 Tomcat 作為系統(tǒng)服務(wù)來運行的,直接打開 Apache Tomcat 的屬性對話框,在Java Tab也添加啟動參數(shù):
-agentlib:jdwp=transport=dt_socket,
address=1043,server=y,suspend=n
請確保每一條配置都是新的行,參數(shù)選項之間沒有空格
但如果Tomcat沒有作為 Windows 系統(tǒng)服務(wù), 啟用方法與前面類似,在 setenv.bat 文件中寫入:
set CATALINA_OPTS="-agentlib:jdwp=transport=dt_socket,address=1043,server=y,suspend=n"
如果運行在Linux上, setenv.sh 中寫入:
export CATALINA_OPTS="-agentlib:jdwp=transport=dt_socket,address=1043,server=y,suspend=n"
按照普通的方式啟動Tomcat即可;
./startup.sh
或者
./catalina.sh start
3 使用JPDA啟動
最后一種啟用遠程調(diào)試的方式是用 JPDA 切換, 用如下的啟動命令將使用默認值自動啟用遠程調(diào)試,
catalina jpda start
該命令默認使用的設(shè)置是
-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n
如果你想要修改默認設(shè)置中的選項怎么辦?可以通過修改 Tomcat 需要的這些環(huán)境變量來實現(xiàn):
//JPDA_TRANSPORT: 指定 jpda 傳輸協(xié)議
//JPDA_ADDRESS: 指定遠程調(diào)試端口
//JPDA_SUSPEND: 指定 jvm 啟動暫緩
export JDPA_ADDRESS=”8080“
然后再運行 catalina jpda start , 那么遠程調(diào)試的端口將變成8080
配置Intellj Idea
確定遠程 Tomcat 啟動的應(yīng)用已經(jīng)開啟了遠程調(diào)試, 下一件事情就是配置Intellij Idea了。這里仍然有兩種方式:Remote Tomcat 或者 Remote。
1 使用 Remote Tomcat 配置
首先保證 IDEA 里面已經(jīng)打開了需要遠程調(diào)試的工程源碼,
然后點擊 Run ? Edit Configurations ? **+ **按鈕 ? Tomcat Server ? Remote
輸入必要的遠程 IP 地址和端口(Tomcat http端口);
然后轉(zhuǎn)到 Startup / Connnection Tab 頁,選擇 ”Debug", 輸入遠程調(diào)試端口,我們例子是 1043。
保存后,開始 debug 啟動遠程調(diào)試,如果運行成功會顯示如下的界面,然后在源碼加斷點開始調(diào)試。
2 使用 Remote 配置(推薦)
第一個方法有個缺陷,你打開的工程源碼必須是編譯通過的工程,否則會啟動會報錯;
而介紹的這第二種方法可以在你的工程目錄亂七八糟,不是一個完整的可以部署的工程,甚至是一個解壓縮的 war/ jar 的情況下都可以調(diào)試。
同上步驟,只是選擇“Remote",然后輸入Name, 修改Host, Port (1043) 即可, 保存后開始Debug。
設(shè)置比 Remote Tomcat 更簡單,這里介紹一個實際案例。
我手里有一個可部署的war包,沒有源碼,在遠程已經(jīng)部署完畢。這時我想調(diào)試那個遠程應(yīng)用,怎么做呢?
解壓縮war包到一個文件夾,然后用Intellij Idea打開這個文件夾,如圖的結(jié)構(gòu),編譯的Class都在 WEB-INF/classes 目錄下
找到我要debug的那個class, 這里示例Handler.class, 通過Idea反編譯出來的類代碼,拷貝到一個新的文件Handler.java
雖然如圖可以看到各種的編譯錯誤,但是完全不影響你啟動,代碼中加斷點和調(diào)試哦。
遠程JVM調(diào)試怎么工作的
一切源于被稱作 Agents 的東西。
運行著各種編譯過的 .class 文件的JVM, 有一種特性,可以允許外部的庫(Java或C++寫的libraries)在運行時注入到 JVM 中。這些外部的庫就稱作 Agents, 他們有能力修改運行中 .class 文件的內(nèi)容。
這些 Agents 擁有的這些 JVM 的功能權(quán)限, 是在 JVM 內(nèi)運行的 Java Code 所無法獲取的, 他們能用來做一些有趣的事情,比如修改運行中的源碼, 性能分析等。 像 JRebel 工具就是用了這些功能達到魔術(shù)般的效果。
傳遞一個 Agent Lib 給 JVM, 通過添加 agentlib:libname[=options] 格式的啟動參數(shù)即可辦到。像上面的遠程調(diào)試我們用的就是 **-agentlib:jdwp=... **來引入 jdwp 這個 Agent 的。
jdwp 是一個 JVM 特定的 JDWP(Java Debug Wire Protocol) 可選實現(xiàn),用來定義調(diào)試者與運行JVM之間的通訊,它的是通過 JVM 本地庫的 jdwp.so 或者 jdwp.dll 支持實現(xiàn)的。
它到底是怎么工作的呢?
簡單來說, jdwp agent 會建立運行應(yīng)用的 JVM 和調(diào)試者(本地或者遠程)之間的橋梁。既然他是一個Agent Library, 它就有能力攔截運行的代碼。
在 JVM 架構(gòu)里, debugging 功能在 JVM 本身的內(nèi)部是找不到的,它是一種抽象到外部工具的方式(也稱作調(diào)試者 debugger)。這些調(diào)試工具或者運行在 JVM 的本地 或者在遠程。這是一種解耦,模塊化的架構(gòu)。
更多關(guān)于遠程部署相關(guān),以及 JDWP 的深入說明,大家有興趣可以自己研究一下。