【问题标题】:Attach jdb on uncaught exception在未捕获的异常上附加 jdb
【发布时间】:2015-02-15 16:33:51
【问题描述】:

所以,我正在启动一个Java程序,如下所示

java -agentlib:jdwp=transport=dt_socket,address=8000, server=y,suspend=n  MyClass

然后我手动附加一个调试器,如下所示

jdb -attach 8000

我想知道是否可以设置 jdb 以便在未捕获的异常情况下(仅)自动附加到正在运行的进程?

原因是我想避免调试器的开销,直到出现未捕获的异常。但是我现在面临的问题是,如果没有附加调试器,那么一旦出现未捕获的异常,JVM 就会中止。

编辑:

来自 Oracle docs,似乎下面的命令可以满足我的需要,但适用于 Windows 机器。

java -agentlib:jdwp=transport=dt_shmem,server=y,onuncaught=y,launch=d:\bin\debugstub.exe MyClass

任何人都知道 linux 的等价物吗?我已经尝试了以下命令。

java -agentlib:jdwp=transport=dt_socket,address=8000,server=y,onuncaught=y,suspend=n,launch=jdb MyClass

调试器似乎已连接,但它立即抛出 IOError。

Initializing jdb ...

java.io.IOException: Input/output error
at java.io.FileInputStream.readBytes(Native Method)
at java.io.FileInputStream.read(FileInputStream.java:272)
at java.io.BufferedInputStream.read1(BufferedInputStream.java:273)
at java.io.BufferedInputStream.read(BufferedInputStream.java:334)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:154)
at java.io.BufferedReader.readLine(BufferedReader.java:317)
at java.io.BufferedReader.readLine(BufferedReader.java:382)
at com.sun.tools.example.debug.tty.TTY.<init>(TTY.java:751)
at com.sun.tools.example.debug.tty.TTY.main(TTY.java:1067)

【问题讨论】:

    标签: java debugging jdb


    【解决方案1】:

    及时附加调试器确实使用了您建议的选项(launchonuncaught)。但是launch 选项在 linux 上需要更多:

    请注意,启动的进程不是在其自己的窗口中启动的。在大多数情况下,启动的进程应该是一个小应用程序,它依次在自己的窗口中启动调试器应用程序。

    在您的情况下,jdb 无法打开它需要的终端 TTY,因为它正在启动的上下文中。根据运行环境的不同,您需要构建一个脚本来启动 jdb 在新窗口或将其附加到pseduo-tty,以便它可以正常运行。

    我通过创建一个使用screen 启动终端的脚本对此进行了测试

    #!/bin/bash
    # according to launch option doc, the first argument is the transport and
    # the second argument will be the address
    #
    screen -dm -L -S autojdb jdb -attach $2
    

    此脚本将在分离的屏幕中启动jdb,并将屏幕的会话命名为autojdb。您可以使用screen -ls 查看您的屏幕列表。当您想访问已启动的调试器时,请使用screen -r autojdb。确保将脚本放在您的路径中或在您的启动选项中提供完整路径(下面的/home/me/screenjdb):

    java -agentlib:jdwp=transport=dt_socket,address=8000,server=y,onuncaught=y,suspend=n,launch=/home/me/screenjdb MyClass
    

    在我的脚本中,我还将-L 传递给了记录会话的屏幕。这将记录会话,但还可以让您查看由于某种原因附加失败时发生的任何错误。

    【讨论】:

      【解决方案2】:

      与其使用调试器并远程附加一个,不如使用Thread.UncaughtExceptionHandler 创建一个uncaught exception handler

      public class ThreadCatch
      {
          public static void main(String... args)
          {
              new ThreadCatch().go();
          }
      
          public void go()
          {
              try {
                  Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
                      @Override
                      public void uncaughtException(Thread t, Throwable e) {
                          System.out.println("Uncaught exception");
                          e.printStackTrace();
                      }
                  });
      
                  final Thread thread = new Thread(new A());
                  thread.start();
                  thread.join();
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      
          class A implements Runnable
          {
              int x = 10;
      
              @Override
              public void run()
              {
                  x++;
                  ObjectTrackingException obj = new ObjectTrackingException();
                  obj.setThrownFrom(this);
                  throw obj;
              }
          }
      }
      
      
      class ObjectTrackingException extends RuntimeException
      {
          private Object thrownFrom;
      
          public Object getThrownFrom() {
              return thrownFrom;
          }
      
          public void setThrownFrom(Object thrownFrom) {
              this.thrownFrom = thrownFrom;
          }
      }
      

      【讨论】:

      • 嗯,我想分析一下程序崩溃时栈上变量的值。我不认为我可以用 UncaughtExceptionHandler 做到这一点,因为堆栈会随着异常向上传递而展开?
      • 为什么不在异常处理程序上设置条件断点呢?这样您就可以捕获异常,并且断点允许您浏览调用堆栈。
      • 所以,据我了解,当从方法 B 抛出异常时,该方法的调用堆栈被破坏,异常被向上抛出。这继续进行,直到异常最终到达 UncaughtExceptionHandler。但是此时,最初发生异常的方法的调用堆栈(以及我要查看的局部变量)不再存在。那么条件断点没有帮助吗?如果我的理解有什么问题,请告诉我...
      • 啊,我没有意识到你想看看局部变量。如果对象状态是您感兴趣的内容,您始终可以将其保存在异常中并将其传播到链上。我已经编辑了我的示例来处理这个用例。这有助于解决您的问题吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-16
      • 1970-01-01
      • 2010-09-28
      • 1970-01-01
      • 2012-05-31
      相关资源
      最近更新 更多