【问题标题】:Why does java app crash in gdb but runs normally in real life?为什么java应用程序在gdb中崩溃但在现实生活中正常运行?
【发布时间】:2015-01-30 05:40:40
【问题描述】:

尝试从 gdb 运行 java app 会导致段错误,但单独运行 app 不会。这个应用程序是一个 .JAR,它使用 JOGL 和一些内存映射来与 GPU 通信。

下面的 Stacktrace 暗示了某种内存访问问题,但我不明白为什么它会在 GDB 中出现,但在现实生活中却没有。是否有一些环境因素 gdb 需要知道才能正确执行?

这个问题在 JVM OpenJDK 6 和 7 以及 Oracle JRE 7 之间仍然存在。oracle JRE 在段错误之前运行得更远一些。所有的段错误在试验之间的发生和位置上都是一致的。

Segfault 在 GPU 和驱动程序之间持续存在(!!):nvidia、radeon、fglrx current 和 fglrx beta (14.xx)。 GDB 将成功附加到我的程序的一个已经运行的实例,但是 gDEBugger 似乎不可能做到这一点,这最终是需要工作的。

实际上并没有使用 gdb 进行调试的意图。相反,我正在尝试使用 gDEBugger 来执行 OpenGL 调试。 gDEBugger 显然依赖 GDB 作为其后端的一部分,因此如果 GDB 失败,gDEBugger 也会失败。这导致尝试单独运行 gdb 以隔离问题。

gDEBugger output:
GDB String:  [Thread debugging using libthread_db enabled]  
GDB String:  Using host libthread_db library  /lib/x86_64-linux-gnu/libthread_db.so.1 .  
Thread Created: 140737353893632 (LWP: 3265)
Thread Created: 140737294624512 (LWP: 3266)
Thread Created: 140737293571840 (LWP: 3267)
Thread Created: 140737292519168 (LWP: 3268)
Thread Created: 140737155180288 (LWP: 3269)
Thread Created: 140737154127616 (LWP: 3270)
Thread Created: 140736913602304 (LWP: 3271)
Thread Created: 140736909629184 (LWP: 3272)
Thread Created: 140736908576512 (LWP: 3273)
Thread Created: 140736907523840 (LWP: 3274)
Thread Created: 140736906471168 (LWP: 3275)
Thread Created: 140736905418496 (LWP: 3276)
Thread Created: 140736278275840 (LWP: 3277)
Thread Created: 140736272963328 (LWP: 3278)
Thread Created: 140736271910656 (LWP: 3279)
Thread Created: 140736270857984 (LWP: 3280)
Thread Created: 140736269805312 (LWP: 3281)
Thread Created: 140737287657216 (LWP: 3285)
Thread Created: 140736261945088 (LWP: 3289)
GDB String:  [Thread 0x7fffb6e67700 (LWP 3289) exited]  
Thread Created: 140736261945088 (LWP: 3290)
API Connection Established: gDEBugger Servers Manager
Thread Created: 140736234641152 (LWP: 3291)
GDB String:  [Thread 0x7fffb6e67700 (LWP 3290) exited]  
API Connection Established: gDEBugger OpenGL Server
GDB String:  [Thread 0x7fffb77e8700 (LWP 3279) exited]  
GDB String:  [Thread 0x7fffb76e7700 (LWP 3280) exited]  
Debug String: gDEBugger OpenGL Server was initialized
Thread Created: 140736270857984 (LWP: 3292)
Thread Created: 140735692441344 (LWP: 3294)
Thread Created: 140735582430976 (LWP: 3295)
Thread Created: 140735574038272 (LWP: 3296)
OpenGL Render Context 1 Created
Signal: SIGSEGV
Process Exit


$ java -versionjava version "1.6.0_33"
OpenJDK Runtime Environment (IcedTea6 1.13.5) (6b33-1.13.5-1ubuntu0.14.04)
OpenJDK 64-Bit Server VM (build 23.25-b01, mixed mode)

$ gdb -version
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1

$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=14.04
DISTRIB_CODENAME=trusty
DISTRIB_DESCRIPTION="Ubuntu 14.04.1 LTS"

$ fglrxinfo
display: :0.0  screen: 0
OpenGL vendor string: Advanced Micro Devices, Inc.
OpenGL renderer string: AMD Radeon HD 5570     
OpenGL version string: 4.4.12967 Compatibility Profile Context 14.20


