Eclipse远程调试Tomcat

最近,一直在研究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继续执行,我们可以看到,控制台输出了启动信息。
 
 
****************************************************
Tomcat6.0远程调试
有些时候,调试不得不用外网,比如说做支付宝的支付接口,服务器后台通 知就不得不用外网的ip。无奈,只能扔到服务器远程调试了。
网上讲关于远程调试tomcat的倒是蛮多,基本都是将改啥catalina.sh,startup.sh的,操作还是复杂点。下面这篇就只针对于 linux下的tomcat远程调试,不用改啥文件,简单而又没啥副作用。本人在tomcat6 的情况下远程调试成功。
相信很 多J2EE的开发者都是在Windows上面开发程序,然后把程序上传到Linux下运行的吧。可是有时候在自己机器上运行的好好的程序,放到服务器下就 出错了。单单看出错信息也推断不出是哪里的问题。这时候试试用Java的JPDA远程调试程序,一定可以让你很满意。
tomcat服务器已经内置了JPDA支持,只要用:
catalina.sh jpda start
这条命令启动tomcat,它就会监听8000端口,等待调试器的连接。要注意不能使用 startup.sh脚本。tomcat会使用 JPDA_ADDRESS这个环境变量的值。比如想监听8017端口:
export  JPDA_ADDRESS=8017 
catalina.sh jpda start
接着,选一个自己喜欢的调试器,基本上现在主流的如Eclipse、NetBean都可以。我主要使用Eclipse,操作只要三步:
1.选择“Run”菜单里的“Open Debug Dialog”。
2.在对话框里找到“Remote Java Application”。 右键菜单选择“New”创建一个配置。
3.新创建的配置的Project选择服务器上的工程。Host和Port分别填写你服务器的IP和JPDA的端口,默认是8000,或 者$JPDA_ADDRESS。
现在就可以像本机调试一样, 设置断点和跟踪调试了。
 
**********************************************************
tomcat 6 debug jpda 简单配置
今日在tomcat下开发,为避免在没有改变java方法和类名的情况下重新加载整个webapp,查阅了些资料后需要启用jvm的jpda, 同时要能远程debug;
研究了半天发现其实很简单,人家已经做的很好了;只需要加jpda 4个字母就可以了;
 
修改startup.bat 找到call "%EXECUTABLE%" start %CMD_LINE_ARGS% 改为call "%EXECUTABLE%" jpda start %CMD_LINE_ARGS% 即可;这样在java的方法里面修改代码就不需要费时费力的重新加载上下文,也不需要重新登录。
 
如果需要修改debug端口,(默认8000),改catalina.bat 中set JPDA_ADDRESS=8000 处;
 
启动命令方式如下:
echo Usage: catalina ( commands ... )
echo commands:
echo debug Start Catalina in a debugger
echo debug -security Debug Catalina with a security manager
echo jpda start Start Catalina under JPDA debugger
echo run Start Catalina in the current window
echo run -security Start in the current window with security manager
echo start Start Catalina in a separate window
echo start -security Start in a separate window with security manager
echo stop Stop Catalina
 
*******************************************************
 
Tomcat jpda Eclipse
 
1. 修改%CATALINE_HOME%/bin下的文件catalina.bat
 找到 这段代码
 
 
if not ""%1"" == ""jpda"" goto noJpda
set JPDA=jpda
if not "%JPDA_TRANSPORT%" == "" goto gotJpdaTransport
set JPDA_TRANSPORT=dt_shmem
:gotJpdaTransport
if not "%JPDA_ADDRESS%" == "" goto gotJpdaAddress
set JPDA_ADDRESS=jdbconn
:gotJpdaAddress
if not "%JPDA_SUSPEND%" == "" goto gotJpdaSuspend
set JPDA_SUSPEND=n
:gotJpdaSuspend
if not "%JPDA_OPTS%" == "" goto gotJpdaOpts
set JPDA_OPTS=-Xdebug -Xrunjdwp:transport=%JPDA_TRANSPORT%,address=%JPDA_ADDRESS%,server=y,suspend=%JPDA_SUSPEND%
:gotJpdaOpts
shift
:noJpda
 
修改红色部分,
set JPDA_TRANSPORT=dt_socket
set JPDA_ADDRESS=8787
说明:dt_shmem表示本机;dt_socket表示远程调试
2. 修改%CATALINE_HOME%/bin下的文件startup.bat
找到 这段代码
call "%EXECUTABLE%" start %CMD_LINE_ARGS%
修改为
call "%EXECUTABLE%" jpda start %CMD_LINE_ARGS%
3. eclipse 设置调试环境
在Eclipse中选择Run?Debug,在弹出的对话框中右击Remote Java Application新建一个远程调试项。
在“Project”中选择要调试的项目,在“Host”中输入需要远程调试项目的IP,也就是tomcat所在的IP,在“Port”中输入设置的端口号,比如上面设置的8787,然后钩选“Allow termination of remote VM”,Connection Type不需要修改。点击Apply按钮,然后点击Debug按钮。开始调试。
 
 
posted @ 2012-10-01 13:40  linux,dev  阅读(519)  评论(0编辑  收藏  举报