• 46 调试启动 suspend=y 的情况下, jps 得到 -- 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 

    测试用例 

    1. /**
    2. * Test13RemoteDebug
    3. *
    4. * @author Jerry.X.He <970655147@qq.com>
    5. * @version 1.0
    6. * @date 2021-11-01 18:53
    7. */
    8. public class Test13RemoteDebug {
    9. // Test13RemoteDebug
    10. // java -Xrunjdwp:transport=dt_socket,suspend=y,server=y,address=3317 com.hx.test12.Test13RemoteDebug
    11. // 新建远程连接 配置 ip, port 进行远程调试
    12. public static void main(String[] args) throws Exception {
    13. int i = 0;
    14. while (true) {
    15. System.out.println(i++);
    16. Thread.sleep(3000);
    17. }
    18. }
    19. }

    jps 信息如下 

    1. master:jdk jerry$ jps
    2. 3266 -- main class information unavailable

    jstack 查看 main 的堆栈信息如下 

    看不到任何堆栈信息, 因为阻塞的时候还没有开始执行任何 java 代码 

    1. "main" #1 prio=5 os_prio=31 tid=0x00007fd877003800 nid=0x1a03 runnable [0x0000000000000000]
    2. java.lang.Thread.State: RUNNABLE
    3. JavaThread state: _thread_blocked
    4. Thread: 0x00007fd877003800 [0x1a03] State: _at_safepoint _has_called_back 0 _at_poll_safepoint 0
    5. 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 本身需要的相关信息 

    1. master:jdk jerry$ ll /var/folders/pw/lb8dvl7d6474r5plrnwtcp180000gn/T/hsperfdata_jerry/
    2. total 576
    3. -rw------- 1 jerry staff 32768 Nov 7 16:01 1770
    4. -rw------- 1 jerry staff 32768 Nov 7 19:55 3338
    5. -rw------- 1 jerry staff 32768 Nov 7 20:18 3520
    6. -rw------- 1 jerry staff 32768 Nov 7 20:20 3526
    7. -rw------- 1 jerry staff 32768 Nov 7 20:20 3527
    8. -rw------- 1 jerry staff 32768 Nov 7 20:24 3639
    9. -rw------- 1 jerry staff 32768 Nov 7 10:51 661
    10. -rw------- 1 jerry staff 32768 Nov 7 10:53 760
    11. -rw------- 1 jerry staff 32768 Nov 7 11:29 948

    基于 jdwp协议 和 HotSpotVM 进行交互

    呵呵 演示版本, 以后有机会 放出来 

    1. import com.alibaba.fastjson.JSON;
    2. import com.hx.codec.utils.IoUtils;
    3. import com.hx.net.client.ClientChannelHandler;
    4. import com.hx.net.client.jdwp.JdwpClient;
    5. import com.hx.net.common.BaseProtocolTests;
    6. import com.hx.net.config.ClientConfig;
    7. import com.hx.net.protocol.jdwp.JdwpClientProtocol;
    8. import com.hx.net.protocol.jdwp.common.JdwpMessage;
    9. import com.hx.net.protocol.jdwp.msg.JdwpHandShake;
    10. import com.hx.net.protocol.jdwp.msg.JdwpRequest;
    11. import com.hx.net.protocol.jdwp.msg.JdwpResponse;
    12. import io.netty.channel.Channel;
    13. import io.netty.channel.ChannelHandler;
    14. import io.netty.channel.ChannelHandlerContext;
    15. import io.netty.channel.SimpleChannelInboundHandler;
    16. import org.junit.FixMethodOrder;
    17. import org.junit.Test;
    18. import org.junit.runners.MethodSorters;
    19. /**
    20. * Test08JdwpClientProtocol
    21. *
    22. * @author Jerry.X.He
    23. * @version 1.0
    24. * @date 2021-11-07 16:30
    25. */
    26. @FixMethodOrder(value = MethodSorters.NAME_ASCENDING)
    27. public class Test08JdwpClientProtocol extends BaseProtocolTests {
    28. @Test
    29. public void test02Server() throws Exception {
    30. JdwpClientProtocol clientProtocol = new JdwpClientProtocol();
    31. ClientConfig clientConfig = new ClientConfig();
    32. clientConfig.setHost("localhost");
    33. clientConfig.setPort(3317);
    34. JdwpClient client = new JdwpClient(clientConfig, clientProtocol, new JdwpClientHandler(), new JdwpSocketChannelHandler());
    35. client.start();
    36. // sleep for biz, then stop
    37. IoUtils.sleep(15000 * 1000);
    38. client.stop();
    39. }
    40. // ------------------------------------------ assist methods ------------------------------------------
    41. /**
    42. * JdwpClientHandler
    43. *
    44. * @author Jerry.X.He
    45. * @version 1.0
    46. * @date 2021-11-07 16:32
    47. */
    48. @ChannelHandler.Sharable
    49. static class JdwpClientHandler extends SimpleChannelInboundHandler<JdwpMessage> {
    50. @Override
    51. protected void channelRead0(ChannelHandlerContext ctx, JdwpMessage msg) throws Exception {
    52. LOGGER.info(" client received : {} ", JSON.toJSONString(msg));
    53. if (msg instanceof JdwpHandShake) {
    54. JdwpRequest entity = new JdwpRequest();
    55. // entity.setLen(11);
    56. entity.setId(1171);
    57. entity.setFlags(0);
    58. entity.setCmdSet(1);
    59. entity.setCmd(7);
    60. entity.setData(new Integer[]{});
    61. ctx.writeAndFlush(entity);
    62. } else if (msg instanceof JdwpResponse) {
    63. }
    64. }
    65. }
    66. /**
    67. * JdwpSocketChannelHandler
    68. *
    69. * @author Jerry.X.He
    70. * @version 1.0
    71. * @date 2021-11-07 16:33
    72. */
    73. static class JdwpSocketChannelHandler implements ClientChannelHandler {
    74. @Override
    75. public void doBiz(Channel channel) throws Exception {
    76. JdwpHandShake handShakeEntity = new JdwpHandShake();
    77. channel.writeAndFlush(handShakeEntity);
    78. System.in.read();
    79. }
    80. }
    81. }

    日式输出如下 

    可以看到的是 客户端发送了 JDWP握手 信息之后, HotspotVM 回复了 JDWP握手回复 信息 

    客户端拿到 HotspotVM 的 JDWP握手回复 之后, 发送了一个 cmdSet 为 1, cmd 为 7 的一个请求过去, 此请求对应的 handler 是 VirtualMachine_Cmds.idSizes 

    然后 HotspotVM 响应给了我的客户端 5 个 8, 也就是对应的 idsSize 里面输出的 5 个 8

    1. [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]
    2. [20:43:00.662] INFO com.hx.net.protocol.Test08JdwpClientProtocol$JdwpClientHandler 151 channelRead0 - client received : {"echo":"JDWP-Handshake"}
    3. [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}
    4. Disconnected from the target VM, address: '127.0.0.1:54486', transport: 'socket'
    5. [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)

    JavaTM Debug Wire Protocol

    提供一个完整的 usage, 让你不再迷茫 

    1. ERROR: JDWP option syntax error: -agentlib:jdwp=trhelp
    2. master:classes jerry$ java -Xrunjdwp:help com.hx.test12.Test13RemoteDebug
    3. Java Debugger JDWP Agent Library
    4. --------------------------------
    5. (see http://java.sun.com/products/jpda for more information)
    6. jdwp usage: java -agentlib:jdwp=[help]|[<option>=<value>, ...]
    7. Option Name and Value Description Default
    8. --------------------- ----------- -------
    9. suspend=y|n wait on startup? y
    10. transport=<name> transport spec none
    11. address=<listen/attach address> transport spec ""
    12. server=y|n listen for debugger? n
    13. launch=<command line> run debugger on event none
    14. onthrow=<exception name> debug on throw none
    15. onuncaught=y|n debug on any uncaught? n
    16. timeout=<timeout value> for listen/attach in milliseconds n
    17. mutf8=y|n output modified utf-8 n
    18. quiet=y|n control over terminal messages n
    19. Obsolete Options
    20. ----------------
    21. strict=y|n
    22. stdalloc=y|n
    23. Examples
    24. --------
    25. - Using sockets connect to a debugger at a specific address:
    26. java -agentlib:jdwp=transport=dt_socket,address=localhost:8000 ...
    27. - Using sockets listen for a debugger to attach:
    28. java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y ...
    29. Notes
    30. -----
    31. - A timeout value of 0 (the default) is no timeout.
    32. Warnings
    33. --------
    34. - The older -Xrunjdwp interface can still be used, but will be removed in
    35. a future release, for example:
    36. java -Xdebug -Xrunjdwp:[help]|[<option>=<value>, ...]

    完 

  • 相关阅读:
    书籍数组中的最长连续序列(4)0716
    初学者对html的认知
    【iOS】——仿写计算器
    人工智能 AI 概念梳理
    DBConvert Studio 3.0.6 -2022-08-13 Crack
    windows中MySQL主从配置【第一篇】
    接住我的下巴,Github上超火的异步编程神仙笔记也太香了
    Kafka简单入门02——ISR机制
    信息系统项目管理师 第四版 口诀
    winform中DevExpress控件一些属性
  • 原文地址:https://blog.csdn.net/u011039332/article/details/121195873