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

des4xlb0  于 2021-06-27  发布在  Java
关注(0)|答案(2)|浏览(376)

尝试从gdb运行java应用程序会导致segfault,但单独运行应用程序不会。这个应用程序是一个.jar,它使用jogl和一点内存Map来与gpu对话。
下面的stacktrace暗示了某种内存访问问题,但我不明白为什么它会在gdb中出现,而不是在现实生活中。gdb是否需要知道一些环境因素来允许正确的执行?
这个问题在jvms openjdk6和7以及oraclejre7之间仍然存在。oraclejre在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的最新形式),情况并没有太大变化。

oug3syen

oug3syen1#

这是太长了,一个单一的评论接受的答案。它的基本链接引用以备将来参考(以防页面消失)。
你们中的一些人可能会对第二部分感兴趣。

目录

小把戏
理由/文件
本机代码和jvm之间的信号链

0. 小把戏

解决这个问题的一种方法是使用以下jvm启动指令强制jvm在出错时调用gdb控制台(请参阅alexey pirogov的这个博客页面,该页面也可以在oraclejava文档中找到,并提供几个使用示例):

-XX:OnError="gdb - %p"
``` `p` 将替换为pid。
下面博客文章的输出示例。据我所知,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)

我发现这个答案中的语句与oraclejavadoc的描述不一致,但我宁愿相信oracledoc。

## 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
用于隐式空检查的实现,等等。
西格奎特
线程转储支持:在标准错误流中转储java堆栈跟踪(可选。)
sigterm,sigint,sighup
用于在vm异常终止时支持关闭挂钩机制(java.lang.runtime.addshutdownhook)(可选。)
信号1
在java.lang.thread.interrupt方法的实现中使用(从solaris 10操作系统开始不使用。在linux上保留。
信号2
内部使用(从solaris 10操作系统开始不使用。
西格布特
热点vm不处理此信号。相反,它在处理致命错误后调用abort函数。如果应用程序使用这个信号,那么它应该终止进程以保留预期的语义。

## 2. 与信号链相关的报价

oracle链接表明可以采取一些措施来更好地处理jvm和非java代码之间的信号。这被称为信号链。
note:i do 不知道它是否有效,以及在使用java应用程序调用的库进行调试时是否有任何积极影响。
我认为这无助于在gdb会话期间拦截“正确”的信号。但是,如果使用自定义处理程序代码+断点,它可能吗?
据我所知,它似乎适合于一个本机应用程序,它将embbed一个jvm,而不是jvm应用程序embbed一个本机库。我在那里保留了完整的报价
引用:
如果具有本机代码的应用程序需要自己的信号处理程序,那么它可能需要与信号链接工具一起使用。
应用程序可以链接并加载 `libjsig.so` 以前的共享库 `libc` / `libthread` / `libpthread` . 此库确保 `signal()` ,  `sigset()` ,和 `sigaction()` 如果处理程序与javahotspot vm已经安装的处理程序冲突,则它们不会实际替换javahotspot vm的信号处理程序。相反,这些调用保存新的信号处理程序,或者将它们链接到vm安装的处理程序后面。在执行期间,当这些信号中的任何一个被触发并且发现不是针对javahotspot vm时,就会调用预安装的处理程序。
拟议程序:
执行以下两个步骤之一以使用 `libjsig.so` 共享库。
将其与创建/嵌入热点vm的应用程序链接[备注:因此这与从java应用程序加载的库无关…],例如:

cc -L libjvm.so-directory -ljsig -ljvm java_application.c

使用 `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热点vm安装并由操作系统看到的信号处理程序。
请注意 `SIGUSR1` 无法链接。
1
w1jd8yoj

w1jd8yoj2#

为什么java应用程序在gdb中崩溃,但在现实生活中却正常运行?
因为它实际上不会崩溃。
java使用推测性加载。如果指针指向可寻址内存,则加载成功。指针很少不指向可寻址内存,尝试的加载会生成 SIGSEGV ... 哪个java运行时截取,使内存再次可寻址,并重新启动load指令。
调试java程序时,通常必须执行以下操作:

(gdb) handle SIGSEGV nostop noprint pass

不幸的是,如果涉及到一些jni代码 SIGSEGV s、 gdb也会很高兴地忽略该信号,从而导致低级(正在调试的)进程的死亡。对于后一个问题,我还没有找到一个可以接受的解决办法。

相关问题