📓 Archive

  • Pricing
  • Chess
  • Syntax
  • DEBUG

    FGJ: Create:2024/01/19 Update: (2024-10-24)

    • debugger #

      • 参数解释 #

        indexexplain
        -Xdebug启用调试特性
        -Xrunjdwp在目标 VM 中加载 JDWP 实现。它通过传输和 JDWP 协议与独立的调试器应用程序通信。从 Java V5 开始,您可以使用 -agentlib:jdwp 选项,而不是 -Xdebug 和 -Xrunjdwp。
        -Djava.compiler=NONE禁止 JIT 编译器的加载
        transport传输方式,有 socket 和 shared memory 两种,我们通常使用 socket(套接字)传输,但是在 Windows 平台上也可以使用shared memory(共享内存)传输。
        server(y/n)VM 是否需要作为调试服务器执行
        address调试服务器的端口号,客户端用来连接服务器的端口号
        suspend(y/n)值是 y 或者 n,若为 y,启动时候自己程序的 VM 将会暂停(挂起),直到客户端进行连接,若为 n,自己程序的 VM 不会挂起
      • 参数样例 #

        Command line arguments for remote JVM.

        JDK版本(attach mode)启动追加命令[参考下图](listen mode)启动追加命令
        JDK9 or later-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8800-agentlib:jdwp=transport=dt_socket,server=n,address=localhost:8800,suspend=y
        JDK 5-8-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8800-agentlib:jdwp=transport=dt_socket,server=n,address=localhost:8800,suspend=y
        JDK 1.4.x-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8800-Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=localhost:8800,suspend=y
        JDK 1.3.x or earliar-Xnoagent -Djava.compiler=NONE -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8800-Xnoagent -Djava.compiler=NONE -Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=localhost:8800,suspend=y

    • mode #

      debug mode:
      Attach: 此种模式下,server=y,address=port。被调试的代码端充当服务器开放端口等待jdi客户端去连接。
      Listen: 此种模式下,server=n,address=ip:port。应当先在ip机器上启动jdi客户端去监听端口port,然后启动需要被调试的代码。例如: IDEA debug按钮采用的方式

      需要注意的是:suspend=y/n. 指定是否被调试JVM需要被挂起。
         attach模式下: 如果调试简单application,则最好是suspend=y,这样的话不至于还没attach上targetVM,程序已经运行完了。如果是web应用,因为一直在运行。所以suspend=n也无妨。后续只要attach上targetVM就可以对targetVM进行控制。
         listen模式下: 感觉没有什么用。启动targetVM 的时候就要与指定ip:port的jdi客户端进行连接。比如:idea 设置远程debug 为listen模式。先启动监听本地localhost:8800.顺便打上断点。然后再启动需要被调试的JVM,启动的时候不管suspend=y/n,都会步入断点。

      • attach #

        • targetVM

          java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8800 _draft.test.debug.HelloWorld 运行后会暂停,等待jdi客户端attach后才开始执行。

        • JDI client

        • Tomcat Debug #

          attach模式也可以参考 调试本地或远程的tomcat

      • listen #

        • targetVM

          java -agentlib:jdwp=transport=dt_socket,server=n,suspend=n,address=localhost:8800 _draft.test.debug.HelloWorld 等待下图jdi客户端监听开始之后再运行此命令。

        • JDI client

        • IDEA本地debug按钮(采用listen方式debug) #

          # 启动命令及参数
          /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/bin/java 
              -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:57982,suspend=y,server=n 
              -javaagent:/Users/stevenobelia/Library/Caches/JetBrains/IntelliJIdea2023.2/captureAgent/debugger-agent.jar 
              -Dfile.encoding=UTF-8 
              -classpath /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/charsets.jar
                  :/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/lib/tools.jar
                  :/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar 
              _draft.test.debug.HelloWorld
          Connected to the target VM, address: '127.0.0.1:57982', transport: 'socket'
          
        • IDEA maven debug #

          /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/bin/java 
              -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:49219,suspend=y,server=n 
              -Dmaven.multiModuleProjectDirectory=/Users/stevenobelia/Documents/project_idea_test/idea-test-project 
              -Djansi.passthrough=true 
              -Dmaven.home=/Users/stevenobelia/software/apache-maven-3.6.0 
              -Dclassworlds.conf=/Users/stevenobelia/software/apache-maven-3.6.0/bin/m2.conf 
              -Dmaven.ext.class.path=/Applications/IntelliJ IDEA.app/Contents/plugins/maven/lib/maven-event-listener.jar 
          
              -javaagent:/Users/stevenobelia/Library/Caches/JetBrains/IntelliJIdea2023.2/captureAgent/debugger-agent.jar 
              -Dfile.encoding=UTF-8 
              -classpath /Users/stevenobelia/software/apache-maven-3.6.0/boot/plexus-classworlds-2.5.2.jar
                  :/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar 
          
              org.codehaus.classworlds.Launcher 
              -Didea.version=2023.2.3 
              -s /Users/stevenobelia/software/apache-maven-3.6.0/conf/settings.xml 
              clean:clean
          
    • 问题 #

      使用idea运行JDI程序的时候会在捕获到VMStartEvent事件后,不会有ClassPrepareEvent,BreakpointEvent事件。
      当前使用的JDI环境为JDK17自带的。并非1.8中${JAVA_HOME}/lib/tools.jar中的类库。
      现象如下图(idea Run按钮运行后与命令行的方式运行出来的结果不同):

      • 原因: #

        Connector在launch的时候,会解析参数,并且生成 shell 执行指令数组。比如[/Library/Java/JavaVirtualMachines/jdk-17.0.2.jdk/Contents/Home/bin/java, -Xdebug, -Xrunjdwp:transport=dt_socket,address=localhost:63879,suspend=y, site.wtfu.framework.HelloWorld]。并通过Runtime.getRuntime().exec(commandArray)执行。 代码参考。重点在于,使用Runtime.getRuntime().exec()执行命令的时候,默认使用当前工程项目(比如本例中的idea-debug)为执行目录。而上面传递的参数中需要被targetVM执行的类site.wtfu.framework.HelloWorld在执行目录中没有,所以targetVM启动的时候报错了。也就直接退出了。

        如果需要验证,可以在编写的debugger程序中加入如下代码:使用process.getErrorStream()获取targetVM异常退出信息。

        public static void main(String[] args) {
            // ...
            try {
                vm = launchingConnector.launch(defaultArguments);
                // print trace message with: vm.setDebugTraceMode(4);
                process = vm.process();
        
                new Thread(()->{
                    BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
                    String line;
                    while (true) {
                        try {
                            if (!((line = reader.readLine()) != null)) break;
                        } catch (IOException e) { throw new RuntimeException(e); }
                        System.err.println(line);
                    }
                }).start();
            }catch(Exception e){e.printStackTrace();}
        }
        

        加入检测异常的代码后运行的到如下结果:

      • 解决 #

        1. 源代码

          修改AbstractLauncher.java源代码,利用Runtime.getRuntime().exec(String[] cmdarray, String[] envp, File dir)传入directory。显然不方便。

        2. 满足targetVM

          将编译好的HelloWorld.class 按照Runtime.getRuntime().exec(cmdarray)所需要的目录构造即可。如下图:

    Reference #


    comments powered by Disqus