Java JDPA
JDPA 是一个框架, 它定义了 debugger 和 debuggee 是如何交互的, 两端交互的API 和 协议, 并不包含具体的实现. 它包含3部分.
- debugger 端的API: JDI (Java Debug Interface)
- debuggee 端的API: JVM TI
- 连接两端的协议: JDWP
jdb
jdb 是 JDK 自带的命令行 debugger 端, 是 JDI 的实现.
- 它能连接本地和远程的目标 Java 应用, 执行 debug 命令.
- 它能连接本地和远程的 core dump 文件, 并获取信息.
它能启动本地要debug 的Java 应用, 并且debug.
jdb -connect sun.jvm.hotspot.jdi.SAPIDAttachingConnector:pid=9302 jdb -connect com.sun.jdi.SocketAttach:port=12306 jdb -connect sun.jvm.hotspot.jdi.SACoreAttachingConnector:javaExecutable=$JAVA_HOME/bin/java,core=core.20441 远程的 core dump 需要 jsadebugd.
jdb 本地启动一个app 的背后
下面本地启动一个需要debug的app, 然后观察 jdb 是如何跟这个app交互的.
$ jdb FreeMemoryExample # 启动 jdb
$ stop at FreeMemoryExample:8 # 设置断点
$ run # 启动应用.
打开另外一个窗口, 然后观察如下:
$ jps #查看当前机器的 java 进程
3790574 TTY
3790626 FreeMemoryExample
3793734 Jps #jps 本身
$ sudo lsof -T -p 3790574 | grep 5598 #查看进程 3790574 里面连接的 tcp, 它的55985连到本地 55888
jdb 3790574 supra 6u IPv6 37707266 0t0 TCP suprabox:55985->localhost:55888
$ sudo lsof -T -p 3790626 | grep 5598 #查看进程 3790626 里面连接的 tcp, 它的55888连到本地 55985
java 3790626 supra 4u IPv4 37703541 0t0 TCP localhost:55888->suprabox:55985
$ ps aux | grep 3790626 #查看 3790626 的启动命令
supra 3790626 0.1 0.2 7021672 41712 pts/0 Sl+ 00:06 0:01 /usr/lib/jvm/java-11-openjdk-amd64/bin/java -Xdebug -Xrunjdwp:transport=dt_socket,address=suprabox:55985,suspend=y FreeMemoryExample
$ ps aux | grep 3790574 #查看 3790574 的启动命令
supra 3790574 0.2 0.4 7619496 65040 pts/0 Sl+ 00:06 0:02 jdb FreeMemoryExample
所以, 当运行 jdb FreeMemoryExample
的时候, 其实它调用java 进程另外启动了需要被 debug 的程序,并且建立了 tcp socket 通信.
被启动的进程使用了参数: -Xdebug -Xrunjdwp:transport=dt_socket,address=suprabox:55985,suspend=y
添加 debug 参数启动应用, 并连接
只要在对应的 Java 进程启动的时候, 添加类似如下参数, 就能启动debug 监听端口, 等待准备被连接:
$ java -agentlib:jdwp=transport=dt_socket,server=y,address=12306 FreeMemoryExample #启动应用端
# 更多参数参看: https://docs.oracle.com/javase/8/docs/technotes/guides/jpda/conninv.html
$ jdb -connect com.sun.jdi.SocketAttach:port=12306 # 启动jdb 并连接
stop at FreeMemoryExample:8 # 设置断点
run # 开始运行
locals #显示本地变量
where # 显示栈