JPDA简介java
顾名思义,JPDA为Java平台上的调试器定义了一个标准的体系结构。该体系结构包括3个主要组成部分:JVM TI、JDI和JDWP。linux
JVM TI的全称是Java Virtual Machine Tool Interface,它定义了JVM为了支持调试而必须提供的功能及相应的访问接口。这些访问接口是以本地语言的形式提供的,由JVM(好比Sun公司的HotSpot VM)负责实现。centos
不过,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接口。api
JDI的全称是Java Debug Interface,它定义了访问JVM TI接口的高层API,以纯Java语言提供,由JDK实现(在Sun JDK的tools.jar能够找到)。调试器直接使用JDI来实现调试的功能。与JPDA back-end相对应,JDI实现的角色就是JPDA front-end。tomcat
JDWP的全称是Java Debug Wire Protocol,它定义了JPDA front-end和JPDA back-end之间通信信息的二进制格式。这里的通信信息主要包括两种:调试器发送给JVM的请求信息和JVM发送给调试器的调试信息。socket
总结一下,调试器 调用JDK提供的JDI实现 (JPDA front-end),经由JDWP协议 ,和JVM自带的JPDA back-end (jdwp.dll, jdwp.so, ...)进行通信。JPDA back-end 经过调用JVM TI接口 ,从而获知调试信息,或发送控制命令。而后,JPDA back-end 将调试信息或命令执行结果,经过JDWP协议 ,返回给调试器 。tcp
如何启用JPDA函数
默认状况下,JVM并无启用JPDA back-end。须要在启动JVM的命令行加载如下参数:ui
-Xdebug -Xrunjdwp:transport=dt_socket, address=8000,server=y,suspend=ythis
-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
若是是linux系统,整理以centos7为例:
经过tar包安装的tomcat找到catalina.sh文件在文件的第一行添加
declare -x CATALINA_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8099"
经过yum按的tomcat找到tomcat的server配置文件,在文件的第一行添加
declare -x CATALINA_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8099"
经过以下两个命令查看端口监听状况是否存在8099端口在监听。
# netstat -lntp #查看监听(Listen)的端口 # netstat -antp #查看全部创建的TCP链接
也有可能你的centos7系统防火墙规则的设定你还须要开放8099端口:
firewall-cmd --zone=public --add-port=8099/tcp --permanent
出现success代表添加成功
命令含义:
--zone #做用域
--add-port=8099/tcp #添加端口,格式为:端口/通信协议
--permanent #永久生效,没有此参数重启后失效
而后重启防火墙
在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继续执行,咱们能够看到,控制台输出了启动信息。