$ gdb --args java -jar RunMe.jar
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from java...Reading symbols from /usr/lib/debug//usr/lib/jvm/java-6-openjdk-amd64/jre/bin/java...done.
done.
(gdb) show configuration
This GDB was configured as follows:
   configure --host=x86_64-linux-gnu --target=x86_64-linux-gnu
             --with-auto-load-dir=$debugdir:$datadir/auto-load
             --with-auto-load-safe-path=$debugdir:$datadir/auto-load
             --with-expat
             --with-gdb-datadir=/usr/share/gdb (relocatable)
             --with-jit-reader-dir=/usr/lib/gdb (relocatable)
             --without-libunwind-ia64
             --with-lzma
             --with-python=/usr (relocatable)
             --with-separate-debug-dir=/usr/lib/debug (relocatable)
             --with-system-gdbinit=/etc/gdb/gdbinit
             --with-zlib
             --without-babeltrace
(gdb) run
Starting program: /usr/bin/java -jar RunMe.jar
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
process 6866 is executing new program: /usr/lib/jvm/java-6-openjdk-amd64/jre/bin/java
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff7fc4700 (LWP 6870)]
[New Thread 0x7ffff486c700 (LWP 6871)]
[New Thread 0x7ffff476b700 (LWP 6872)]
[New Thread 0x7ffff466a700 (LWP 6873)]
[New Thread 0x7fffea2d6700 (LWP 6874)]
[New Thread 0x7fffea1d5700 (LWP 6875)]
[New Thread 0x7fffea0d4700 (LWP 6876)]
[New Thread 0x7fffe9d0a700 (LWP 6877)]
[New Thread 0x7fffe9c09700 (LWP 6878)]
[New Thread 0x7fffe9b08700 (LWP 6879)]
[New Thread 0x7fffe9a07700 (LWP 6880)]
[New Thread 0x7fffe9906700 (LWP 6881)]
...
[New Thread 0x7fffe8110700 (LWP 6882)]
[New Thread 0x7fffe3169700 (LWP 6883)]
[New Thread 0x7fffe3068700 (LWP 6884)]
[New Thread 0x7fffe2f67700 (LWP 6885)]
[New Thread 0x7fffe2e66700 (LWP 6886)]
[New Thread 0x7fffe2d65700 (LWP 6887)]
[Thread 0x7fffe2d65700 (LWP 6887) exited]
[New Thread 0x7fffe2d65700 (LWP 6891)]
[Thread 0x7fffe2d65700 (LWP 6891) exited]
[New Thread 0x7fffe2d65700 (LWP 6895)]
[Thread 0x7fffe2d65700 (LWP 6895) exited]
[New Thread 0x7fffe2d65700 (LWP 6896)]
[New Thread 0x7fffe0efd700 (LWP 6897)]
libEGL warning: DRI2: failed to authenticate
[New Thread 0x7fff9799f700 (LWP 6898)]
[New Thread 0x7fff9719e700 (LWP 6899)]
[New Thread 0x7fff9699d700 (LWP 6900)]
[Thread 0x7fffe2d65700 (LWP 6896) exited]
[New Thread 0x7fffe2d65700 (LWP 6901)]
[New Thread 0x7fffe01ab700 (LWP 6902)]
[New Thread 0x7fff92f00700 (LWP 6903)]
[New Thread 0x7fff92dff700 (LWP 6904)]
[New Thread 0x7fff92cfe700 (LWP 6905)]
Setting up sound system...[New Thread 0x7fff92bfd700 (LWP 6906)]

[New Thread 0x7fff92afc700 (LWP 6907)]
[New Thread 0x7fff929fb700 (LWP 6908)]
[New Thread 0x7fff928fa700 (LWP 6909)]
[New Thread 0x7fff927f9700 (LWP 6910)]
[New Thread 0x7fff926f8700 (LWP 6911)]
[New Thread 0x7fff925f7700 (LWP 6912)]

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fffe2f67700 (LWP 6885)]
0x00007ffff6b3a770 in acl_CopyRight ()
   from /usr/lib/jvm/java-6-openjdk-amd64/jre/lib/amd64/server/libjvm.so
(gdb) where
#0  0x00007ffff6b3a770 in acl_CopyRight ()
   from /usr/lib/jvm/java-6-openjdk-amd64/jre/lib/amd64/server/libjvm.so
