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

时间:2014-12-02 03:53:37

标签: java opengl segmentation-fault gdb jogl

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

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

这个问题在JVM OpenJDK 6和7以及Oracle JRE 7之间仍然存在.Oracle JRE在segfault之前运行起来稍微远一点。所有段错误在试验之间的发生和位置都是一致的。

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

UPDATE:切换到AMD CodeXL(基本上是最新形式的gDEBugger),情况没有太大变化。

2 个答案:

答案 0 :(得分:33)

  

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

因为它实际上没有崩溃。

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

调试java程序时,通常必须这样做:

(gdb) handle SIGSEGV nostop noprint pass

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

答案 1 :(得分:0)

这个时间太长,无法对已接受的答案发表任何评论。它的基本链接引用以供将来参考(以防页面消失)。

您中的某些人可能会对第2部分感兴趣。

目录

  1. 小把戏
  2. 原因/文档
  3. 本机代码与JVM之间的信号链接

0。小把戏

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

-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是一个多线程进程,因此在幕后它使用信号进行OS级线程化。

但是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

    在实现中用于隐式null检查,等等。

  • SIGQUIT

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

  • SIGTERM,SIGINT,SIGHUP

    用于在虚拟机异常终止时支持关闭挂钩机制(java.lang.Runtime.addShutdownHook)。 (可选。)

  • SIGUSR1

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

  • SIGUSR2

    内部使用。 (可配置。)从Solaris 10 OS开始不使用。

  • SIGABRT

    HotSpot VM无法处理此信号。而是在致命错误处理后调用中止函数。如果应用程序使用此信号,则应终止该过程以保留预期的语义。

2。与信号链接相关的报价

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

注意: 我不知道它是否有效,并且在调试Java应用程序调用的库时是否有积极作用。

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

从我的理解来看,它似乎适合于嵌入JVM的本机应用程序,而不适合嵌入了本机库的JVM应用程序。我在那里保留报价以求完善

报价:

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

应用程序可以在libjsig.so / libc / libthread之前链接和加载libpthread共享库。该库可确保拦截signal()sigset()sigaction()之类的调用,以便在处理程序与Java HotSpot VM的信号处理程序冲突时,它们实际上不会替换Java HotSpot VM的信号处理程序。 Java HotSpot虚拟机。而是,这些调用保存新的信号处理程序,或将它们链接到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