46 调试启动 suspend=y 的情况下, jps 得到 -- main class information unavailable
呵呵 最近有一些 需要远程调试 flink 代码的需求然后 太久了不用, 有些 生疏, 然后 碰到了一些问题如下配置添加到了 flink taskManager 上面之后, 发现 taskManager 一直没有启动起来然后 使用 jps 查看进程, taskmanage 对应的进程一直查询不出来, 最终得到的显示是 "-- main class information unavailable"然
前言
呵呵 最近有一些 需要远程调试 flink 代码的需求
然后 太久了不用, 有些 生疏, 然后 碰到了一些问题
如下配置添加到了 flink taskManager 上面之后, 发现 taskManager 一直没有启动起来
java -Xrunjdwp:transport=dt_socket,suspend=y,server=y,address=3317 com.hx.test12.Test13RemoteDebug
然后 使用 jps 查看进程, taskmanage 对应的进程一直查询不出来, 最终得到的显示是 "-- main class information unavailable"
然后 才在网上搜索了一下 这个 suspend=y 的意思
呵呵 当然网上搜索到的那是一个 理解, 但是缺少一些 实质性的一些判断 来让你确信这个理解的东西
另外 我们还可以看一下 jps 在这里 为什么显示的是 "-- main class information unavailable", 另外就是 jps 明显停顿了几秒, 为什么会停顿几秒 ?
以下调试 vm 部分基于 jdk9, 其他基于 jdk8
测试用例
/**
* Test13RemoteDebug
*
* @author Jerry.X.He <970655147@qq.com>
* @version 1.0
* @date 2021-11-01 18:53
*/
public class Test13RemoteDebug {
// Test13RemoteDebug
// java -Xrunjdwp:transport=dt_socket,suspend=y,server=y,address=3317 com.hx.test12.Test13RemoteDebug
// 新建远程连接 配置 ip, port 进行远程调试
public static void main(String[] args) throws Exception {
int i = 0;
while (true) {
System.out.println(i++);
Thread.sleep(3000);
}
}
}
jps 信息如下
master:jdk jerry$ jps
3266 -- main class information unavailable
jstack 查看 main 的堆栈信息如下
看不到任何堆栈信息, 因为阻塞的时候还没有开始执行任何 java 代码
"main" #1 prio=5 os_prio=31 tid=0x00007fd877003800 nid=0x1a03 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
JavaThread state: _thread_blocked
Thread: 0x00007fd877003800 [0x1a03] State: _at_safepoint _has_called_back 0 _at_poll_safepoint 0
JavaThread state: _thread_blocked
HotspotVM 阻塞在了那里?
vm 是处于 createVM 函数中, 因为我们配置了 suspend=y, 因此 这里需要等待 debugMonitor 被唤醒[gdata->jvmti]
从整理 createVM 的流程上来看, 已经创建了 vm, 已经初始化过了
注意这张图, 和待会儿 jps 的 "-- main class information unavailable" 有关系
HotspotVM 怎么被唤醒 ?
新建一个 java 远程连接, 并连接
正常流程的唤醒是在 有 debugger 连接上了之后, 这里调用的是 debugMonitorNotifyAll, 唤醒的就是上面 wait 的 main 线程
这里看到的上面 (*t) -> Accept 里面除了 正常的 tcp 握手之外, 包含的是一个 JDWP 逻辑意义上的一个 握手, 客户端需要发送 "JDWP-Handshake" 这十四个字节序列到 服务端
是由 JDWP 规范约束的
main 线程被唤醒之后, 继续走 createVM 之后的流程, 程序 正常启动
程序正常执行
jps 为什么显示 "-- main class information unavailable" ?
获取对应的进程的启动命令的时候, 需要 attach 到给定的 vm, 获取 PerfDataPrologue 的信息, 判断 vm 是否准备好了, 这里等待 了 syncWaitMs[默认是5s], 可是一直没有等到 PerfDataPrologue.accessable, 最终抛出了异常 “Could not synchronize with target”
异常来到 jps 外层, 我们看到的 “-- main class information unavailable” 是获取 mainClass 阶段的一个默认的错误信息
意思是只要是 获取 mainClass 阶段发生了任意异常, 我们都会得到 "-- main class information unavailable"
我们来看一下 PerfDataPrologue.access 是哪里被设置为 true 的?
可以看到的是在 createVM 里面, 然后你可以回顾一下 上面的 wait 的哪一张图片, create_vm_timer.end() 是 createVM 里面的倒数第几行代码
jps 是如何获取所有的进程信息的?
读取的是 hsperfdata_* 文件夹下面的信息, 里面的是各个进程号, 然后根据 进程号获取 jps 本身需要的相关信息
master:jdk jerry$ ll /var/folders/pw/lb8dvl7d6474r5plrnwtcp180000gn/T/hsperfdata_jerry/
total 576
-rw------- 1 jerry staff 32768 Nov 7 16:01 1770
-rw------- 1 jerry staff 32768 Nov 7 19:55 3338
-rw------- 1 jerry staff 32768 Nov 7 20:18 3520
-rw------- 1 jerry staff 32768 Nov 7 20:20 3526
-rw------- 1 jerry staff 32768 Nov 7 20:20 3527
-rw------- 1 jerry staff 32768 Nov 7 20:24 3639
-rw------- 1 jerry staff 32768 Nov 7 10:51 661
-rw------- 1 jerry staff 32768 Nov 7 10:53 760
-rw------- 1 jerry staff 32768 Nov 7 11:29 948
基于 jdwp协议 和 HotSpotVM 进行交互
呵呵 演示版本, 以后有机会 放出来
import com.alibaba.fastjson.JSON;
import com.hx.codec.utils.IoUtils;
import com.hx.net.client.ClientChannelHandler;
import com.hx.net.client.jdwp.JdwpClient;
import com.hx.net.common.BaseProtocolTests;
import com.hx.net.config.ClientConfig;
import com.hx.net.protocol.jdwp.JdwpClientProtocol;
import com.hx.net.protocol.jdwp.common.JdwpMessage;
import com.hx.net.protocol.jdwp.msg.JdwpHandShake;
import com.hx.net.protocol.jdwp.msg.JdwpRequest;
import com.hx.net.protocol.jdwp.msg.JdwpResponse;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
/**
* Test08JdwpClientProtocol
*
* @author Jerry.X.He
* @version 1.0
* @date 2021-11-07 16:30
*/
@FixMethodOrder(value = MethodSorters.NAME_ASCENDING)
public class Test08JdwpClientProtocol extends BaseProtocolTests {
@Test
public void test02Server() throws Exception {
JdwpClientProtocol clientProtocol = new JdwpClientProtocol();
ClientConfig clientConfig = new ClientConfig();
clientConfig.setHost("localhost");
clientConfig.setPort(3317);
JdwpClient client = new JdwpClient(clientConfig, clientProtocol, new JdwpClientHandler(), new JdwpSocketChannelHandler());
client.start();
// sleep for biz, then stop
IoUtils.sleep(15000 * 1000);
client.stop();
}
// ------------------------------------------ assist methods ------------------------------------------
/**
* JdwpClientHandler
*
* @author Jerry.X.He
* @version 1.0
* @date 2021-11-07 16:32
*/
@ChannelHandler.Sharable
static class JdwpClientHandler extends SimpleChannelInboundHandler<JdwpMessage> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, JdwpMessage msg) throws Exception {
LOGGER.info(" client received : {} ", JSON.toJSONString(msg));
if (msg instanceof JdwpHandShake) {
JdwpRequest entity = new JdwpRequest();
// entity.setLen(11);
entity.setId(1171);
entity.setFlags(0);
entity.setCmdSet(1);
entity.setCmd(7);
entity.setData(new Integer[]{});
ctx.writeAndFlush(entity);
} else if (msg instanceof JdwpResponse) {
}
}
}
/**
* JdwpSocketChannelHandler
*
* @author Jerry.X.He
* @version 1.0
* @date 2021-11-07 16:33
*/
static class JdwpSocketChannelHandler implements ClientChannelHandler {
@Override
public void doBiz(Channel channel) throws Exception {
JdwpHandShake handShakeEntity = new JdwpHandShake();
channel.writeAndFlush(handShakeEntity);
System.in.read();
}
}
}
日式输出如下
可以看到的是 客户端发送了 JDWP握手 信息之后, HotspotVM 回复了 JDWP握手回复 信息
客户端拿到 HotspotVM 的 JDWP握手回复 之后, 发送了一个 cmdSet 为 1, cmd 为 7 的一个请求过去, 此请求对应的 handler 是 VirtualMachine_Cmds.idSizes
然后 HotspotVM 响应给了我的客户端 5 个 8, 也就是对应的 idsSize 里面输出的 5 个 8
[20:43:00.458] INFO com.hx.net.interceptor.common.ChannelStateInterceptor 23 channelActive - channelActive : [id: 0x27ca94e3, L:/127.0.0.1:54489 - R:localhost/127.0.0.1:3317]
[20:43:00.662] INFO com.hx.net.protocol.Test08JdwpClientProtocol$JdwpClientHandler 151 channelRead0 - client received : {"echo":"JDWP-Handshake"}
[20:43:00.732] INFO com.hx.net.protocol.Test08JdwpClientProtocol$JdwpClientHandler 151 channelRead0 - client received : {"data":[0,0,0,8,0,0,0,8,0,0,0,8,0,0,0,8,0,0,0,8],"errorCode":0,"flags":-128,"id":1171,"len":31}
Disconnected from the target VM, address: '127.0.0.1:54486', transport: 'socket'
[20:43:14.224] INFO com.hx.net.interceptor.common.ChannelStateInterceptor 29 channelInactive - channelInactive : [id: 0x27ca94e3, L:/127.0.0.1:54489 ! R:localhost/127.0.0.1:3317]
VirtualMachine_Cmds.idSizes 如下
jpda 的各个角色
在我们通常调试的场景中
HotspotVM 提供了 jvmti 的实现, jwdp 服务端的实现, 实现的是 调试相关操作 落地到 vm 的相关处理
idea 提供了 jdwp 客户端的实现, 和 jdi的调用 最终就是我们看到的这个前端这一套
奉上 jpda, jdwp 相关规范文档
Java™ Platform Debugger Architecture (JPDA)
提供一个完整的 usage, 让你不再迷茫
ERROR: JDWP option syntax error: -agentlib:jdwp=trhelp
master:classes jerry$ java -Xrunjdwp:help com.hx.test12.Test13RemoteDebug
Java Debugger JDWP Agent Library
--------------------------------
(see http://java.sun.com/products/jpda for more information)
jdwp usage: java -agentlib:jdwp=[help]|[<option>=<value>, ...]
Option Name and Value Description Default
--------------------- ----------- -------
suspend=y|n wait on startup? y
transport=<name> transport spec none
address=<listen/attach address> transport spec ""
server=y|n listen for debugger? n
launch=<command line> run debugger on event none
onthrow=<exception name> debug on throw none
onuncaught=y|n debug on any uncaught? n
timeout=<timeout value> for listen/attach in milliseconds n
mutf8=y|n output modified utf-8 n
quiet=y|n control over terminal messages n
Obsolete Options
----------------
strict=y|n
stdalloc=y|n
Examples
--------
- Using sockets connect to a debugger at a specific address:
java -agentlib:jdwp=transport=dt_socket,address=localhost:8000 ...
- Using sockets listen for a debugger to attach:
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y ...
Notes
-----
- A timeout value of 0 (the default) is no timeout.
Warnings
--------
- The older -Xrunjdwp interface can still be used, but will be removed in
a future release, for example:
java -Xdebug -Xrunjdwp:[help]|[<option>=<value>, ...]
完
更多推荐
所有评论(0)