#1  0x00007ffff6d51309 in Unsafe_CopyMemory2 (env=<optimized out>, 
    unsafe=<optimized out>, srcObj=0x0, srcOffset=140737008618496, dstObj=0x0, 
    dstOffset=140737006779392, size=1024)
    at /build/buildd/openjdk-6-6b33-1.13.5/build/openjdk/hotspot/src/share/vm/prims/unsafe.cpp:689
#2  0x00007fffed011790 in ?? ()
#3  0x0000000000000400 in ?? ()
#4  0x0000000000000000 in ?? ()
Warning: the current language does not match this frame.
(gdb) quit
A debugging session is active.

    Inferior 1 [process 6866] will be killed.

Quit anyway? (y or n) y

更新:改用 AMD CodeXL(基本上是最新形式的 gDEBugger),情况没有太大变化。

【问题讨论】:

    标签: java opengl segmentation-fault gdb jogl


    【解决方案1】:

    为什么java应用程序在gdb中崩溃,但在现实生活中运行正常?

    因为它实际上并没有崩溃。

    Java 使用推测加载。如果指针指向可寻址内存,则加载成功。很少有指针不指向可寻址内存,尝试加载会生成SIGSEGV ... java 运行时拦截,使内存再次可寻址,并重新启动加载指令。

    在调试java程序时,一般要这样做:

    (gdb) handle SIGSEGV nostop noprint pass
    

    不幸的是,如果涉及一些 JNI 代码,并且 那个 代码SIGSEGVs,GDB 也会很高兴地忽略该信号,从而导致劣质(被调试)进程死亡。对于后一个问题,我还没有找到可接受的解决方案。

    【讨论】:

    • 在尝试您的handle 命令后,我确认它解决了 GDB 中的问题。
    • 我在尝试在某个热点功能设置断点时遇到了同样的问题,它运行良好。但是设置捕捉点,例如catch syscall futex 并打印其 backtrace 仍然与 frame.c:534: internal-error: frame_id get_frame_id(frame_info*): Assertion 'fi-&gt;level == 0' failed. 崩溃。你知道如何解决这个问题吗?
    • 我认为这是一个单独的问题...stackoverflow.com/questions/54365079/…
    • 对安装特定 sigsegv 处理程序的 Ada 本机代码特别讨厌。 JVM 导致的每个后续 SIGSEGV 都会使应用程序崩溃...
    • 感谢您指出问题原因。但是在我的情况下,我知道我的程序稍后会在本机 .so 文件中出现真正的分段错误......如何调试这个?
    【解决方案2】:

    对于已接受答案的单个评论来说太长了。它基本上是链接引用以供将来参考(以防页面消失)。

    你们中的一些人可能会对第 2 部分感兴趣。

    目录

    1. 小技巧
    2. 原因/文档
    3. 本机代码和 JVM 之间的信号链

    0。小技巧

    解决该问题的一种方法可能是使用以下 JVM 启动指令强制 JVM 在出错时调用 GDB 控制台(请参阅this blog page from Alexey Pirogov,也可以在 Oracle Java doc 中找到它以及几个使用示例):

    -XX:OnError="gdb - %p"
    

    p 将替换为 PID。

    下面blog post 的示例输出。从我读到的内容来看,JVM 似乎能够判断给定的 SIGSEGV 是由 Java 引起的(并静默使用),还是来自 (C++) 库。 据我了解,这意味着 GDB 会话将从“合法”的 SIGSEGV 事件开始,并具有正确的上下文。

    # A fatal error has been detected by the Java Runtime Environment:
    #
    #  SIGSEGV (0xb) at pc=0x00007f7348cba806, pid=10055, tid=10057
    #
    # JRE version: OpenJDK Runtime Environment (10.0.2+13) (build 10.0.2+13->    Ubuntu-1ubuntu0.18.04.4)
    # Java VM: OpenJDK 64-Bit Server VM (10.0.2+13-Ubuntu-1ubuntu0.18.04.4, mixed mode, tiered, compressed oops, g1 gc, linux-amd64)
    # Problematic frame:
    # C  [libJNIDemo.so+0x806]  Java_jnidemo_JNIDemoJava_nativeCrash+0x1c
    #
    ...
    (gdb)
    

    我发现this SO answer 中的陈述与Oracle Java doc 描述不一致,但我更愿意相信Oracle 文档。

    1。原因/文档

    我找到了这个链接https://www.ateam-oracle.com/why-am-i-seeing-sigsegv-when-i-strace-a-java-application-on-linux

    它为 JVM 幕后实现提供了一些见解。

    JVM 是一个多线程进程,因此在后台它使用信号来执行操作系统级别的线程。

    但是 JVM 也在做大量其他非常聪明的事情;例如,在常规 C/C++ 程序中 [emphazis mine] 当您期望指向某个结构的指针时遇到 NULL(零)会导致您的应用程序崩溃。正如您现在可能猜到的那样,该崩溃实际上是操作系统向您的进程发送信号 - 特别是 SIGSEGV。如果您的应用程序没有为该信号注册信号处理程序(并且 99.5% 的 c/c++ 应用程序没有),那么信号会返回到操作系统,然后操作系统会终止应用程序并(通常)保存内存状态到核心文件中。

    JVM 确实为 SIGSEGV 注册了一个信号处理程序,而不仅仅是因为它不想在出现问题时崩溃。 JVM 为 SIGSEGV 注册了一个信号处理程序,因为它实际上将 SIGSEGV 和一堆其他信号用于自己的目的。 [强调我的]

    [...] 这是完全正常且完全安全的。

    上面的链接也指向这个https://docs.oracle.com/javase/7/docs/webnotes/tsg/TSG-VM/html/signals.html

    信号

    • SIGSEGV、SIGBUS、SIGFPE、SIGPIPE、SIGILL

      在实现中用于隐式空值检查等。

    • 退出

      线程转储支持:在标准错误流中转储 Java 堆栈跟踪。 (可选)

    • SIGTERM、SIGINT、SIGHUP

      用于支持VM异常终止时的关闭钩子机制(java.lang.Runtime.addShutdownHook)。 (可选)

    • SIGUSR1

      在 java.lang.Thread.interrupt 方法的实现中使用。 (可配置。)从 Solaris 10 OS 开始不使用。在 Linux 上保留。

    • SIGUSR2

      内部使用。 (可配置。)从 Solaris 10 操作系统开始不使用。

    • SIGABRT

      HotSpot VM 不处理此信号。相反,它在致命错误处理后调用 abort 函数。如果应用程序使用此信号,那么它应该终止进程以保留预期的语义。

    2。与信号链相关的报价

    Oracle 链接表明可以采取一些措施来更好地处理 JVM 和非 Java 代码之间的信号。这称为信号链

    注意: 我不知道它是否有效,以及在调试 Java 应用调用的库时是否有任何积极作用。

    我认为在 GDB 会话期间拦截“正确”信号无济于事。但也许使用自定义处理程序代码 + 断点可以?

    据我了解,它似乎适合嵌入 JVM 的本机应用程序,而不是嵌入本机库的 JVM 应用程序。为了完整起见,我在此处保留引号

    引用:

    如果具有本机代码的应用程序需要自己的信号处理程序,那么它可能需要与信号链工具一起使用。

    应用程序可以在libc/libthread/libpthread 之前链接和加载libjsig.so 共享库。该库可确保拦截诸如signal()sigset()sigaction() 之类的调用,以便在处理程序与Java HotSpot VM 已安装的处理程序冲突时,它们实际上不会替换Java HotSpot VM 的信号处理程序。相反,这些调用保存了新的信号处理程序,或者将它们链接到 VM 安装的处理程序后面。在执行期间,当这些信号中的任何一个被引发并且发现不是针对 Java HotSpot VM 时,就会调用预安装的处理程序。

    建议的程序:

    执行这两个过程之一以使用libjsig.so 共享库。

    1. 将其与创建/嵌入 HotSpot VM 的应用程序链接[备注:因此这与从 Java 应用程序加载的库无关...],例如:

      cc -L libjvm.so-directory -ljsig -ljvm java_application.c
      
    2. 使用LD_PRELOAD 环境变量,例如[参见https://stackoverflow.com/questions/426230/what-is-the-ld-preload-trick]:

      export LD_PRELOAD=libjvm.so-directory/libjsig.so; java_application (ksh)
      
      setenv LD_PRELOAD libjvm.so-directory/libjsig.so; java_application (csh)
      

    插入的 signal()sigset()sigaction() 返回保存的信号处理程序,而不是 Java HotSpot VM 安装且操作系统可以看到的信号处理程序。

    请注意,SIGUSR1 不能被链接。

    1

    【讨论】:

      猜你喜欢
      • 2020-02-01
      • 2013-01-30
      • 2011-11-22
      • 2015-03-31
      • 1970-01-01
      • 1970-01-01
      • 2019-01-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多